diff --git a/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/ChildA.php b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/ChildA.php new file mode 100644 index 0000000000000..2234ecda28e88 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/ChildA.php @@ -0,0 +1,30 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints\Fixtures; + +use Symfony\Component\Validator\Constraints as Assert; + +class ChildA +{ + /** + * @Assert\Valid + * @Assert\NotNull + * @Assert\NotBlank + */ + public $name; + /** + * @var ChildB + * @Assert\Valid + * @Assert\NotNull + */ + public $childB; +} diff --git a/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/ChildB.php b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/ChildB.php new file mode 100644 index 0000000000000..1b02d889f2f62 --- /dev/null +++ b/src/Symfony/Component/Validator/Tests/Constraints/Fixtures/ChildB.php @@ -0,0 +1,29 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Component\Validator\Tests\Constraints\Fixtures; + +use Symfony\Component\Validator\Constraints as Assert; + +class ChildB +{ + /** + * @Assert\Valid + * @Assert\NotBlank + */ + public $name; + /** + * @var ChildA + * @Assert\Valid + * @Assert\NotBlank + */ + public $childA; +} diff --git a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php index b93146b06d159..ddc1974a5fc6d 100644 --- a/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php +++ b/src/Symfony/Component/Validator/Tests/Fixtures/Entity.php @@ -33,6 +33,14 @@ class Entity extends EntityParent implements EntityInterfaceB * @Assert\Choice(choices={"A", "B"}, message="Must be one of %choices%") */ public $firstName; + /** + * @Assert\Valid + */ + public $childA; + /** + * @Assert\Valid + */ + public $childB; protected $lastName; public $reference; public $reference2; @@ -97,4 +105,36 @@ public function validateMe(ExecutionContextInterface $context) public static function validateMeStatic($object, ExecutionContextInterface $context) { } + + /** + * @return mixed + */ + public function getChildA() + { + return $this->childA; + } + + /** + * @param mixed $childA + */ + public function setChildA($childA) + { + $this->childA = $childA; + } + + /** + * @return mixed + */ + public function getChildB() + { + return $this->childB; + } + + /** + * @param mixed $childB + */ + public function setChildB($childB) + { + $this->childB = $childB; + } } diff --git a/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php b/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php index 0e2ad41d697a0..e2e1d47a3f7d8 100644 --- a/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php +++ b/src/Symfony/Component/Validator/Tests/Mapping/Loader/AnnotationLoaderTest.php @@ -19,6 +19,7 @@ use Symfony\Component\Validator\Constraints\NotNull; use Symfony\Component\Validator\Constraints\Range; use Symfony\Component\Validator\Constraints\IsTrue; +use Symfony\Component\Validator\Constraints\Valid; use Symfony\Component\Validator\Mapping\ClassMetadata; use Symfony\Component\Validator\Mapping\Loader\AnnotationLoader; use Symfony\Component\Validator\Tests\Fixtures\ConstraintA; @@ -67,6 +68,8 @@ public function testLoadClassMetadata() 'message' => 'Must be one of %choices%', 'choices' => array('A', 'B'), ))); + $expected->addPropertyConstraint('childA', new Valid()); + $expected->addPropertyConstraint('childB', new Valid()); $expected->addGetterConstraint('lastName', new NotNull()); $expected->addGetterConstraint('valid', new IsTrue()); $expected->addGetterConstraint('permissions', new IsTrue()); @@ -137,6 +140,8 @@ public function testLoadClassMetadataAndMerge() 'message' => 'Must be one of %choices%', 'choices' => array('A', 'B'), ))); + $expected->addPropertyConstraint('childA', new Valid()); + $expected->addPropertyConstraint('childB', new Valid()); $expected->addGetterConstraint('lastName', new NotNull()); $expected->addGetterConstraint('valid', new IsTrue()); $expected->addGetterConstraint('permissions', new IsTrue()); diff --git a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php index ab045fc477b21..cd77e948d4b1b 100644 --- a/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php +++ b/src/Symfony/Component/Validator/Tests/Validator/RecursiveValidatorTest.php @@ -15,6 +15,8 @@ use Symfony\Component\Validator\ConstraintValidatorFactory; use Symfony\Component\Validator\Context\ExecutionContextFactory; use Symfony\Component\Validator\Mapping\Factory\MetadataFactoryInterface; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildA; +use Symfony\Component\Validator\Tests\Constraints\Fixtures\ChildB; use Symfony\Component\Validator\Tests\Fixtures\Entity; use Symfony\Component\Validator\Validator\RecursiveValidator; @@ -34,6 +36,45 @@ protected function createValidator(MetadataFactoryInterface $metadataFactory, ar public function testEmptyGroupsArrayDoesNotTriggerDeprecation() { $entity = new Entity(); + $childA = new ChildA(); + $childB = new ChildB(); + $childA->name = false; + $childB->name = 'fake'; + $entity->childA = array($childA); + $entity->childB = array($childB); + $validatorContext = $this->getMock('Symfony\Component\Validator\Validator\ContextualValidatorInterface'); + $validatorContext + ->expects($this->once()) + ->method('validate') + ->with($entity, null, array()) + ->willReturnSelf(); + + $validator = $this + ->getMockBuilder('Symfony\Component\Validator\Validator\RecursiveValidator') + ->disableOriginalConstructor() + ->setMethods(array('startContext')) + ->getMock(); + $validator + ->expects($this->once()) + ->method('startContext') + ->willReturn($validatorContext); + + $validator->validate($entity, null, array()); + } + + public function testRelationBetweenChildAAndChildB() + { + $entity = new Entity(); + $childA = new ChildA(); + $childB = new ChildB(); + + $childA->childB = $childB; + $childB->childA = $childA; + + $childA->name = false; + $childB->name = 'fake'; + $entity->childA = array($childA); + $entity->childB = array($childB); $validatorContext = $this->getMock('Symfony\Component\Validator\Validator\ContextualValidatorInterface'); $validatorContext diff --git a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php index 96476680a42b5..941557e4f9d5a 100644 --- a/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php +++ b/src/Symfony/Component/Validator/Validator/RecursiveContextualValidator.php @@ -224,7 +224,7 @@ public function validateProperty($object, $propertyName, $groups = null) $this->validateGenericNode( $propertyValue, $object, - $cacheKey.':'.$propertyName, + $cacheKey.':'.get_class($object).':'.$propertyName, $propertyMetadata, $propertyPath, $groups, @@ -280,7 +280,7 @@ public function validatePropertyValue($objectOrClass, $propertyName, $value, $gr $this->validateGenericNode( $value, $object, - $cacheKey.':'.$propertyName, + $cacheKey.':'.get_class($object).':'.$propertyName, $propertyMetadata, $propertyPath, $groups, @@ -589,7 +589,7 @@ private function validateClassNode($object, $cacheKey, ClassMetadataInterface $m $this->validateGenericNode( $propertyValue, $object, - $cacheKey.':'.$propertyName, + $cacheKey.':'.get_class($object).':'.$propertyName, $propertyMetadata, PropertyPath::append($propertyPath, $propertyName), $groups,