[Validator] Fixed: Objects are not traversed unless they are instances of Traversable
This commit is contained in:
parent
2c65a28608
commit
a3555fbd99
@ -21,12 +21,27 @@ use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
|||||||
*
|
*
|
||||||
* @api
|
* @api
|
||||||
*/
|
*/
|
||||||
class Valid extends Traverse
|
class Valid extends Constraint
|
||||||
{
|
{
|
||||||
|
/**
|
||||||
|
* @deprecated Deprecated since version 2.5, to be removed in Symfony 3.0.
|
||||||
|
* Use the {@link Traverse} constraint instead.
|
||||||
|
*/
|
||||||
|
public $traverse = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Deprecated since version 2.5, to be removed in Symfony 3.0.
|
||||||
|
* Use the {@link Traverse} constraint instead.
|
||||||
|
*/
|
||||||
|
public $deep = false;
|
||||||
|
|
||||||
public function __construct($options = null)
|
public function __construct($options = null)
|
||||||
{
|
{
|
||||||
if (is_array($options) && array_key_exists('groups', $options)) {
|
if (is_array($options) && array_key_exists('groups', $options)) {
|
||||||
throw new ConstraintDefinitionException(sprintf('The option "groups" is not supported by the constraint %s', __CLASS__));
|
throw new ConstraintDefinitionException(sprintf(
|
||||||
|
'The option "groups" is not supported by the constraint %s',
|
||||||
|
__CLASS__
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
parent::__construct($options);
|
parent::__construct($options);
|
||||||
|
@ -77,8 +77,7 @@ class GenericMetadata implements MetadataInterface
|
|||||||
if ($constraint instanceof Valid) {
|
if ($constraint instanceof Valid) {
|
||||||
$this->cascadingStrategy = CascadingStrategy::CASCADE;
|
$this->cascadingStrategy = CascadingStrategy::CASCADE;
|
||||||
|
|
||||||
// Continue. Valid extends Traverse, so the return statement in the
|
return $this;
|
||||||
// next block is going be executed.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($constraint instanceof Traverse) {
|
if ($constraint instanceof Traverse) {
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Validator\Mapping;
|
namespace Symfony\Component\Validator\Mapping;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
use Symfony\Component\Validator\ValidationVisitorInterface;
|
use Symfony\Component\Validator\ValidationVisitorInterface;
|
||||||
use Symfony\Component\Validator\PropertyMetadataInterface as LegacyPropertyMetadataInterface;
|
use Symfony\Component\Validator\PropertyMetadataInterface as LegacyPropertyMetadataInterface;
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
@ -58,6 +59,24 @@ abstract class MemberMetadata extends ElementMetadata implements PropertyMetadat
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// BC with Symfony < 2.5
|
||||||
|
// Only process if the traversal strategy was not already set by the
|
||||||
|
// Traverse constraint
|
||||||
|
if ($constraint instanceof Valid && !$this->traversalStrategy) {
|
||||||
|
if (true === $constraint->traverse) {
|
||||||
|
// Try to traverse cascaded objects, but ignore if they do not
|
||||||
|
// implement Traversable
|
||||||
|
$this->traversalStrategy = TraversalStrategy::TRAVERSE
|
||||||
|
| TraversalStrategy::IGNORE_NON_TRAVERSABLE;
|
||||||
|
|
||||||
|
if ($constraint->deep) {
|
||||||
|
$this->traversalStrategy |= TraversalStrategy::RECURSIVE;
|
||||||
|
}
|
||||||
|
} elseif (false === $constraint->traverse) {
|
||||||
|
$this->traversalStrategy = TraversalStrategy::NONE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
parent::addConstraint($constraint);
|
parent::addConstraint($constraint);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -25,6 +25,8 @@ class TraversalStrategy
|
|||||||
|
|
||||||
const RECURSIVE = 4;
|
const RECURSIVE = 4;
|
||||||
|
|
||||||
|
const IGNORE_NON_TRAVERSABLE = 8;
|
||||||
|
|
||||||
private function __construct()
|
private function __construct()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -37,13 +37,13 @@ class ClassNode extends Node
|
|||||||
* to this node
|
* to this node
|
||||||
* @param string[] $groups The groups in which this
|
* @param string[] $groups The groups in which this
|
||||||
* node should be validated
|
* node should be validated
|
||||||
* @param string[] $cascadedGroups The groups in which
|
* @param string[]|null $cascadedGroups The groups in which
|
||||||
* cascaded objects should be
|
* cascaded objects should be
|
||||||
* validated
|
* validated
|
||||||
*
|
*
|
||||||
* @throws UnexpectedTypeException If the given value is not an object
|
* @throws UnexpectedTypeException If the given value is not an object
|
||||||
*/
|
*/
|
||||||
public function __construct($object, ClassMetadataInterface $metadata, $propertyPath, array $groups, array $cascadedGroups)
|
public function __construct($object, ClassMetadataInterface $metadata, $propertyPath, array $groups, $cascadedGroups = null)
|
||||||
{
|
{
|
||||||
if (!is_object($object)) {
|
if (!is_object($object)) {
|
||||||
throw new UnexpectedTypeException($object, 'object');
|
throw new UnexpectedTypeException($object, 'object');
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Validator\Node;
|
namespace Symfony\Component\Validator\Node;
|
||||||
|
|
||||||
|
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
|
||||||
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
use Symfony\Component\Validator\Mapping\MetadataInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,11 +66,17 @@ abstract class Node
|
|||||||
* this node
|
* this node
|
||||||
* @param string[] $groups The groups in which this node
|
* @param string[] $groups The groups in which this node
|
||||||
* should be validated
|
* should be validated
|
||||||
* @param string[] $cascadedGroups The groups in which cascaded
|
* @param string[]|null $cascadedGroups The groups in which cascaded
|
||||||
* objects should be validated
|
* objects should be validated
|
||||||
|
*
|
||||||
|
* @throws UnexpectedTypeException If $cascadedGroups is invalid
|
||||||
*/
|
*/
|
||||||
public function __construct($value, MetadataInterface $metadata, $propertyPath, array $groups, array $cascadedGroups)
|
public function __construct($value, MetadataInterface $metadata, $propertyPath, array $groups, $cascadedGroups = null)
|
||||||
{
|
{
|
||||||
|
if (null !== $cascadedGroups && !is_array($cascadedGroups)) {
|
||||||
|
throw new UnexpectedTypeException($cascadedGroups, 'null or array');
|
||||||
|
}
|
||||||
|
|
||||||
$this->value = $value;
|
$this->value = $value;
|
||||||
$this->metadata = $metadata;
|
$this->metadata = $metadata;
|
||||||
$this->propertyPath = $propertyPath;
|
$this->propertyPath = $propertyPath;
|
||||||
|
@ -46,11 +46,11 @@ class PropertyNode extends Node
|
|||||||
* to this node
|
* to this node
|
||||||
* @param string[] $groups The groups in which this
|
* @param string[] $groups The groups in which this
|
||||||
* node should be validated
|
* node should be validated
|
||||||
* @param string[] $cascadedGroups The groups in which
|
* @param string[]|null $cascadedGroups The groups in which
|
||||||
* cascaded objects should
|
* cascaded objects should
|
||||||
* be validated
|
* be validated
|
||||||
*/
|
*/
|
||||||
public function __construct($value, PropertyMetadataInterface $metadata, $propertyPath, array $groups, array $cascadedGroups)
|
public function __construct($value, PropertyMetadataInterface $metadata, $propertyPath, array $groups, $cascadedGroups = null)
|
||||||
{
|
{
|
||||||
parent::__construct(
|
parent::__construct(
|
||||||
$value,
|
$value,
|
||||||
|
@ -12,8 +12,10 @@
|
|||||||
namespace Symfony\Component\Validator\NodeTraverser;
|
namespace Symfony\Component\Validator\NodeTraverser;
|
||||||
|
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||||
use Symfony\Component\Validator\Exception\NoSuchMetadataException;
|
use Symfony\Component\Validator\Exception\NoSuchMetadataException;
|
||||||
use Symfony\Component\Validator\Mapping\CascadingStrategy;
|
use Symfony\Component\Validator\Mapping\CascadingStrategy;
|
||||||
|
use Symfony\Component\Validator\Mapping\ClassMetadataInterface;
|
||||||
use Symfony\Component\Validator\Mapping\TraversalStrategy;
|
use Symfony\Component\Validator\Mapping\TraversalStrategy;
|
||||||
use Symfony\Component\Validator\MetadataFactoryInterface;
|
use Symfony\Component\Validator\MetadataFactoryInterface;
|
||||||
use Symfony\Component\Validator\Node\ClassNode;
|
use Symfony\Component\Validator\Node\ClassNode;
|
||||||
@ -80,100 +82,219 @@ class NodeTraverser implements NodeTraverserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($isTopLevelCall) {
|
if ($isTopLevelCall) {
|
||||||
$this->traversalStarted = false;
|
|
||||||
|
|
||||||
foreach ($this->visitors as $visitor) {
|
foreach ($this->visitors as $visitor) {
|
||||||
/** @var NodeVisitorInterface $visitor */
|
/** @var NodeVisitorInterface $visitor */
|
||||||
$visitor->afterTraversal($nodes);
|
$visitor->afterTraversal($nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->traversalStarted = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Node $node
|
||||||
|
*
|
||||||
|
* @return Boolean
|
||||||
|
*/
|
||||||
|
private function enterNode(Node $node)
|
||||||
|
{
|
||||||
|
$continueTraversal = true;
|
||||||
|
|
||||||
|
foreach ($this->visitors as $visitor) {
|
||||||
|
if (false === $visitor->enterNode($node)) {
|
||||||
|
$continueTraversal = false;
|
||||||
|
|
||||||
|
// Continue, so that the enterNode() method of all visitors
|
||||||
|
// is called
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $continueTraversal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param Node $node
|
||||||
|
*/
|
||||||
|
private function leaveNode(Node $node)
|
||||||
|
{
|
||||||
|
foreach ($this->visitors as $visitor) {
|
||||||
|
$visitor->leaveNode($node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function traverseNode(Node $node)
|
private function traverseNode(Node $node)
|
||||||
{
|
{
|
||||||
$stopTraversal = false;
|
$continue = $this->enterNode($node);
|
||||||
|
|
||||||
foreach ($this->visitors as $visitor) {
|
// Visitors have two possibilities to influence the traversal:
|
||||||
if (false === $visitor->enterNode($node)) {
|
//
|
||||||
$stopTraversal = true;
|
// 1. If a visitor's enterNode() method returns false, the traversal is
|
||||||
|
// skipped entirely.
|
||||||
|
// 2. If a visitor's enterNode() method removes a group from the node,
|
||||||
|
// that group will be skipped in the subtree of that node.
|
||||||
|
|
||||||
|
if (false === $continue) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (null === $node->value) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The "cascadedGroups" property is set by the NodeValidator when
|
||||||
|
// traversing group sequences
|
||||||
|
$cascadedGroups = null !== $node->cascadedGroups
|
||||||
|
? $node->cascadedGroups
|
||||||
|
: $node->groups;
|
||||||
|
|
||||||
|
if (0 === count($cascadedGroups)) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$cascadingStrategy = $node->metadata->getCascadingStrategy();
|
||||||
|
$traversalStrategy = $node->metadata->getTraversalStrategy();
|
||||||
|
|
||||||
|
if (is_array($node->value)) {
|
||||||
|
// Arrays are always traversed, independent of the specified
|
||||||
|
// traversal strategy
|
||||||
|
// (BC with Symfony < 2.5)
|
||||||
|
$this->cascadeEachObjectIn(
|
||||||
|
$node->value,
|
||||||
|
$node->propertyPath,
|
||||||
|
$node->cascadedGroups,
|
||||||
|
$traversalStrategy
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($cascadingStrategy & CascadingStrategy::CASCADE) {
|
||||||
|
// If the value is a scalar, pass it anyway, because we want
|
||||||
|
// a NoSuchMetadataException to be thrown in that case
|
||||||
|
// (BC with Symfony < 2.5)
|
||||||
|
$this->cascadeObject(
|
||||||
|
$node->value,
|
||||||
|
$node->propertyPath,
|
||||||
|
$node->cascadedGroups,
|
||||||
|
$traversalStrategy
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Traverse only if the TRAVERSE bit is set
|
||||||
|
if (!($traversalStrategy & TraversalStrategy::TRAVERSE)) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$node->value instanceof \Traversable) {
|
||||||
|
if ($traversalStrategy & TraversalStrategy::IGNORE_NON_TRAVERSABLE) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
throw new ConstraintDefinitionException(sprintf(
|
||||||
|
'Traversal was enabled for "%s", but this class '.
|
||||||
|
'does not implement "\Traversable".',
|
||||||
|
get_class($node->value)
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the traversal, but execute the leaveNode() methods anyway to
|
$this->cascadeEachObjectIn(
|
||||||
// perform possible cleanups
|
$node->value,
|
||||||
if (!$stopTraversal && null !== $node->value) {
|
$node->propertyPath,
|
||||||
$cascadingStrategy = $node->metadata->getCascadingStrategy();
|
$node->groups,
|
||||||
$traversalStrategy = $node->metadata->getTraversalStrategy();
|
$traversalStrategy
|
||||||
|
);
|
||||||
|
|
||||||
if (is_array($node->value)) {
|
$this->leaveNode($node);
|
||||||
$this->cascadeCollection(
|
|
||||||
$node->value,
|
|
||||||
$node->propertyPath,
|
|
||||||
$node->cascadedGroups,
|
|
||||||
$traversalStrategy
|
|
||||||
);
|
|
||||||
} elseif ($cascadingStrategy & CascadingStrategy::CASCADE) {
|
|
||||||
// If the value is a scalar, pass it anyway, because we want
|
|
||||||
// a NoSuchMetadataException to be thrown in that case
|
|
||||||
// (BC with Symfony < 2.5)
|
|
||||||
$this->cascadeObject(
|
|
||||||
$node->value,
|
|
||||||
$node->propertyPath,
|
|
||||||
$node->cascadedGroups,
|
|
||||||
$traversalStrategy
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach ($this->visitors as $visitor) {
|
|
||||||
$visitor->leaveNode($node);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function traverseClassNode(ClassNode $node, $traversalStrategy = TraversalStrategy::IMPLICIT)
|
private function traverseClassNode(ClassNode $node, $traversalStrategy = TraversalStrategy::IMPLICIT)
|
||||||
{
|
{
|
||||||
$stopTraversal = false;
|
$continue = $this->enterNode($node);
|
||||||
|
|
||||||
foreach ($this->visitors as $visitor) {
|
// Visitors have two possibilities to influence the traversal:
|
||||||
if (false === $visitor->enterNode($node)) {
|
//
|
||||||
$stopTraversal = true;
|
// 1. If a visitor's enterNode() method returns false, the traversal is
|
||||||
|
// skipped entirely.
|
||||||
|
// 2. If a visitor's enterNode() method removes a group from the node,
|
||||||
|
// that group will be skipped in the subtree of that node.
|
||||||
|
|
||||||
|
if (false === $continue) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (0 === count($node->groups)) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($node->metadata->getConstrainedProperties() as $propertyName) {
|
||||||
|
foreach ($node->metadata->getPropertyMetadata($propertyName) as $propertyMetadata) {
|
||||||
|
$this->traverseNode(new PropertyNode(
|
||||||
|
$propertyMetadata->getPropertyValue($node->value),
|
||||||
|
$propertyMetadata,
|
||||||
|
$node->propertyPath
|
||||||
|
? $node->propertyPath.'.'.$propertyName
|
||||||
|
: $propertyName,
|
||||||
|
$node->groups,
|
||||||
|
$node->cascadedGroups
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop the traversal, but execute the leaveNode() methods anyway to
|
// If no specific traversal strategy was requested when this method
|
||||||
// perform possible cleanups
|
// was called, use the traversal strategy of the class' metadata
|
||||||
if (!$stopTraversal && count($node->groups) > 0) {
|
if (TraversalStrategy::IMPLICIT === $traversalStrategy) {
|
||||||
foreach ($node->metadata->getConstrainedProperties() as $propertyName) {
|
$traversalStrategy = $node->metadata->getTraversalStrategy();
|
||||||
foreach ($node->metadata->getPropertyMetadata($propertyName) as $propertyMetadata) {
|
|
||||||
$this->traverseNode(new PropertyNode(
|
|
||||||
$propertyMetadata->getPropertyValue($node->value),
|
|
||||||
$propertyMetadata,
|
|
||||||
$node->propertyPath
|
|
||||||
? $node->propertyPath.'.'.$propertyName
|
|
||||||
: $propertyName,
|
|
||||||
$node->groups,
|
|
||||||
$node->cascadedGroups
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($traversalStrategy & TraversalStrategy::IMPLICIT) {
|
|
||||||
$traversalStrategy = $node->metadata->getTraversalStrategy();
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($traversalStrategy & TraversalStrategy::TRAVERSE) {
|
|
||||||
$this->cascadeCollection(
|
|
||||||
$node->value,
|
|
||||||
$node->propertyPath,
|
|
||||||
$node->groups,
|
|
||||||
$traversalStrategy
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($this->visitors as $visitor) {
|
// Traverse only if the TRAVERSE bit is set
|
||||||
$visitor->leaveNode($node);
|
if (!($traversalStrategy & TraversalStrategy::TRAVERSE)) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!$node->value instanceof \Traversable) {
|
||||||
|
if ($traversalStrategy & TraversalStrategy::IGNORE_NON_TRAVERSABLE) {
|
||||||
|
$this->leaveNode($node);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new ConstraintDefinitionException(sprintf(
|
||||||
|
'Traversal was enabled for "%s", but this class '.
|
||||||
|
'does not implement "\Traversable".',
|
||||||
|
get_class($node->value)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->cascadeEachObjectIn(
|
||||||
|
$node->value,
|
||||||
|
$node->propertyPath,
|
||||||
|
$node->groups,
|
||||||
|
$traversalStrategy
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->leaveNode($node);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy)
|
private function cascadeObject($object, $propertyPath, array $groups, $traversalStrategy)
|
||||||
@ -181,24 +302,31 @@ class NodeTraverser implements NodeTraverserInterface
|
|||||||
try {
|
try {
|
||||||
$classMetadata = $this->metadataFactory->getMetadataFor($object);
|
$classMetadata = $this->metadataFactory->getMetadataFor($object);
|
||||||
|
|
||||||
|
if (!$classMetadata instanceof ClassMetadataInterface) {
|
||||||
|
// error
|
||||||
|
}
|
||||||
|
|
||||||
$classNode = new ClassNode(
|
$classNode = new ClassNode(
|
||||||
$object,
|
$object,
|
||||||
$classMetadata,
|
$classMetadata,
|
||||||
$propertyPath,
|
$propertyPath,
|
||||||
$groups,
|
|
||||||
$groups
|
$groups
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->traverseClassNode($classNode, $traversalStrategy);
|
$this->traverseClassNode($classNode, $traversalStrategy);
|
||||||
} catch (NoSuchMetadataException $e) {
|
} catch (NoSuchMetadataException $e) {
|
||||||
if (!$object instanceof \Traversable || !($traversalStrategy & TraversalStrategy::TRAVERSE)) {
|
// Rethrow if the TRAVERSE bit is not set
|
||||||
|
if (!($traversalStrategy & TraversalStrategy::TRAVERSE)) {
|
||||||
throw $e;
|
throw $e;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Metadata doesn't necessarily have to exist for
|
// Rethrow if the object does not implement Traversable
|
||||||
// traversable objects, because we know how to validate
|
if (!$object instanceof \Traversable) {
|
||||||
// them anyway.
|
throw $e;
|
||||||
$this->cascadeCollection(
|
}
|
||||||
|
|
||||||
|
// In that case, iterate the object and cascade each entry
|
||||||
|
$this->cascadeEachObjectIn(
|
||||||
$object,
|
$object,
|
||||||
$propertyPath,
|
$propertyPath,
|
||||||
$groups,
|
$groups,
|
||||||
@ -207,15 +335,25 @@ class NodeTraverser implements NodeTraverserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function cascadeCollection($collection, $propertyPath, array $groups, $traversalStrategy)
|
private function cascadeEachObjectIn($collection, $propertyPath, array $groups, $traversalStrategy)
|
||||||
{
|
{
|
||||||
if (!($traversalStrategy & TraversalStrategy::RECURSIVE)) {
|
if ($traversalStrategy & TraversalStrategy::RECURSIVE) {
|
||||||
|
// Try to traverse nested objects, but ignore if they do not
|
||||||
|
// implement Traversable
|
||||||
|
$traversalStrategy |= TraversalStrategy::IGNORE_NON_TRAVERSABLE;
|
||||||
|
} else {
|
||||||
|
// If the RECURSIVE bit is not set, change the strategy to IMPLICIT
|
||||||
|
// in order to respect the metadata's traversal strategy of each entry
|
||||||
|
// in the collection
|
||||||
$traversalStrategy = TraversalStrategy::IMPLICIT;
|
$traversalStrategy = TraversalStrategy::IMPLICIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach ($collection as $key => $value) {
|
foreach ($collection as $key => $value) {
|
||||||
if (is_array($value)) {
|
if (is_array($value)) {
|
||||||
$this->cascadeCollection(
|
// Arrays are always cascaded, independent of the specified
|
||||||
|
// traversal strategy
|
||||||
|
// (BC with Symfony < 2.5)
|
||||||
|
$this->cascadeEachObjectIn(
|
||||||
$value,
|
$value,
|
||||||
$propertyPath.'['.$key.']',
|
$propertyPath.'['.$key.']',
|
||||||
$groups,
|
$groups,
|
||||||
@ -226,6 +364,7 @@ class NodeTraverser implements NodeTraverserInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Scalar and null values in the collection are ignored
|
// Scalar and null values in the collection are ignored
|
||||||
|
// (BC with Symfony < 2.5)
|
||||||
if (is_object($value)) {
|
if (is_object($value)) {
|
||||||
$this->cascadeObject(
|
$this->cascadeObject(
|
||||||
$value,
|
$value,
|
||||||
|
@ -146,7 +146,10 @@ class NodeValidator extends AbstractVisitor implements GroupManagerInterface
|
|||||||
foreach ($groupSequence->groups as $groupInSequence) {
|
foreach ($groupSequence->groups as $groupInSequence) {
|
||||||
$node = clone $node;
|
$node = clone $node;
|
||||||
$node->groups = array($groupInSequence);
|
$node->groups = array($groupInSequence);
|
||||||
$node->cascadedGroups = array($groupSequence->cascadedGroup ?: $groupInSequence);
|
|
||||||
|
if (null !== $groupSequence->cascadedGroup) {
|
||||||
|
$node->cascadedGroups = array($groupSequence->cascadedGroup);
|
||||||
|
}
|
||||||
|
|
||||||
$this->nodeTraverser->traverse(array($node));
|
$this->nodeTraverser->traverse(array($node));
|
||||||
|
|
||||||
|
@ -14,6 +14,7 @@ namespace Symfony\Component\Validator\Tests\Validator;
|
|||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
use Symfony\Component\Validator\Constraints\Callback;
|
use Symfony\Component\Validator\Constraints\Callback;
|
||||||
use Symfony\Component\Validator\Constraints\GroupSequence;
|
use Symfony\Component\Validator\Constraints\GroupSequence;
|
||||||
|
use Symfony\Component\Validator\Constraints\Traverse;
|
||||||
use Symfony\Component\Validator\ConstraintViolationInterface;
|
use Symfony\Component\Validator\ConstraintViolationInterface;
|
||||||
use Symfony\Component\Validator\Context\ExecutionContext;
|
use Symfony\Component\Validator\Context\ExecutionContext;
|
||||||
use Symfony\Component\Validator\ExecutionContextInterface;
|
use Symfony\Component\Validator\ExecutionContextInterface;
|
||||||
@ -225,6 +226,45 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
'groups' => 'Group',
|
'groups' => 'Group',
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validator->validateCollection($array, 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(1, $violations);
|
||||||
|
$this->assertSame('Message value', $violations[0]->getMessage());
|
||||||
|
$this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
|
||||||
|
$this->assertSame(array('%param%' => 'value'), $violations[0]->getMessageParameters());
|
||||||
|
$this->assertSame('[key]', $violations[0]->getPropertyPath());
|
||||||
|
$this->assertSame($array, $violations[0]->getRoot());
|
||||||
|
$this->assertSame($entity, $violations[0]->getInvalidValue());
|
||||||
|
$this->assertNull($violations[0]->getMessagePluralization());
|
||||||
|
$this->assertNull($violations[0]->getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testArrayLegacyApi()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$array = array('key' => $entity);
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) use ($test, $entity, $array) {
|
||||||
|
$test->assertSame($test::ENTITY_CLASS, $context->getClassName());
|
||||||
|
$test->assertNull($context->getPropertyName());
|
||||||
|
$test->assertSame('[key]', $context->getPropertyPath());
|
||||||
|
$test->assertSame('Group', $context->getGroup());
|
||||||
|
$test->assertSame($test->metadata, $context->getMetadata());
|
||||||
|
$test->assertSame($test->metadataFactory, $context->getMetadataFactory());
|
||||||
|
$test->assertSame($array, $context->getRoot());
|
||||||
|
$test->assertSame($entity, $context->getValue());
|
||||||
|
$test->assertSame($entity, $value);
|
||||||
|
|
||||||
|
$context->addViolation('Message %param%', array('%param%' => 'value'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
|
||||||
$violations = $this->validator->validate($array, 'Group');
|
$violations = $this->validator->validate($array, 'Group');
|
||||||
|
|
||||||
/** @var ConstraintViolationInterface[] $violations */
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
@ -264,6 +304,45 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
'groups' => 'Group',
|
'groups' => 'Group',
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validator->validateCollection($array, 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(1, $violations);
|
||||||
|
$this->assertSame('Message value', $violations[0]->getMessage());
|
||||||
|
$this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
|
||||||
|
$this->assertSame(array('%param%' => 'value'), $violations[0]->getMessageParameters());
|
||||||
|
$this->assertSame('[2][key]', $violations[0]->getPropertyPath());
|
||||||
|
$this->assertSame($array, $violations[0]->getRoot());
|
||||||
|
$this->assertSame($entity, $violations[0]->getInvalidValue());
|
||||||
|
$this->assertNull($violations[0]->getMessagePluralization());
|
||||||
|
$this->assertNull($violations[0]->getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveArrayLegacyApi()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$array = array(2 => array('key' => $entity));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) use ($test, $entity, $array) {
|
||||||
|
$test->assertSame($test::ENTITY_CLASS, $context->getClassName());
|
||||||
|
$test->assertNull($context->getPropertyName());
|
||||||
|
$test->assertSame('[2][key]', $context->getPropertyPath());
|
||||||
|
$test->assertSame('Group', $context->getGroup());
|
||||||
|
$test->assertSame($test->metadata, $context->getMetadata());
|
||||||
|
$test->assertSame($test->metadataFactory, $context->getMetadataFactory());
|
||||||
|
$test->assertSame($array, $context->getRoot());
|
||||||
|
$test->assertSame($entity, $context->getValue());
|
||||||
|
$test->assertSame($entity, $value);
|
||||||
|
|
||||||
|
$context->addViolation('Message %param%', array('%param%' => 'value'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
|
||||||
$violations = $this->validator->validate($array, 'Group');
|
$violations = $this->validator->validate($array, 'Group');
|
||||||
|
|
||||||
/** @var ConstraintViolationInterface[] $violations */
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
@ -278,17 +357,24 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertNull($violations[0]->getCode());
|
$this->assertNull($violations[0]->getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function testTraversableTraverseEnabled()
|
||||||
* @expectedException \Symfony\Component\Validator\Exception\NoSuchMetadataException
|
|
||||||
*/
|
|
||||||
public function testTraversableTraverseDisabled()
|
|
||||||
{
|
{
|
||||||
$test = $this;
|
$test = $this;
|
||||||
$entity = new Entity();
|
$entity = new Entity();
|
||||||
$traversable = new \ArrayIterator(array('key' => $entity));
|
$traversable = new \ArrayIterator(array('key' => $entity));
|
||||||
|
|
||||||
$callback = function () use ($test) {
|
$callback = function ($value, ExecutionContextInterface $context) use ($test, $entity, $traversable) {
|
||||||
$test->fail('Should not be called');
|
$test->assertSame($test::ENTITY_CLASS, $context->getClassName());
|
||||||
|
$test->assertNull($context->getPropertyName());
|
||||||
|
$test->assertSame('[key]', $context->getPropertyPath());
|
||||||
|
$test->assertSame('Group', $context->getGroup());
|
||||||
|
$test->assertSame($test->metadata, $context->getMetadata());
|
||||||
|
$test->assertSame($test->metadataFactory, $context->getMetadataFactory());
|
||||||
|
$test->assertSame($traversable, $context->getRoot());
|
||||||
|
$test->assertSame($entity, $context->getValue());
|
||||||
|
$test->assertSame($entity, $value);
|
||||||
|
|
||||||
|
$context->addViolation('Message %param%', array('%param%' => 'value'));
|
||||||
};
|
};
|
||||||
|
|
||||||
$this->metadata->addConstraint(new Callback(array(
|
$this->metadata->addConstraint(new Callback(array(
|
||||||
@ -296,10 +382,21 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
'groups' => 'Group',
|
'groups' => 'Group',
|
||||||
)));
|
)));
|
||||||
|
|
||||||
$this->validator->validate($traversable, 'Group');
|
$violations = $this->validator->validateCollection($traversable, 'Group', true);
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(1, $violations);
|
||||||
|
$this->assertSame('Message value', $violations[0]->getMessage());
|
||||||
|
$this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
|
||||||
|
$this->assertSame(array('%param%' => 'value'), $violations[0]->getMessageParameters());
|
||||||
|
$this->assertSame('[key]', $violations[0]->getPropertyPath());
|
||||||
|
$this->assertSame($traversable, $violations[0]->getRoot());
|
||||||
|
$this->assertSame($entity, $violations[0]->getInvalidValue());
|
||||||
|
$this->assertNull($violations[0]->getMessagePluralization());
|
||||||
|
$this->assertNull($violations[0]->getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testTraversableTraverseEnabled()
|
public function testTraversableTraverseEnabledLegacyApi()
|
||||||
{
|
{
|
||||||
$test = $this;
|
$test = $this;
|
||||||
$entity = new Entity();
|
$entity = new Entity();
|
||||||
@ -338,6 +435,27 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$this->assertNull($violations[0]->getCode());
|
$this->assertNull($violations[0]->getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Validator\Exception\NoSuchMetadataException
|
||||||
|
*/
|
||||||
|
public function testTraversableTraverseDisabledLegacyApi()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$traversable = new \ArrayIterator(array('key' => $entity));
|
||||||
|
|
||||||
|
$callback = function () use ($test) {
|
||||||
|
$test->fail('Should not be called');
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
|
||||||
|
$this->validator->validate($traversable, 'Group');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Validator\Exception\NoSuchMetadataException
|
* @expectedException \Symfony\Component\Validator\Exception\NoSuchMetadataException
|
||||||
*/
|
*/
|
||||||
@ -358,6 +476,29 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
'groups' => 'Group',
|
'groups' => 'Group',
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
$this->validator->validateCollection($traversable, 'Group');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Validator\Exception\NoSuchMetadataException
|
||||||
|
*/
|
||||||
|
public function testRecursiveTraversableRecursiveTraversalDisabledLegacyApi()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$traversable = new \ArrayIterator(array(
|
||||||
|
2 => new \ArrayIterator(array('key' => $entity)),
|
||||||
|
));
|
||||||
|
|
||||||
|
$callback = function () use ($test) {
|
||||||
|
$test->fail('Should not be called');
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
|
||||||
$this->validator->validate($traversable, 'Group', true);
|
$this->validator->validate($traversable, 'Group', true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -388,6 +529,47 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
'groups' => 'Group',
|
'groups' => 'Group',
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validator->validateCollection($traversable, 'Group', true);
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(1, $violations);
|
||||||
|
$this->assertSame('Message value', $violations[0]->getMessage());
|
||||||
|
$this->assertSame('Message %param%', $violations[0]->getMessageTemplate());
|
||||||
|
$this->assertSame(array('%param%' => 'value'), $violations[0]->getMessageParameters());
|
||||||
|
$this->assertSame('[2][key]', $violations[0]->getPropertyPath());
|
||||||
|
$this->assertSame($traversable, $violations[0]->getRoot());
|
||||||
|
$this->assertSame($entity, $violations[0]->getInvalidValue());
|
||||||
|
$this->assertNull($violations[0]->getMessagePluralization());
|
||||||
|
$this->assertNull($violations[0]->getCode());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveTraversableRecursiveTraversalEnabledLegacyApi()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$traversable = new \ArrayIterator(array(
|
||||||
|
2 => new \ArrayIterator(array('key' => $entity)),
|
||||||
|
));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) use ($test, $entity, $traversable) {
|
||||||
|
$test->assertSame($test::ENTITY_CLASS, $context->getClassName());
|
||||||
|
$test->assertNull($context->getPropertyName());
|
||||||
|
$test->assertSame('[2][key]', $context->getPropertyPath());
|
||||||
|
$test->assertSame('Group', $context->getGroup());
|
||||||
|
$test->assertSame($test->metadata, $context->getMetadata());
|
||||||
|
$test->assertSame($test->metadataFactory, $context->getMetadataFactory());
|
||||||
|
$test->assertSame($traversable, $context->getRoot());
|
||||||
|
$test->assertSame($entity, $context->getValue());
|
||||||
|
$test->assertSame($entity, $value);
|
||||||
|
|
||||||
|
$context->addViolation('Message %param%', array('%param%' => 'value'));
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
|
||||||
$violations = $this->validator->validate($traversable, 'Group', true, true);
|
$violations = $this->validator->validate($traversable, 'Group', true, true);
|
||||||
|
|
||||||
/** @var ConstraintViolationInterface[] $violations */
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
@ -1675,6 +1857,28 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase
|
|||||||
$test->assertSame('Separate violation', $violations[0]->getMessage());
|
$test->assertSame('Separate violation', $violations[0]->getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
|
||||||
|
*/
|
||||||
|
public function testExpectTraversableIfTraverse()
|
||||||
|
{
|
||||||
|
$entity = new Entity();
|
||||||
|
|
||||||
|
$this->validator->validateValue($entity, new Traverse());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
|
||||||
|
*/
|
||||||
|
public function testExpectTraversableIfTraverseOnClass()
|
||||||
|
{
|
||||||
|
$entity = new Entity();
|
||||||
|
|
||||||
|
$this->metadata->addConstraint(new Traverse());
|
||||||
|
|
||||||
|
$this->validator->validate($entity);
|
||||||
|
}
|
||||||
|
|
||||||
public function testGetMetadataFactory()
|
public function testGetMetadataFactory()
|
||||||
{
|
{
|
||||||
$this->assertSame($this->metadataFactory, $this->validator->getMetadataFactory());
|
$this->assertSame($this->metadataFactory, $this->validator->getMetadataFactory());
|
||||||
|
@ -55,6 +55,41 @@ class LegacyValidatorTest extends AbstractValidatorTest
|
|||||||
$this->markTestSkipped('Not supported in the legacy API');
|
$this->markTestSkipped('Not supported in the legacy API');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testArray()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Not supported in the legacy API');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveArray()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Not supported in the legacy API');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTraversableTraverseEnabled()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Not supported in the legacy API');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveTraversableRecursiveTraversalDisabled()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Not supported in the legacy API');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRecursiveTraversableRecursiveTraversalEnabled()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Not supported in the legacy API');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExpectTraversableIfTraverse()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Not supported in the legacy API');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testExpectTraversableIfTraverseOnClass()
|
||||||
|
{
|
||||||
|
$this->markTestSkipped('Not supported in the legacy API');
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Validator\Exception\ValidatorException
|
* @expectedException \Symfony\Component\Validator\Exception\ValidatorException
|
||||||
*/
|
*/
|
||||||
|
@ -72,6 +72,25 @@ abstract class AbstractValidator implements ValidatorInterface
|
|||||||
return $this->metadataFactory->hasMetadataFor($object);
|
return $this->metadataFactory->hasMetadataFor($object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function traverse($value, $constraints, $groups = null)
|
||||||
|
{
|
||||||
|
if (!is_array($constraints)) {
|
||||||
|
$constraints = array($constraints);
|
||||||
|
}
|
||||||
|
|
||||||
|
$metadata = new GenericMetadata();
|
||||||
|
$metadata->addConstraints($constraints);
|
||||||
|
$groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
|
||||||
|
|
||||||
|
$this->nodeTraverser->traverse(array(new GenericNode(
|
||||||
|
$value,
|
||||||
|
$metadata,
|
||||||
|
$this->defaultPropertyPath,
|
||||||
|
$groups,
|
||||||
|
$groups
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
protected function traverseObject($object, $groups = null)
|
protected function traverseObject($object, $groups = null)
|
||||||
{
|
{
|
||||||
$classMetadata = $this->metadataFactory->getMetadataFor($object);
|
$classMetadata = $this->metadataFactory->getMetadataFor($object);
|
||||||
@ -158,25 +177,6 @@ abstract class AbstractValidator implements ValidatorInterface
|
|||||||
$this->nodeTraverser->traverse($nodes);
|
$this->nodeTraverser->traverse($nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function traverseValue($value, $constraints, $groups = null)
|
|
||||||
{
|
|
||||||
if (!is_array($constraints)) {
|
|
||||||
$constraints = array($constraints);
|
|
||||||
}
|
|
||||||
|
|
||||||
$metadata = new GenericMetadata();
|
|
||||||
$metadata->addConstraints($constraints);
|
|
||||||
$groups = $groups ? $this->normalizeGroups($groups) : $this->defaultGroups;
|
|
||||||
|
|
||||||
$this->nodeTraverser->traverse(array(new GenericNode(
|
|
||||||
$value,
|
|
||||||
$metadata,
|
|
||||||
$this->defaultPropertyPath,
|
|
||||||
$groups,
|
|
||||||
$groups
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function normalizeGroups($groups)
|
protected function normalizeGroups($groups)
|
||||||
{
|
{
|
||||||
if (is_array($groups)) {
|
if (is_array($groups)) {
|
||||||
|
@ -58,7 +58,7 @@ class ContextualValidator extends AbstractValidator implements ContextualValidat
|
|||||||
*/
|
*/
|
||||||
public function validate($value, $constraints, $groups = null)
|
public function validate($value, $constraints, $groups = null)
|
||||||
{
|
{
|
||||||
$this->traverseValue($value, $constraints, $groups);
|
$this->traverse($value, $constraints, $groups);
|
||||||
|
|
||||||
return $this->context->getViolations();
|
return $this->context->getViolations();
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ class ContextualValidator extends AbstractValidator implements ContextualValidat
|
|||||||
'deep' => $deep,
|
'deep' => $deep,
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->traverseValue($collection, $constraint, $groups);
|
$this->traverse($collection, $constraint, $groups);
|
||||||
|
|
||||||
return $this->context->getViolations();
|
return $this->context->getViolations();
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ class LegacyValidator extends Validator implements LegacyValidatorInterface
|
|||||||
'deep' => $deep,
|
'deep' => $deep,
|
||||||
));
|
));
|
||||||
|
|
||||||
return $this->validateValue($value, $constraint, $groups);
|
return parent::validate($value, $constraint, $groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($traverse && $value instanceof \Traversable) {
|
if ($traverse && $value instanceof \Traversable) {
|
||||||
@ -44,7 +44,7 @@ class LegacyValidator extends Validator implements LegacyValidatorInterface
|
|||||||
new Traverse(array('traverse' => true, 'deep' => $deep)),
|
new Traverse(array('traverse' => true, 'deep' => $deep)),
|
||||||
);
|
);
|
||||||
|
|
||||||
return $this->validateValue($value, $constraints, $groups);
|
return parent::validate($value, $constraints, $groups);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->validateObject($value, $groups);
|
return $this->validateObject($value, $groups);
|
||||||
|
@ -38,7 +38,7 @@ class Validator extends AbstractValidator
|
|||||||
{
|
{
|
||||||
$this->contextManager->startContext($value);
|
$this->contextManager->startContext($value);
|
||||||
|
|
||||||
$this->traverseValue($value, $constraints, $groups);
|
$this->traverse($value, $constraints, $groups);
|
||||||
|
|
||||||
return $this->contextManager->stopContext()->getViolations();
|
return $this->contextManager->stopContext()->getViolations();
|
||||||
}
|
}
|
||||||
@ -61,7 +61,7 @@ class Validator extends AbstractValidator
|
|||||||
'deep' => $deep,
|
'deep' => $deep,
|
||||||
));
|
));
|
||||||
|
|
||||||
$this->traverseValue($collection, $constraint, $groups);
|
$this->traverse($collection, $constraint, $groups);
|
||||||
|
|
||||||
return $this->contextManager->stopContext()->getViolations();
|
return $this->contextManager->stopContext()->getViolations();
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user