From c83067eb43c3ec4457e9abee19f900209c2ffce8 Mon Sep 17 00:00:00 2001 From: Ogizanagi Date: Sun, 14 Dec 2014 20:23:35 +0100 Subject: [PATCH] [Doctrine] [Bridge] Add a "repository" option to the UniqueEntity validator The repository option expects an entity path, in order to select its repository. This allows to use properly the UniqueEntity constraint on hierarchical (inheritance) entities, using the root class repository in order to check the uniqueness, and not just the child repository. --- .../Doctrine/Tests/Fixtures/Employee.php | 19 ++++++++ .../Bridge/Doctrine/Tests/Fixtures/Person.php | 45 +++++++++++++++++++ .../Constraints/UniqueEntityValidatorTest.php | 35 +++++++++++++++ .../Validator/Constraints/UniqueEntity.php | 1 + .../Constraints/UniqueEntityValidator.php | 22 ++++++++- 5 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php create mode 100644 src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php new file mode 100644 index 000000000000..24f08b00d781 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Employee.php @@ -0,0 +1,19 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\Entity; + +/** @Entity */ +class Employee extends Person +{ +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php new file mode 100644 index 000000000000..19a5d8b9569f --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/Person.php @@ -0,0 +1,45 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Tests\Fixtures; + +use Doctrine\ORM\Mapping\DiscriminatorColumn; +use Doctrine\ORM\Mapping\DiscriminatorMap; +use Doctrine\ORM\Mapping\Id; +use Doctrine\ORM\Mapping\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\InheritanceType; + +/** + * @Entity + * @InheritanceType("SINGLE_TABLE") + * @DiscriminatorColumn(name="discr", type="string") + * @DiscriminatorMap({"person" = "Person", "employee" = "Employee"}) + */ +class Person +{ + /** @Id @Column(type="integer") */ + protected $id; + + /** @Column(type="string") */ + public $name; + + public function __construct($id, $name) + { + $this->id = $id; + $this->name = $name; + } + + public function __toString() + { + return (string) $this->name; + } +} diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index f1f5bbdc9806..95abaa39884d 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -16,6 +16,8 @@ use Doctrine\Common\Persistence\ObjectRepository; use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper; use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\Employee; +use Symfony\Bridge\Doctrine\Tests\Fixtures\Person; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity; @@ -134,6 +136,8 @@ private function createSchema(ObjectManager $em) $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'), $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\Person'), + $em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\Employee'), )); } @@ -459,4 +463,35 @@ public function testEntityManagerNullObject() $this->validator->validate($entity, $constraint); } + + public function testValidateInheritanceUniqueness() + { + $constraint = new UniqueEntity(array( + 'message' => 'myMessage', + 'fields' => array('name'), + 'em' => self::EM_NAME, + 'repository' => 'Symfony\Bridge\Doctrine\Tests\Fixtures\Person', + )); + + $entity1 = new Person(1, 'Foo'); + $entity2 = new Employee(2, 'Foo'); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->em->persist($entity1); + $this->em->flush(); + + $this->validator->validate($entity1, $constraint); + + $this->assertNoViolation(); + + $this->validator->validate($entity2, $constraint); + + $this->buildViolation('myMessage') + ->atPath('property.path.name') + ->setInvalidValue('Foo') + ->assertRaised(); + } } diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php index fc6e213bd772..3dbbd82da4e6 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -26,6 +26,7 @@ class UniqueEntity extends Constraint public $message = 'This value is already used.'; public $service = 'doctrine.orm.validator.unique'; public $em = null; + public $repository = null; public $repositoryMethod = 'findBy'; public $fields = array(); public $errorPath = null; diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 6494adb91241..41f49dcefe0c 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -111,7 +111,27 @@ public function validate($entity, Constraint $constraint) } } - $repository = $em->getRepository(get_class($entity)); + if (null !== $constraint->repository) { + /* Retrieve repository from given entity name. + * We ensure the retrieved repository can handle the entity + * by checking the entity is the same, or subclass of the supported entity. + */ + $repository = $em->getRepository($constraint->repository); + $supportedClass = $repository->getClassName(); + + $className = $class->getName(); + + if (!($supportedClass === $className || is_subclass_of($className, $supportedClass))) { + throw new ConstraintDefinitionException(sprintf( + "Unable to use the given '%s' repository for the '%s' entity.", + get_class($repository), + $className + )); + } + } else { + $repository = $em->getRepository(get_class($entity)); + } + $result = $repository->{$constraint->repositoryMethod}($criteria); /* If the result is a MongoCursor, it must be advanced to the first