Adding roles to a user programmatically in Drupal 8

For a new Drupal 8 site I needed to hook into the user registration process and add a role to users with email addresses on a specific domain.

As with most tasks in Drupal 8, if you're used to working with procedural code from Drupal 7, working with object-oriented code can involve a bit of a mental shift, and sometimes figuring out which object is relevant involves a bit of digging around in the core code, or judicious use of a debugger.

This is a good example of the OO approach - the User class extends ContentEntityBase, which extends Entity, so some of the relevant methods aren't in User. Also, while there is effectively a hook_user_insert, you won't find it in the user module, but in the entity module.

One thing that makes this easier and more logical in Drupal 8 is the fact that roles now have machine names rather than numeric IDs.

/**
 * Implements hook_ENTITY_TYPE_insert().
 */
function mymodule_users_user_insert($entity) {

  // Add the specified role to users whose email address is @mydomain.com.
  $email = $entity->getEmail();
  $domain = substr($email, strrpos($email, '@') + 1);

  if ($domain == 'mydomain.com') {
    $entity->addRole('mydomainrole');
    $entity->save();
  }
}

Here's the equivalent Drupal 7 code:

/**
 * Implements hook_user_insert().
 **/
function mymodule_user_insert(&$edit, $account, $category) {
  $email = $account->mail;
  $domain = substr($email, strrpos($email, '@') + 1);

  if ($domain == 'mydomain.com') {
    $user = user_load($account->uid);
    $role = user_role_load_by_name('user manager');
    $user->roles[$role->rid] = $role->name;
    user_save($user);
  }
}

To my mind, the Drupal 8 code is clearer and more logical to the reader, and probably easier to get your head around, unless you're coming from the Drupal island.

Tags: 
All tags