[Validator] Improved test coverage of the Traverse constraint
This commit is contained in:
parent
9ca61df923
commit
01ceeda376
@ -24,8 +24,6 @@ class Traverse extends Constraint
|
|||||||
{
|
{
|
||||||
public $traverse = true;
|
public $traverse = true;
|
||||||
|
|
||||||
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)) {
|
||||||
|
@ -25,6 +25,9 @@ class Valid extends Constraint
|
|||||||
{
|
{
|
||||||
public $traverse = true;
|
public $traverse = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @deprecated Deprecated as of version 2.5, to be removed in Symfony 3.0.
|
||||||
|
*/
|
||||||
public $deep = true;
|
public $deep = true;
|
||||||
|
|
||||||
public function __construct($options = null)
|
public function __construct($options = null)
|
||||||
@ -38,11 +41,4 @@ class Valid extends Constraint
|
|||||||
|
|
||||||
parent::__construct($options);
|
parent::__construct($options);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getDefaultOption()
|
|
||||||
{
|
|
||||||
// Traverse is extended for backwards compatibility reasons
|
|
||||||
// The parent class should be removed in 3.0
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -111,10 +111,8 @@ class LegacyExecutionContext extends ExecutionContext
|
|||||||
public function validate($value, $subPath = '', $groups = null, $traverse = false, $deep = false)
|
public function validate($value, $subPath = '', $groups = null, $traverse = false, $deep = false)
|
||||||
{
|
{
|
||||||
if (is_array($value)) {
|
if (is_array($value)) {
|
||||||
$constraint = new Traverse(array(
|
// The $traverse flag is ignored for arrays
|
||||||
'traverse' => true,
|
$constraint = new Valid(array('traverse' => true, 'deep' => $deep));
|
||||||
'deep' => $deep,
|
|
||||||
));
|
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->getValidator()
|
->getValidator()
|
||||||
@ -125,16 +123,13 @@ class LegacyExecutionContext extends ExecutionContext
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($traverse && $value instanceof \Traversable) {
|
if ($traverse && $value instanceof \Traversable) {
|
||||||
$constraints = array(
|
$constraint = new Valid(array('traverse' => true, 'deep' => $deep));
|
||||||
new Valid(),
|
|
||||||
new Traverse(array('traverse' => true, 'deep' => $deep)),
|
|
||||||
);
|
|
||||||
|
|
||||||
return $this
|
return $this
|
||||||
->getValidator()
|
->getValidator()
|
||||||
->inContext($this)
|
->inContext($this)
|
||||||
->atPath($subPath)
|
->atPath($subPath)
|
||||||
->validate($value, $constraints, $groups)
|
->validate($value, $constraint, $groups)
|
||||||
;
|
;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -201,20 +201,12 @@ class ClassMetadata extends ElementMetadata implements LegacyMetadataInterface,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ($constraint instanceof Traverse) {
|
if ($constraint instanceof Traverse) {
|
||||||
if (true === $constraint->traverse) {
|
if ($constraint->traverse) {
|
||||||
// If traverse is true, traversal should be explicitly enabled
|
// If traverse is true, traversal should be explicitly enabled
|
||||||
$this->traversalStrategy = TraversalStrategy::TRAVERSE;
|
$this->traversalStrategy = TraversalStrategy::TRAVERSE;
|
||||||
|
} else {
|
||||||
if (!$constraint->deep) {
|
|
||||||
$this->traversalStrategy |= TraversalStrategy::STOP_RECURSION;
|
|
||||||
}
|
|
||||||
} elseif (false === $constraint->traverse) {
|
|
||||||
// If traverse is false, traversal should be explicitly disabled
|
// If traverse is false, traversal should be explicitly disabled
|
||||||
$this->traversalStrategy = TraversalStrategy::NONE;
|
$this->traversalStrategy = TraversalStrategy::NONE;
|
||||||
} else {
|
|
||||||
// Else, traverse depending on the contextual information that
|
|
||||||
// is available during validation
|
|
||||||
$this->traversalStrategy = TraversalStrategy::IMPLICIT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// The constraint is not added
|
// The constraint is not added
|
||||||
|
@ -12,8 +12,10 @@
|
|||||||
namespace Symfony\Component\Validator\Mapping;
|
namespace Symfony\Component\Validator\Mapping;
|
||||||
|
|
||||||
use Symfony\Component\Validator\Constraint;
|
use Symfony\Component\Validator\Constraint;
|
||||||
|
use Symfony\Component\Validator\Constraints\Traverse;
|
||||||
use Symfony\Component\Validator\Constraints\Valid;
|
use Symfony\Component\Validator\Constraints\Valid;
|
||||||
use Symfony\Component\Validator\Exception\BadMethodCallException;
|
use Symfony\Component\Validator\Exception\BadMethodCallException;
|
||||||
|
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
|
||||||
use Symfony\Component\Validator\ValidationVisitorInterface;
|
use Symfony\Component\Validator\ValidationVisitorInterface;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -103,9 +105,20 @@ class GenericMetadata implements MetadataInterface
|
|||||||
* @param Constraint $constraint The constraint to add
|
* @param Constraint $constraint The constraint to add
|
||||||
*
|
*
|
||||||
* @return GenericMetadata This object
|
* @return GenericMetadata This object
|
||||||
|
*
|
||||||
|
* @throws ConstraintDefinitionException When trying to add the
|
||||||
|
* {@link Traverse} constraint
|
||||||
*/
|
*/
|
||||||
public function addConstraint(Constraint $constraint)
|
public function addConstraint(Constraint $constraint)
|
||||||
{
|
{
|
||||||
|
if ($constraint instanceof Traverse) {
|
||||||
|
throw new ConstraintDefinitionException(sprintf(
|
||||||
|
'The constraint "%s" can only be put on classes. Please use '.
|
||||||
|
'"Symfony\Component\Validator\Constraints\Valid" instead.',
|
||||||
|
get_class($constraint)
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if ($constraint instanceof Valid) {
|
if ($constraint instanceof Valid) {
|
||||||
$this->cascadingStrategy = CascadingStrategy::CASCADE;
|
$this->cascadingStrategy = CascadingStrategy::CASCADE;
|
||||||
|
|
||||||
|
@ -58,21 +58,6 @@ abstract class MemberMetadata extends ElementMetadata implements PropertyMetadat
|
|||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
// BC with Symfony < 2.5
|
|
||||||
if ($constraint instanceof Valid) {
|
|
||||||
if (true === $constraint->traverse) {
|
|
||||||
// Try to traverse cascaded objects, but ignore if they do not
|
|
||||||
// implement Traversable
|
|
||||||
$this->traversalStrategy = TraversalStrategy::IMPLICIT;
|
|
||||||
|
|
||||||
if (!$constraint->deep) {
|
|
||||||
$this->traversalStrategy |= TraversalStrategy::STOP_RECURSION;
|
|
||||||
}
|
|
||||||
} elseif (false === $constraint->traverse) {
|
|
||||||
$this->traversalStrategy = TraversalStrategy::NONE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
parent::addConstraint($constraint);
|
parent::addConstraint($constraint);
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -328,18 +328,208 @@ abstract class Abstract2Dot5ApiTest extends AbstractValidatorTest
|
|||||||
$this->assertNull($violations[0]->getCode());
|
$this->assertNull($violations[0]->getCode());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testTraversalEnabledOnClass()
|
||||||
|
{
|
||||||
|
$entity = new Entity();
|
||||||
|
$traversable = new \ArrayIterator(array('key' => $entity));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) {
|
||||||
|
$context->addViolation('Message');
|
||||||
|
};
|
||||||
|
|
||||||
|
$traversableMetadata = new ClassMetadata('ArrayIterator');
|
||||||
|
$traversableMetadata->addConstraint(new Traverse(true));
|
||||||
|
|
||||||
|
$this->metadataFactory->addMetadata($traversableMetadata);
|
||||||
|
$this->metadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validate($traversable, new Valid(), 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(1, $violations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testTraversalDisabledOnClass()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$traversable = new \ArrayIterator(array('key' => $entity));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
|
||||||
|
$test->fail('Should not be called');
|
||||||
|
};
|
||||||
|
|
||||||
|
$traversableMetadata = new ClassMetadata('ArrayIterator');
|
||||||
|
$traversableMetadata->addConstraint(new Traverse(false));
|
||||||
|
|
||||||
|
$this->metadataFactory->addMetadata($traversableMetadata);
|
||||||
|
$this->metadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validate($traversable, new Valid(), 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(0, $violations);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
|
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
|
||||||
*/
|
*/
|
||||||
public function testExpectTraversableIfTraverseOnClass()
|
public function testExpectTraversableIfTraversalEnabledOnClass()
|
||||||
{
|
{
|
||||||
$entity = new Entity();
|
$entity = new Entity();
|
||||||
|
|
||||||
$this->metadata->addConstraint(new Traverse());
|
$this->metadata->addConstraint(new Traverse(true));
|
||||||
|
|
||||||
$this->validator->validate($entity);
|
$this->validator->validate($entity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testReferenceTraversalDisabledOnClass()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$entity->reference = new \ArrayIterator(array('key' => new Reference()));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
|
||||||
|
$test->fail('Should not be called');
|
||||||
|
};
|
||||||
|
|
||||||
|
$traversableMetadata = new ClassMetadata('ArrayIterator');
|
||||||
|
$traversableMetadata->addConstraint(new Traverse(false));
|
||||||
|
|
||||||
|
$this->metadataFactory->addMetadata($traversableMetadata);
|
||||||
|
$this->referenceMetadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
$this->metadata->addPropertyConstraint('reference', new Valid());
|
||||||
|
|
||||||
|
$violations = $this->validate($entity, new Valid(), 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(0, $violations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferenceTraversalEnabledOnReferenceDisabledOnClass()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$entity->reference = new \ArrayIterator(array('key' => new Reference()));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
|
||||||
|
$test->fail('Should not be called');
|
||||||
|
};
|
||||||
|
|
||||||
|
$traversableMetadata = new ClassMetadata('ArrayIterator');
|
||||||
|
$traversableMetadata->addConstraint(new Traverse(false));
|
||||||
|
|
||||||
|
$this->metadataFactory->addMetadata($traversableMetadata);
|
||||||
|
$this->referenceMetadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
$this->metadata->addPropertyConstraint('reference', new Valid(array(
|
||||||
|
'traverse' => true,
|
||||||
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validate($entity, new Valid(), 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(0, $violations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferenceTraversalDisabledOnReferenceEnabledOnClass()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$entity->reference = new \ArrayIterator(array('key' => new Reference()));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
|
||||||
|
$test->fail('Should not be called');
|
||||||
|
};
|
||||||
|
|
||||||
|
$traversableMetadata = new ClassMetadata('ArrayIterator');
|
||||||
|
$traversableMetadata->addConstraint(new Traverse(true));
|
||||||
|
|
||||||
|
$this->metadataFactory->addMetadata($traversableMetadata);
|
||||||
|
$this->referenceMetadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
$this->metadata->addPropertyConstraint('reference', new Valid(array(
|
||||||
|
'traverse' => false,
|
||||||
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validate($entity, new Valid(), 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(0, $violations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferenceTraversalRecursionEnabledOnReferenceTraversalEnabledOnClass()
|
||||||
|
{
|
||||||
|
$entity = new Entity();
|
||||||
|
$entity->reference = new \ArrayIterator(array(
|
||||||
|
2 => new \ArrayIterator(array('key' => new Reference())),
|
||||||
|
));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) {
|
||||||
|
$context->addViolation('Message');
|
||||||
|
};
|
||||||
|
|
||||||
|
$traversableMetadata = new ClassMetadata('ArrayIterator');
|
||||||
|
$traversableMetadata->addConstraint(new Traverse(true));
|
||||||
|
|
||||||
|
$this->metadataFactory->addMetadata($traversableMetadata);
|
||||||
|
$this->referenceMetadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
$this->metadata->addPropertyConstraint('reference', new Valid(array(
|
||||||
|
'deep' => true,
|
||||||
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validate($entity, new Valid(), 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(1, $violations);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testReferenceTraversalRecursionDisabledOnReferenceTraversalEnabledOnClass()
|
||||||
|
{
|
||||||
|
$test = $this;
|
||||||
|
$entity = new Entity();
|
||||||
|
$entity->reference = new \ArrayIterator(array(
|
||||||
|
2 => new \ArrayIterator(array('key' => new Reference())),
|
||||||
|
));
|
||||||
|
|
||||||
|
$callback = function ($value, ExecutionContextInterface $context) use ($test) {
|
||||||
|
$test->fail('Should not be called');
|
||||||
|
};
|
||||||
|
|
||||||
|
$traversableMetadata = new ClassMetadata('ArrayIterator');
|
||||||
|
$traversableMetadata->addConstraint(new Traverse(true));
|
||||||
|
|
||||||
|
$this->metadataFactory->addMetadata($traversableMetadata);
|
||||||
|
$this->referenceMetadata->addConstraint(new Callback(array(
|
||||||
|
'callback' => $callback,
|
||||||
|
'groups' => 'Group',
|
||||||
|
)));
|
||||||
|
$this->metadata->addPropertyConstraint('reference', new Valid(array(
|
||||||
|
'deep' => false,
|
||||||
|
)));
|
||||||
|
|
||||||
|
$violations = $this->validate($entity, new Valid(), 'Group');
|
||||||
|
|
||||||
|
/** @var ConstraintViolationInterface[] $violations */
|
||||||
|
$this->assertCount(0, $violations);
|
||||||
|
}
|
||||||
|
|
||||||
public function testAddCustomizedViolation()
|
public function testAddCustomizedViolation()
|
||||||
{
|
{
|
||||||
$entity = new Entity();
|
$entity = new Entity();
|
||||||
|
Reference in New Issue
Block a user