Changing user display name in Drupal 7 and Drupal 8

Wed, 31/08/2016 - 21:15

Every website that displays user information on the front end will use profile fields such as first and last names for representing the members. By default Drupal shows only the username, which is definitely something you will want to change.

Modifying this is relatively simple. You could always choose which fields to use in Views, Rules and other modules. However this approach has several downsides:

  • You will need to manually configure this for all new modules.
  • There will be more elements to maintain.
  • Configuration will be stored in the database. Granted, you could export some of the settings (e.g. Rules or Views), but this is far from an optimal solution.
  • It's not possible to do some processing (e.g. set proper capitalization rules for first/last names) or change this dynamically depending on current user's role or section of the site.

The right way is to change the way user display names are formatted by the system itself. This article will show you how to manage this for both Drupal 7 and Drupal 8 in your custom code.

Base setup

For both versions of Drupal I will assume that you have the following fields added to the user entity:

  • Title (machine name: title)
  • First name (machine name: field_first_name)
  • Last name (machine name: field_last_name)

You can configure this at /admin/config/people/accounts/fields for both versions of Drupal.

Our end goal will be to show user's display name in the following format: [title] [first name] [last name]. We will use the t() function to format the output for two reasons:

  • By passing the user entered values with the @ placeholder, we will automatically sanitize user input. t() will run the values through check_plain() which will make sure that everything is safe for display on the website.
  • This will allow us to have different order for different languages, because the format will be stored as a translatable string. This means that you could have "Mr. John Doe" format for English, but "Mr. Doe John" for German language, without any changes in your code. Again, this is a good development practice, even if your language is only in English.

Also, to make this really usable you might want to enforce correct casing. I will keep this simple by converting the input to lowercase and capitalizing only the first letter. You might want to add a more robust check and see if only some characters are uppercased (e.g. "MacMillan"), in which case you would skip converting the string to lowercase and just capitalizing the first character.

In addition, we will use Drupal's alternatives for PHP's native functions ucfirst() and strtolower() because they will take unicode characters into account. This is a good development practice, even when your website is only in English language.

Lastly, you can add any other logic to format the display name: "protect" the username and hide it for roles that cannot access user profiles, include user IDs or add mailto links on administrative pages.

Onto the code!

Drupal 7

To show the name of the user, you need to use the format_username() function. You just need to pass the user object to it, and it will return formatted name of the user. With a clean Drupal install, it will just show the username:

$account = user_load(1);
$display_name = format_username($account);
// Returns: admin (or however you named the superuser).

To change this you will need to implement hook_username_alter() in a custom module. Since the $name argument is passed by reference, all you have to do is to store the new display name in the $name variable.

Here's the most basic example:

/**
 * Implements hook_username_alter().
 */
function YOUR_MODULE_username_alter(&$name, $account) {
  $name = t('@username (@uid)', array(
    '@uid' => $account->uid,
    '@username' => $account->name,
  ));
}

You can also make this more complex and use profile fields. Below is an example that takes user's first name, last name and title, and puts everything together in correct casing:

/**
 * Implements hook_username_alter().
 */
function YOUR_MODULE_username_alter(&$name, $account) {
  // Make sure that this user has all elements set.
  if (
    isset($account->field_title[LANGUAGE_NONE][0]['value']) &&
    isset($account->field_first_name[LANGUAGE_NONE][0]['value']) &&
    isset($account->field_last_name[LANGUAGE_NONE][0]['value'])
  ) {
    $title = $account->field_title[LANGUAGE_NONE][0]['value'];
    // Make sure that we are using the corect casing for first/last names.
    $first_name = drupal_ucfirst(drupal_strtolower($account->field_first_name[LANGUAGE_NONE][0]['value']));
    $last_name = drupal_ucfirst(drupal_strtolower($account->field_last_name[LANGUAGE_NONE][0]['value']));
    // Finally, format the display name.
    $name = t('@title @first_name @last_name', array(
      '@title' => $title,
      '@first_name' => $first_name,
      '@last_name' => $last_name,
    ));
  }
}

While this will work fine on the front end and in admin lists, we are not done yet. By changing the format of the username, the return value of [user:name] token will also be altered. This is fine for the most part, except when it comes to the email notifications. In certain messages users will need to receive their username in order to login to the website. If they see "Mr. John Doe" (display name) instead of "john.doe" (real username), the website will not let them login.

To fix this, we will need to provide a new token in our custom module that will return the real username. Create the YOUR_MODULE.tokens.inc file and add the following code:

<?php

/**
 * @file
 * Builds placeholder replacement tokens.
 */

/**
 * Implements hook_token_info().
 */
function YOUR_MODULE_token_info() {
  // Real username, not formatted user's name.
  $info['tokens']['user']['username'] = array(
    'name' => t('Real username'),
    'description' => t('Real username.'),
  );

  return $info;
}

/**
 * Implements hook_tokens().
 */
function YOUR_MODULE_tokens($type, $tokens, array $data = array(), array $options = array()) {
  $replacements = array();
  switch ($type) {
    // Username replacements.
    case 'user':
    case 'account':
    case 'current-user':
      foreach ($tokens as $name => $original) {
        switch ($name) {
          case 'username':
            $replacements[$original] = $data['user']->name;
            break;
        }
      }
      break;
  }

  return $replacements;
}

After this all you have to do is to go to /admin/config/people/accounts and find all the lines where [user:name] is mentioned as the username and replace it with [user:username]. The following tabs use this by default:

  • Welcome (new user created by administrator)
  • Welcome (no approval required)
  • Account activation

If you are using the [user:name] token somewhere else (e.g. Commerce, Rules, etc.), you should double-check the configuration and update the token if necessary.

Links:

Drupal 8

Doing this in Drupal 8 is pretty similar to Drupal 7, with the exception of having to provide your own custom token for the real username. This is already included in the core: [user:display-name] will show formatted name of the user, while [user:name] will show the username necessary for logging in to the website.

To get the display name of an account, just use the user_format_name() function. Here's the code:

$account = user_load(1);
$display_name = user_format_name($account);
// Returns: admin (or however you named the superuser).

The hook_username_alter() has been renamed to hook_user_format_name_alter(), but otherwise functions in the same way. To keep things consistent, here's a simple example that shows how to include user ID right next to the username:

/**
 * Implements hook_user_format_name_alter().
 */
function YOUR_MODULE_user_format_name_alter(&$name, $account) {
  $name = t('@username (@uid)', array(
    '@uid' => $account->uid,
    '@username' => $account->name,
  ));
}

Now let's format the username in a more complex way: we will get first name, last name and title, and make sure that the correct casing is enforced. Do pay attention that the drupal_strtolower() and drupal_ucfirst() functions have been moved within the Unicode class.

use \Drupal\Component\Utility\Unicode;

/**
 * Implements hook_user_format_name_alter().
 */
function YOUR_MODULE_user_format_name_alter(&$name, $account) {
  $member = \Drupal\user\Entity\User::load($account->id());
  // Make sure that this user has all elements set.
  if (
    !empty($member->field_title->value) &&
    !empty($member->field_first_name->value) &&
    !empty($member->field_last_name->value)
  ) {
    $title = $member->field_title->value;
    // Make sure that we are using the corect casing for first/last names.
    $first_name = Unicode::ucfirst(Unicode::strtolower($member->field_first_name->value));
    $last_name = Unicode::ucfirst(Unicode::strtolower($member->field_last_name->value));
    // Finally, format the display name.
    $name = t('@title @first_name @last_name', [
      '@title' => $title,
      '@first_name' => $first_name,
      '@last_name' => $last_name,
    ]);
  }
}

That's it! If you already had the module installed on dev site, make sure to clear caches before you can see the changes.

Links: