From 6c554c6c1af6ca6a454c4aaed2b88135667c1153 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Thu, 16 Oct 2014 15:46:22 +0200 Subject: [PATCH] [Form] Deprecated "cascade_validation" --- UPGRADE-2.8.md | 43 ++++++++++ src/Symfony/Component/Form/CHANGELOG.md | 2 + .../Validator/Constraints/FormValidator.php | 15 ++++ .../Type/FormTypeValidatorExtension.php | 11 ++- .../Constraints/FormValidatorTest.php | 86 ++++++++++++++----- .../Type/FormTypeValidatorExtensionTest.php | 28 ++++++ .../AbstractConstraintValidatorTest.php | 26 ++++-- 7 files changed, 180 insertions(+), 31 deletions(-) diff --git a/UPGRADE-2.8.md b/UPGRADE-2.8.md index 07c336c7c2..a58bb1834f 100644 --- a/UPGRADE-2.8.md +++ b/UPGRADE-2.8.md @@ -1,8 +1,51 @@ UPGRADE FROM 2.7 to 2.8 ======================= +Form +---- + + * The "cascade_validation" option was deprecated. Use the "constraints" + option together with the `Valid` constraint instead. Contrary to + "cascade_validation", "constraints" must be set on the respective child forms, + not the parent form. + + Before: + + ```php + $form = $this->createForm('form', $article, array('cascade_validation' => true)) + ->add('author', new AuthorType()) + ->getForm(); + ``` + + After: + + ```php + use Symfony\Component\Validator\Constraints\Valid; + + $form = $this->createForm('form', $article) + ->add('author', new AuthorType(), array( + 'constraints' => new Valid(), + )) + ->getForm(); + ``` + + Alternatively, you can set the `Valid` constraint in the model itself: + + ```php + use Symfony\Component\Validator\Constraints as Assert; + + class Article + { + /** + * @Assert\Valid + */ + private $author; + } + ``` + Translator ---------- + * The `getMessages()` method of the `Symfony\Component\Translation\Translator` was deprecated and will be removed in Symfony 3.0. You should use the `getCatalogue()` method of the `Symfony\Component\Translation\TranslatorBagInterface`. diff --git a/src/Symfony/Component/Form/CHANGELOG.md b/src/Symfony/Component/Form/CHANGELOG.md index 014bdbe26e..330f0b1417 100644 --- a/src/Symfony/Component/Form/CHANGELOG.md +++ b/src/Symfony/Component/Form/CHANGELOG.md @@ -6,6 +6,8 @@ CHANGELOG * deprecated option "read_only" in favor of "attr['readonly']" * added the html5 "range" FormType + * deprecated the "cascade_validation" option in favor of setting "constraints" + with the Valid constraint 2.7.0 ----- diff --git a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php index 19e9dd5d70..25a8f3a3ed 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php +++ b/src/Symfony/Component/Form/Extension/Validator/Constraints/FormValidator.php @@ -13,6 +13,7 @@ namespace Symfony\Component\Form\Extension\Validator\Constraints; use Symfony\Component\Form\FormInterface; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\ConstraintValidator; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Exception\UnexpectedTypeException; @@ -63,6 +64,20 @@ class FormValidator extends ConstraintValidator // in the form $constraints = $config->getOption('constraints'); foreach ($constraints as $constraint) { + // For the "Valid" constraint, validate the data in all groups + if ($constraint instanceof Valid) { + if ($validator) { + $validator->atPath('data')->validate($form->getData(), $constraint, $groups); + } else { + // 2.4 API + $this->context->validateValue($form->getData(), $constraint, 'data', $groups); + } + + continue; + } + + // Otherwise validate a constraint only once for the first + // matching group foreach ($groups as $group) { if (in_array($group, $constraint->groups)) { if ($validator) { diff --git a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php index 16030ae03f..97c0d67997 100644 --- a/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php +++ b/src/Symfony/Component/Form/Extension/Validator/Type/FormTypeValidatorExtension.php @@ -67,10 +67,18 @@ class FormTypeValidatorExtension extends BaseValidatorExtension return is_object($constraints) ? array($constraints) : (array) $constraints; }; + $cascadeValidationNormalizer = function (Options $options, $cascadeValidation) { + if (null !== $cascadeValidation) { + @trigger_error('The "cascade_validation" option is deprecated since version 2.8 and will be removed in 3.0. Use "constraints" with a Valid constraint instead.', E_USER_DEPRECATED); + } + + return null === $cascadeValidation ? false : $cascadeValidation; + }; + $resolver->setDefaults(array( 'error_mapping' => array(), 'constraints' => array(), - 'cascade_validation' => false, + 'cascade_validation' => null, 'invalid_message' => 'This value is not valid.', 'invalid_message_parameters' => array(), 'allow_extra_fields' => false, @@ -78,6 +86,7 @@ class FormTypeValidatorExtension extends BaseValidatorExtension )); $resolver->setNormalizer('constraints', $constraintsNormalizer); + $resolver->setNormalizer('cascade_validation', $cascadeValidationNormalizer); } /** diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php index ba40307c3a..74d6095d17 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Constraints/FormValidatorTest.php @@ -18,9 +18,9 @@ use Symfony\Component\Form\FormInterface; use Symfony\Component\Form\Extension\Validator\Constraints\Form; use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator; use Symfony\Component\Form\SubmitButtonBuilder; -use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\NotBlank; +use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest; use Symfony\Component\Validator\Validation; @@ -109,6 +109,52 @@ class FormValidatorTest extends AbstractConstraintValidatorTest $this->assertNoViolation(); } + public function testValidateIfParentWithCascadeValidation() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array('validation_groups' => array('group1', 'group2')); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, 'group1'); + $this->expectValidateAt(1, 'data', $object, 'group2'); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + + public function testValidateIfChildWithValidConstraint() + { + $object = $this->getMock('\stdClass'); + + $parent = $this->getBuilder('parent') + ->setCompound(true) + ->setDataMapper($this->getDataMapper()) + ->getForm(); + $options = array( + 'validation_groups' => array('group1', 'group2'), + 'constraints' => array(new Valid()), + ); + $form = $this->getBuilder('name', '\stdClass', $options)->getForm(); + $parent->add($form); + + $form->setData($object); + + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); + + $this->validator->validate($form, new Form()); + + $this->assertNoViolation(); + } + public function testDontValidateIfParentWithoutCascadeValidation() { $object = $this->getMock('\stdClass'); @@ -387,12 +433,13 @@ class FormValidatorTest extends AbstractConstraintValidatorTest { $object = $this->getMock('\stdClass'); - $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + $parent = $this->getBuilder('parent') ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); $form = $this->getForm('name', '\stdClass', array( 'validation_groups' => 'form_group', + 'constraints' => array(new Valid()), )); $parent->add($form); @@ -402,7 +449,7 @@ class FormValidatorTest extends AbstractConstraintValidatorTest $parent->submit(array('name' => $object, 'submit' => '')); - $this->expectValidateAt(0, 'data', $object, 'button_group'); + $this->expectValidateAt(0, 'data', $object, array('button_group')); $this->validator->validate($form, new Form()); @@ -413,12 +460,13 @@ class FormValidatorTest extends AbstractConstraintValidatorTest { $object = $this->getMock('\stdClass'); - $parent = $this->getBuilder('parent', null, array('cascade_validation' => true)) + $parent = $this->getBuilder('parent') ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); $form = $this->getForm('name', '\stdClass', array( 'validation_groups' => 'form_group', + 'constraints' => array(new Valid()), )); $parent->add($form); @@ -428,7 +476,7 @@ class FormValidatorTest extends AbstractConstraintValidatorTest $form->setData($object); - $this->expectValidateAt(0, 'data', $object, 'form_group'); + $this->expectValidateAt(0, 'data', $object, array('form_group')); $this->validator->validate($form, new Form()); @@ -439,20 +487,18 @@ class FormValidatorTest extends AbstractConstraintValidatorTest { $object = $this->getMock('\stdClass'); - $parentOptions = array( - 'validation_groups' => 'group', - 'cascade_validation' => true, - ); + $parentOptions = array('validation_groups' => 'group'); $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); - $form = $this->getBuilder('name', '\stdClass')->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); $parent->add($form); $form->setData($object); - $this->expectValidateAt(0, 'data', $object, 'group'); + $this->expectValidateAt(0, 'data', $object, array('group')); $this->validator->validate($form, new Form()); @@ -463,21 +509,18 @@ class FormValidatorTest extends AbstractConstraintValidatorTest { $object = $this->getMock('\stdClass'); - $parentOptions = array( - 'validation_groups' => array($this, 'getValidationGroups'), - 'cascade_validation' => true, - ); + $parentOptions = array('validation_groups' => array($this, 'getValidationGroups')); $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); - $form = $this->getBuilder('name', '\stdClass')->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); $parent->add($form); $form->setData($object); - $this->expectValidateAt(0, 'data', $object, 'group1'); - $this->expectValidateAt(1, 'data', $object, 'group2'); + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); $this->validator->validate($form, new Form()); @@ -492,19 +535,18 @@ class FormValidatorTest extends AbstractConstraintValidatorTest 'validation_groups' => function (FormInterface $form) { return array('group1', 'group2'); }, - 'cascade_validation' => true, ); $parent = $this->getBuilder('parent', null, $parentOptions) ->setCompound(true) ->setDataMapper($this->getDataMapper()) ->getForm(); - $form = $this->getBuilder('name', '\stdClass')->getForm(); + $formOptions = array('constraints' => array(new Valid())); + $form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm(); $parent->add($form); $form->setData($object); - $this->expectValidateAt(0, 'data', $object, 'group1'); - $this->expectValidateAt(1, 'data', $object, 'group2'); + $this->expectValidateAt(0, 'data', $object, array('group1', 'group2')); $this->validator->validate($form, new Form()); diff --git a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php index 4ca10da50e..f24f840441 100644 --- a/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php +++ b/src/Symfony/Component/Form/Tests/Extension/Validator/Type/FormTypeValidatorExtensionTest.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Form\Tests\Extension\Validator\Type; use Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension; +use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\ConstraintViolationList; class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest @@ -37,6 +38,33 @@ class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest $form->submit(array()); } + public function testValidConstraint() + { + $form = $this->createForm(array('constraints' => $valid = new Valid())); + + $this->assertSame(array($valid), $form->getConfig()->getOption('constraints')); + } + + /** + * @group legacy + */ + public function testCascadeValidationCanBeSetToTrue() + { + $form = $this->createForm(array('cascade_validation' => true)); + + $this->assertTrue($form->getConfig()->getOption('cascade_validation')); + } + + /** + * @group legacy + */ + public function testCascadeValidationCanBeSetToFalse() + { + $form = $this->createForm(array('cascade_validation' => false)); + + $this->assertFalse($form->getConfig()->getOption('cascade_validation')); + } + public function testValidatorInterfaceSinceSymfony25() { // Mock of ValidatorInterface since apiVersion 2.5 diff --git a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php index e23709f3fc..20de505b58 100644 --- a/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Constraints/AbstractConstraintValidatorTest.php @@ -220,14 +220,24 @@ abstract class AbstractConstraintValidatorTest extends \PHPUnit_Framework_TestCa protected function expectValidateAt($i, $propertyPath, $value, $group) { - $validator = $this->context->getValidator()->inContext($this->context); - $validator->expects($this->at(2 * $i)) - ->method('atPath') - ->with($propertyPath) - ->will($this->returnValue($validator)); - $validator->expects($this->at(2 * $i + 1)) - ->method('validate') - ->with($value, $this->logicalOr(null, array()), $group); + switch ($this->getApiVersion()) { + case Validation::API_VERSION_2_4: + $this->context->expects($this->at($i)) + ->method('validate') + ->with($value, $propertyPath, $group); + break; + case Validation::API_VERSION_2_5: + case Validation::API_VERSION_2_5_BC: + $validator = $this->context->getValidator()->inContext($this->context); + $validator->expects($this->at(2 * $i)) + ->method('atPath') + ->with($propertyPath) + ->will($this->returnValue($validator)); + $validator->expects($this->at(2 * $i + 1)) + ->method('validate') + ->with($value, $this->logicalOr(null, array(), $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group); + break; + } } protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null)