I have a use case where the content author would like to search for an entity through the entity reference autocomplete widget but not by the title field. They would like to search for results by another field on the entity. It would be very nice to not have to completely override the autocomplete callback function but instead have an alter at the end of the autocomplete request that allows other modules to add their matches

eg:


function entityreference_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id = '', $string = '') {
  $matches = array();

  $entity = NULL;
  if ($entity_id !== 'NULL') {
    $entity = entity_load_single($entity_type, $entity_id);
    if (!$entity || !entity_access('view', $entity_type, $entity)) {
      return MENU_ACCESS_DENIED;
    }
  }

  $handler = entityreference_get_selection_handler($field, $instance, $entity_type, $entity);

  if ($type == 'tags') {
    // The user enters a comma-separated list of tags. We only autocomplete the last tag.
    $tags_typed = drupal_explode_tags($string);
    $tag_last = drupal_strtolower(array_pop($tags_typed));
    if (!empty($tag_last)) {
      $prefix = count($tags_typed) ? implode(', ', $tags_typed) . ', ' : '';
    }
  }
  else {
    // The user enters a single tag.
    $prefix = '';
    $tag_last = $string;
  }

  if (isset($tag_last)) {
    // Get an array of matching entities.
    $entity_labels = $handler->getReferencableEntities($tag_last, $instance['widget']['settings']['match_operator'], 10);

    // Loop through the products and convert them into autocomplete output.
    foreach ($entity_labels as $values) {
      foreach ($values as $entity_id => $label) {
        $key = "$label ($entity_id)";
        // Strip things like starting/trailing white spaces, line breaks and tags.
        $key = preg_replace('/\s\s+/', ' ', str_replace("\n", '', trim(decode_entities(strip_tags($key)))));
        // Names containing commas or quotes must be wrapped in quotes.
        if (strpos($key, ',') !== FALSE || strpos($key, '"') !== FALSE) {
          $key = '"' . str_replace('"', '""', $key) . '"';
        }
        $matches[$prefix . $key] = '<div class="reference-autocomplete">' . $label . '</div>';
      }
    }
  }

  // implment a hook to futz with autocomplete results so that other modules may add their results or alter these ones :)
  foreach (module_implements('entityreference_alter_matches') as $module) {
    $function = $module . '_entityreference_alter_matches';
    $function($matches, $field, $instance, $entity_type, $entity_id, $string);
  }

  drupal_json_output($matches);
}


Files: 
CommentFileSizeAuthor
#3 1863944-alter_autocomplete_callback-3.patch473 bytesguillaumev
PASSED: [[SimpleTest]]: [MySQL] 121 pass(es). View

Comments

rudiedirkx’s picture

I'd like this too, but of course using drupal_alter():

$context = compact('type', 'field', 'instance', 'entity_type', 'entity');
drupal_alter('entityreference_matches', $matches, $context);
mcpuddin’s picture

This would be very nice to have. The only other option I found is this roundabout way of altering the callback.

guillaumev’s picture

Status: Active » Needs review
FileSize
473 bytes
PASSED: [[SimpleTest]]: [MySQL] 121 pass(es). View

I also need this, to alter the output of the results. Here is a patch that adds the alter hook from #1

rudiedirkx’s picture

Status: Needs review » Reviewed & tested by the community

Perfect.

@mcpuddin That's smart. Did you copy all logic or use the normal function and json_decode() its result?

DamienMcKenna’s picture

MorinLuc0’s picture

Status: Reviewed & tested by the community » Needs work

This patch in #3 will not solve the above issue.

If a user already typed in a text in the entity reference field the string parameter has already been searched at the point of execution before your drupal_alter takes effect.

Essentially your list will have already been trimmed by what was searched in the entity reference against the label.

If we wanted to make the entity reference field configurable to search based on a configured field the handlers would need to be modified to access a configured parameter. Because at this point it always uses the entity label as the search key.

  /**
   * Build an EntityFieldQuery to get referencable entities.
   */
  protected function buildEntityFieldQuery($match = NULL, $match_operator = 'CONTAINS') {
    $query = new EntityFieldQuery();
    $query->entityCondition('entity_type', $this->field['settings']['target_type']);
    if (!empty($this->field['settings']['handler_settings']['target_bundles'])) {
      $query->entityCondition('bundle', $this->field['settings']['handler_settings']['target_bundles'], 'IN');
    }
    if (isset($match)) {
      $entity_info = entity_get_info($this->field['settings']['target_type']);
      if (isset($entity_info['entity keys']['label'])) {
        $query->propertyCondition($entity_info['entity keys']['label'], $match, $match_operator);
      }
    }