The User entity is one of the Drupal core entities that programmatically adds form elements in their entity form class and still uses validation constraints.
AccountForm::form()
adds the form elements and User::baseFieldDefinitions()
adds the validation constraints to the entity fields.
(AccountForm
is the class extended by the ProfileForm
and the RegistrationForm
classes, which are two of the three entity form classes used for the User entity.)
// Account information.
$form['account'] = [
'#type' => 'container',
'#weight' => -10,
];
// The mail field is NOT required if account originally had no mail set
// and the user performing the edit has 'administer users' permission.
// This allows users without email address to be edited and deleted.
// Also see \Drupal\user\Plugin\Validation\Constraint\UserMailRequired.
$form['account']['mail'] = [
'#type' => 'email',
'#title' => $this->t('Email address'),
'#description' => $this->t('A valid email address. All emails from the system will be sent to this address. The email address is not made public and will only be used if you wish to receive a new password or wish to receive certain news or notifications by email.'),
'#required' => !(!$account->getEmail() && $user->hasPermission('administer users')),
'#default_value' => !$register ? $account->getEmail() : '',
];
// Only show name field on registration form or user can change own username.
$form['account']['name'] = [
'#type' => 'textfield',
'#title' => $this->t('Username'),
'#maxlength' => UserInterface::USERNAME_MAX_LENGTH,
'#description' => $this->t("Several special characters are allowed, including space, period (.), hyphen (-), apostrophe ('), underscore (_), and the @ sign."),
'#required' => TRUE,
'#attributes' => [
'class' => [
'username',
],
'autocorrect' => 'off',
'autocapitalize' => 'off',
'spellcheck' => 'false',
],
'#default_value' => !$register ? $account->getAccountName() : '',
'#access' => $account->name->access('edit'),
];
$fields['name'] = BaseFieldDefinition::create('string')
->setLabel(t('Name'))
->setDescription(t('The name of this user.'))
->setRequired(TRUE)
->setConstraints([
// No Length constraint here because the UserName constraint also covers
// that.
'UserName' => [],
'UserNameUnique' => [],
]);
$fields['name']
->getItemDefinition()
->setClass('\\Drupal\\user\\UserNameItem');
$fields['pass'] = BaseFieldDefinition::create('password')
->setLabel(t('Password'))
->setDescription(t('The password of this user (hashed).'))
->addConstraint('ProtectedUserField');
The AccountForm
, ProfileForm, and
RegisterForm classes, which extend the [
ContentEntityForm][6] class, doen't extend [
ContentEntityForm::validateForm()][7], though. They implement methods that is necessary to Drupal to understand which entity fields are edited and which violations should be shown: [
AccountForm::getEditedFieldNames()][8] and [
AccountForm::flagViolations()`]6.
protected function getEditedFieldNames(FormStateInterface $form_state) {
return array_merge([
'name',
'pass',
'mail',
'timezone',
'langcode',
'preferred_langcode',
'preferred_admin_langcode',
], parent::getEditedFieldNames($form_state));
}
protected function flagViolations(EntityConstraintViolationListInterface $violations, array $form, FormStateInterface $form_state) {
// Manually flag violations of fields not handled by the form display. This
// is necessary as entity form displays only flag violations for fields
// contained in the display.
$field_names = [
'name',
'pass',
'mail',
'timezone',
'langcode',
'preferred_langcode',
'preferred_admin_langcode',
];
foreach ($violations->getByFields($field_names) as $violation) {
list($field_name) = explode('.', $violation->getPropertyPath(), 2);
$form_state->setErrorByName($field_name, $violation->getMessage());
}
parent::flagViolations($violations, $form, $form_state);
}
ContentEntityForm::validateForm()
shows only the validation errors for those form elements that are reported to be edited. This means that if a form element name isn't returned from AccountForm::getEditedFieldNames()
or ContentEntityForm::getEditedFieldNames()
, that form element and the corresponding entity field aren't considered edited.
To answer the question: Yes, it's possible to add form elements in the form()
method of the entity form class and use validation constraints (and their "automatic" validation), as long as the entity form class implements the getEditedFieldNames()
and flagViolations()
methods.