[Validator] Fixed: @Valid does not recurse the traversal of collections anymore by default

This commit is contained in:
Bernhard Schussek 2012-05-18 12:22:33 +02:00
parent 5e87dd885c
commit 8f7e2f602c
6 changed files with 116 additions and 3 deletions

View File

@ -593,6 +593,28 @@
* Core translation messages are changed. Dot is added at the end of each message. * Core translation messages are changed. Dot is added at the end of each message.
Overwritten core translations should be fixed if any. More info here. Overwritten core translations should be fixed if any. More info here.
* Collections (arrays or instances of `\Traversable`) in properties
annotated with `Valid` are not traversed recursively by default anymore.
This means that if a collection contains an entry which is again a
collection, the inner collection won't be traversed anymore as it
happened before. You can set the BC behavior by setting the new property
`deep` of `Valid` to `true`.
Before:
```
/** @Assert\Valid */
private $recursiveCollection;
```
After:
```
/** @Assert\Valid(deep = true) */
private $recursiveCollection;
```
### Session ### Session
* Flash messages now return an array based on their type. The old method is * Flash messages now return an array based on their type. The old method is

View File

@ -20,3 +20,6 @@ CHANGELOG
* [BC BREAK] ConstraintValidatorInterface method `isValid` has been renamed to * [BC BREAK] ConstraintValidatorInterface method `isValid` has been renamed to
`validate`, its return value was dropped. ConstraintValidator still contains `validate`, its return value was dropped. ConstraintValidator still contains
`isValid` for BC `isValid` for BC
* [BC BREAK] collections in fields annotated with `Valid` are not traversed
recursively anymore by default. `Valid` contains a new property `deep`
which enables the BC behavior.

View File

@ -21,4 +21,6 @@ use Symfony\Component\Validator\Constraint;
class Valid extends Constraint class Valid extends Constraint
{ {
public $traverse = true; public $traverse = true;
public $deep = false;
} }

View File

@ -137,11 +137,11 @@ class GraphWalker
} }
if ($metadata->isCascaded()) { if ($metadata->isCascaded()) {
$this->walkReference($value, $propagatedGroup ?: $group, $propertyPath, $metadata->isCollectionCascaded()); $this->walkReference($value, $propagatedGroup ?: $group, $propertyPath, $metadata->isCollectionCascaded(), $metadata->isCollectionCascadedDeeply());
} }
} }
public function walkReference($value, $group, $propertyPath, $traverse) public function walkReference($value, $group, $propertyPath, $traverse, $deep = false)
{ {
if (null !== $value) { if (null !== $value) {
if (!is_object($value) && !is_array($value)) { if (!is_object($value) && !is_array($value)) {
@ -152,7 +152,8 @@ class GraphWalker
foreach ($value as $key => $element) { foreach ($value as $key => $element) {
// Ignore any scalar values in the collection // Ignore any scalar values in the collection
if (is_object($element) || is_array($element)) { if (is_object($element) || is_array($element)) {
$this->walkReference($element, $group, $propertyPath.'['.$key.']', $traverse); // Only repeat the traversal if $deep is set
$this->walkReference($element, $group, $propertyPath.'['.$key.']', $deep, $deep);
} }
} }
} }

View File

@ -22,6 +22,7 @@ abstract class MemberMetadata extends ElementMetadata
public $property; public $property;
public $cascaded = false; public $cascaded = false;
public $collectionCascaded = false; public $collectionCascaded = false;
public $collectionCascadedDeeply = false;
private $reflMember; private $reflMember;
/** /**
@ -52,7 +53,9 @@ abstract class MemberMetadata extends ElementMetadata
if ($constraint instanceof Valid) { if ($constraint instanceof Valid) {
$this->cascaded = true; $this->cascaded = true;
/* @var Valid $constraint */
$this->collectionCascaded = $constraint->traverse; $this->collectionCascaded = $constraint->traverse;
$this->collectionCascadedDeeply = $constraint->deep;
} else { } else {
parent::addConstraint($constraint); parent::addConstraint($constraint);
} }
@ -156,6 +159,17 @@ abstract class MemberMetadata extends ElementMetadata
return $this->collectionCascaded; return $this->collectionCascaded;
} }
/**
* Returns whether arrays or traversable objects stored in this member
* should be traversed recursively for inner arrays/traversable objects
*
* @return Boolean
*/
public function isCollectionCascadedDeeply()
{
return $this->collectionCascadedDeeply;
}
/** /**
* Returns the value of this property in the given object * Returns the value of this property in the given object
* *

View File

@ -357,6 +357,77 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
$this->assertEquals($violations, $this->walker->getViolations()); $this->assertEquals($violations, $this->walker->getViolations());
} }
public function testWalkCascadedPropertyDoesNotRecurseByDefault()
{
$entity = new Entity();
$entityMetadata = new ClassMetadata(get_class($entity));
$this->factory->addClassMetadata($entityMetadata);
$this->factory->addClassMetadata(new ClassMetadata('ArrayIterator'));
// add a constraint for the entity that always fails
$entityMetadata->addConstraint(new FailingConstraint());
// validate iterator when validating the property "reference"
$this->metadata->addPropertyConstraint('reference', new Valid());
$this->walker->walkPropertyValue(
$this->metadata,
'reference',
new \ArrayIterator(array(
// The inner iterator should not be traversed by default
'key' => new \ArrayIterator(array(
'nested' => $entity,
)),
)),
'Default',
'path'
);
$violations = new ConstraintViolationList();
$this->assertEquals($violations, $this->walker->getViolations());
}
public function testWalkCascadedPropertyRecursesIfDeepIsSet()
{
$entity = new Entity();
$entityMetadata = new ClassMetadata(get_class($entity));
$this->factory->addClassMetadata($entityMetadata);
$this->factory->addClassMetadata(new ClassMetadata('ArrayIterator'));
// add a constraint for the entity that always fails
$entityMetadata->addConstraint(new FailingConstraint());
// validate iterator when validating the property "reference"
$this->metadata->addPropertyConstraint('reference', new Valid(array(
'deep' => true,
)));
$this->walker->walkPropertyValue(
$this->metadata,
'reference',
new \ArrayIterator(array(
// The inner iterator should now be traversed
'key' => new \ArrayIterator(array(
'nested' => $entity,
)),
)),
'Default',
'path'
);
$violations = new ConstraintViolationList();
$violations->add(new ConstraintViolation(
'Failed',
array(),
'Root',
'path[key][nested]',
$entity
));
$this->assertEquals($violations, $this->walker->getViolations());
}
public function testWalkCascadedPropertyDoesNotValidateNestedScalarValues() public function testWalkCascadedPropertyDoesNotValidateNestedScalarValues()
{ {
// validate array when validating the property "reference" // validate array when validating the property "reference"