class NoFieldItemsExistWithHigherCardinalityValidator
Validates the NoFieldItemsExistWithHigherCardinality constraint.
This validator checks whether existing field items of a specified field exceed the given cardinality limit. It performs an aggregate query to find the maximum delta (index of field items) for the specified field across all entities of the given entity type, and compares it against the provided cardinality.
The validation:
- Skips if cardinality is unlimited (-1)
- Skips if the field storage configuration doesn't exist
- Uses EntityTypeManager to query the maximum field delta
- Adds a violation if the maximum delta exceeds the cardinality
This validator implements ContainerInjectionInterface to access the entity type manager service from the Drupal service container.
Hierarchy
- class \Drupal\field\Plugin\Validation\Constraint\NoFieldItemsExistWithHigherCardinalityValidator implements \Drupal\Core\DependencyInjection\ContainerInjectionInterface extends \Symfony\Component\Validator\ConstraintValidator
Expanded class hierarchy of NoFieldItemsExistWithHigherCardinalityValidator
See also
\Drupal\field\Plugin\Validation\Constraint\NoFieldItemsExistWithHigherCardinality
1 file declares its use of NoFieldItemsExistWithHigherCardinalityValidator
- NoFieldItemsExistWithHigherCardinalityTest.php in core/
modules/ field/ tests/ src/ Kernel/ Plugin/ Validation/ Constraint/ NoFieldItemsExistWithHigherCardinalityTest.php
File
-
core/
modules/ field/ src/ Plugin/ Validation/ Constraint/ NoFieldItemsExistWithHigherCardinalityValidator.php, line 36
Namespace
Drupal\field\Plugin\Validation\ConstraintView source
class NoFieldItemsExistWithHigherCardinalityValidator extends ConstraintValidator implements ContainerInjectionInterface {
public function __construct(protected EntityTypeManagerInterface $entityTypeManager) {
}
/**
* {@inheritdoc}
*/
public static function create(ContainerInterface $container) : self {
return new self($container->get('entity_type.manager'));
}
/**
* {@inheritdoc}
*/
public function validate(mixed $value, SymfonyConstraint $constraint) : void {
assert($constraint instanceof NoFieldItemsExistWithHigherCardinality);
// Cardinality should be an int, but could be passed differently.
$cardinality = (int) $value;
if ($cardinality === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) {
return;
}
$object = $this->context
->getObject();
assert($object instanceof TypedDataInterface);
$entity_type = TypeResolver::resolveExpression($constraint->entityType, $object);
$field_name = TypeResolver::resolveExpression($constraint->fieldName, $object);
// We cannot check this constraint if the field storage does not exist.
$fieldStorageConfig = $this->entityTypeManager
->getStorage('field_storage_config')
->load($entity_type . '.' . $field_name);
if ($fieldStorageConfig === NULL) {
return;
}
if ($fieldStorageConfig->hasCustomStorage()) {
// If the field storage has custom storage, we cannot check this
// constraint.
return;
}
$max_delta_alias = 'max_delta';
$query = $this->entityTypeManager
->getStorage($entity_type)
->getAggregateQuery()
->aggregate($field_name . '.%delta', 'MAX', NULL, $max_delta_alias)
->accessCheck(FALSE);
$result = $query->execute();
$max_delta = 0;
if (is_array($result) && !empty($result)) {
if ($result[0][$max_delta_alias] !== NULL) {
// Delta starts at 0, so we need to add 1 to get the count of
// existing values.
$max_delta = (int) $result[0][$max_delta_alias] + 1;
}
}
if ($max_delta > $cardinality) {
$this->context
->addViolation($constraint->message, [
'@entity_type' => $entity_type,
'@field_name' => $field_name,
'@max_delta' => $max_delta,
'@cardinality' => $cardinality,
]);
}
}
}
Buggy or inaccurate documentation? Please file an issue. Need support? Need help programming? Connect with the Drupal community.