Drupal User Entity Reference Field with Custom Autocomplete that uses an Address Field

Category: 

User reference fields (aka entity reference fields) are great. As you may have guessed, we can use these fields to reference users on other entities (e.g. nodes).

Say we had a user reference field on the Article content type... by default, when selecting a user to reference, we could configure the field widget to be an autocomplete. This allows us to begin typing the user's login name as a way to reference them. This works well in most cases.

What if we had an address field on our user entities, and collected the user's first and last names? It may be more usable for site administrators to be able to search across the user's actual name instead of their user name for logging in.

We can use hook_menu(), hook_form_BASE_FORM_ID_alter(), and a custom callback function to provide a custom autocomplete widget that searches across our address field's values instead...

/**
 * Implements hook_menu().
 */
function my_module_menu() {
  $items = array();
  $items['my_module/autocomplete'] = array(
    'page callback' => 'my_module_field_users_autocomplete',
    'access arguments' => array('access user profiles'),
    'type' => MENU_CALLBACK
  );
  return $items;
}

/**
 * Implements hook_form_BASE_FORM_ID_alter().
 */
function my_module_form_node_form_alter(&$form, &$form_state, $form_id) {
  $field_name = 'field_users'; // Change this to your user reference field's machine name.
  if (!isset($form[$field_name])) { return; }
  // Attach a custom autocomplete path to our user's field so we can search
  // by address field first and last names.
  foreach($form[$field_name]['und'] as $delta => $element) {
    if (!is_numeric($delta)) { continue; }
    $form[$field_name]['und'][$delta]['target_id']['#autocomplete_path'] = 'my_module/autocomplete';
  }
}

/**
 *
 */
function my_module_field_users_autocomplete($string) {
  $field_name = 'field_address'; // Change this to your address field's machine name.
  $column_name = $field_name . '_name_line';
  $matches = array();
  $query = db_select('users', 'u');
  $query->innerJoin(
    'field_data_' . $field_name,
    'address',
    "address.entity_id = u.uid AND address.bundle = 'user'"
  );
  $query->condition("address.$column_name", '%' . db_like($string) . '%', 'LIKE');
  $query->condition('u.uid', 0, '<>');
  $query->fields('u', array('uid', 'name'));
  $query->fields('address', array($column_name));
  $query->range(0, 10);
  $results = $query->execute();
  foreach ($results as $row) {
    $key = $row->{$column_name} . " ($row->uid)";
    $matches[$key] = check_plain($row->{$column_name});
  }
  drupal_json_output($matches);
}

Now when we reference users on an article, we can type their real name, instead of having to guess their user name. Neat-o!

Comments

Why not just use Real Name module, which already solves this use case? What are the advantages of this approach?

tyler's picture

I hadn't heard of Real Name until you mentioned it. It sounds like it has a different use case, as in it doesn't appear to be related to the Address Field in any way.

Interesting module though, I could see that being beneficial if additional address information wasn't required.

Add new comment