0

Discovered an oddity that seems inherent in the node_reference.module 7.x-2.1. It is a rather specific use-case, so I can see why it might not have come up frequently before. Just wondering if anyone has any work around ideas, either that or I'll document my findings if I reach a solution.

How to reproduce the issue:

  1. Create a reference list view, and use it to power an autocomplete field.
  2. Create another view and attempt to use the created field as a filter.
  3. Everything will work fine, unless that field has a large number of values. The field I'm using has upwards of 90,000 items.
  4. This is because the views filter UI attempts to render all possible values as a checkbox list.

The reason it trips up is because:

function _node_reference_options($field, $flat = TRUE) {

Goes on to call:

$references = node_reference_potential_references($field);

Without any options. Which leads to:

references_potential_references_view(
  $entity_type, $view_name, $display_name, $args, $options);

Being called without any options, and it seems this code does not read the pager settings to get its limit value. Instead it relies on $options['limit'].

$limit = !empty($options['limit']) ? $options['limit'] : 0;
$view->display_handler->set_option('pager', array(
  'type' => 'some', 'options' => array('items_per_page' => $limit)));

I'm not sure which bit of the code is at fault — i.e. who should definite a limit, or read the pager settings — but there definitely needs to be a defined limit and not 0. Especially when dealing with so many possible values. Setting a limit specifically for the pager in Views UI does not work as it is circumvented.

Currently I can't even get to edit my view with the filter specified, unless I hack the references module to pass:

$references = node_reference_potential_references($field, array('limit' => 10));

I'm only adding this as a filter so I can state that the field isn't empty. I could just leave the hack in until I've set the filter to a state that doesn't read the values. However this feels rather unsafe — especially if someone else was to take over the code at a future point.

1 Answer 1

0

I haven't found a neat general solution to this issue, but in terms of a specific solution you can use hook_query_alter. Basically in one of my bespoke modules I added the following:

/**
 * Implements hook_query_alter().
 */
function bespoke_module_query_alter(QueryAlterableInterface $query) {
  if ($query->hasTag('views_autocomplete_for_ref_files')) {
    $view = $query->getMetaData('view');
    if ( !empty($view) && $view->current_display == 'references_lead_image_by_default' ) {
      $query->range(0, 10);
    }
  }
}

NOTE: Awkwardly there doesn't seem to be an ability of SelectQuery to get back the current range settings. The code could be more general if it could detect that no limit was set.

Out of the above views_autocomplete_for_ref_files is the machine name of the reference list view, and references_lead_image_by_default is the name of the display. I can focus on the default display because I have code elsewhere that switches the view in use depending on certain contexts. The default display is never actually used by the site, except when it is requested by the Views UI (because the widget hook below is not used for the Views UI display).

/**
 * Implements hook_field_widget_form_alter()
 *
 * Modifies the autocomplete path for autocomplete widgets
 */
function bespoke_module_field_widget_node_reference_autocomplete_form_alter (&$element, 
    &$form_state, $context) {
  $instance = &$context['instance'];
  $field = &$context['field'];
  /// only step in if the field uses a view to populate
  if ( $field['settings']['view']['view_name'] == 'autocomplete_for_ref_files' ) {
    /// switch the view display depending on content type
    if ( (arg(0) == 'node') && ($node=node_load(arg(1))) && ($node->type) ) {
      $field['settings']['view']['display_name'] = 
        'references_lead_image_for_' . $node->type;
    }
    /// hijack the current autocomplete path to point to our own callback
    $element['nid']['#autocomplete_path'] = 
      $instance['widget']['settings']['autocomplete_path'] . '/' . 
      $instance['entity_type'] . '/' . $instance['bundle'] . '/' . $field['field_name'];
    /// hijack the autocomplete validate function, to validate with our own criteria
    $element['nid']['#element_validate'] = 
      array('bespoke_module_node_reference_autocomplete_validate');
  }
}

I'll keep looking to see if I can find a more general solution.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.