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
|
||||
=======================
|
||||
|
||||
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`.
|
||||
|
||||
|
@ -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
|
||||
-----
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -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());
|
||||
|
||||
|
@ -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
|
||||
|
@ -220,6 +220,14 @@ abstract class AbstractConstraintValidatorTest extends \PHPUnit_Framework_TestCa
|
||||
|
||||
protected function expectValidateAt($i, $propertyPath, $value, $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')
|
||||
@ -227,7 +235,9 @@ abstract class AbstractConstraintValidatorTest extends \PHPUnit_Framework_TestCa
|
||||
->will($this->returnValue($validator));
|
||||
$validator->expects($this->at(2 * $i + 1))
|
||||
->method('validate')
|
||||
->with($value, $this->logicalOr(null, array()), $group);
|
||||
->with($value, $this->logicalOr(null, array(), $this->isInstanceOf('\Symfony\Component\Validator\Constraints\Valid')), $group);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function expectValidateValueAt($i, $propertyPath, $value, $constraints, $group = null)
|
||||
|
Reference in New Issue
Block a user