3

I have a custom module saving user accounts from a customer API.

For this I use the \Drupal\user\Entity\User::create method, and I initialise the user account with the code above:

  /**
   * Save a Drupal user using `mail`.
   * 
   * @param array $data
   *   The User data to save.
   * 
   * @return bool|int
   *   Result: SAVED_NEW or FALSE.
   */
  public static function saveUser(array $data) {
    // Only if user exists on API
    if (empty($data['mail'])) {
      return FALSE;
    }
    // Create the user
    $email = $data['mail'];
    $user = \Drupal\user\Entity\User::create();
    $lang = \Drupal::languageManager()->getCurrentLanguage()->getId();

    // The Basics
    $user->setEmail($email);
    $user->setUsername($email);
    $user->set("init", $email);
    $user->set("langcode", $lang);
    $user->set("preferred_langcode", $lang);
    $user->set("preferred_admin_langcode", $lang);
    $user->enforceIsNew();
    $user->setPassword(user_password());
    $user->activate();

    // Save user.
    return $user->save();
  }

I like to look for a good way of doing things and to read about getting D9 ready for my modules. When it is necessary, I also inspect the core code to see the available methods and how core modules are made to inspire me.

I found this interesting topic about Entity Validation API, where it speaks about using validation on entities. I also found that user object has methods to know if a validation is necessary and to validate, and the preSave method will throw an exception if the validation is necessary and not done.

All the examples and code snippet I found on internet about creating a user programmatically never use any kind of validation, and I wonder if this is something new or if it is just not really necessary for my case.

As I said, I save data from a remote API, and I do not manage the quality of the email address I use as email and username. I also do not verify if those values already exist.

Should I add something like this ?

// Entity validation
if ($user->isValidationRequired()) {
  $violations = $user->validate();
  if (count($violations)) {
    \Drupal::messenger()->addError($violations[0]->getMessage());
    return FALSE;
  }
}
4
  • 1
    It depends. If your user is being created through user input that could be potentially invalid, then yes, you'll want to validate it. But if you are creating the entity yourself, with defined values that will not have any possibility of being invalid, you don't need to validate the user.
    – Jaypan
    Commented Jun 5, 2020 at 14:53
  • Thanks for your answer. And what about the fact the username or email can already exist in Drupal ?
    – TytooF
    Commented Jun 5, 2020 at 16:08
  • 1
    I don't see how that changes my answer.
    – Jaypan
    Commented Jun 5, 2020 at 16:09
  • If the username or email already exists then it would be invalid.
    – sonfd
    Commented Jun 5, 2020 at 21:16

1 Answer 1

3

If you want to first verify the user you programmatically created passes validation, which means (for example) that its username and emails are unique, you can check if there are validation violations. You don't need to fist call isValidationRequired() as that method returns the value set with setValidationRequired(), which is called only from ContentEntityForm::buildEntity() and ContentEntityForm::validateForm(). (In other words, if the entity isn't created via its entity form, isValidationRequired() will return FALSE.)

$violations = $user->validate();
if (count($violations)) {
  // The user account didn't pass the validation.
}

Note also that you can pass to User::create() an array containing the values to assign to the user fields, instead of calling each setter as your code does.

You could also create the user account with \Drupal::entityTypeManager()->getStorage('user')->create($values), where $values is the same array accepted by User::create().

3
  • Thanks kiamlaluno for your answer. I think I understand better now that I also read some more information about validation constraint. The validate method is more for entity which declare fields using BaseFieldDefinition::create instead of a form array. It's more OO, flexible and allow validation even if we do use code creation (like via an API).
    – TytooF
    Commented Jun 9, 2020 at 13:52
  • All the Drupal core content entities use BaseFieldDefinition::create(). I think only configuration entities don't use it. (In fact, they don't have fields).
    – avpaderno
    Commented Jun 9, 2020 at 15:03
  • I saw the User entity use this method to add all the entity field, but it puzzle me that it also use a normal form AccountForm to generate the properties of fields as label. I was imagining to use an entity to generate a form to exchange with the API, but finally it's easier without, specially because I also use #states.
    – TytooF
    Commented Jun 10, 2020 at 12:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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