8000 bug #20287 Properly format value in UniqueEntityValidator (alcaeus) · symfony/symfony@a42973e · GitHub
[go: up one dir, main page]

Skip to content

Commit a42973e

Browse files
committed
bug #20287 Properly format value in UniqueEntityValidator (alcaeus)
This PR was merged into the 3.1 branch. Discussion ---------- Properly format value in UniqueEntityValidator | Q | A | | --- | --- | | Branch? | 3.1 | | Bug fix? | yes | | New feature? | no | | BC breaks? | no | | Deprecations? | no | | Tests pass? | yes | | Fixed tickets | / | | License | MIT | This PR fixes a small issue introduced in #15279. Having an array in a field considered for a unique check causes an array to string conversion error in the translator. This can be avoided by formatting the value parameter with `formatValue`. Commits ------- 9043a55 Properly format value in UniqueEntityValidator
2 parents 172d0a4 + 9043a55 commit a42973e

File tree

5 files changed

+166
-16
lines changed

5 files changed

+166
-16
lines changed

src/Symfony/Bridge/Doctrine/Test/DoctrineTestHelper.php

Lines changed: 25 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
namespace Symfony\Bridge\Doctrine\Test;
1313

1414
use Doctrine\Common\Annotations\AnnotationReader;
15+
use Doctrine\Common\Cache\ArrayCache;
16+
use Doctrine\ORM\Configuration;
1517
use Doctrine\ORM\Mapping\Driver\AnnotationDriver;
1618
use Doctrine\ORM\EntityManager;
1719

@@ -25,22 +27,19 @@ class DoctrineTestHelper
2527
/**
2628
* Returns an entity manager for testing.
2729
*
30+
* @param Configuration|null $config
31+
*
2832
* @return EntityManager
2933
*/
30-
public static function createTestEntityManager()
34+
public static function createTestEntityManager(Configuration $config = null)
3135
{
3236
if (!extension_loaded('pdo_sqlite')) {
3337
\PHPUnit_Framework_TestCase::markTestSkipped('Extension pdo_sqlite is required.');
3438
}
3539

36-
$config = new \Doctrine\ORM\Configuration();
37-
$config->setEntityNamespaces(array('SymfonyTestsDoctrine' => 'Symfony\Bridge\Doctrine\Tests\Fixtures'));
38-
$config->setAutoGenerateProxyClasses(true);
39-
$config->setProxyDir(\sys_get_temp_dir());
40-
$config->setProxyNamespace('SymfonyTests\Doctrine');
41-
$config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader()));
42-
$config->setQueryCacheImpl(new \Doctrine\Common\Cache\ArrayCache());
43-
$config->setMetadataCacheImpl(new \Doctrine\Common\Cache\ArrayCache());
40+
if (null === $config) {
41+
$config = self::createTestConfiguration();
42+
}
4443

4544
$params = array(
4645
'driver' => 'pdo_sqlite',
@@ -50,6 +49,23 @@ public static function createTestEntityManager()
5049
return EntityManager::create($params, $config);
5150
}
5251

52+
/**
53+
* @return Configuration
54+
*/
55+
public static function createTestConfiguration()
56+
{
57+
$config = new Configuration();
58+
$config->setEntityNamespaces(array('SymfonyTestsDoctrine' => 'Symfony\Bridge\Doctrine\Tests\Fixtures'));
59+
$config->setAutoGenerateProxyClasses(true);
60+
$config->setProxyDir(\sys_get_temp_dir());
61+
$config->setProxyNamespace('SymfonyTests\Doctrine');
62+
$config->setMetadataDriverImpl(new AnnotationDriver(new AnnotationReader()));
63+
$config->setQueryCacheImpl(new ArrayCache());
64+
$config->setMetadataCacheImpl(new ArrayCache());
65+
66+
return $config;
67+
}
68+
5369
/**
5470
* This class cannot be instantiated.
5571
*/
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the Symfony package.
5+
*
6+
* (c) Fabien Potencier <fabien@symfony.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Symfony\Bridge\Doctrine\Test;
13+
14+
use Doctrine\Common\Persistence\ObjectRepository;
15+
use Doctrine\ORM\EntityManagerInterface;
16+
use Doctrine\ORM\Mapping\ClassMetadata;
17+
use Doctrine\ORM\Repository\RepositoryFactory;
18+
19+
/**
20+
* @author Andreas Braun <alcaeus@alcaeus.org>
21+
*/
22+
final class TestRepositoryFactory implements RepositoryFactory
23+
{
24+
/**
25+
* The list of EntityRepository instances.
26+
*
27+
* @var ObjectRepository[]
28+
*/
29+
private $repositoryList = array();
30+
31+
/**
32+
* {@inheritdoc}
33+
*/
34+
public function getRepository(EntityManagerInterface $entityManager, $entityName)
35+
{
36+
$repositoryHash = $this->getRepositoryHash($entityManager, $entityName);
37+
38+
if (isset($this->repositoryList[$repositoryHash])) {
39+
return $this->repositoryList[$repositoryHash];
40+
}
41+
42+
return $this->repositoryList[$repositoryHash] = $this->createRepository($entityManager, $entityName);
43+
}
44+
45+
/**
46+
* @param EntityManagerInterface $entityManager
47+
* @param string $entityName
48+
* @param ObjectRepository $repository
49+
*/
50+
public function setRepository(EntityManagerInterface $entityManager, $entityName, ObjectRepository $repository)
51+
{
52+
$repositoryHash = $this->getRepositoryHash($entityManager, $entityName);
53+
54+
$this->repositoryList[$repositoryHash] = $repository;
55+
}
56+
57+
/**
58+
* Create a new repository instance for an entity class.
59+
*
60+
* @param EntityManagerInterface $entityManager The EntityManager instance.
61+
* @param string $entityName The name of the entity.
62+
*
63+
* @return ObjectRepository
64+
*/
65+
private function createRepository(EntityManagerInterface $entityManager, $entityName)
66+
{
67+
/* @var $metadata ClassMetadata */
68+
$metadata = $entityManager->getClassMetadata($entityName);
69+
$repositoryClassName = $metadata->customRepositoryClassName
70+
?: $entityManager->getConfiguration()->getDefaultRepositoryClassName();
71+
72+
return new $repositoryClassName($entityManager, $metadata);
73+
}
74+
75+
/**
76+
* @param EntityManagerInterface $entityManager
77+
* @param string $entityName
78+
*
79+
* @return string
80+
*/
81+
private function getRepositoryHash(EntityManagerInterface $entityManager, $entityName)
82+
{
83+
return $entityManager->getClassMetadata($entityName)->getName().spl_object_hash($entityManager);
84+
}
85+
}

src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdEntity.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ class SingleIntIdEntity
2424
/** @Column(type="string", nullable=true) */
2525
public $name;
2626

27+
/** @Column(type="array", nullable=true) */
28+
public $phoneNumbers = array();
29+
2730
public function __construct($id, $name)
2831
{
2932
$this->id = $id;

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

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
use Doctrine\Common\Persistence\ObjectManager;
1717
use Doctrine\Common\Persistence\ObjectRepository;
1818
use Symfony\Bridge\Doctrine\Test\DoctrineTestHelper;
19+
use Symfony\Bridge\Doctrine\Test\TestRepositoryFactory;
1920
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity;
2021
use Symfony\Bridge\Doctrine\Tests\Fixtures\DoubleNameEntity;
2122
use Symfony\Bridge\Doctrine\Tests\Fixtures\AssociationEntity;
@@ -48,9 +49,16 @@ class UniqueEntityValidatorTest extends AbstractConstraintValidatorTest
4849
*/
4950
protected $repository;
5051

52+
protected $repositoryFactory;
53+
5154
protected function setUp()
5255
{
53-
$this->em = DoctrineTestHelper::createTestEntityManager();
56+
$this->repositoryFactory = new TestRepositoryFactory();
57+
58+
$config = DoctrineTestHelper::createTestConfiguration();
59+
$config->setRepositoryFactory($this->repositoryFactory);
60+
61+
$this->em = DoctrineTestHelper::createTestEntityManager($config);
5462
$this->registry = $this->createRegistryMock($this->em);
5563
$this->createSchema($this->em);
5664

@@ -164,7 +172,7 @@ public function testValidateUniqueness()
164172

165173
$this->buildViolation('myMessage')
166174
->atPath('property.path.name')
167-
->setParameter('{{ value }}', 'Foo')
175+
->setParameter('{{ value }}', '"Foo"')
168176
->setInvalidValue('Foo')
169177
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
170178
->assertRaised();
@@ -189,7 +197,7 @@ public function testValidateCustomErrorPath()
189197

190198
$this->buildViolation('myMessage')
191199
->atPath('property.path.bar')
192-
->setParameter('{{ value }}', 'Foo')
200+
->setParameter('{{ value }}', '"Foo"')
193201
->setInvalidValue('Foo')
194202
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
195203
->assertRaised();
@@ -242,7 +250,7 @@ public function testValidateUniquenessWithIgnoreNull()
242250

243251
$this->buildViolation('myMessage')
244252
->atPath('property.path.name')
245-
->setParameter('{{ value }}', 'Foo')
253+
->setParameter('{{ value }}', '"Foo"')
246254
->setInvalidValue('Foo')
247255
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
248256
->assertRaised();
@@ -275,7 +283,7 @@ public function testValidateUniquenessWithValidCustomErrorPath()
275283

276284
$this->buildViolation('myMessage')
277285
->atPath('property.path.name2')
278-
->setParameter('{{ value }}', 'Bar')
286+
->setParameter('{{ value }}', '"Bar"')
279287
->setInvalidValue('Bar')
280288
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
281289
->assertRaised();
@@ -446,7 +454,7 @@ public function testValidateUniquenessNotToStringEntityWithAssociatedEntity()
446454

447455
$this->buildViolation('myMessage')
448456
->atPath('property.path.single')
449-
->setParameter('{{ value }}', $expectedValue)
457+
->setParameter('{{ value }}', '"'.$expectedValue.'"')
450458
->setInvalidValue($expectedValue)
451459
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
452460
->assertRaised();
@@ -472,6 +480,44 @@ public function testAssociatedEntityWithNull()
472480
$this->assertNoViolation();
473481
}
474482

483+
public function testValidateUniquenessWithArrayValue()
484+
{
485+
$repository = $this->createRepositoryMock();
486+
$this->repositoryFactory->setRepository($this->em, 'Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity', $repository);
487+
488+
$constraint = new UniqueEntity(array(
489+
'message' => 'myMessage',
490+
'fields' => array('phoneNumbers'),
491+
'em' => self::EM_NAME,
492+
'repositoryMethod' => 'findByCustom',
493+
));
494+
495+
$entity1 = new SingleIntIdEntity(1, 'foo');
496+
$entity1->phoneNumbers[] = 123;
497+
498+
$repository->expects($this->once())
499+
->method('findByCustom')
500+
->will($this->returnValue(array($entity1)))
501+
;
502+
503+
$this->em->persist($entity1);
504+
$this->em->flush();
505+
506+
$entity2 = new SingleIntIdEntity(2, 'bar');
507+
$entity2->phoneNumbers[] = 123;
508+
$this->em->persist($entity2);
509+
$this->em->flush();
510+
511+
$this->validator->validate($entity2, $constraint);
512+
513+
$this->buildViolation('myMessage')
514+
->atPath('property.path.phoneNumbers')
515+
->setParameter('{{ value }}', 'array')
516+
->setInvalidValue(array(123))
517+
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
518+
->assertRaised();
519+
}
520+
475521
/**
476522
* @expectedException \Symfony\Component\Validator\Exception\ConstraintDefinitionException
477523
* @expectedExceptionMessage Object manager "foo" does not exist.

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,7 @@ public function validate($entity, Constraint $constraint)
133133

134134
$this->context->buildViolation($constraint->message)
135135
->atPath($errorPath)
136-
->setParameter('{{ value }}', $invalidValue)
136+
->setParameter('{{ value }}', $this->formatValue($invalidValue, static::OBJECT_TO_STRING | static::PRETTY_DATE))
137137
->setInvalidValue($invalidValue)
138138
->setCode(UniqueEntity::NOT_UNIQUE_ERROR)
139139
->addViolation();

0 commit comments

Comments
 (0)
0