Drupal's constraint plugins are a wrapper around the Symfony Validator component. Previously, constraint plugins were created by passing a single array of options to the constructor, e.g.
$constraint = new MyConstraint(['message' => 'Custom validation message']);
In order to improve the API for both type safety and required arguments, Symfony has deprecated the single array format in favor of using named arguments:
$constraint = new MyConstraint(message: 'Custom validation message');
Previously there was no need to write a constructor in most constraint plugins as the base Constraint constructor would take care of handling the options array. This feature will be removed in Symfony 8, which will ship alongside Drupal 12, so custom constraint plugins should add a custom constructor with named arguments.
If you do not need to provide backward compatibility you can use named arguments and constructor property promotion immediately:
use Symfony\Component\Validator\Attribute\HasNamedArguments;
use Symfony\Component\Validator\Constraint;
class MyConstraint extends Constraint {
#[HasNamedArguments]
public function __construct(
public string $message = 'Default validation message.',
mixed ...$args,
) {
parent::__construct(...$args);
}
}
In order to ease the transition in contrib modules, you can provide backward compatibility as follows:
use Symfony\Component\Validator\Attribute\HasNamedArguments;
use Symfony\Component\Validator\Constraint;
class MyConstraint extends Constraint {
public string $message = 'Default validation message.';
#[HasNamedArguments]
public function __construct(?array $options = null, ?string $message = null, mixed ...$args) {
if (is_array($options)) {
@trigger_error(sprintf('Passing an array of options to configure the "%s" constraint is deprecated in drupal:11.3.0 and is removed in drupal:12.0.0. Use named arguments instead. See https://www.drupal.org/node/XXX', static::class), E_USER_DEPRECATED);
}
parent::__construct($options, ...$args);
$this->message = $message ?? $this->message;
}
}
The #[HasNamedArguments] attribute tells Symfony and Drupal that named arguments are supported when constructing the constraint.
In this example $message is the only configurable setting for the constraint but this should be extended to all settings that the constraint supports. The argument should be typed, and only nullable if the setting is optional.
In addition, passing any value that is not an associative array or NULL to the addConstraint() method of any class implementing the following interfaces is deprecated:
- Drupal\Component\Plugin\Context\ContextDefinitionInterface
- Drupal\Core\Entity\EntityTypeInterface
- Drupal\Core\Field\FieldConfigInterface
- Drupal\Core\TypedData\DataDefinitionInterface
In Drupal 12, the signature of those methods will look like this:
public function addConstraint(string $constraint_name, ?array $options = NULL): static;
The keys of the $options associative array are the names of the constraint properties being set, and the array values are the values to set for the corresponding properties.
For example:
Before
$properties['entity'] = DataReferenceDefinition::create('entity')->addConstraint('EntityType', 'taxonomy_term');
Now
$properties['entity'] = DataReferenceDefinition::create('entity')->addConstraint('EntityType', ['type' => 'taxonomy_term']);
Lastly, passing any value that is not an associative array or NULL to the craete() of Drupal\Core\Validation\ConstraintManager is deprecated. In Drupal 12, the signature will be updated to:
public function create(string $name, ?array $options): \Symfony\Component\Validator\Constraint<?code>