feature #15019 [Form] Deprecated "cascade_validation" (webmozart)
This PR was merged into the 2.8 branch.
Discussion
----------
[Form] Deprecated "cascade_validation"
| Q | A
| ------------- | ---
| Bug fix? | yes
| New feature? | no
| BC breaks? | no
| Deprecations? | yes
| Tests pass? | yes
| Fixed tickets | #11268 (requires explicit work though)
| License | MIT
| Doc PR | TODO
This is #12237 rebased on 2.8.
The "cascade_validation" option was designed for a 1% use case and comparatively used way too often when the `Valid` constraint should have been used instead. Also, there seem to be bugs with that option (#5204).
The option is now deprecated. When using the 2.5 Validator API, you can set the "constraints" option of the respective child to a `Valid` constraint instead. Alternatively, set the constraint in the model (as most people hopefully do).
Commits
-------
6c554c6
[Form] Deprecated "cascade_validation"
This commit is contained in:
commit
04ae391318
@ -1,8 +1,51 @@
|
|||||||
UPGRADE FROM 2.7 to 2.8
|
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
|
Translator
|
||||||
----------
|
----------
|
||||||
|
|
||||||
* The `getMessages()` method of the `Symfony\Component\Translation\Translator` was deprecated and will be removed in
|
* 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`.
|
Symfony 3.0. You should use the `getCatalogue()` method of the `Symfony\Component\Translation\TranslatorBagInterface`.
|
||||||
|
|
||||||
|
@ -6,6 +6,8 @@ CHANGELOG
|
|||||||
|
|
||||||
* deprecated option "read_only" in favor of "attr['readonly']"
|
* deprecated option "read_only" in favor of "attr['readonly']"
|
||||||
* added the html5 "range" FormType
|
* added the html5 "range" FormType
|
||||||
|
* deprecated the "cascade_validation" option in favor of setting "constraints"
|
||||||
|
with the Valid constraint
|
||||||
|
|
||||||
2.7.0
|
2.7.0
|
||||||
-----
|
-----
|
||||||
|
@ -13,6 +13,7 @@ namespace Symfony\Component\Form\Extension\Validator\Constraints;
|
|||||||
|
|
||||||
use Symfony\Component\Form\FormInterface;
|
use Symfony\Component\Form\FormInterface;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
use Symfony\Component\Validator\ConstraintValidator;
|
use Symfony\Component\Validator\ConstraintValidator;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
||||||
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||||
@ -63,6 +64,20 @@ class FormValidator extends ConstraintValidator
|
|||||||
// in the form
|
// in the form
|
||||||
$constraints = $config->getOption('constraints');
|
$constraints = $config->getOption('constraints');
|
||||||
foreach ($constraints as $constraint) {
|
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) {
|
foreach ($groups as $group) {
|
||||||
if (in_array($group, $constraint->groups)) {
|
if (in_array($group, $constraint->groups)) {
|
||||||
if ($validator) {
|
if ($validator) {
|
||||||
|
@ -67,10 +67,18 @@ class FormTypeValidatorExtension extends BaseValidatorExtension
|
|||||||
return is_object($constraints) ? array($constraints) : (array) $constraints;
|
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(
|
$resolver->setDefaults(array(
|
||||||
'error_mapping' => array(),
|
'error_mapping' => array(),
|
||||||
'constraints' => array(),
|
'constraints' => array(),
|
||||||
'cascade_validation' => false,
|
'cascade_validation' => null,
|
||||||
'invalid_message' => 'This value is not valid.',
|
'invalid_message' => 'This value is not valid.',
|
||||||
'invalid_message_parameters' => array(),
|
'invalid_message_parameters' => array(),
|
||||||
'allow_extra_fields' => false,
|
'allow_extra_fields' => false,
|
||||||
@ -78,6 +86,7 @@ class FormTypeValidatorExtension extends BaseValidatorExtension
|
|||||||
));
|
));
|
||||||
|
|
||||||
$resolver->setNormalizer('constraints', $constraintsNormalizer);
|
$resolver->setNormalizer('constraints', $constraintsNormalizer);
|
||||||
|
$resolver->setNormalizer('cascade_validation', $cascadeValidationNormalizer);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -18,9 +18,9 @@ use Symfony\Component\Form\FormInterface;
|
|||||||
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
|
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
|
||||||
use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator;
|
use Symfony\Component\Form\Extension\Validator\Constraints\FormValidator;
|
||||||
use Symfony\Component\Form\SubmitButtonBuilder;
|
use Symfony\Component\Form\SubmitButtonBuilder;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContextInterface;
|
|
||||||
use Symfony\Component\Validator\Constraints\NotNull;
|
use Symfony\Component\Validator\Constraints\NotNull;
|
||||||
use Symfony\Component\Validator\Constraints\NotBlank;
|
use Symfony\Component\Validator\Constraints\NotBlank;
|
||||||
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest;
|
use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest;
|
||||||
use Symfony\Component\Validator\Validation;
|
use Symfony\Component\Validator\Validation;
|
||||||
|
|
||||||
@ -109,6 +109,52 @@ class FormValidatorTest extends AbstractConstraintValidatorTest
|
|||||||
$this->assertNoViolation();
|
$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()
|
public function testDontValidateIfParentWithoutCascadeValidation()
|
||||||
{
|
{
|
||||||
$object = $this->getMock('\stdClass');
|
$object = $this->getMock('\stdClass');
|
||||||
@ -387,12 +433,13 @@ class FormValidatorTest extends AbstractConstraintValidatorTest
|
|||||||
{
|
{
|
||||||
$object = $this->getMock('\stdClass');
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
$parent = $this->getBuilder('parent', null, array('cascade_validation' => true))
|
$parent = $this->getBuilder('parent')
|
||||||
->setCompound(true)
|
->setCompound(true)
|
||||||
->setDataMapper($this->getDataMapper())
|
->setDataMapper($this->getDataMapper())
|
||||||
->getForm();
|
->getForm();
|
||||||
$form = $this->getForm('name', '\stdClass', array(
|
$form = $this->getForm('name', '\stdClass', array(
|
||||||
'validation_groups' => 'form_group',
|
'validation_groups' => 'form_group',
|
||||||
|
'constraints' => array(new Valid()),
|
||||||
));
|
));
|
||||||
|
|
||||||
$parent->add($form);
|
$parent->add($form);
|
||||||
@ -402,7 +449,7 @@ class FormValidatorTest extends AbstractConstraintValidatorTest
|
|||||||
|
|
||||||
$parent->submit(array('name' => $object, 'submit' => ''));
|
$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());
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
@ -413,12 +460,13 @@ class FormValidatorTest extends AbstractConstraintValidatorTest
|
|||||||
{
|
{
|
||||||
$object = $this->getMock('\stdClass');
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
$parent = $this->getBuilder('parent', null, array('cascade_validation' => true))
|
$parent = $this->getBuilder('parent')
|
||||||
->setCompound(true)
|
->setCompound(true)
|
||||||
->setDataMapper($this->getDataMapper())
|
->setDataMapper($this->getDataMapper())
|
||||||
->getForm();
|
->getForm();
|
||||||
$form = $this->getForm('name', '\stdClass', array(
|
$form = $this->getForm('name', '\stdClass', array(
|
||||||
'validation_groups' => 'form_group',
|
'validation_groups' => 'form_group',
|
||||||
|
'constraints' => array(new Valid()),
|
||||||
));
|
));
|
||||||
|
|
||||||
$parent->add($form);
|
$parent->add($form);
|
||||||
@ -428,7 +476,7 @@ class FormValidatorTest extends AbstractConstraintValidatorTest
|
|||||||
|
|
||||||
$form->setData($object);
|
$form->setData($object);
|
||||||
|
|
||||||
$this->expectValidateAt(0, 'data', $object, 'form_group');
|
$this->expectValidateAt(0, 'data', $object, array('form_group'));
|
||||||
|
|
||||||
$this->validator->validate($form, new Form());
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
@ -439,20 +487,18 @@ class FormValidatorTest extends AbstractConstraintValidatorTest
|
|||||||
{
|
{
|
||||||
$object = $this->getMock('\stdClass');
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
$parentOptions = array(
|
$parentOptions = array('validation_groups' => 'group');
|
||||||
'validation_groups' => 'group',
|
|
||||||
'cascade_validation' => true,
|
|
||||||
);
|
|
||||||
$parent = $this->getBuilder('parent', null, $parentOptions)
|
$parent = $this->getBuilder('parent', null, $parentOptions)
|
||||||
->setCompound(true)
|
->setCompound(true)
|
||||||
->setDataMapper($this->getDataMapper())
|
->setDataMapper($this->getDataMapper())
|
||||||
->getForm();
|
->getForm();
|
||||||
$form = $this->getBuilder('name', '\stdClass')->getForm();
|
$formOptions = array('constraints' => array(new Valid()));
|
||||||
|
$form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm();
|
||||||
$parent->add($form);
|
$parent->add($form);
|
||||||
|
|
||||||
$form->setData($object);
|
$form->setData($object);
|
||||||
|
|
||||||
$this->expectValidateAt(0, 'data', $object, 'group');
|
$this->expectValidateAt(0, 'data', $object, array('group'));
|
||||||
|
|
||||||
$this->validator->validate($form, new Form());
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
@ -463,21 +509,18 @@ class FormValidatorTest extends AbstractConstraintValidatorTest
|
|||||||
{
|
{
|
||||||
$object = $this->getMock('\stdClass');
|
$object = $this->getMock('\stdClass');
|
||||||
|
|
||||||
$parentOptions = array(
|
$parentOptions = array('validation_groups' => array($this, 'getValidationGroups'));
|
||||||
'validation_groups' => array($this, 'getValidationGroups'),
|
|
||||||
'cascade_validation' => true,
|
|
||||||
);
|
|
||||||
$parent = $this->getBuilder('parent', null, $parentOptions)
|
$parent = $this->getBuilder('parent', null, $parentOptions)
|
||||||
->setCompound(true)
|
->setCompound(true)
|
||||||
->setDataMapper($this->getDataMapper())
|
->setDataMapper($this->getDataMapper())
|
||||||
->getForm();
|
->getForm();
|
||||||
$form = $this->getBuilder('name', '\stdClass')->getForm();
|
$formOptions = array('constraints' => array(new Valid()));
|
||||||
|
$form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm();
|
||||||
$parent->add($form);
|
$parent->add($form);
|
||||||
|
|
||||||
$form->setData($object);
|
$form->setData($object);
|
||||||
|
|
||||||
$this->expectValidateAt(0, 'data', $object, 'group1');
|
$this->expectValidateAt(0, 'data', $object, array('group1', 'group2'));
|
||||||
$this->expectValidateAt(1, 'data', $object, 'group2');
|
|
||||||
|
|
||||||
$this->validator->validate($form, new Form());
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
@ -492,19 +535,18 @@ class FormValidatorTest extends AbstractConstraintValidatorTest
|
|||||||
'validation_groups' => function (FormInterface $form) {
|
'validation_groups' => function (FormInterface $form) {
|
||||||
return array('group1', 'group2');
|
return array('group1', 'group2');
|
||||||
},
|
},
|
||||||
'cascade_validation' => true,
|
|
||||||
);
|
);
|
||||||
$parent = $this->getBuilder('parent', null, $parentOptions)
|
$parent = $this->getBuilder('parent', null, $parentOptions)
|
||||||
->setCompound(true)
|
->setCompound(true)
|
||||||
->setDataMapper($this->getDataMapper())
|
->setDataMapper($this->getDataMapper())
|
||||||
->getForm();
|
->getForm();
|
||||||
$form = $this->getBuilder('name', '\stdClass')->getForm();
|
$formOptions = array('constraints' => array(new Valid()));
|
||||||
|
$form = $this->getBuilder('name', '\stdClass', $formOptions)->getForm();
|
||||||
$parent->add($form);
|
$parent->add($form);
|
||||||
|
|
||||||
$form->setData($object);
|
$form->setData($object);
|
||||||
|
|
||||||
$this->expectValidateAt(0, 'data', $object, 'group1');
|
$this->expectValidateAt(0, 'data', $object, array('group1', 'group2'));
|
||||||
$this->expectValidateAt(1, 'data', $object, 'group2');
|
|
||||||
|
|
||||||
$this->validator->validate($form, new Form());
|
$this->validator->validate($form, new Form());
|
||||||
|
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Form\Tests\Extension\Validator\Type;
|
namespace Symfony\Component\Form\Tests\Extension\Validator\Type;
|
||||||
|
|
||||||
use Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension;
|
use Symfony\Component\Form\Extension\Validator\Type\FormTypeValidatorExtension;
|
||||||
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
use Symfony\Component\Validator\ConstraintViolationList;
|
use Symfony\Component\Validator\ConstraintViolationList;
|
||||||
|
|
||||||
class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest
|
class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest
|
||||||
@ -37,6 +38,33 @@ class FormTypeValidatorExtensionTest extends BaseValidatorExtensionTest
|
|||||||
$form->submit(array());
|
$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()
|
public function testValidatorInterfaceSinceSymfony25()
|
||||||
{
|
{
|
||||||
// Mock of ValidatorInterface since apiVersion 2.5
|
// Mock of ValidatorInterface since apiVersion 2.5
|
||||||
|
@ -220,14 +220,24 @@ abstract class AbstractConstraintValidatorTest extends \PHPUnit_Framework_TestCa
|
|||||||
|
|
||||||
protected function expectValidateAt($i, $propertyPath, $value, $group)
|
protected function expectValidateAt($i, $propertyPath, $value, $group)
|
||||||
{
|
{
|
||||||
$validator = $this->context->getValidator()->inContext($this->context);
|
switch ($this->getApiVersion()) {
|
||||||
$validator->expects($this->at(2 * $i))
|
case Validation::API_VERSION_2_4:
|
||||||
->method('atPath')
|
$this->context->expects($this->at($i))
|
||||||
->with($propertyPath)
|
->method('validate')
|
||||||
->will($this->returnValue($validator));
|
->with($value, $propertyPath, $group);
|
||||||
$validator->expects($this->at(2 * $i + 1))
|
break;
|
||||||
->method('validate')
|
case Validation::API_VERSION_2_5:
|
||||||
->with($value, $this->logicalOr(null, array()), $group);
|
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)
|
protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null)
|
||||||
|
Reference in New Issue
Block a user