Merge branch '4.4' into 5.1

* 4.4:
  disallow FrameworkBundle 4.4+
  propagate validation groups to subforms
  [Form] [Validator] Add failing testcase to demonstrate group sequence issue
This commit is contained in:
Nicolas Grekas 2020-10-02 14:58:01 +02:00
commit 8ea3e7a6bb
2 changed files with 37 additions and 2 deletions

View File

@ -25,6 +25,7 @@ use Symfony\Component\Validator\Exception\UnexpectedTypeException;
class FormValidator extends ConstraintValidator class FormValidator extends ConstraintValidator
{ {
private $resolvedGroups; private $resolvedGroups;
private $fieldFormConstraints;
/** /**
* {@inheritdoc} * {@inheritdoc}
@ -67,6 +68,7 @@ class FormValidator extends ConstraintValidator
if ($hasChildren && $form->isRoot()) { if ($hasChildren && $form->isRoot()) {
$this->resolvedGroups = new \SplObjectStorage(); $this->resolvedGroups = new \SplObjectStorage();
$this->fieldFormConstraints = [];
} }
if ($groups instanceof GroupSequence) { if ($groups instanceof GroupSequence) {
@ -84,14 +86,16 @@ class FormValidator extends ConstraintValidator
foreach ($form->all() as $field) { foreach ($form->all() as $field) {
if ($field->isSubmitted()) { 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 // otherwise resolving the groups would reuse the same
// sequence recursively, thus some fields could fail // sequence recursively, thus some fields could fail
// in different steps without breaking early enough // in different steps without breaking early enough
$this->resolvedGroups[$field] = (array) $group; $this->resolvedGroups[$field] = (array) $group;
$fieldFormConstraint = new Form(); $fieldFormConstraint = new Form();
$fieldFormConstraint->groups = $group;
$this->fieldFormConstraints[] = $fieldFormConstraint;
$this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); $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()) { if ($field->isSubmitted()) {
$this->resolvedGroups[$field] = $groups; $this->resolvedGroups[$field] = $groups;
$fieldFormConstraint = new Form(); $fieldFormConstraint = new Form();
$this->fieldFormConstraints[] = $fieldFormConstraint;
$this->context->setNode($this->context->getValue(), $field, $this->context->getMetadata(), $this->context->getPropertyPath()); $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);
} }
@ -145,6 +150,7 @@ class FormValidator extends ConstraintValidator
if ($hasChildren && $form->isRoot()) { if ($hasChildren && $form->isRoot()) {
// destroy storage to avoid memory leaks // destroy storage to avoid memory leaks
$this->resolvedGroups = new \SplObjectStorage(); $this->resolvedGroups = new \SplObjectStorage();
$this->fieldFormConstraints = [];
} }
} elseif (!$form->isSynchronized()) { } elseif (!$form->isSynchronized()) {
$childrenSynchronized = true; $childrenSynchronized = true;
@ -155,6 +161,7 @@ class FormValidator extends ConstraintValidator
$childrenSynchronized = false; $childrenSynchronized = false;
$fieldFormConstraint = new Form(); $fieldFormConstraint = new Form();
$this->fieldFormConstraints[] = $fieldFormConstraint;
$this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath()); $this->context->setNode($this->context->getValue(), $child, $this->context->getMetadata(), $this->context->getPropertyPath());
$validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $fieldFormConstraint); $validator->atPath(sprintf('children[%s]', $child->getName()))->validate($child, $fieldFormConstraint);
} }

View File

@ -233,6 +233,34 @@ class FormValidatorFunctionalTest extends TestCase
$this->assertInstanceOf(Length::class, $errors[0]->getCause()->getConstraint()); $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() public function testCascadeValidationToChildFormsUsingPropertyPaths()
{ {
$form = $this->formFactory->create(FormType::class, null, [ $form = $this->formFactory->create(FormType::class, null, [