bug #36865 [Form] validate subforms in all validation groups (xabbuh)
This PR was merged into the 3.4 branch.
Discussion
----------
[Form] validate subforms in all validation groups
| Q | A
| ------------- | ---
| Branch? | 3.4
| Bug fix? | yes
| New feature? | no
| Deprecations? | no
| Tickets | Fix #36852
| License | MIT
| Doc PR |
Commits
-------
b819d94d14
validate subforms in all validation groups
This commit is contained in:
commit
2e8ae40183
|
@ -63,12 +63,16 @@ class FormValidator extends ConstraintValidator
|
|||
/** @var Constraint[] $constraints */
|
||||
$constraints = $config->getOption('constraints', []);
|
||||
|
||||
$hasChildren = $form->count() > 0;
|
||||
|
||||
if ($hasChildren && $form->isRoot()) {
|
||||
$this->resolvedGroups = new \SplObjectStorage();
|
||||
}
|
||||
|
||||
if ($groups instanceof GroupSequence) {
|
||||
// Validate the data, the form AND nested fields in sequence
|
||||
$violationsCount = $this->context->getViolations()->count();
|
||||
$fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s';
|
||||
$hasChildren = $form->count() > 0;
|
||||
$this->resolvedGroups = $hasChildren ? new \SplObjectStorage() : null;
|
||||
|
||||
foreach ($groups->groups as $group) {
|
||||
if ($validateDataGraph) {
|
||||
|
@ -86,7 +90,8 @@ class FormValidator extends ConstraintValidator
|
|||
// sequence recursively, thus some fields could fail
|
||||
// in different steps without breaking early enough
|
||||
$this->resolvedGroups[$field] = (array) $group;
|
||||
$validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $formConstraint);
|
||||
$fieldFormConstraint = new Form();
|
||||
$validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $fieldFormConstraint);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -94,12 +99,9 @@ class FormValidator extends ConstraintValidator
|
|||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasChildren) {
|
||||
// destroy storage at the end of the sequence to avoid memory leaks
|
||||
$this->resolvedGroups = null;
|
||||
}
|
||||
} else {
|
||||
$fieldPropertyPath = \is_object($data) ? 'children[%s]' : 'children%s';
|
||||
|
||||
if ($validateDataGraph) {
|
||||
$validator->atPath('data')->validate($data, null, $groups);
|
||||
}
|
||||
|
@ -125,6 +127,19 @@ class FormValidator extends ConstraintValidator
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($form->all() as $field) {
|
||||
if ($field->isSubmitted()) {
|
||||
$this->resolvedGroups[$field] = $groups;
|
||||
$fieldFormConstraint = new Form();
|
||||
$validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $fieldFormConstraint);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($hasChildren && $form->isRoot()) {
|
||||
// destroy storage to avoid memory leaks
|
||||
$this->resolvedGroups = new \SplObjectStorage();
|
||||
}
|
||||
} elseif (!$form->isSynchronized()) {
|
||||
$childrenSynchronized = true;
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace Symfony\Component\Form\Extension\Validator;
|
|||
|
||||
use Symfony\Component\Form\AbstractExtension;
|
||||
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
|
||||
use Symfony\Component\Validator\Constraints\Valid;
|
||||
use Symfony\Component\Validator\Constraints\Traverse;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
use Symfony\Component\Validator\Validator\ValidatorInterface;
|
||||
|
||||
|
@ -37,7 +37,7 @@ class ValidatorExtension extends AbstractExtension
|
|||
|
||||
/* @var $metadata ClassMetadata */
|
||||
$metadata->addConstraint(new Form());
|
||||
$metadata->addPropertyConstraint('children', new Valid());
|
||||
$metadata->addConstraint(new Traverse(false));
|
||||
|
||||
$this->validator = $validator;
|
||||
}
|
||||
|
|
|
@ -6,8 +6,8 @@
|
|||
|
||||
<class name="Symfony\Component\Form\Form">
|
||||
<constraint name="Symfony\Component\Form\Extension\Validator\Constraints\Form" />
|
||||
<property name="children">
|
||||
<constraint name="Valid" />
|
||||
</property>
|
||||
<constraint name="Symfony\Component\Validator\Constraints\Traverse">
|
||||
<option name="traverse">false</option>
|
||||
</constraint>
|
||||
</class>
|
||||
</constraint-mapping>
|
||||
|
|
|
@ -615,7 +615,8 @@ class FormValidatorTest extends ConstraintValidatorTestCase
|
|||
|
||||
$this->assertTrue($form->isSubmitted());
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
$this->expectNoValidate();
|
||||
|
||||
$this->expectValidateValueAt(0, 'children[child]', $form->get('child'), new Form());
|
||||
|
||||
$this->validator->validate($form, new Form());
|
||||
|
||||
|
@ -638,7 +639,8 @@ class FormValidatorTest extends ConstraintValidatorTestCase
|
|||
|
||||
$this->assertTrue($form->isSubmitted());
|
||||
$this->assertTrue($form->isSynchronized());
|
||||
$this->expectNoValidate();
|
||||
|
||||
$this->expectValidateValueAt(0, 'children[child]', $form->get('child'), new Form());
|
||||
|
||||
$this->validator->validate($form, new Form());
|
||||
|
||||
|
|
|
@ -54,9 +54,8 @@ class ValidatorExtensionTest extends TestCase
|
|||
$this->assertInstanceOf(FormConstraint::class, $metadata->getConstraints()[0]);
|
||||
|
||||
$this->assertSame(CascadingStrategy::NONE, $metadata->cascadingStrategy);
|
||||
$this->assertSame(TraversalStrategy::IMPLICIT, $metadata->traversalStrategy);
|
||||
$this->assertSame(CascadingStrategy::CASCADE, $metadata->getPropertyMetadata('children')[0]->cascadingStrategy);
|
||||
$this->assertSame(TraversalStrategy::IMPLICIT, $metadata->getPropertyMetadata('children')[0]->traversalStrategy);
|
||||
$this->assertSame(TraversalStrategy::NONE, $metadata->traversalStrategy);
|
||||
$this->assertCount(0, $metadata->getPropertyMetadata('children'));
|
||||
}
|
||||
|
||||
public function testDataConstraintsInvalidateFormEvenIfFieldIsNotSubmitted()
|
||||
|
@ -138,6 +137,33 @@ class ValidatorExtensionTest extends TestCase
|
|||
$this->assertInstanceOf(Length::class, $errors[1]->getCause()->getConstraint());
|
||||
}
|
||||
|
||||
public function testConstraintsInDifferentGroupsOnSingleField()
|
||||
{
|
||||
$form = $this->createForm(FormType::class, null, [
|
||||
'validation_groups' => new GroupSequence(['group1', 'group2']),
|
||||
])
|
||||
->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());
|
||||
}
|
||||
|
||||
private function createForm($type, $data = null, array $options = [])
|
||||
{
|
||||
$validator = Validation::createValidatorBuilder()
|
||||
|
|
Reference in New Issue