Update some field values after user update own account 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-&gt;id());
  $original_email = $account-&gt;original-&gt;get('mail')-&gt;value;
  $updated_email = $current_user-&gt;get('mail')-&gt;value;

  if ($original_email != $updated_email) {
    $current_user-&gt;setUsername($updated_email);
    $current_user-&gt;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-&gt;save(); in the code.

Update:

Using hook_user_presave()

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

  if ($original_email != $updated_email) {
    $current_user-&gt;setUsername($updated_email);
    \Drupal::messenger()-&gt;addMessage(t("if"));
  }
  else {
    \Drupal::messenger()-&gt;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 ?
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-&gt;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-&gt;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-&gt;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-&gt;original)) {
    $original_email = $account-&gt;original-&gt;get('mail')-&gt;value;
    $updated_email = $account-&gt;get('mail')-&gt;value;

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


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

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

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