0

On my Drupal 8 project, usernames are only allowed to be emails. For that, on account creation, I am setting the name field equal to the email field, and users don't have the permission to edit their own usernames.

I am writing the code to update the username when the users change email. This is the code I wrote so far.

/**
 * Implements HOOK_user_update().
 * Set Username = mail if mail has been updated by user.
 */
function hook_user_update($account) {
  $current_user = \Drupal\user\Entity\User::load($account->id());
  $original_email = $account->original->get('mail')->value;
  $updated_email = $current_user->get('mail')->value;

  if ($original_email != $updated_email) {
    $current_user->setUsername($updated_email);
    $current_user->save();
  }

}

Since the code is saving a user object, the same hook is invoked again when $current_user is saved. I also tried with hook_user_save() and hook_user_presave(), but they don't work until I include $current_user->save(); in the code.

Update:

Using hook_user_presave()

function custom_module_user_presave($account) {
  $current_user = \Drupal\user\Entity\User::load($account->id());
  $original_email = $account->original->get('mail')->value;
  $updated_email = $current_user->get('mail')->value;

  if ($original_email != $updated_email) {
    $current_user->setUsername($updated_email);
    \Drupal::messenger()->addMessage(t("if"));
  }
  else {
    \Drupal::messenger()->addMessage(t("else"));
  }

However, after updating the email and saving an existing user, I always get the message: else which means $original_email != $updated_email which is logic as the user has not been not saved yet.

any help please ?

1
1

Drupal core doesn't use any hook_ENTITY_TYPE_save() hook and hook_ENTITY_TYPE_update() is called after saving in the database an entity that has been updated.
As the documentation says, that hook implementations may not alter the stored entity data. They shouldn't call save() on the entity they receive as argument, or that will invoke those hook implementations once again; if then those hook implementations don't check if they have been already invoked (which is the case of most, if not all, the hook implementations) and save again the entity they received as argument, that would cause an infinite loop.

The only hook invoked before the entity is saved is hook_ENTITY_TYPE_presave(), which is invoked either when an entity is created or updated. It's easy to understand in which case the hook is invoked, since in the latter case $entity->original will contain the entity before the change.

As side note, the documentation reference to that hook as hook_ENTITY_TYPE_presave(); the implementation done from the mymodule module for the User entity is mymodule_user_presave(). (Replace ENTITY_TYPE with the entity type for which the hook should be invoked, such as user for the User entity and node for the Node entity.)

As for the used code, that is wrong since:

  • There isn't the need to load the account with \Drupal\user\Entity\User::load($account->id()) because the full User object is already passed to the hook as argument

  • Since the hook is invoked whenever a new user is created, or an existing user is updated, the code needs to first verify $account->original is defined

  • The code is setting the username for $current_user, but Drupal core is saving $account; since in PHP those are two different objects, the code isn't changing the username for the User object being saved

  • $original_email != $updated_email is not comparing the changed email with the original email, but it's comparing the email before the User object is edited with the original email (which is still the email before the User object is edited)

I would use the following code.

function custom_module_user_presave($account) {
  if (isset($account->original)) {
    $original_email = $account->original->get('mail')->value;
    $updated_email = $account->get('mail')->value;

    if ($original_email != $updated_email) {
      $account->setUsername($updated_email);
    }
  }
}

Even better, the code should check the value returned from $account->isNew().

function custom_module_user_presave($account) {
  if (!$account->isNew()) {
    $original_email = $account->original->get('mail')->value;
    $updated_email = $account->get('mail')->value;

    if ($original_email != $updated_email) {
      $account->setUsername($updated_email);
    }
  }
}
6
  • as i have mentioned in my question, I have aleady tried with hook_user_presave() but for some reason isn't working... Can you check please the update to my question – Vanessa Abi Rashed May 28 '20 at 8:31
  • You need to verify $account->original is defined. Then, \Drupal::messenger()->addMessage() is not the correct way to debug a hook. – apaderno May 28 '20 at 8:47
  • Also, there is no need to load the account, as that is already passed to the hook. – apaderno May 28 '20 at 8:51
  • I found this issue here drupal.org/project/drupal/issues/2839195 but not sure if related to my issues on why $account->original is not defined ! What do you think ? is it a core issue ? – Vanessa Abi Rashed May 28 '20 at 12:11
  • No, that issue is about an implementation detail. The only case $account->original is not set is when the the user is created. Since hook_user_presave() is invoked either when a user is created or when a user is updated, you need to check in which case the hook is invoked. – apaderno May 28 '20 at 16:13

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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