From ee1adadbfb8b620900028089135c575f20733dc7 Mon Sep 17 00:00:00 2001 From: Bernhard Schussek Date: Wed, 19 Feb 2014 11:56:27 +0100 Subject: [PATCH] [Validator] Implemented handling of arrays and Traversables in LegacyExecutionContext::validate() --- .../Context/LegacyExecutionContext.php | 30 +++++- .../Tests/Validator/AbstractValidatorTest.php | 95 +++++++++++++++++++ .../Validator/Validator/AbstractValidator.php | 2 - .../Validator/ContextualValidator.php | 15 +++ .../Validator/Validator/LegacyValidator.php | 12 ++- .../Validator/Validator/Validator.php | 15 +++ .../Validator/ValidatorInterface.php | 2 +- 7 files changed, 163 insertions(+), 8 deletions(-) diff --git a/src/Symfony/Component/Validator/Context/LegacyExecutionContext.php b/src/Symfony/Component/Validator/Context/LegacyExecutionContext.php index ad38accc62..db30eb92b5 100644 --- a/src/Symfony/Component/Validator/Context/LegacyExecutionContext.php +++ b/src/Symfony/Component/Validator/Context/LegacyExecutionContext.php @@ -12,6 +12,8 @@ namespace Symfony\Component\Validator\Context; use Symfony\Component\Translation\TranslatorInterface; +use Symfony\Component\Validator\Constraints\Traverse; +use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Exception\InvalidArgumentException; use Symfony\Component\Validator\ExecutionContextInterface as LegacyExecutionContextInterface; use Symfony\Component\Validator\Group\GroupManagerInterface; @@ -100,7 +102,33 @@ class LegacyExecutionContext extends ExecutionContext implements LegacyExecution */ public function validate($value, $subPath = '', $groups = null, $traverse = false, $deep = false) { - // TODO handle $traverse and $deep + if (is_array($value)) { + $constraint = new Traverse(array( + 'traverse' => true, + 'deep' => $deep, + )); + + return $this + ->getValidator() + ->inContext($this) + ->atPath($subPath) + ->validateValue($value, $constraint, $groups) + ; + } + + if ($traverse && $value instanceof \Traversable) { + $constraints = array( + new Valid(), + new Traverse(array('traverse' => true, 'deep' => $deep)), + ); + + return $this + ->getValidator() + ->inContext($this) + ->atPath($subPath) + ->validateValue($value, $constraints, $groups) + ; + } return $this ->getValidator() diff --git a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php index 2b55290a1d..b7a9ee2e19 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/AbstractValidatorTest.php @@ -15,6 +15,7 @@ use Symfony\Component\Validator\Constraint; use Symfony\Component\Validator\Constraints\Callback; use Symfony\Component\Validator\Constraints\GroupSequence; use Symfony\Component\Validator\ConstraintViolationInterface; +use Symfony\Component\Validator\Context\ExecutionContext; use Symfony\Component\Validator\ExecutionContextInterface; use Symfony\Component\Validator\MetadataFactoryInterface; use Symfony\Component\Validator\Tests\Fixtures\FakeMetadataFactory; @@ -1416,6 +1417,100 @@ abstract class AbstractValidatorTest extends \PHPUnit_Framework_TestCase $this->assertSame('Violation in Group 2', $violations[0]->getMessage()); } + public function testValidateInContext() + { + $test = $this; + $entity = new Entity(); + $entity->reference = new Reference(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->validate($value->reference, 'subpath'); + }; + + $callback2 = function ($value, ExecutionContextInterface $context) use ($test, $entity) { + $test->assertSame($test::REFERENCE_CLASS, $context->getClassName()); + $test->assertNull($context->getPropertyName()); + $test->assertSame('subpath', $context->getPropertyPath()); + $test->assertSame('Group', $context->getGroup()); + $test->assertSame($test->referenceMetadata, $context->getMetadata()); + $test->assertSame($test->metadataFactory, $context->getMetadataFactory()); + $test->assertSame($entity, $context->getRoot()); + $test->assertSame($entity->reference, $context->getValue()); + $test->assertSame($entity->reference, $value); + + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + + $this->metadata->addConstraint(new Callback(array( + 'callback' => $callback1, + 'groups' => 'Group', + ))); + $this->referenceMetadata->addConstraint(new Callback(array( + 'callback' => $callback2, + 'groups' => 'Group', + ))); + + $violations = $this->validator->validate($entity, '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('subpath', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getMessagePluralization()); + $this->assertNull($violations[0]->getCode()); + } + + public function testValidateArrayInContext() + { + $test = $this; + $entity = new Entity(); + $entity->reference = new Reference(); + + $callback1 = function ($value, ExecutionContextInterface $context) { + $context->validate(array('key' => $value->reference), 'subpath'); + }; + + $callback2 = function ($value, ExecutionContextInterface $context) use ($test, $entity) { + $test->assertSame($test::REFERENCE_CLASS, $context->getClassName()); + $test->assertNull($context->getPropertyName()); + $test->assertSame('subpath[key]', $context->getPropertyPath()); + $test->assertSame('Group', $context->getGroup()); + $test->assertSame($test->referenceMetadata, $context->getMetadata()); + $test->assertSame($test->metadataFactory, $context->getMetadataFactory()); + $test->assertSame($entity, $context->getRoot()); + $test->assertSame($entity->reference, $context->getValue()); + $test->assertSame($entity->reference, $value); + + $context->addViolation('Message %param%', array('%param%' => 'value')); + }; + + $this->metadata->addConstraint(new Callback(array( + 'callback' => $callback1, + 'groups' => 'Group', + ))); + $this->referenceMetadata->addConstraint(new Callback(array( + 'callback' => $callback2, + 'groups' => 'Group', + ))); + + $violations = $this->validator->validate($entity, '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('subpath[key]', $violations[0]->getPropertyPath()); + $this->assertSame($entity, $violations[0]->getRoot()); + $this->assertSame($entity->reference, $violations[0]->getInvalidValue()); + $this->assertNull($violations[0]->getMessagePluralization()); + $this->assertNull($violations[0]->getCode()); + } + public function testGetMetadataFactory() { $this->assertSame($this->metadataFactory, $this->validator->getMetadataFactory()); diff --git a/src/Symfony/Component/Validator/Validator/AbstractValidator.php b/src/Symfony/Component/Validator/Validator/AbstractValidator.php index f0de724ffe..d4d4e62e9c 100644 --- a/src/Symfony/Component/Validator/Validator/AbstractValidator.php +++ b/src/Symfony/Component/Validator/Validator/AbstractValidator.php @@ -12,9 +12,7 @@ namespace Symfony\Component\Validator\Validator; use Symfony\Component\Validator\Constraint; -use Symfony\Component\Validator\Constraints\Traverse; use Symfony\Component\Validator\Context\ExecutionContextInterface; -use Symfony\Component\Validator\Exception\UnexpectedTypeException; use Symfony\Component\Validator\Exception\ValidatorException; use Symfony\Component\Validator\Mapping\ClassMetadataInterface; use Symfony\Component\Validator\Mapping\GenericMetadata; diff --git a/src/Symfony/Component/Validator/Validator/ContextualValidator.php b/src/Symfony/Component/Validator/Validator/ContextualValidator.php index 560f088522..8a58f48d9f 100644 --- a/src/Symfony/Component/Validator/Validator/ContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/ContextualValidator.php @@ -12,6 +12,7 @@ namespace Symfony\Component\Validator\Validator; use Symfony\Component\Validator\Constraint; +use Symfony\Component\Validator\Constraints\Traverse; use Symfony\Component\Validator\ConstraintViolationListInterface; use Symfony\Component\Validator\Context\ExecutionContextInterface; use Symfony\Component\Validator\Context\ExecutionContextManagerInterface; @@ -41,6 +42,8 @@ class ContextualValidator extends AbstractValidator implements ContextualValidat public function atPath($subPath) { $this->defaultPropertyPath = $this->context->getPropertyPath($subPath); + + return $this; } /** @@ -62,6 +65,18 @@ class ContextualValidator extends AbstractValidator implements ContextualValidat return $this->context->getViolations(); } + public function validateCollection($collection, $groups = null, $deep = false) + { + $constraint = new Traverse(array( + 'traverse' => true, + 'deep' => $deep, + )); + + $this->traverseValue($collection, $constraint, $groups); + + return $this->context->getViolations(); + } + /** * Validates a property of a value against its current value. * diff --git a/src/Symfony/Component/Validator/Validator/LegacyValidator.php b/src/Symfony/Component/Validator/Validator/LegacyValidator.php index 9586f200dd..059d84c1da 100644 --- a/src/Symfony/Component/Validator/Validator/LegacyValidator.php +++ b/src/Symfony/Component/Validator/Validator/LegacyValidator.php @@ -24,17 +24,21 @@ class LegacyValidator extends Validator implements LegacyValidatorInterface public function validate($value, $groups = null, $traverse = false, $deep = false) { if (is_array($value)) { - return $this->validateValue($value, new Traverse(array( + $constraint = new Traverse(array( 'traverse' => true, 'deep' => $deep, - )), $groups); + )); + + return $this->validateValue($value, $constraint, $groups); } if ($traverse && $value instanceof \Traversable) { - return $this->validateValue($value, array( + $constraints = array( new Valid(), new Traverse(array('traverse' => true, 'deep' => $deep)), - ), $groups); + ); + + return $this->validateValue($value, $constraints, $groups); } return $this->validateObject($value, $groups); diff --git a/src/Symfony/Component/Validator/Validator/Validator.php b/src/Symfony/Component/Validator/Validator/Validator.php index fe55818e70..18022ba19f 100644 --- a/src/Symfony/Component/Validator/Validator/Validator.php +++ b/src/Symfony/Component/Validator/Validator/Validator.php @@ -11,6 +11,7 @@ namespace Symfony\Component\Validator\Validator; +use Symfony\Component\Validator\Constraints\Traverse; use Symfony\Component\Validator\Context\ExecutionContextManagerInterface; use Symfony\Component\Validator\NodeTraverser\NodeTraverserInterface; use Symfony\Component\Validator\MetadataFactoryInterface; @@ -42,6 +43,20 @@ class Validator extends AbstractValidator return $this->contextManager->stopContext()->getViolations(); } + public function validateCollection($collection, $groups = null, $deep = false) + { + $this->contextManager->startContext($collection); + + $constraint = new Traverse(array( + 'traverse' => true, + 'deep' => $deep, + )); + + $this->traverseValue($collection, $constraint, $groups); + + return $this->contextManager->stopContext()->getViolations(); + } + public function validateProperty($object, $propertyName, $groups = null) { $this->contextManager->startContext($object); diff --git a/src/Symfony/Component/Validator/Validator/ValidatorInterface.php b/src/Symfony/Component/Validator/Validator/ValidatorInterface.php index 7985a1f9ef..6d1833d162 100644 --- a/src/Symfony/Component/Validator/Validator/ValidatorInterface.php +++ b/src/Symfony/Component/Validator/Validator/ValidatorInterface.php @@ -35,7 +35,7 @@ interface ValidatorInterface */ public function validateObject($object, $groups = null); -// public function validateCollection($collection, $groups = null); + public function validateCollection($collection, $groups = null, $deep = false); /** * Validates a property of a value against its current value.