[Validator] Group "Default" is now propagated to validated references when group sequences are validated
This conforms to JSR303 (see version 1.0 final, page 39).
This commit is contained in:
parent
6a148465da
commit
940ce9aedf
@ -42,6 +42,22 @@ class GraphWalker
|
||||
{
|
||||
$this->context->setCurrentClass($metadata->getClassName());
|
||||
|
||||
if ($group === Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
|
||||
$groups = $metadata->getGroupSequence();
|
||||
foreach ($groups as $group) {
|
||||
$this->walkClassForGroup($metadata, $object, $group, $propertyPath, Constraint::DEFAULT_GROUP);
|
||||
|
||||
if (count($this->getViolations()) > 0) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
$this->walkClassForGroup($metadata, $object, $group, $propertyPath);
|
||||
}
|
||||
}
|
||||
|
||||
protected function walkClassForGroup(ClassMetadata $metadata, $object, $group, $propertyPath, $propagatedGroup = null)
|
||||
{
|
||||
foreach ($metadata->findConstraints($group) as $constraint) {
|
||||
$this->walkConstraint($constraint, $object, $group, $propertyPath);
|
||||
}
|
||||
@ -50,15 +66,15 @@ class GraphWalker
|
||||
foreach ($metadata->getConstrainedProperties() as $property) {
|
||||
$localPropertyPath = empty($propertyPath) ? $property : $propertyPath.'.'.$property;
|
||||
|
||||
$this->walkProperty($metadata, $property, $object, $group, $localPropertyPath);
|
||||
$this->walkProperty($metadata, $property, $object, $group, $localPropertyPath, $propagatedGroup);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function walkProperty(ClassMetadata $metadata, $property, $object, $group, $propertyPath)
|
||||
public function walkProperty(ClassMetadata $metadata, $property, $object, $group, $propertyPath, $propagatedGroup = null)
|
||||
{
|
||||
foreach ($metadata->getMemberMetadatas($property) as $member) {
|
||||
$this->walkMember($member, $member->getValue($object), $group, $propertyPath);
|
||||
$this->walkMember($member, $member->getValue($object), $group, $propertyPath, $propagatedGroup);
|
||||
}
|
||||
}
|
||||
|
||||
@ -69,7 +85,7 @@ class GraphWalker
|
||||
}
|
||||
}
|
||||
|
||||
protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath)
|
||||
protected function walkMember(MemberMetadata $metadata, $value, $group, $propertyPath, $propagatedGroup = null)
|
||||
{
|
||||
$this->context->setCurrentProperty($metadata->getPropertyName());
|
||||
|
||||
@ -78,7 +94,7 @@ class GraphWalker
|
||||
}
|
||||
|
||||
if ($metadata->isCascaded()) {
|
||||
$this->walkReference($value, $group, $propertyPath);
|
||||
$this->walkReference($value, $propagatedGroup ?: $group, $propertyPath);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,108 +32,54 @@ class Validator implements ValidatorInterface
|
||||
public function validate($object, $groups = null)
|
||||
{
|
||||
$metadata = $this->metadataFactory->getClassMetadata(get_class($object));
|
||||
$groupChain = $this->buildGroupChain($metadata, $groups);
|
||||
|
||||
$closure = function(GraphWalker $walker, $group) use ($metadata, $object) {
|
||||
$walk = function(GraphWalker $walker, $group) use ($metadata, $object) {
|
||||
return $walker->walkClass($metadata, $object, $group, '');
|
||||
};
|
||||
|
||||
return $this->validateGraph($object, $closure, $groupChain);
|
||||
return $this->validateGraph($object, $walk, $groups);
|
||||
}
|
||||
|
||||
public function validateProperty($object, $property, $groups = null)
|
||||
{
|
||||
$metadata = $this->metadataFactory->getClassMetadata(get_class($object));
|
||||
$groupChain = $this->buildGroupChain($metadata, $groups);
|
||||
|
||||
$closure = function(GraphWalker $walker, $group) use ($metadata, $property, $object) {
|
||||
$walk = function(GraphWalker $walker, $group) use ($metadata, $property, $object) {
|
||||
return $walker->walkProperty($metadata, $property, $object, $group, '');
|
||||
};
|
||||
|
||||
return $this->validateGraph($object, $closure, $groupChain);
|
||||
return $this->validateGraph($object, $walk, $groups);
|
||||
}
|
||||
|
||||
public function validatePropertyValue($class, $property, $value, $groups = null)
|
||||
{
|
||||
$metadata = $this->metadataFactory->getClassMetadata($class);
|
||||
$groupChain = $this->buildGroupChain($metadata, $groups);
|
||||
|
||||
$closure = function(GraphWalker $walker, $group) use ($metadata, $property, $value) {
|
||||
$walk = function(GraphWalker $walker, $group) use ($metadata, $property, $value) {
|
||||
return $walker->walkPropertyValue($metadata, $property, $value, $group, '');
|
||||
};
|
||||
|
||||
return $this->validateGraph($object, $closure, $groupChain);
|
||||
return $this->validateGraph($class, $walk, $groups);
|
||||
}
|
||||
|
||||
public function validateValue($value, Constraint $constraint, $groups = null)
|
||||
{
|
||||
$groupChain = $this->buildSimpleGroupChain($groups);
|
||||
|
||||
$closure = function(GraphWalker $walker, $group) use ($constraint, $value) {
|
||||
$walk = function(GraphWalker $walker, $group) use ($constraint, $value) {
|
||||
return $walker->walkConstraint($constraint, $value, $group, '');
|
||||
};
|
||||
|
||||
return $this->validateGraph($value, $closure, $groupChain);
|
||||
return $this->validateGraph($value, $walk, $groups);
|
||||
}
|
||||
|
||||
protected function validateGraph($root, \Closure $closure, GroupChain $groupChain)
|
||||
protected function validateGraph($root, \Closure $walk, $groups = null)
|
||||
{
|
||||
$walker = new GraphWalker($root, $this->metadataFactory, $this->validatorFactory);
|
||||
$groups = $groups ? (array)$groups : array(Constraint::DEFAULT_GROUP);
|
||||
|
||||
foreach ($groupChain->getGroups() as $group) {
|
||||
$closure($walker, $group);
|
||||
}
|
||||
|
||||
foreach ($groupChain->getGroupSequences() as $sequence) {
|
||||
$violationCount = count($walker->getViolations());
|
||||
|
||||
foreach ($sequence as $group) {
|
||||
$closure($walker, $group);
|
||||
|
||||
if (count($walker->getViolations()) > $violationCount) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
foreach ($groups as $group) {
|
||||
$walk($walker, $group);
|
||||
}
|
||||
|
||||
return $walker->getViolations();
|
||||
}
|
||||
|
||||
protected function buildSimpleGroupChain($groups)
|
||||
{
|
||||
if (null === $groups) {
|
||||
$groups = array(Constraint::DEFAULT_GROUP);
|
||||
} else {
|
||||
$groups = (array)$groups;
|
||||
}
|
||||
|
||||
$chain = new GroupChain();
|
||||
|
||||
foreach ($groups as $group) {
|
||||
$chain->addGroup($group);
|
||||
}
|
||||
|
||||
return $chain;
|
||||
}
|
||||
|
||||
protected function buildGroupChain(ClassMetadata $metadata, $groups)
|
||||
{
|
||||
if (null === $groups) {
|
||||
$groups = array(Constraint::DEFAULT_GROUP);
|
||||
} else {
|
||||
$groups = (array)$groups;
|
||||
}
|
||||
|
||||
$chain = new GroupChain();
|
||||
|
||||
foreach ($groups as $group) {
|
||||
if ($group == Constraint::DEFAULT_GROUP && $metadata->hasGroupSequence()) {
|
||||
$chain->addGroupSequence($metadata->getGroupSequence());
|
||||
} else {
|
||||
$chain->addGroup($group);
|
||||
}
|
||||
}
|
||||
|
||||
return $chain;
|
||||
}
|
||||
}
|
@ -26,7 +26,7 @@ class Entity extends EntityParent implements EntityInterface
|
||||
*/
|
||||
protected $firstName;
|
||||
protected $lastName;
|
||||
protected $reference;
|
||||
public $reference;
|
||||
|
||||
private $internal;
|
||||
|
||||
|
@ -6,4 +6,5 @@ use Symfony\Component\Validator\Constraint;
|
||||
|
||||
class FailingConstraint extends Constraint
|
||||
{
|
||||
public $message = '';
|
||||
}
|
@ -9,6 +9,8 @@ class FailingConstraintValidator extends ConstraintValidator
|
||||
{
|
||||
public function isValid($value, Constraint $constraint)
|
||||
{
|
||||
$this->setMessage($constraint->message, array());
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
namespace Symfony\Tests\Component\Validator\Fixtures;
|
||||
|
||||
class Reference
|
||||
{
|
||||
}
|
@ -3,6 +3,7 @@
|
||||
namespace Symfony\Tests\Component\Validator;
|
||||
|
||||
require_once __DIR__.'/Fixtures/Entity.php';
|
||||
require_once __DIR__.'/Fixtures/Reference.php';
|
||||
require_once __DIR__.'/Fixtures/ConstraintA.php';
|
||||
require_once __DIR__.'/Fixtures/ConstraintAValidator.php';
|
||||
require_once __DIR__.'/Fixtures/FailingConstraint.php';
|
||||
@ -10,6 +11,7 @@ require_once __DIR__.'/Fixtures/FailingConstraintValidator.php';
|
||||
require_once __DIR__.'/Fixtures/FakeClassMetadataFactory.php';
|
||||
|
||||
use Symfony\Tests\Component\Validator\Fixtures\Entity;
|
||||
use Symfony\Tests\Component\Validator\Fixtures\Reference;
|
||||
use Symfony\Tests\Component\Validator\Fixtures\FakeClassMetadataFactory;
|
||||
use Symfony\Tests\Component\Validator\Fixtures\ConstraintA;
|
||||
use Symfony\Tests\Component\Validator\Fixtures\FailingConstraint;
|
||||
@ -25,6 +27,7 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
|
||||
const CLASSNAME = 'Symfony\Tests\Component\Validator\Fixtures\Entity';
|
||||
|
||||
protected $factory;
|
||||
protected $walker;
|
||||
protected $metadata;
|
||||
|
||||
public function setUp()
|
||||
@ -61,6 +64,93 @@ class GraphWalkerTest extends \PHPUnit_Framework_TestCase
|
||||
$this->assertEquals(1, count($this->walker->getViolations()));
|
||||
}
|
||||
|
||||
public function testWalkClassInDefaultGroupTraversesGroupSequence()
|
||||
{
|
||||
$entity = new Entity();
|
||||
|
||||
$this->metadata->addPropertyConstraint('firstName', new FailingConstraint(array(
|
||||
'groups' => 'First',
|
||||
)));
|
||||
$this->metadata->addGetterConstraint('lastName', new FailingConstraint(array(
|
||||
'groups' => 'Second',
|
||||
)));
|
||||
$this->metadata->setGroupSequence(array('First', 'Second'));
|
||||
|
||||
$this->walker->walkClass($this->metadata, $entity, 'Default', '');
|
||||
|
||||
// After validation of group "First" failed, no more group was
|
||||
// validated
|
||||
$violations = new ConstraintViolationList();
|
||||
$violations->add(new ConstraintViolation(
|
||||
'',
|
||||
array(),
|
||||
'Root',
|
||||
'firstName',
|
||||
''
|
||||
));
|
||||
|
||||
$this->assertEquals($violations, $this->walker->getViolations());
|
||||
}
|
||||
|
||||
public function testWalkClassInGroupSequencePropagatesDefaultGroup()
|
||||
{
|
||||
$entity = new Entity();
|
||||
$entity->reference = new Reference();
|
||||
|
||||
$this->metadata->addPropertyConstraint('reference', new Valid());
|
||||
$this->metadata->setGroupSequence(array('First'));
|
||||
|
||||
$referenceMetadata = new ClassMetadata(get_class($entity->reference));
|
||||
$referenceMetadata->addConstraint(new FailingConstraint(array(
|
||||
// this constraint is only evaluated if group "Default" is
|
||||
// propagated to the reference
|
||||
'groups' => 'Default',
|
||||
)));
|
||||
$this->factory->addClassMetadata($referenceMetadata);
|
||||
|
||||
$this->walker->walkClass($this->metadata, $entity, 'Default', '');
|
||||
|
||||
// The validation of the reference's FailingConstraint in group
|
||||
// "Default" was launched
|
||||
$violations = new ConstraintViolationList();
|
||||
$violations->add(new ConstraintViolation(
|
||||
'',
|
||||
array(),
|
||||
'Root',
|
||||
'reference',
|
||||
$entity->reference
|
||||
));
|
||||
|
||||
$this->assertEquals($violations, $this->walker->getViolations());
|
||||
}
|
||||
|
||||
public function testWalkClassInOtherGroupTraversesNoGroupSequence()
|
||||
{
|
||||
$entity = new Entity();
|
||||
|
||||
$this->metadata->addPropertyConstraint('firstName', new FailingConstraint(array(
|
||||
'groups' => 'First',
|
||||
)));
|
||||
$this->metadata->addGetterConstraint('lastName', new FailingConstraint(array(
|
||||
'groups' => 'Second',
|
||||
)));
|
||||
$this->metadata->setGroupSequence(array('First', 'Second'));
|
||||
|
||||
$this->walker->walkClass($this->metadata, $entity, 'Second', '');
|
||||
|
||||
// Only group "Second" was validated
|
||||
$violations = new ConstraintViolationList();
|
||||
$violations->add(new ConstraintViolation(
|
||||
'',
|
||||
array(),
|
||||
'Root',
|
||||
'lastName',
|
||||
''
|
||||
));
|
||||
|
||||
$this->assertEquals($violations, $this->walker->getViolations());
|
||||
}
|
||||
|
||||
public function testWalkPropertyValueValidatesConstraints()
|
||||
{
|
||||
$this->metadata->addPropertyConstraint('firstName', new ConstraintA());
|
||||
|
@ -2,79 +2,139 @@
|
||||
|
||||
namespace Symfony\Tests\Component\Validator;
|
||||
|
||||
use Symfony\Component\Validator\Constraint;
|
||||
require_once __DIR__.'/Fixtures/Entity.php';
|
||||
require_once __DIR__.'/Fixtures/FailingConstraint.php';
|
||||
require_once __DIR__.'/Fixtures/FailingConstraintValidator.php';
|
||||
require_once __DIR__.'/Fixtures/FakeClassMetadataFactory.php';
|
||||
|
||||
use Symfony\Tests\Component\Validator\Fixtures\Entity;
|
||||
use Symfony\Tests\Component\Validator\Fixtures\FakeClassMetadataFactory;
|
||||
use Symfony\Tests\Component\Validator\Fixtures\FailingConstraint;
|
||||
use Symfony\Component\Validator\Validator;
|
||||
use Symfony\Component\Validator\ConstraintViolation;
|
||||
use Symfony\Component\Validator\ConstraintViolationList;
|
||||
use Symfony\Component\Validator\Mapping\Metadata;
|
||||
use Symfony\Component\Validator\Specification\PropertySpecification;
|
||||
use Symfony\Component\Validator\Specification\ClassSpecification;
|
||||
use Symfony\Component\Validator\Specification\Specification;
|
||||
|
||||
|
||||
class ValidatorTest_Class
|
||||
{
|
||||
public $firstName = 'Bernhard';
|
||||
|
||||
public $reference;
|
||||
|
||||
public function getLastName()
|
||||
{
|
||||
return 'Schussek';
|
||||
}
|
||||
|
||||
public function isAustralian()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
use Symfony\Component\Validator\ConstraintValidatorFactory;
|
||||
use Symfony\Component\Validator\Mapping\ClassMetadata;
|
||||
|
||||
class ValidatorTest extends \PHPUnit_Framework_TestCase
|
||||
{
|
||||
public function testValidatePropertyConstraint()
|
||||
protected $factory;
|
||||
protected $validator;
|
||||
|
||||
public function setUp()
|
||||
{
|
||||
/*
|
||||
$subject = new ValidatorTest_Class();
|
||||
$subjectClass = get_class($subject);
|
||||
$this->factory = new FakeClassMetadataFactory();
|
||||
$this->validator = new Validator($this->factory, new ConstraintValidatorFactory());
|
||||
}
|
||||
|
||||
$constraint = new Constraint();
|
||||
$property = new PropertySpecification($subjectClass, 'firstName', array($constraint));
|
||||
$class = new ClassSpecification($subjectClass, array($property));
|
||||
$specification = new Specification(array($class));
|
||||
$metadata = new Metadata($specification);
|
||||
public function testValidate_defaultGroup()
|
||||
{
|
||||
$entity = new Entity();
|
||||
$metadata = new ClassMetadata(get_class($entity));
|
||||
$metadata->addPropertyConstraint('firstName', new FailingConstraint());
|
||||
$metadata->addPropertyConstraint('lastName', new FailingConstraint(array(
|
||||
'groups' => 'Custom',
|
||||
)));
|
||||
$this->factory->addClassMetadata($metadata);
|
||||
|
||||
$validatorMock = $this->getMock('Symfony\Component\Validator\ConstraintValidatorInterface');
|
||||
$validatorMock->expects($this->once())
|
||||
->method('isValid')
|
||||
->with($this->equalTo('Bernhard'), $this->equalTo($constraint))
|
||||
->will($this->returnValue(false));
|
||||
$validatorMock->expects($this->atLeastOnce())
|
||||
->method('getMessageTemplate')
|
||||
->will($this->returnValue('message'));
|
||||
$validatorMock->expects($this->atLeastOnce())
|
||||
->method('getMessageParameters')
|
||||
->will($this->returnValue(array('param' => 'value')));
|
||||
|
||||
$factoryMock = $this->getMock('Symfony\Component\Validator\ConstraintValidatorFactoryInterface');
|
||||
$factoryMock->expects($this->once())
|
||||
->method('getInstance')
|
||||
->with($this->equalTo($constraint->validatedBy()))
|
||||
->will($this->returnValue($validatorMock));
|
||||
|
||||
$validator = new Validator($metadata, $factoryMock);
|
||||
|
||||
$builder = new PropertyPathBuilder();
|
||||
$expected = new ConstraintViolationList();
|
||||
$expected->add(new ConstraintViolation(
|
||||
'message',
|
||||
array('param' => 'value'),
|
||||
$subjectClass,
|
||||
$builder->atProperty('firstName')->getPropertyPath(),
|
||||
'Bernhard'
|
||||
// Only the constraint of group "Default" failed
|
||||
$violations = new ConstraintViolationList();
|
||||
$violations->add(new ConstraintViolation(
|
||||
'',
|
||||
array(),
|
||||
$entity,
|
||||
'firstName',
|
||||
''
|
||||
));
|
||||
|
||||
$this->assertEquals($expected, $validator->validateProperty($subject, 'firstName'));
|
||||
*/
|
||||
$this->assertEquals($violations, $this->validator->validate($entity));
|
||||
}
|
||||
|
||||
public function testValidate_oneGroup()
|
||||
{
|
||||
$entity = new Entity();
|
||||
$metadata = new ClassMetadata(get_class($entity));
|
||||
$metadata->addPropertyConstraint('firstName', new FailingConstraint());
|
||||
$metadata->addPropertyConstraint('lastName', new FailingConstraint(array(
|
||||
'groups' => 'Custom',
|
||||
)));
|
||||
$this->factory->addClassMetadata($metadata);
|
||||
|
||||
// Only the constraint of group "Custom" failed
|
||||
$violations = new ConstraintViolationList();
|
||||
$violations->add(new ConstraintViolation(
|
||||
'',
|
||||
array(),
|
||||
$entity,
|
||||
'lastName',
|
||||
''
|
||||
));
|
||||
|
||||
$this->assertEquals($violations, $this->validator->validate($entity, 'Custom'));
|
||||
}
|
||||
|
||||
public function testValidate_multipleGroups()
|
||||
{
|
||||
$entity = new Entity();
|
||||
$metadata = new ClassMetadata(get_class($entity));
|
||||
$metadata->addPropertyConstraint('firstName', new FailingConstraint(array(
|
||||
'groups' => 'First',
|
||||
)));
|
||||
$metadata->addPropertyConstraint('lastName', new FailingConstraint(array(
|
||||
'groups' => 'Second',
|
||||
)));
|
||||
$this->factory->addClassMetadata($metadata);
|
||||
|
||||
// The constraints of both groups failed
|
||||
$violations = new ConstraintViolationList();
|
||||
$violations->add(new ConstraintViolation(
|
||||
'',
|
||||
array(),
|
||||
$entity,
|
||||
'firstName',
|
||||
''
|
||||
));
|
||||
$violations->add(new ConstraintViolation(
|
||||
'',
|
||||
array(),
|
||||
$entity,
|
||||
'lastName',
|
||||
''
|
||||
));
|
||||
|
||||
$result = $this->validator->validate($entity, array('First', 'Second'));
|
||||
|
||||
$this->assertEquals($violations, $result);
|
||||
}
|
||||
|
||||
public function testValidateProperty()
|
||||
{
|
||||
$entity = new Entity();
|
||||
$metadata = new ClassMetadata(get_class($entity));
|
||||
$metadata->addPropertyConstraint('firstName', new FailingConstraint());
|
||||
$this->factory->addClassMetadata($metadata);
|
||||
|
||||
$result = $this->validator->validateProperty($entity, 'firstName');
|
||||
|
||||
$this->assertEquals(1, count($result));
|
||||
}
|
||||
|
||||
public function testValidatePropertyValue()
|
||||
{
|
||||
$entity = new Entity();
|
||||
$metadata = new ClassMetadata(get_class($entity));
|
||||
$metadata->addPropertyConstraint('firstName', new FailingConstraint());
|
||||
$this->factory->addClassMetadata($metadata);
|
||||
|
||||
$result = $this->validator->validatePropertyValue(get_class($entity), 'firstName', 'Bernhard');
|
||||
|
||||
$this->assertEquals(1, count($result));
|
||||
}
|
||||
|
||||
public function testValidateValue()
|
||||
{
|
||||
$result = $this->validator->validateValue('Bernhard', new FailingConstraint());
|
||||
|
||||
$this->assertEquals(1, count($result));
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user