From 0c70a410e539486c1901e96201a9a863458034a6 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Mon, 16 Jan 2012 20:44:37 +0100 Subject: [PATCH] [Form] Made validation of form children configurable. Set the option "cascade_validation" to `true` if you need it. --- .../Type/FieldTypeValidatorExtension.php | 2 + .../Validator/DelegatingValidator.php | 23 ++++++ .../Form/Resources/config/validation.xml | 7 +- .../Validator/DelegatingValidatorTest.php | 75 +++++++++++++++++++ 4 files changed, 104 insertions(+), 3 deletions(-) diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php index 2595101374..1cd0b10f59 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/FieldTypeValidatorExtension.php @@ -38,6 +38,7 @@ class FieldTypeValidatorExtension extends AbstractTypeExtension $builder ->setAttribute('validation_groups', $options['validation_groups']) ->setAttribute('validation_constraint', $options['validation_constraint']) + ->setAttribute('cascade_validation', $options['cascade_validation']) ->addValidator(new DelegatingValidator($this->validator)); } @@ -46,6 +47,7 @@ class FieldTypeValidatorExtension extends AbstractTypeExtension return array( 'validation_groups' => null, 'validation_constraint' => null, + 'cascade_validation' => false, ); } diff --git a/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php b/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php index a8b0672402..2b7c099403 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Validator/DelegatingValidator.php @@ -127,6 +127,29 @@ class DelegatingValidator implements FormValidatorInterface } } + static public function validateFormChildren(FormInterface $form, ExecutionContext $context) + { + if ($form->getAttribute('cascade_validation')) { + $propertyPath = $context->getPropertyPath(); + $graphWalker = $context->getGraphWalker(); + + // The Execute constraint is called on class level, so we need to + // set the property manually + $context->setCurrentProperty('children'); + + // Adjust the property path accordingly + if (!empty($propertyPath)) { + $propertyPath .= '.'; + } + + $propertyPath .= 'children'; + + foreach (self::getFormValidationGroups($form) as $group) { + $graphWalker->walkReference($form->getChildren(), $group, $propertyPath, true); + } + } + } + static protected function getFormValidationGroups(FormInterface $form) { $groups = null; diff --git a/src/Symfony/Component/Form/Resources/config/validation.xml b/src/Symfony/Component/Form/Resources/config/validation.xml index 5512560c6d..7bd69402c0 100644 --- a/src/Symfony/Component/Form/Resources/config/validation.xml +++ b/src/Symfony/Component/Form/Resources/config/validation.xml @@ -10,9 +10,10 @@ Symfony\Component\Form\Extension\Validator\Validator\DelegatingValidator validateFormData + + Symfony\Component\Form\Extension\Validator\Validator\DelegatingValidator + validateFormChildren + - - - diff --git a/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php b/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php index 22701304a6..1d16892dc6 100644 --- a/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php +++ b/tests/Symfony/Tests/Component/Form/Extension/Validator/Validator/DelegatingValidatorTest.php @@ -798,6 +798,81 @@ class DelegatingValidatorTest extends \PHPUnit_Framework_TestCase DelegatingValidator::validateFormData($form, $context); } + public function testValidateFormChildren() + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $form = $this->getBuilder() + ->setAttribute('cascade_validation', true) + ->setAttribute('validation_groups', array('group1', 'group2')) + ->getForm(); + $form->add($this->getForm('firstName')); + + $graphWalker->expects($this->at(0)) + ->method('walkReference') + ->with($form->getChildren(), 'group1', 'children', true); + $graphWalker->expects($this->at(1)) + ->method('walkReference') + ->with($form->getChildren(), 'group2', 'children', true); + + DelegatingValidator::validateFormChildren($form, $context); + } + + public function testValidateFormChildrenAppendsPropertyPath() + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $context->setPropertyPath('path'); + $form = $this->getBuilder() + ->setAttribute('cascade_validation', true) + ->getForm(); + $form->add($this->getForm('firstName')); + + $graphWalker->expects($this->once()) + ->method('walkReference') + ->with($form->getChildren(), 'Default', 'path.children', true); + + DelegatingValidator::validateFormChildren($form, $context); + } + + public function testValidateFormChildrenSetsCurrentPropertyToData() + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $form = $this->getBuilder() + ->setAttribute('cascade_validation', true) + ->getForm(); + $form->add($this->getForm('firstName')); + $test = $this; + + $graphWalker->expects($this->once()) + ->method('walkReference') + ->will($this->returnCallback(function () use ($context, $test) { + $test->assertEquals('children', $context->getCurrentProperty()); + })); + + DelegatingValidator::validateFormChildren($form, $context); + } + + public function testValidateFormChildrenDoesNothingIfDisabled() + { + $graphWalker = $this->getMockGraphWalker(); + $metadataFactory = $this->getMockMetadataFactory(); + $context = new ExecutionContext('Root', $graphWalker, $metadataFactory); + $form = $this->getBuilder() + ->setAttribute('cascade_validation', false) + ->getForm(); + $form->add($this->getForm('firstName')); + + $graphWalker->expects($this->never()) + ->method('walkReference'); + + DelegatingValidator::validateFormChildren($form, $context); + } + public function testValidateIgnoresNonRoot() { $form = $this->getMockForm();