diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index cdf4cde4ef..1c63ecf5f4 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -25,6 +25,7 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException; class FormValidator extends ConstraintValidator { private $resolvedGroups; + private $fieldFormConstraints; /** * {@inheritdoc} @@ -67,6 +68,7 @@ class FormValidator extends ConstraintValidator if ($hasChildren && $form->isRoot()) { $this->resolvedGroups = new \SplObjectStorage(); + $this->fieldFormConstraints = []; } if ($groups instanceof GroupSequence) { @@ -84,14 +86,16 @@ class FormValidator extends ConstraintValidator foreach ($form->all() as $field) { if ($field->isSubmitted()) { - // remember to validate this field is one group only + // remember to validate this field in one group only // otherwise resolving the groups would reuse the same // sequence recursively, thus some fields could fail // in different steps without breaking early enough $this->resolvedGroups[$field] = (array) $group; $fieldFormConstraint = new Form(); + $fieldFormConstraint->groups = $group; + $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); - $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint); + $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint, $group); } } @@ -136,6 +140,7 @@ class FormValidator extends ConstraintValidator if ($field->isSubmitted()) { $this->resolvedGroups[$field] = $groups; $fieldFormConstraint = new Form(); + $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); $validator->atPath(sprintf('children[%s]', $field->getName()))->validate($field, $fieldFormConstraint); } @@ -145,6 +150,7 @@ class FormValidator extends ConstraintValidator if ($hasChildren && $form->isRoot()) { // destroy storage to avoid memory leaks $this->resolvedGroups = new \SplObjectStorage(); + $this->fieldFormConstraints = []; } } elseif (!$form->isSynchronized()) { $childrenSynchronized = true; @@ -155,6 +161,7 @@ class FormValidator extends ConstraintValidator $childrenSynchronized = false; $fieldFormConstraint = new Form(); + $this->fieldFormConstraints[] = $fieldFormConstraint; $this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath()); $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $fieldFormConstraint); } diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php index a811671bd7..8469b99e1b 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorFunctionalTest.php @@ -233,6 +233,34 @@ class FormValidatorFunctionalTest extends TestCase $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint()); } + public function testConstraintsInDifferentGroupsOnSingleFieldWithAdditionalFieldThatHasNoConstraintsAddedBeforeTheFieldWithConstraints() + { + $form = $this->formFactory->create(FormType::class, null, [ + 'validation_groups' => new GroupSequence(['group1', 'group2']), + ]) + ->add('bar') + ->add('foo', TextType::class, [ + 'constraints' => [ + new NotBlank([ + 'groups' => ['group1'], + ]), + new Length([ + 'groups' => ['group2'], + 'max' => 3, + ]), + ], + ]); + $form->submit([ + 'foo' => 'test@example.com', + ]); + + $errors = $form->getErrors(true); + + $this->assertFalse($form->isValid()); + $this->assertCount(1, $errors); + $this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint()); + } + public function testCascadeValidationToChildFormsUsingPropertyPaths() { $form = $this->formFactory->create(FormType::class, null, [