Skip to content

Commit

Permalink
validate subforms in all validation groups
Browse files Browse the repository at this point in the history
  • Loading branch information
xabbuh committed May 29, 2020
1 parent 5ec5bfb commit b819d94
Show file tree
Hide file tree
Showing 5 changed files with 61 additions and 18 deletions.
Expand Up @@ -63,12 +63,16 @@ public function validate($form, Constraint $formConstraint)
/** @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) {
Expand All @@ -86,20 +90,18 @@ public function validate($form, Constraint $formConstraint)
// 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);
}
}

if ($violationsCount < $this->context->getViolations()->count()) {
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);
}
Expand All @@ -125,6 +127,19 @@ public function validate($form, Constraint $formConstraint)
}
}
}

foreach ($form->all() as $field) {
if ($field->isSubmitted()) {
$this->resolvedGroups[$field] = $groups;
$fieldFormConstraint = new Form();
$validator->atPath(sprintf($fieldPropertyPath, $field->getPropertyPath()))->validate($field, $fieldFormConstraint);

This comment has been minimized.

Copy link
@TvitiDev

TvitiDev Jun 4, 2020

This need use $field->getName(), else fail validation don't will point to the original property name

This comment has been minimized.

Copy link
@xabbuh

xabbuh Jun 4, 2020

Author Member

see #37025, #37027 and #37085

This comment has been minimized.

Copy link
@TvitiDev

TvitiDev Jun 4, 2020

Thanks. How soon will this fix be in version 3.4.42?

This comment has been minimized.

Copy link
@xabbuh

xabbuh Jun 4, 2020

Author Member

We usually do patch releases once a month.

}
}
}

if ($hasChildren && $form->isRoot()) {
// destroy storage to avoid memory leaks
$this->resolvedGroups = new \SplObjectStorage();
}
} elseif (!$form->isSynchronized()) {
$childrenSynchronized = true;
Expand Down
Expand Up @@ -13,7 +13,7 @@

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;

Expand All @@ -37,7 +37,7 @@ public function __construct(ValidatorInterface $validator)

/* @var $metadata ClassMetadata */
$metadata->addConstraint(new Form());
$metadata->addPropertyConstraint('children', new Valid());
$metadata->addConstraint(new Traverse(false));

$this->validator = $validator;
}
Expand Down
6 changes: 3 additions & 3 deletions src/Symfony/Component/Form/Resources/config/validation.xml
Expand Up @@ -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>
Expand Up @@ -615,7 +615,8 @@ public function testViolationIfExtraData()

$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());

Expand All @@ -638,7 +639,8 @@ public function testViolationFormatIfMultipleExtraFields()

$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());

Expand Down
Expand Up @@ -54,9 +54,8 @@ public function test2Dot5ValidationApi()
$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()
Expand Down Expand Up @@ -138,6 +137,33 @@ public function testFieldsValidateInSequenceWithNestedGroupsArray()
$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()
Expand Down

0 comments on commit b819d94

Please sign in to comment.