8000 [Doctrine Bridge] fix UniqueEntityValidator for composite object prim… · symfony/symfony@5aadce3 · GitHub
[go: up one dir, main page]

Skip to content

Commit 5aadce3

Browse files
committed
[Doctrine Bridge] fix UniqueEntityValidator for composite object primary keys
1 parent cbb5332 commit 5aadce3

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<?php
2+
3+
namespace Symfony\Bridge\Doctrine\Tests\Fixtures;
4+
5+
use Doctrine\ORM\Mapping as ORM;
6+
7+
/**
8+
* an entity that has two objects (class without toString methods) as primary key.
9+
*
10+
* @ORM\Entity
11+
*/
12+
class CompositeObjectNoToStringIdEntity
13+
{
14+
/**
15+
* @var SingleIntIdNoToStringEntity
16+
*
17+
* @ORM\Id
18+
* @ORM\ManyToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"persist"})
19+
* @ORM\JoinColumn(name="object_one_id")
20+
*/
21+
protected $objectOne;
22+
23+
/**
24+
* @var SingleIntIdNoToStringEntity
25+
*
26+
* @ORM\Id
27+
* @ORM\ManyToOne(targetEntity="SingleIntIdNoToStringEntity", cascade={"persist"})
28+
* @ORM\JoinColumn(name="object_two_id")
29+
*/
30+
protected $objectTwo;
31+
32+
public function __construct(SingleIntIdNoToStringEntity $objectOne, SingleIntIdNoToStringEntity $objectTwo)
33+
{
34+
$this->objectOne = $objectOne;
35+
$this->objectTwo = $objectTwo;
36+
}
37+
38+
/**
39+
* @return SingleIntIdNoToStringEntity
40+
*/
41+
public function getObjectOne()
42+
{
43+
return $this->objectOne;
44+
}
45+
46+
/**
47+
* @return SingleIntIdNoToStringEntity
48+
*/
49+
public function getObjectTwo()
50+
{
51+
return $this->objectTwo;
52+
}
53+
}

src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
use Doctrine\Common\Persistence\ObjectRepository;
1818
use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper;
1919
use Symfony\Bridge\Doctrine\Test\TestRepositoryFactory;
20+
use Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity;
2021
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity;
2122
use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity;
2223
use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity;
@@ -140,6 +141,7 @@ private function createSchema(ObjectManager $em)
140141
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeIntIdEntity'),
141142
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity'),
142143
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity2'),
144+
$em->getClassMetadata('Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity'),
143145
));
144146
}
145147

@@ -561,4 +563,33 @@ public function testEntityManagerNullObject()
561563

562564
$this->validator->validate($entity, $constraint);
563565
}
566+
567+
public function testValidateUniquenessWithCompositeObjectNoToStringIdEntity()
568+
{
569+
$constraint = new UniqueEntity(array(
570+
'message' => 'myMessage',
571+
'fields' => array('objectOne', 'objectTwo'),
572+
'em' => self::EM_NAME,
573+
));
574+
575+
$objectOne = new SingleIntIdNoToStringEntity(1, 'foo');
576+
$objectTwo = new SingleIntIdNoToStringEntity(2, 'bar');
577+
$entity = new CompositeObjectNoToStringIdEntity($objectOne, $objectTwo);
578+
579+
$this->em->persist($entity);
580+
$this->em->flush();
581+
582+
$newEntity = new CompositeObjectNoToStringIdEntity($objectOne, $objectTwo);
583+
584+
$this->validator->validate($newEntity, $constraint);
585+
586+
$expectedValue = 'Object of class "Symfony\Bridge\Doctrine\Tests\Fixtures\CompositeObjectNoToStringIdEntity" identified by "(Object of class "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity" identified by "1"), (Object of class "Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity" identified by "2")"';
587+
588+
$this->buildViolation('myMessage')
589+
->atPath('property.path.objectOne')
590+
->setParameter('{{ value }}', '"'.$expectedValue.'"')
591+
->setInvalidValue($expectedValue)
592+
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
593+
->assertRaised();
594+
}
564595
}

src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Bridge\Doctrine\Validator\Constraints;
1313

1414
use Doctrine\Common\Persistence\ManagerRegistry;
15+
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
16+
use Doctrine\Common\Persistence\ObjectManager;
1517
use Symfony\Component\Validator\Constraint;
1618
use Symfony\Component\Validator\Exception\UnexpectedTypeException;
1719
use Symfony\Component\Validator\Exception\ConstraintDefinitionException;
@@ -128,7 +130,7 @@ public function validate($entity, Constraint $constraint)
128130
$invalidValue = isset($criteria[$errorPath]) ? $criteria[$errorPath] : $criteria[$fields[0]];
129131

130132
if (is_object($invalidValue) && !method_exists($invalidValue, '__toString')) {
131-
$invalidValue = sprintf('Object of class "%s" identified by "%s"', get_class($entity), implode(', ', $class->getIdentifierValues($entity)));
133+
$invalidValue = $this->buildInvalidValueString($em, $class, $entity);
132134
}
133135

134136
$this->context->buildViolation($constraint->message)
@@ -138,4 +140,25 @@ public function validate($entity, Constraint $constraint)
138140
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
139141
->addViolation();
140142
}
143+
144+
/**
145+
* @param ObjectManager $em
146+
* @param ClassMetadata $class
147+
* @param object $entity
148+
*
149+
* @return string
150+
*/
151+
private function buildInvalidValueString(ObjectManager $em, ClassMetadata $class, $entity)
152+
{
153+
$identifiers = array_map(function ($identifier) use ($em) {
154+
// identifiers can be objects (without any __toString method) if its a composite PK
155+
if (is_object($identifier) && !method_exists($identifier, '__toString')) {
156+
return sprintf('(%s)', $this->buildInvalidValueString($em, $em->getClassMetadata(get_class($identifier)), $identifier));
157+
}
158+
159+
return $identifier;
160+
}, $class->getIdentifierValues($entity));
161+
162+
return sprintf('Object of class "%s" identified by "%s"', get_class($entity), implode(', ', $identifiers));
163+
}
141164
}

0 commit comments

Comments
 (0)
0