[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.
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
* 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
`validate`, its return value was dropped. ConstraintValidator still contains
`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
{
public $traverse = true;
public $deep = false;
}

View File

@ -137,11 +137,11 @@ class GraphWalker
}
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 (!is_object($value) && !is_array($value)) {
@ -152,7 +152,8 @@ class GraphWalker
foreach ($value as $key => $element) {
// Ignore any scalar values in the collection
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 $cascaded = false;
public $collectionCascaded = false;
public $collectionCascadedDeeply = false;
private $reflMember;
/**
@ -52,7 +53,9 @@ abstract class MemberMetadata extends ElementMetadata
if ($constraint instanceof Valid) {
$this->cascaded = true;
/* @var Valid $constraint */
$this->collectionCascaded = $constraint->traverse;
$this->collectionCascadedDeeply = $constraint->deep;
} else {
parent::addConstraint($constraint);
}
@ -156,6 +159,17 @@ abstract class MemberMetadata extends ElementMetadata
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
*

View File

@ -357,6 +357,77 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
$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()
{
// validate array when validating the property "reference"