瀏覽代碼

[Validator] Implemented traversing of \Traversable objects using the @Valid constraint. Can be disabled by setting the @Valid option 'traverse' to false

Bernhard Schussek 14 年之前
父節點
當前提交
0c3ca26e6e

+ 1 - 13
src/Symfony/Component/Validator/Constraints/Valid.php

@@ -15,19 +15,7 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
 
 class Valid extends \Symfony\Component\Validator\Constraint
 {
-    /**
-     * This constraint does not accept any options
-     *
-     * @param  mixed $options           Unsupported argument!
-     *
-     * @throws InvalidOptionsException  When the parameter $options is not NULL
-     */
-    public function __construct($options = null)
-    {
-        if (null !== $options && count($options) > 0) {
-            throw new ConstraintDefinitionException('The constraint Valid does not accept any options');
-        }
-    }
+    public $traverse = true;
 
     /**
      * {@inheritDoc}

+ 11 - 7
src/Symfony/Component/Validator/GraphWalker.php

@@ -129,20 +129,24 @@ class GraphWalker
         }
 
         if ($metadata->isCascaded()) {
-            $this->walkReference($value, $propagatedGroup ?: $group, $propertyPath);
+            $this->walkReference($value, $propagatedGroup ?: $group, $propertyPath, $metadata->isCollectionCascaded());
         }
     }
 
-    protected function walkReference($value, $group, $propertyPath)
+    protected function walkReference($value, $group, $propertyPath, $traverse)
     {
         if (null !== $value) {
-            if (is_array($value)) {
+            if (!is_object($value) && !is_array($value)) {
+                throw new UnexpectedTypeException($value, 'object or array');
+            }
+
+            if ($traverse && (is_array($value) || $value instanceof \Traversable)) {
                 foreach ($value as $key => $element) {
-                    $this->walkReference($element, $group, $propertyPath.'['.$key.']');
+                    $this->walkReference($element, $group, $propertyPath.'['.$key.']', $traverse);
                 }
-            } else if (!is_object($value)) {
-                throw new UnexpectedTypeException($value, 'object or array');
-            } else {
+            }
+
+            if (is_object($value)) {
                 $metadata = $this->metadataFactory->getClassMetadata(get_class($value));
                 $this->walkObject($metadata, $value, $group, $propertyPath);
             }

+ 13 - 0
src/Symfony/Component/Validator/Mapping/MemberMetadata.php

@@ -22,6 +22,7 @@ abstract class MemberMetadata extends ElementMetadata
     public $name;
     public $property;
     public $cascaded = false;
+    public $collectionCascaded = false;
     private $reflMember;
 
     /**
@@ -52,6 +53,7 @@ abstract class MemberMetadata extends ElementMetadata
 
         if ($constraint instanceof Valid) {
             $this->cascaded = true;
+            $this->collectionCascaded = $constraint->traverse;
         } else {
             parent::addConstraint($constraint);
         }
@@ -144,6 +146,17 @@ abstract class MemberMetadata extends ElementMetadata
         return $this->cascaded;
     }
 
+    /**
+     * Returns whether arrays or traversable objects stored in this member
+     * should be traversed and validated in each entry
+     *
+     * @return Boolean
+     */
+    public function isCollectionCascaded()
+    {
+        return $this->collectionCascaded;
+    }
+
     /**
      * Returns the value of this property in the given object
      *

+ 62 - 1
tests/Symfony/Tests/Component/Validator/GraphWalkerTest.php

@@ -237,7 +237,7 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($violations, $this->walker->getViolations());
     }
 
-    public function testWalkCascadedPropertyValidatesArrays()
+    public function testWalkCascadedPropertyValidatesArraysByDefault()
     {
         $entity = new Entity();
         $entityMetadata = new ClassMetadata(get_class($entity));
@@ -269,6 +269,67 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
         $this->assertEquals($violations, $this->walker->getViolations());
     }
 
+    public function testWalkCascadedPropertyValidatesTraversableByDefault()
+    {
+        $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 array when validating the property "reference"
+        $this->metadata->addPropertyConstraint('reference', new Valid());
+
+        $this->walker->walkPropertyValue(
+            $this->metadata,
+            'reference',
+            new \ArrayIterator(array('key' => $entity)),
+            'Default',
+            'path'
+        );
+
+        $violations = new ConstraintViolationList();
+        $violations->add(new ConstraintViolation(
+            '',
+            array(),
+            'Root',
+            'path[key]',
+            $entity
+        ));
+
+        $this->assertEquals($violations, $this->walker->getViolations());
+    }
+
+    public function testWalkCascadedPropertyDoesNotValidateTraversableIfDisabled()
+    {
+        $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 array when validating the property "reference"
+        $this->metadata->addPropertyConstraint('reference', new Valid(array(
+            'traverse' => false,
+        )));
+
+        $this->walker->walkPropertyValue(
+            $this->metadata,
+            'reference',
+            new \ArrayIterator(array('key' => $entity)),
+            'Default',
+            'path'
+        );
+
+        $violations = new ConstraintViolationList();
+
+        $this->assertEquals($violations, $this->walker->getViolations());
+    }
+
     public function testWalkCascadedPropertyDoesNotValidateNullValues()
     {
         $this->metadata->addPropertyConstraint('reference', new Valid());