From a0c7fa5f09a81aa679fdf793fd5e1a439e2e3734 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Fri, 14 Mar 2025 16:27:32 +0100 Subject: [PATCH 01/11] Add NoEntitiesExistYetWithHigherCardinality valdator from parent issue --- .../field/config/schema/field.schema.yml | 4 + ...oEntitiesExistYetWithHigherCardinality.php | 47 ++++++++++++ ...ExistYetWithHigherCardinalityValidator.php | 76 +++++++++++++++++++ 3 files changed, 127 insertions(+) create mode 100644 core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinality.php create mode 100644 core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php diff --git a/core/modules/field/config/schema/field.schema.yml b/core/modules/field/config/schema/field.schema.yml index cdd1d4bab05c..26f98da302f2 100644 --- a/core/modules/field/config/schema/field.schema.yml +++ b/core/modules/field/config/schema/field.schema.yml @@ -45,6 +45,10 @@ field.storage.*.*: cardinality: type: integer label: 'Maximum number of values users can enter' + constraints: + NoEntitiesExistYetWithHigherCardinality: + entityType: '%parent.entity_type' + fieldName: '%parent.field_name' translatable: type: boolean label: 'Translatable' diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinality.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinality.php new file mode 100644 index 000000000000..a2bb7bd30264 --- /dev/null +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinality.php @@ -0,0 +1,47 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\field\Plugin\Validation\Constraint; + +use Drupal\Core\StringTranslation\TranslatableMarkup; +use Symfony\Component\Validator\Constraint as SymfonyConstraint; +use Drupal\Core\Validation\Attribute\Constraint; + +/** + * Checks if a plugin exists and optionally implements a particular interface. + */ +#[Constraint( + id: 'NoEntitiesExistYetWithHigherCardinality', + label: new TranslatableMarkup('No entities exist with higher cardinality', [], ['context' => 'Validation']) +)] +class NoEntitiesExistYetWithHigherCardinality extends SymfonyConstraint { + + /** + * The error message if a plugin does not implement the expected interface. + * + * @var string + */ + public string $message = "The field '@field_name' of entity type '@entity_type' has more entries (@max_delta) than the cardinality (@cardinality) allows."; + /** + * The entity type to check. + * + * @var string + */ + public string $entityType; + + /** + * The field name to check. + * + * @var string + */ + public string $fieldName; + + /** + * {@inheritdoc} + */ + public function getRequiredOptions(): array { + return ['entityType', 'fieldName']; + } + +} diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php new file mode 100644 index 000000000000..d251dab2854d --- /dev/null +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php @@ -0,0 +1,76 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\field\Plugin\Validation\Constraint; + +use Drupal\Core\Config\Schema\TypeResolver; +use Drupal\Core\DependencyInjection\ContainerInjectionInterface; +use Drupal\Core\Entity\EntityTypeManagerInterface; +use Drupal\Core\Field\FieldStorageDefinitionInterface; +use Drupal\Core\TypedData\TypedDataInterface; +use Symfony\Component\DependencyInjection\ContainerInterface; +use Symfony\Component\Validator\Constraint as SymfonyConstraint; +use Symfony\Component\Validator\ConstraintValidator; + +class NoEntitiesExistYetWithHigherCardinalityValidator extends ConstraintValidator implements ContainerInjectionInterface { + + public function __construct( + protected EntityTypeManagerInterface $entityTypeManager, + ) { + } + + public static function create(ContainerInterface $container): self { + return new static( + $container->get('entity_type.manager') + ); + } + + /** + * {@inheritdoc} + */ + public function validate(mixed $cardinality, SymfonyConstraint $constraint): void { + assert($constraint instanceof NoEntitiesExistYetWithHigherCardinality); + + if ($cardinality === FieldStorageDefinitionInterface::CARDINALITY_UNLIMITED) { + return; + } + + /** + * We cannot check this constraint if the field storage does not exist. + */ + $fieldStorageConfig = $this->entityTypeManager->getStorage('field_storage_config') + ->load($constraint->entityType . '.' . $constraint->fieldName); + if ($fieldStorageConfig === NULL) { + return; + } + + $object = $this->context->getObject(); + assert($object instanceof TypedDataInterface); + + $entity_type = TypeResolver::resolveExpression($constraint->entityType, $object); + $field_name = TypeResolver::resolveExpression($constraint->fieldName, $object); + + $max_delta_alias = 'max_delta'; + $result = $this->entityTypeManager->getStorage($entity_type) + ->getAggregateQuery() + ->accessCheck(FALSE) + ->aggregate($field_name . '.%delta', 'MAX', NULL, $max_delta_alias) + ->execute(); + + $max_delta = 0; + if (is_array($result) && !empty($result)) { + $max_delta = $result[0][$max_delta_alias] ?? 0; + } + + if ($max_delta > $cardinality) { + $this->context->addViolation($constraint->message, [ + '@entity_type' => $entity_type, + '@field_name' => $field_name, + '@max_delta' => $max_delta, + '@cardinality' => $cardinality, + ]); + } + } + +} -- GitLab From b14281093ba3eb99f49a284413313913bc9d937b Mon Sep 17 00:00:00 2001 From: anjaliprasannan <anjaliprasannan243@gmail.com> Date: Sat, 22 Mar 2025 22:11:27 +0530 Subject: [PATCH 02/11] Issue #3513035: Implement NoEntitiesExistYetWithHigherCardinality tests --- ...itiesExistYetWithHigherCardinalityTest.php | 125 ++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php diff --git a/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php b/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php new file mode 100644 index 000000000000..8e2c19e6fc49 --- /dev/null +++ b/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php @@ -0,0 +1,125 @@ +<?php + +declare(strict_types=1); + +namespace Drupal\Tests\field\Unit\Plugin\Validation\Constraint; + +use Drupal\field\Plugin\Validation\Constraint\NoEntitiesExistYetWithHigherCardinality; +use Drupal\Tests\UnitTestCase; +use Symfony\Component\Validator\Context\ExecutionContextInterface; +use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface; + +/** + * Tests the NoEntitiesExistYetWithHigherCardinality constraint. + * + * @group field + */ +class NoEntitiesExistYetWithHigherCardinalityTest extends UnitTestCase { + + /** + * Tests the constraint's required options. + */ + public function testRequiredOptions(): void { + $options = [ + 'entityType' => 'node', + 'fieldName' => 'field_test', + ]; + $constraint = new NoEntitiesExistYetWithHigherCardinality($options); + $requiredOptions = $constraint->getRequiredOptions(); + + $this->assertTrue(is_array($requiredOptions)); + $this->assertEquals(['entityType', 'fieldName'], $requiredOptions); + } + + /** + * Tests the constraint initialization with valid options. + */ + public function testValidOptions(): void { + $options = [ + 'entityType' => 'node', + 'fieldName' => 'field_test', + ]; + + $constraint = new NoEntitiesExistYetWithHigherCardinality($options); + + $this->assertEquals('node', $constraint->entityType); + $this->assertEquals('field_test', $constraint->fieldName); + $this->assertEquals( + "The field '@field_name' of entity type '@entity_type' has more entries (@max_delta) than the cardinality (@cardinality) allows.", + $constraint->message + ); + } + + /** + * Tests the constraint initialization with missing required options. + */ + public function testMissingOptions(): void { + $this->expectException(\Symfony\Component\Validator\Exception\MissingOptionsException::class); + $this->expectExceptionMessage('The options "entityType" must be set for constraint'); + + new NoEntitiesExistYetWithHigherCardinality(['fieldName' => 'field_test']); + } + + /** + * Tests the constraint's default configuration. + */ + public function testDefaultConfiguration(): void { + $options = [ + 'entityType' => 'user', + 'fieldName' => 'field_example', + ]; + + $constraint = new NoEntitiesExistYetWithHigherCardinality($options); + + $defaultConfig = $constraint->getDefaultOption(); + $this->assertNull($defaultConfig); + } + + /** + * Tests the message template with different parameters. + * + * @dataProvider messageParametersProvider + */ + public function testMessageParameters(string $entityType, string $fieldName, int $maxDelta, int $cardinality, string $expectedMessage): void { + $options = [ + 'entityType' => $entityType, + 'fieldName' => $fieldName, + ]; + + $constraint = new NoEntitiesExistYetWithHigherCardinality($options); + + // Simulate the violation building process. + $parameters = [ + '@field_name' => $fieldName, + '@entity_type' => $entityType, + '@max_delta' => (string) $maxDelta, + '@cardinality' => (string) $cardinality, + ]; + + $message = strtr($constraint->message, $parameters); + $this->assertEquals($expectedMessage, $message); + } + + /** + * Data provider for testMessageParameters. + */ + public function messageParametersProvider(): array { + return [ + [ + 'node', + 'field_body', + 3, + 2, + "The field 'field_body' of entity type 'node' has more entries (3) than the cardinality (2) allows.", + ], + [ + 'user', + 'field_address', + 5, + 1, + "The field 'field_address' of entity type 'user' has more entries (5) than the cardinality (1) allows.", + ], + ]; + } + +} -- GitLab From d6a8fd712ddca926f64ba0ff3e287b9dbd580d84 Mon Sep 17 00:00:00 2001 From: anjaliprasannan <anjaliprasannan243@gmail.com> Date: Sat, 22 Mar 2025 22:25:34 +0530 Subject: [PATCH 03/11] Issue #3513035: phpcs fix --- ...ExistYetWithHigherCardinalityValidator.php | 24 ++++++++++++++++--- ...itiesExistYetWithHigherCardinalityTest.php | 12 ++++------ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php index d251dab2854d..2905e13a4f92 100644 --- a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php @@ -13,6 +13,26 @@ use Symfony\Component\Validator\Constraint as SymfonyConstraint; use Symfony\Component\Validator\ConstraintValidator; +/** + * Validates the NoEntitiesExistYetWithHigherCardinality constraint. + * + * This validator checks whether existing entities of a specified type have more + * field values than allowed by the given cardinality limit. It performs an + * aggregate query to find the maximum delta (number of field values) for the + * specified field across all entities of the given 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. + * + * @see \Drupal\field\Plugin\Validation\Constraint\NoEntitiesExistYetWithHigherCardinality + */ class NoEntitiesExistYetWithHigherCardinalityValidator extends ConstraintValidator implements ContainerInjectionInterface { public function __construct( @@ -36,9 +56,7 @@ public function validate(mixed $cardinality, SymfonyConstraint $constraint): voi return; } - /** - * We cannot check this constraint if the field storage does not exist. - */ + // We cannot check this constraint if the field storage does not exist. $fieldStorageConfig = $this->entityTypeManager->getStorage('field_storage_config') ->load($constraint->entityType . '.' . $constraint->fieldName); if ($fieldStorageConfig === NULL) { diff --git a/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php b/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php index 8e2c19e6fc49..85dcaa536db8 100644 --- a/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php +++ b/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php @@ -6,8 +6,6 @@ use Drupal\field\Plugin\Validation\Constraint\NoEntitiesExistYetWithHigherCardinality; use Drupal\Tests\UnitTestCase; -use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Violation\ConstraintViolationBuilderInterface; /** * Tests the NoEntitiesExistYetWithHigherCardinality constraint. @@ -41,7 +39,7 @@ public function testValidOptions(): void { ]; $constraint = new NoEntitiesExistYetWithHigherCardinality($options); - + $this->assertEquals('node', $constraint->entityType); $this->assertEquals('field_test', $constraint->fieldName); $this->assertEquals( @@ -56,7 +54,7 @@ public function testValidOptions(): void { public function testMissingOptions(): void { $this->expectException(\Symfony\Component\Validator\Exception\MissingOptionsException::class); $this->expectExceptionMessage('The options "entityType" must be set for constraint'); - + new NoEntitiesExistYetWithHigherCardinality(['fieldName' => 'field_test']); } @@ -70,7 +68,7 @@ public function testDefaultConfiguration(): void { ]; $constraint = new NoEntitiesExistYetWithHigherCardinality($options); - + $defaultConfig = $constraint->getDefaultOption(); $this->assertNull($defaultConfig); } @@ -87,7 +85,7 @@ public function testMessageParameters(string $entityType, string $fieldName, int ]; $constraint = new NoEntitiesExistYetWithHigherCardinality($options); - + // Simulate the violation building process. $parameters = [ '@field_name' => $fieldName, @@ -103,7 +101,7 @@ public function testMessageParameters(string $entityType, string $fieldName, int /** * Data provider for testMessageParameters. */ - public function messageParametersProvider(): array { + public static function messageParametersProvider(): array { return [ [ 'node', -- GitLab From f14dae533db8a64394e96d2638711048736823d0 Mon Sep 17 00:00:00 2001 From: anjaliprasannan <anjaliprasannan243@gmail.com> Date: Mon, 24 Mar 2025 10:05:01 +0530 Subject: [PATCH 04/11] #3513035 Phpcs fix. --- .../NoEntitiesExistYetWithHigherCardinalityTest.php | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php b/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php index 85dcaa536db8..a612d4c4a726 100644 --- a/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php +++ b/core/modules/field/tests/src/Unit/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityTest.php @@ -6,6 +6,7 @@ use Drupal\field\Plugin\Validation\Constraint\NoEntitiesExistYetWithHigherCardinality; use Drupal\Tests\UnitTestCase; +use Symfony\Component\Validator\Exception\MissingOptionsException as MissingOptionsExceptionAlias; /** * Tests the NoEntitiesExistYetWithHigherCardinality constraint. @@ -37,7 +38,7 @@ public function testValidOptions(): void { 'entityType' => 'node', 'fieldName' => 'field_test', ]; - + $constraint = new NoEntitiesExistYetWithHigherCardinality($options); $this->assertEquals('node', $constraint->entityType); @@ -52,7 +53,7 @@ public function testValidOptions(): void { * Tests the constraint initialization with missing required options. */ public function testMissingOptions(): void { - $this->expectException(\Symfony\Component\Validator\Exception\MissingOptionsException::class); + $this->expectException(MissingOptionsExceptionAlias::class); $this->expectExceptionMessage('The options "entityType" must be set for constraint'); new NoEntitiesExistYetWithHigherCardinality(['fieldName' => 'field_test']); @@ -66,7 +67,7 @@ public function testDefaultConfiguration(): void { 'entityType' => 'user', 'fieldName' => 'field_example', ]; - + $constraint = new NoEntitiesExistYetWithHigherCardinality($options); $defaultConfig = $constraint->getDefaultOption(); @@ -83,7 +84,7 @@ public function testMessageParameters(string $entityType, string $fieldName, int 'entityType' => $entityType, 'fieldName' => $fieldName, ]; - + $constraint = new NoEntitiesExistYetWithHigherCardinality($options); // Simulate the violation building process. @@ -93,7 +94,7 @@ public function testMessageParameters(string $entityType, string $fieldName, int '@max_delta' => (string) $maxDelta, '@cardinality' => (string) $cardinality, ]; - + $message = strtr($constraint->message, $parameters); $this->assertEquals($expectedMessage, $message); } -- GitLab From 7fcf3bab232bbbd08c0f3572264c7c935e386d40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Brala?= <49259-bbrala@users.noreply.drupalcode.org> Date: Thu, 27 Mar 2025 14:00:36 +0000 Subject: [PATCH 05/11] Small update from parent issue in the order of the validate function. --- ...itiesExistYetWithHigherCardinalityValidator.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php index 2905e13a4f92..5a3438633560 100644 --- a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php @@ -56,19 +56,19 @@ public function validate(mixed $cardinality, SymfonyConstraint $constraint): voi return; } - // We cannot check this constraint if the field storage does not exist. - $fieldStorageConfig = $this->entityTypeManager->getStorage('field_storage_config') - ->load($constraint->entityType . '.' . $constraint->fieldName); - if ($fieldStorageConfig === NULL) { - 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; + } + $max_delta_alias = 'max_delta'; $result = $this->entityTypeManager->getStorage($entity_type) ->getAggregateQuery() -- GitLab From 7fda1e2193c191a3f11ce406e2fb60bbc056edd6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bj=C3=B6rn=20Brala?= <49259-bbrala@users.noreply.drupalcode.org> Date: Thu, 27 Mar 2025 14:44:29 +0000 Subject: [PATCH 06/11] Small phpcs fix --- .../NoEntitiesExistYetWithHigherCardinalityValidator.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php index 5a3438633560..60dc8b0b94c2 100644 --- a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php @@ -68,7 +68,7 @@ public function validate(mixed $cardinality, SymfonyConstraint $constraint): voi if ($fieldStorageConfig === NULL) { return; } - + $max_delta_alias = 'max_delta'; $result = $this->entityTypeManager->getStorage($entity_type) ->getAggregateQuery() -- GitLab From d70d52dbb693af0c0bd44e4624e08478456d96f9 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Fri, 28 Mar 2025 15:04:17 +0100 Subject: [PATCH 07/11] fix: Deprecation test of installing a field without an installed schema fails. This check fixes that. --- ...iesExistYetWithHigherCardinalityValidator.php | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php index 60dc8b0b94c2..6ad53235d43f 100644 --- a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php @@ -5,6 +5,7 @@ namespace Drupal\field\Plugin\Validation\Constraint; use Drupal\Core\Config\Schema\TypeResolver; +use Drupal\Core\Database\DatabaseExceptionWrapper; use Drupal\Core\DependencyInjection\ContainerInjectionInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Field\FieldStorageDefinitionInterface; @@ -70,11 +71,20 @@ public function validate(mixed $cardinality, SymfonyConstraint $constraint): voi } $max_delta_alias = 'max_delta'; - $result = $this->entityTypeManager->getStorage($entity_type) + $query = $this->entityTypeManager->getStorage($entity_type) ->getAggregateQuery() ->accessCheck(FALSE) - ->aggregate($field_name . '.%delta', 'MAX', NULL, $max_delta_alias) - ->execute(); + ->aggregate($field_name . '.%delta', 'MAX', NULL, $max_delta_alias); + + // When the schema for the entity does not exist the query will throw an + // exception. This should only happen in tests. + // @see https://www.drupal.org/node/3475719 + // @todo Remove in Drupal 12. + try { + $result = $query->execute(); + } catch (DatabaseExceptionWrapper $exception) { + return; + } $max_delta = 0; if (is_array($result) && !empty($result)) { -- GitLab From 7131064709687ba92ac6dca4191f2d06b36b796f Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Fri, 28 Mar 2025 15:09:44 +0100 Subject: [PATCH 08/11] fix: Don't check NoEntitiesExistYetWithHigherCardinalityValidator when custom_storage is involved --- .../NoEntitiesExistYetWithHigherCardinalityValidator.php | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php index 6ad53235d43f..f6a15bdccf52 100644 --- a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php @@ -70,6 +70,12 @@ public function validate(mixed $cardinality, SymfonyConstraint $constraint): voi 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() -- GitLab From 47b4e3da6bc522fec90b0a4041c109e65a1d6435 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Fri, 28 Mar 2025 15:11:25 +0100 Subject: [PATCH 09/11] build: codestyle fixes --- .../NoEntitiesExistYetWithHigherCardinalityValidator.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php index f6a15bdccf52..aafd8f68e687 100644 --- a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php @@ -88,7 +88,8 @@ public function validate(mixed $cardinality, SymfonyConstraint $constraint): voi // @todo Remove in Drupal 12. try { $result = $query->execute(); - } catch (DatabaseExceptionWrapper $exception) { + } + catch (DatabaseExceptionWrapper) { return; } -- GitLab From c038907b0aa1119bf7af1ca44192dacc47f2d2d5 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Fri, 28 Mar 2025 21:35:04 +0100 Subject: [PATCH 10/11] doc: fix some comments as per feedback. Those were copied from another class that was the base of this one --- .../Constraint/NoEntitiesExistYetWithHigherCardinality.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinality.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinality.php index a2bb7bd30264..88b976e568ac 100644 --- a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinality.php +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinality.php @@ -9,7 +9,7 @@ use Drupal\Core\Validation\Attribute\Constraint; /** - * Checks if a plugin exists and optionally implements a particular interface. + * Checks if an entity with a higher cardinality than specified exists. */ #[Constraint( id: 'NoEntitiesExistYetWithHigherCardinality', @@ -18,11 +18,12 @@ class NoEntitiesExistYetWithHigherCardinality extends SymfonyConstraint { /** - * The error message if a plugin does not implement the expected interface. + * The error message if an entity with a higher cardinality exists. * * @var string */ public string $message = "The field '@field_name' of entity type '@entity_type' has more entries (@max_delta) than the cardinality (@cardinality) allows."; + /** * The entity type to check. * -- GitLab From 848ad1c9226bafb7f107f5f457ce3b751dc25b41 Mon Sep 17 00:00:00 2001 From: bjorn <bjorn@swis.nl> Date: Wed, 2 Apr 2025 14:54:04 +0200 Subject: [PATCH 11/11] build: fix missing docblock --- .../NoEntitiesExistYetWithHigherCardinalityValidator.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php index aafd8f68e687..e2dd2dbf5b52 100644 --- a/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php +++ b/core/modules/field/src/Plugin/Validation/Constraint/NoEntitiesExistYetWithHigherCardinalityValidator.php @@ -41,6 +41,9 @@ public function __construct( ) { } + /** + * {@inheritdoc} + */ public static function create(ContainerInterface $container): self { return new static( $container->get('entity_type.manager') -- GitLab