8

I'll like to add this functionality to the autocomplete widget in Entityreference field to show node ID next to the titles in the dropdown list. The reason behind the idea is to differentiate between multiple nodes with same title.

Example:

  • This is a title (3)
  • This is a title (2)
  • This is a title (1)

I know the node ID is shown once a selection is made but I like to show it in the dropdown list in order to pick the right node quickly based on the node ID.

2

3 Answers 3

21
+500

Install the Views and Entity Reference modules, create a new View and Add a Entity Reference Display:

enter image description here

Then Add in the fields the content title and the nid, click in the nid and check Exclude from display, Save and click in the title and go to rewrite the output of the title as [title] - ([nid])

enter image description here enter image description here

Go to edit the settings of the format and check the title, this will allow you to search by title.

enter image description here

Save the view.

Go to edit your Entity Reference field and select in the Mode Views: .... (like the following image) and select your View (in this case the name is: articles_with_id) and save the settings:

enter image description here

Then go to see the result:

enter image description here

EDIT: This is now working in Drupal 8, at least in the version 8.3.4.

17
  • 3
    OMG, I always wondered what the views option was for. This is filthy!!!
    – No Sssweat
    Jan 25, 2016 at 7:23
  • 1
    @NoSssweat I'm learning english now, can you provide me a synonym of filthy please? I can't understand the phrase "This is filthy" Jan 25, 2016 at 14:36
  • 3
    No, it means it's a really good/impressive solution. Ex: Alexander Nylander's Filthy Shootout Goal
    – No Sssweat
    Jan 25, 2016 at 19:49
  • 2
    @AdrianCidAlmaguer I agree this solution is "sick"! (idiom)
    – John R
    Feb 2, 2016 at 10:55
  • 3
    The only problem with this solution, is that the entity reference field, once selected, shows the ID twice in the entity edit form, because it is included by default once it is selected.
    – Yuri
    May 26, 2016 at 15:29
5

Create Entity Reference field with the default configuration

enter image description here

The function entityreference_autocomplete_callback_get_matches determines what the output of the autocomplete should be.

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);
    $has_view_access = (entity_access('view', $entity_type, $entity) !== FALSE);
    $has_update_access = (entity_access('update', $entity_type, $entity) !== FALSE);
    if (!$entity || !($has_view_access || $has_update_access)) {
      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 .' - ('. $entity_id . ')</div>';//****
      }
    }
  }
  drupal_json_output($matches);
}

the last line $matches[$prefix . $key] = '<div class="reference-autocomplete">' determines the output and the $entity_id is available which is the ID. You can do what I did in that line (shown by **), simply write:

 $matches[$prefix . $key] = '<div class="reference-autocomplete">' . $label .' - ('. $entity_id . ')</div>';

you can use $entity_id to fetch other fields and anything you want.

One More Thing!

Some times it is not a good idea to change the function of the core module (if it is not important to you the above solution is enough).

If you need to override the core function of the entity_reference module, build a small module and name it elabel

it is elabel.info

;$Id;
name = My Entity Reference Label
description = This module creates special Entity Reference Label
package = My Modules
core = 7.x
php = 5.1
files[] = elabel.module

and it is elabel.module

<?php function elabel_menu_alter(&$items){
    unset($items['entityreference/autocomplete/single/%/%/%']);
    unset($items['entityreference/autocomplete/tags/%/%/%']);

      $items['entityreference/autocomplete/single/%/%/%'] = array(
    'title' => 'Entity Reference Autocomplete',
    'page callback' => 'elabel_autocomplete_callback',
    'page arguments' => array(2, 3, 4, 5),
    'access callback' => 'entityreference_autocomplete_access_callback',
    'access arguments' => array(2, 3, 4, 5),
    'type' => MENU_CALLBACK,
  );

    $items['entityreference/autocomplete/tags/%/%/%'] = array(
    'title' => 'Entity Reference Autocomplete',
    'page callback' => 'elabel_autocomplete_callback',
    'page arguments' => array(2, 3, 4, 5),
    'access callback' => 'entityreference_autocomplete_access_callback',
    'access arguments' => array(2, 3, 4, 5),
    'type' => MENU_CALLBACK,
  );
  return $items;

}

function elabel_autocomplete_callback($type, $field_name, $entity_type, $bundle_name, $entity_id = '', $string = '') {
  // If the request has a '/' in the search text, then the menu system will have
  // split it into multiple arguments and $string will only be a partial. We want
  //  to make sure we recover the intended $string.
  $args = func_get_args();
  // Shift off the $type, $field_name, $entity_type, $bundle_name, and $entity_id args.
  array_shift($args);
  array_shift($args);
  array_shift($args);
  array_shift($args);
  array_shift($args);
  $string = implode('/', $args);

  $field = field_info_field($field_name);
  $instance = field_info_instance($entity_type, $field_name, $bundle_name);

  return elabel_autocomplete_callback_get_matches($type, $field, $instance, $entity_type, $entity_id, $string);
}

function elabel_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);
    $has_view_access = (entity_access('view', $entity_type, $entity) !== FALSE);
    $has_update_access = (entity_access('update', $entity_type, $entity) !== FALSE);
    if (!$entity || !($has_view_access || $has_update_access)) {
      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 .'('.$entity_id.')' .'</div>';
      }
    }
  }

  drupal_json_output($matches);
}

I tried this code and it works perfectly If there are other type of entity references and you don't need to do this for them, simply add an IF statement and check for bundle or content type.

1
(function ($, Drupal, drupalSettings) {
  'use strict';

  Drupal.behaviors.my_module = {
    attach: function (context) {

      function autocomplete_tid_remove(field) {

        var autocomplete_tid = $(field).val();
        var autocomplete_tid_stripped = autocomplete_tid.replace(/(\([^*]+\))(?=.*\1)/,'');
        $(field).val(autocomplete_tid_stripped);

      }

      jQuery('.form-autocomplete').on('click', function () {
        window.currentElement = jQuery(this);
      });

      jQuery('.form-autocomplete', context).once('my_module').on('autocompleteclose', function () {
        autocomplete_tid_remove(currentElement);
      });
    }
  };

})(jQuery, Drupal, drupalSettings);

@Adrian Cid Almaguer answer is great! But shows nid twice as mentioned by @yuri. I was able to overcome this using js.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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