From 559d573ec0907ccde2ee0227dcf8f252bd794774 Mon Sep 17 00:00:00 2001 From: Mathieu Lechat Date: Wed, 15 Nov 2017 13:19:45 +0100 Subject: [PATCH 1/5] Allow validating every class against unique entity constraint --- .../Constraints/UniqueEntityValidatorTest.php | 17 ------ .../Validator/Constraints/UniqueEntity.php | 1 + .../Constraints/UniqueEntityValidator.php | 58 +++++++++---------- 3 files changed, 28 insertions(+), 48 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php index 878e19ccb5cd9..11987179a2f51 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -664,23 +664,6 @@ public function testValidateInheritanceUniqueness() ->assertRaised(); } - /** - * @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException - * @expectedExceptionMessage The "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity" entity repository does not support the "Symfony\Bridge\Doctrine\Tests\Fixtures\Person" entity. The entity should be an instance of or extend "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity". - */ - public function testInvalidateRepositoryForInheritance() - { - $constraint = new UniqueEntity(array( - 'message' => 'myMessage', - 'fields' => array('name'), - 'em' => self::EM_NAME, - 'entityClass' => 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity', - )); - - $entity = new Person(1, 'Foo'); - $this->validator->validate($entity, $constraint); - } - public function testValidateUniquenessWithCompositeObjectNoToStringIdEntity() { $constraint = new UniqueEntity(array( diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php index c9b13dcf59960..c1437fe69eae6 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -33,6 +33,7 @@ class UniqueEntity extends Constraint public $fields = array(); public $errorPath = null; public $ignoreNull = true; + public $forUpdate = false; protected static $errorNames = array( self::NOT_UNIQUE_ERROR => 'NOT_UNIQUE_ERROR', diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index e03ef3c555b37..3e9e8b9ddef2b 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -64,6 +64,8 @@ public function validate($entity, Constraint $constraint) return; } + $entityClass = $constraint->entityClass ?: get_class($entity); + if ($constraint->em) { $em = $this->registry->getManager($constraint->em); @@ -71,25 +73,28 @@ public function validate($entity, Constraint $constraint) throw new ConstraintDefinitionException(sprintf('Object manager "%s" does not exist.', $constraint->em)); } } else { - $em = $this->registry->getManagerForClass(get_class($entity)); + $em = $this->registry->getManagerForClass($entityClass); if (!$em) { throw new ConstraintDefinitionException(sprintf('Unable to find the object manager associated with an entity of class "%s".', get_class($entity))); } } - $class = $em->getClassMetadata(get_class($entity)); - /* @var $class \Doctrine\Common\Persistence\Mapping\ClassMetadata */ + $class = $em->getClassMetadata($entityClass); $criteria = array(); $hasNullValue = false; - foreach ($fields as $fieldName) { - if (!$class->hasField($fieldName) && !$class->hasAssociation($fieldName)) { - throw new ConstraintDefinitionException(sprintf('The field "%s" is not mapped by Doctrine, so it cannot be validated for uniqueness.', $fieldName)); + foreach ($fields as $formField => $entityField) { + if (!$class->hasField($entityField) && !$class->hasAssociation($entityField)) { + throw new ConstraintDefinitionException(sprintf('The field "%s" is not mapped by Doctrine, so it cannot be validated for uniqueness.', $entityField)); } - $fieldValue = $class->reflFields[$fieldName]->getValue($entity); + $field = new \ReflectionProperty($entityClass, is_int($formField) ? $entityField : $formField); + if (!$field->isPublic()) { + $field->setAccessible(true); + } + $fieldValue = $field->getValue($entity); if (null === $fieldValue) { $hasNullValue = true; @@ -99,14 +104,14 @@ public function validate($entity, Constraint $constraint) continue; } - $criteria[$fieldName] = $fieldValue; + $criteria[$entityField] = $fieldValue; - if (null !== $criteria[$fieldName] && $class->hasAssociation($fieldName)) { + if (null !== $criteria[$entityField] && $class->hasAssociation($entityField)) { /* Ensure the Proxy is initialized before using reflection to * read its identifiers. This is necessary because the wrapped * getter methods in the Proxy are being bypassed. */ - $em->initializeObject($criteria[$fieldName]); + $em->initializeObject($criteria[$entityField]); } } @@ -121,22 +126,7 @@ public function validate($entity, Constraint $constraint) return; } - if (null !== $constraint->entityClass) { - /* 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->entityClass); - $supportedClass = $repository->getClassName(); - - if (!$entity instanceof $supportedClass) { - throw new ConstraintDefinitionException(sprintf('The "%s" entity repository does not support the "%s" entity. The entity should be an instance of or extend "%s".', $constraint->entityClass, $class->getName(), $supportedClass)); - } - } else { - $repository = $em->getRepository(get_class($entity)); - } - - $result = $repository->{$constraint->repositoryMethod}($criteria); + $result = $em->getRepository($entityClass)->{$constraint->repositoryMethod}($criteria); if ($result instanceof \IteratorAggregate) { $result = $result->getIterator(); @@ -152,14 +142,20 @@ public function validate($entity, Constraint $constraint) reset($result); } - /* If no entity matched the query criteria or a single entity matched, - * which is the same as the entity being validated, the criteria is - * unique. - */ - if (0 === count($result) || (1 === count($result) && $entity === ($result instanceof \Iterator ? $result->current() : current($result)))) { + if (0 === count($result)) { return; } + if (1 === count($result)) { + if ($constraint->forUpdate) { + return; + } + + if ($entity === ($result instanceof \Iterator ? $result->current() : current($result))) { + return; + } + } + $errorPath = null !== $constraint->errorPath ? $constraint->errorPath : $fields[0]; $invalidValue = isset($criteria[$errorPath]) ? $criteria[$errorPath] : $criteria[$fields[0]]; From 0b87dea1d19c55e80e44564517774a2d8a7fe206 Mon Sep 17 00:00:00 2001 From: Mathieu Lechat Date: Wed, 15 Nov 2017 15:34:08 +0100 Subject: [PATCH 2/5] Implement check using an interface --- .../Validator/Constraints/EntityDto.php | 17 +++++++++++++++++ .../Validator/Constraints/UniqueEntity.php | 1 - .../Constraints/UniqueEntityValidator.php | 6 ++++-- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 src/Symfony/Bridge/Doctrine/Validator/Constraints/EntityDto.php diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/EntityDto.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/EntityDto.php new file mode 100644 index 0000000000000..eb9ea121bee61 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/EntityDto.php @@ -0,0 +1,17 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Symfony\Bridge\Doctrine\Validator\Constraints; + +interface EntityDto +{ + public function isForEntity($entity): bool; +} diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php index c1437fe69eae6..c9b13dcf59960 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -33,7 +33,6 @@ class UniqueEntity extends Constraint public $fields = array(); public $errorPath = null; public $ignoreNull = true; - public $forUpdate = false; protected static $errorNames = array( self::NOT_UNIQUE_ERROR => 'NOT_UNIQUE_ERROR', diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 3e9e8b9ddef2b..c0ffe4ed34616 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -147,11 +147,13 @@ public function validate($entity, Constraint $constraint) } if (1 === count($result)) { - if ($constraint->forUpdate) { + $match = $result instanceof \Iterator ? $result->current() : current($result); + + if ($entity === $match) { return; } - if ($entity === ($result instanceof \Iterator ? $result->current() : current($result))) { + if ($entity instanceof EntityDto && $entity->isForEntity($match)) { return; } } From 0b765afd99727cb95717461eaa11bb445444eb33 Mon Sep 17 00:00:00 2001 From: Mathieu Lechat Date: Wed, 15 Nov 2017 17:39:48 +0100 Subject: [PATCH 3/5] Rename $entity $object --- .../Constraints/UniqueEntityValidator.php | 21 ++++++++++--------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index c0ffe4ed34616..eee189a5b990e 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -34,13 +34,13 @@ public function __construct(ManagerRegistry $registry) } /** - * @param object $entity + * @param object $object * @param Constraint $constraint * * @throws UnexpectedTypeException * @throws ConstraintDefinitionException */ - public function validate($entity, Constraint $constraint) + public function validate($object, Constraint $constraint) { if (!$constraint instanceof UniqueEntity) { throw new UnexpectedTypeException($constraint, __NAMESPACE__.'\UniqueEntity'); @@ -60,11 +60,12 @@ public function validate($entity, Constraint $constraint) throw new ConstraintDefinitionException('At least one field has to be specified.'); } - if (null === $entity) { + if (null === $object) { return; } - $entityClass = $constraint->entityClass ?: get_class($entity); + $objectClass = get_class($object); + $entityClass = $constraint->entityClass ?: $objectClass; if ($constraint->em) { $em = $this->registry->getManager($constraint->em); @@ -76,7 +77,7 @@ public function validate($entity, Constraint $constraint) $em = $this->registry->getManagerForClass($entityClass); if (!$em) { - throw new ConstraintDefinitionException(sprintf('Unable to find the object manager associated with an entity of class "%s".', get_class($entity))); + throw new ConstraintDefinitionException(sprintf('Unable to find the object manager associated with an entity of class "%s".', $objectClass)); } } @@ -85,16 +86,16 @@ public function validate($entity, Constraint $constraint) $criteria = array(); $hasNullValue = false; - foreach ($fields as $formField => $entityField) { + foreach ($fields as $objectField => $entityField) { if (!$class->hasField($entityField) && !$class->hasAssociation($entityField)) { throw new ConstraintDefinitionException(sprintf('The field "%s" is not mapped by Doctrine, so it cannot be validated for uniqueness.', $entityField)); } - $field = new \ReflectionProperty($entityClass, is_int($formField) ? $entityField : $formField); + $field = new \ReflectionProperty($objectClass, is_int($objectField) ? $entityField : $objectField); if (!$field->isPublic()) { $field->setAccessible(true); } - $fieldValue = $field->getValue($entity); + $fieldValue = $field->getValue($object); if (null === $fieldValue) { $hasNullValue = true; @@ -149,11 +150,11 @@ public function validate($entity, Constraint $constraint) if (1 === count($result)) { $match = $result instanceof \Iterator ? $result->current() : current($result); - if ($entity === $match) { + if ($object === $match) { return; } - if ($entity instanceof EntityDto && $entity->isForEntity($match)) { + if ($object instanceof EntityDto && $object->isForEntity($match)) { return; } } From 475ac56e3ebc1891575f29d3645d9fcaa14cbe3f Mon Sep 17 00:00:00 2001 From: Mathieu Lechat Date: Wed, 15 Nov 2017 17:51:04 +0100 Subject: [PATCH 4/5] Replace interface with annotation option --- .../Validator/Constraints/EntityDto.php | 17 ----------------- .../Validator/Constraints/UniqueEntity.php | 1 + .../Constraints/UniqueEntityValidator.php | 13 +++++++++++-- 3 files changed, 12 insertions(+), 19 deletions(-) delete mode 100644 src/Symfony/Bridge/Doctrine/Validator/Constraints/EntityDto.php diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/EntityDto.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/EntityDto.php deleted file mode 100644 index eb9ea121bee61..0000000000000 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/EntityDto.php +++ /dev/null @@ -1,17 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Bridge\Doctrine\Validator\Constraints; - -interface EntityDto -{ - public function isForEntity($entity): bool; -} diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php index c9b13dcf59960..55a45a892d287 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntity.php @@ -30,6 +30,7 @@ class UniqueEntity extends Constraint public $em = null; public $entityClass = null; public $repositoryMethod = 'findBy'; + public $isEntityToUpdateMethod = null; public $fields = array(); public $errorPath = null; public $ignoreNull = true; diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index eee189a5b990e..4f037f233b68f 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -154,8 +154,17 @@ public function validate($object, Constraint $constraint) return; } - if ($object instanceof EntityDto && $object->isForEntity($match)) { - return; + $method = $constraint->isEntityToUpdateMethod; + if (null !== $method) { + if (!method_exists($object, $method)) { + throw new ConstraintDefinitionException(sprintf('Method "%s" does not exist in class %s', $method, $objectClass)); + } + + $reflMethod = new \ReflectionMethod($object, $method); + + if ($reflMethod->isStatic() ? $reflMethod->invoke(null, $object, $match) : $reflMethod->invoke($object, $match)) { + return; + } } } From 1e461b10333ab938f3399373dc407b001d232a32 Mon Sep 17 00:00:00 2001 From: Mathieu Lechat Date: Thu, 16 Nov 2017 09:41:38 +0100 Subject: [PATCH 5/5] Simplify callback call --- .../Validator/Constraints/UniqueEntityValidator.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 4f037f233b68f..80ec1a446badb 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -148,9 +148,9 @@ public function validate($object, Constraint $constraint) } if (1 === count($result)) { - $match = $result instanceof \Iterator ? $result->current() : current($result); + $entity = $result instanceof \Iterator ? $result->current() : current($result); - if ($object === $match) { + if ($object === $entity) { return; } @@ -160,9 +160,7 @@ public function validate($object, Constraint $constraint) throw new ConstraintDefinitionException(sprintf('Method "%s" does not exist in class %s', $method, $objectClass)); } - $reflMethod = new \ReflectionMethod($object, $method); - - if ($reflMethod->isStatic() ? $reflMethod->invoke(null, $object, $match) : $reflMethod->invoke($object, $match)) { + if (call_user_func([$object, $method], $entity)) { return; } } pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy