Loading core/lib/Drupal/Core/Validation/ConstraintValidatorFactory.php +10 −6 Original line number Diff line number Diff line Loading @@ -8,6 +8,9 @@ /** * Defines a constraint validator factory that works with container injection. * * @TODO Decide what to do with this class or how to reuse constraint * validators in https://drupal.org/project/drupal/issues/3097071 */ class ConstraintValidatorFactory extends BaseConstraintValidatorFactory { Loading @@ -25,12 +28,13 @@ public function __construct(ClassResolverInterface $class_resolver) { */ public function getInstance(Constraint $constraint) { $class_name = $constraint->validatedBy(); if (!isset($this->validators[$class_name])) { $this->validators[$class_name] = $this->classResolver->getInstanceFromDefinition($class_name); } return $this->validators[$class_name]; // Constraint validator instances should always be initialized newly and // never shared, because the current validation context is getting injected // into them through setter injection and in a case of a recursive // validation where a validator triggers a validation chain leading to the // same validator the context of the first call would be exchanged with the // one of the subsequent validation chain. return $this->classResolver->getInstanceFromDefinition($class_name); } } core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/TestValidatedReferenceConstraint.php 0 → 100644 +24 −0 Original line number Diff line number Diff line <?php namespace Drupal\entity_test\Plugin\Validation\Constraint; use Symfony\Component\Validator\Constraint; /** * Validates referenced entities. * * @Constraint( * id = "TestValidatedReferenceConstraint", * label = @Translation("Test validated reference constraint.") * ) */ class TestValidatedReferenceConstraint extends Constraint { /** * The default violation message. * * @var string */ public $message = 'Invalid referenced entity.'; } core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/TestValidatedReferenceConstraintValidator.php 0 → 100644 +29 −0 Original line number Diff line number Diff line <?php namespace Drupal\entity_test\Plugin\Validation\Constraint; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; /** * Validates referenced entities. */ class TestValidatedReferenceConstraintValidator extends ConstraintValidator { /** * {@inheritdoc} */ public function validate($items, Constraint $constraint) { if (!isset($items)) { return; } foreach ($items as $item) { $violations = $item->entity->validate(); foreach ($violations as $violation) { // Add the reason for the validation failure to the current context. $this->context->buildViolation($constraint->message)->addViolation(); } } } } core/tests/Drupal/KernelTests/Core/TypedData/RecursiveContextualValidatorTest.php +37 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ namespace Drupal\KernelTests\Core\TypedData; use Drupal\Core\Entity\Plugin\DataType\EntityAdapter; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\entity_test\Entity\EntityTest; use Drupal\KernelTests\KernelTestBase; Loading Loading @@ -41,4 +42,40 @@ public function testRecursiveValidate() { $this->assertCount(0, \Drupal::typedDataManager()->getValidator()->validate($adapter, $adapter->getConstraints())); } /** * Test recursive propagation of violations. */ public function testRecursiveViolationPropagation() { // We create an entity reference field with a constraint which will // trigger the validation of the referenced entities. Then we add a // required field and populate it only on the parent entity, so that // the child entity fails the validation. $definitions['field_test'] = BaseFieldDefinition::create('entity_reference') ->setLabel('Test reference') ->setSetting('target_type', 'entity_test') ->addConstraint('TestValidatedReferenceConstraint'); $definitions['string_required'] = BaseFieldDefinition::create('string') ->setLabel('Required string') ->setRequired(TRUE); $this->container->get('state')->set('entity_test.additional_base_field_definitions', $definitions); drupal_flush_all_caches(); $this->installEntitySchema('entity_test'); $child = EntityTest::create([ 'name' => 'test2', 'user_id' => ['target_id' => 0], ]); $parent = EntityTest::create([ 'name' => 'test', 'user_id' => ['target_id' => 0], 'string_required' => 'some string', 'field_test' => ['entity' => $child], ]); // The child entity should fail the validation and the violation should // propagate to the parent. $violations = $parent->validate(); $this->assertCount(1, $violations); $this->assertEquals('field_test', $violations[0]->getPropertyPath()); $this->assertEquals('Invalid referenced entity.', $violations[0]->getMessage()); } } Loading
core/lib/Drupal/Core/Validation/ConstraintValidatorFactory.php +10 −6 Original line number Diff line number Diff line Loading @@ -8,6 +8,9 @@ /** * Defines a constraint validator factory that works with container injection. * * @TODO Decide what to do with this class or how to reuse constraint * validators in https://drupal.org/project/drupal/issues/3097071 */ class ConstraintValidatorFactory extends BaseConstraintValidatorFactory { Loading @@ -25,12 +28,13 @@ public function __construct(ClassResolverInterface $class_resolver) { */ public function getInstance(Constraint $constraint) { $class_name = $constraint->validatedBy(); if (!isset($this->validators[$class_name])) { $this->validators[$class_name] = $this->classResolver->getInstanceFromDefinition($class_name); } return $this->validators[$class_name]; // Constraint validator instances should always be initialized newly and // never shared, because the current validation context is getting injected // into them through setter injection and in a case of a recursive // validation where a validator triggers a validation chain leading to the // same validator the context of the first call would be exchanged with the // one of the subsequent validation chain. return $this->classResolver->getInstanceFromDefinition($class_name); } }
core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/TestValidatedReferenceConstraint.php 0 → 100644 +24 −0 Original line number Diff line number Diff line <?php namespace Drupal\entity_test\Plugin\Validation\Constraint; use Symfony\Component\Validator\Constraint; /** * Validates referenced entities. * * @Constraint( * id = "TestValidatedReferenceConstraint", * label = @Translation("Test validated reference constraint.") * ) */ class TestValidatedReferenceConstraint extends Constraint { /** * The default violation message. * * @var string */ public $message = 'Invalid referenced entity.'; }
core/modules/system/tests/modules/entity_test/src/Plugin/Validation/Constraint/TestValidatedReferenceConstraintValidator.php 0 → 100644 +29 −0 Original line number Diff line number Diff line <?php namespace Drupal\entity_test\Plugin\Validation\Constraint; use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\ConstraintValidator; /** * Validates referenced entities. */ class TestValidatedReferenceConstraintValidator extends ConstraintValidator { /** * {@inheritdoc} */ public function validate($items, Constraint $constraint) { if (!isset($items)) { return; } foreach ($items as $item) { $violations = $item->entity->validate(); foreach ($violations as $violation) { // Add the reason for the validation failure to the current context. $this->context->buildViolation($constraint->message)->addViolation(); } } } }
core/tests/Drupal/KernelTests/Core/TypedData/RecursiveContextualValidatorTest.php +37 −0 Original line number Diff line number Diff line Loading @@ -3,6 +3,7 @@ namespace Drupal\KernelTests\Core\TypedData; use Drupal\Core\Entity\Plugin\DataType\EntityAdapter; use Drupal\Core\Field\BaseFieldDefinition; use Drupal\entity_test\Entity\EntityTest; use Drupal\KernelTests\KernelTestBase; Loading Loading @@ -41,4 +42,40 @@ public function testRecursiveValidate() { $this->assertCount(0, \Drupal::typedDataManager()->getValidator()->validate($adapter, $adapter->getConstraints())); } /** * Test recursive propagation of violations. */ public function testRecursiveViolationPropagation() { // We create an entity reference field with a constraint which will // trigger the validation of the referenced entities. Then we add a // required field and populate it only on the parent entity, so that // the child entity fails the validation. $definitions['field_test'] = BaseFieldDefinition::create('entity_reference') ->setLabel('Test reference') ->setSetting('target_type', 'entity_test') ->addConstraint('TestValidatedReferenceConstraint'); $definitions['string_required'] = BaseFieldDefinition::create('string') ->setLabel('Required string') ->setRequired(TRUE); $this->container->get('state')->set('entity_test.additional_base_field_definitions', $definitions); drupal_flush_all_caches(); $this->installEntitySchema('entity_test'); $child = EntityTest::create([ 'name' => 'test2', 'user_id' => ['target_id' => 0], ]); $parent = EntityTest::create([ 'name' => 'test', 'user_id' => ['target_id' => 0], 'string_required' => 'some string', 'field_test' => ['entity' => $child], ]); // The child entity should fail the validation and the violation should // propagate to the parent. $violations = $parent->validate(); $this->assertCount(1, $violations); $this->assertEquals('field_test', $violations[0]->getPropertyPath()); $this->assertEquals('Invalid referenced entity.', $violations[0]->getMessage()); } }