Options module invokes a hook_options_list() when a module wants to re-use its widget. For example taxonomy select lists, make use of the hook to provide an array of taxonomy terms.

However even if you only want a few terms in your select list - for example "terms that the user selected in a term reference field on their profiel" you're out of luck. If even core's default behaviour of the subtree, you always end up calling taxonomy_get_tree(), because taxonomy_options_list() calls taxonomy_allowed_values() - and even with a parent set, taxonomy_get_tree() will always get every single term in your vocabulary. So this is pretty much unusable apart from tiny vocabularies.

What I'd like to be able to do, is provide my own custom allowed values callback, but still be able to use the taxonomy_term_reference field type, and still use an options select widget. At the moment, I can define my own widget, then copy and paste a tonne of code from options.module but that's ugly as hell.

Attached patch allows field definitions to have an 'allowed_values_callback' key in $settings which is used by options module if set, and if not falls back to the field type module.

Note that user_reference and node_reference in D6 both let you use views for stuff like this, but without views in core, taxonomy module's running into the same problems - we should at least let developers mess with this stuff, even if it's not possible to expose in the UI.

Comments

catch’s picture

Status: Active » Needs review

Status: Needs review » Needs work

The last submitted patch, options_list_callback.patch, failed testing.

catch’s picture

Title: hook_options_list() is too inflexible » Allow modules to bypass taxonomy_get_tree() for term reference select lists
Component: field system » taxonomy.module
Status: Needs work » Needs review
Issue tags: +Performance
StatusFileSize
new813 bytes

Thinking about it, and following a discussion with bangpound, text and number fields don't really need this, because you set options in the field definition anyway. node and user reference can do what they like because they're in contrib, so can field modules, so it's actually just taxonomy module which needs to support this, because it builds its options list with taxonomy_get_tree(). We can't fix taxonomy_get_tree() completely, at least until D8, so this is the equivalent of variable_get('taxonomy_override_selector') for Field API.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

this is pretty much required when dealing with large vocabs.

yched’s picture

+1

catch’s picture

StatusFileSize
new814 bytes

Changed the isset() to !empty(), just in case someone sets options_list_callback to FALSE. This is the only change, so leaving RTBC.

yched’s picture

Hmm, actually a field setting should be declared with a default value (empty string ?) in hook_field_info().

catch’s picture

Status: Reviewed & tested by the community » Needs review
StatusFileSize
new989 bytes

Like this?

yched’s picture

Status: Needs review » Reviewed & tested by the community

Yup :-)

moshe weitzman’s picture

Priority: Normal » Critical
Status: Reviewed & tested by the community » Needs work

Actually, is this sufficient? We are seeing linear slowdown when creating thousands of terms. I think that taxonomy_field_validate() actually uses the full allowed values list and computing these allowed values gets more and more expensive as term count increases. We need to fix allowed values as in the OP or not use it in validation.

Upgrading priority as this is not just a UI fail anymore

yched’s picture

re #10 : whether the new setting should restrict the list of valid values depends on the feature catch is after, I guess ?

Regarding the performance cost of taxonomy_field_validate() calling taxonomy_allowed_values() calling taxonomy_get_tree() : we had similar problems in nodereference some time ago. We solved it by deferring the validate check to a helper function, accepting an optional array of candidate nids as param; the contract then becomes "filter invalid values out of the list I'm giving you, plz", which is more manageable performance-wise.

See _node_reference_potential_references_standard(), and how it's called in node_reference_options_list(), node_reference_field_validate(), node_reference_autocomplete()

I guess a similar approach could be used for taxo terms ? Might be a separate issue than the use case described in the OP, though.

catch’s picture

Status: Needs work » Reviewed & tested by the community

taxonomy_field_validate() does not call taxonomy_allowed_values() as of two months ago: #611752: taxonomy_field_validate calls taxonomy_allowed_values

http://api.drupal.org/api/function/taxonomy_field_validate/7

If that's causing scaling issues as it is now, please open a new issue.

moshe weitzman’s picture

Hmm. My slowdown must be caused by something else. This one is back to RTBC.

dries’s picture

Status: Reviewed & tested by the community » Needs review
+++ modules/taxonomy/taxonomy.module	4 Mar 2010 12:16:50 -0000
@@ -1094,7 +1095,14 @@ function taxonomy_field_widget_info_alte
 function taxonomy_options_list($field) {
-  return taxonomy_allowed_values($field);
+  if (!empty($field['settings']['options_list_callback'])) {
+    $function = $field['settings']['options_list_callback'];
+    $options = $function($field);
+  }
+  else {
+    $options = taxonomy_allowed_values($field);
+  }
+  return $options;

This looks rather hacky to me. Why not always force the callback to be set explicitly?

yched’s picture

re catch #12 : oopsie, you're right, I was on crack.

catch’s picture

@Dries. Fields via the UI are always going to use taxonomy_allowed_values(). I guess we could default to that rather than checking whether its set if Field API supports default settings (not sure if it actually does), but not sure that's any more or less hacky.

yched’s picture

Feld API does support default values for settings, whether field, instance, widget or formatter settings.

re #15-16 : that's a choice of semantics.
I think the current choice of '' as a default value, with a semantic of 'this will use the dafault : taxonomy_allowed_values()' is more future proof (allows later renaming or refactoring without needing to change the existing $field settings).

Maybe a slight code refactor of

$function = !empty($field['settings']['options_list_callback']) ? $field['settings']['options_list_callback'] : 'taxonomy_allowed_values';
$options = $function($field);

?
Matter of taste, really.

catch’s picture

StatusFileSize
new735 bytes

Ternary and returning straight from the function call lets us do it on two lines. Agreed with yched that the empty string is more robust for later changes than storing the default serialized every time.

moshe weitzman’s picture

Status: Needs review » Reviewed & tested by the community

RTBC again.

Related issue - #556842: taxonomy_get_tree() memory issues

webchick’s picture

Dries, does that last patch address your comments? I personally find a bunchofcodecrammedononelinewithaternaryoperatorincrediblyhardtoread.

dries’s picture

Status: Reviewed & tested by the community » Fixed

Alrighty then. Committed to CVS HEAD.

Status: Fixed » Closed (fixed)

Automatically closed -- issue fixed for 2 weeks with no activity.

les lim’s picture