Skip to content

Commit a2890a6

Browse files
committed
bug #57188 [DoctrineBridge] Fix UniqueEntityValidator with proxy object (HypeMC)
This PR was merged into the 7.1 branch. Discussion ---------- [DoctrineBridge] Fix `UniqueEntityValidator` with proxy object | Q | A | ------------- | --- | Branch? | 7.1 | Bug fix? | yes | New feature? | no | Deprecations? | no | Issues | Fix #57075 | License | MIT Before #38662, `$fieldValue = $class->reflFields[$fieldName]->getValue($entity);` was used to get the value of a property, so it makes sense to keep using it when the object is an entity. Commits ------- 99f279b [DoctrineBridge] Fix `UniqueEntityValidator` with proxy object
2 parents 6644076 + 99f279b commit a2890a6

File tree

3 files changed

+80
-9
lines changed

3 files changed

+80
-9
lines changed
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
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\Tests\Fixtures;
13+
14+
use Doctrine\ORM\Mapping\Column;
15+
use Doctrine\ORM\Mapping\Entity;
16+
use Doctrine\ORM\Mapping\Id;
17+
18+
#[Entity]
19+
class SingleIntIdWithPrivateNameEntity
20+
{
21+
public function __construct(
22+
#[Id, Column(type: 'integer')]
23+
protected int $id,
24+
25+
#[Column(type: 'string', nullable: true)]
26+
private ?string $name,
27+
) {
28+
}
29+
30+
public function getName(): ?string
31+
{
32+
return $this->name;
33+
}
34+
35+
public function __toString(): string
36+
{
37+
return (string) $this->name;
38+
}
39+
}

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

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity;
3737
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity;
3838
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdStringWrapperNameEntity;
39+
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdWithPrivateNameEntity;
3940
use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity;
4041
use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper;
4142
use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapperType;
@@ -90,12 +91,17 @@ protected function createRegistryMock($em = null)
9091
return $registry;
9192
}
9293

93-
protected function createRepositoryMock()
94+
protected function createRepositoryMock(string $className)
9495
{
95-
return $this->getMockBuilder(MockableRepository::class)
96+
$repositoryMock = $this->getMockBuilder(MockableRepository::class)
9697
->disableOriginalConstructor()
9798
->onlyMethods(['find', 'findAll', 'findOneBy', 'findBy', 'getClassName', 'findByCustom'])
9899
->getMock();
100+
101+
$repositoryMock->method('getClassName')
102+
->willReturn($className);
103+
104+
return $repositoryMock;
99105
}
100106

101107
protected function createEntityManagerMock($repositoryMock)
@@ -109,6 +115,10 @@ protected function createEntityManagerMock($repositoryMock)
109115
$classMetadata = $this->createMock(
110116
class_exists(ClassMetadataInfo::class) ? ClassMetadataInfo::class : ClassMetadata::class
111117
);
118+
$classMetadata
119+
->method('getName')
120+
->willReturn($repositoryMock->getClassName())
121+
;
112122
$classMetadata
113123
->expects($this->any())
114124
->method('hasField')
@@ -138,6 +148,7 @@ private function createSchema($em)
138148
$schemaTool = new SchemaTool($em);
139149
$schemaTool->createSchema([
140150
$em->getClassMetadata(SingleIntIdEntity::class),
151+
$em->getClassMetadata(SingleIntIdWithPrivateNameEntity::class),
141152
$em->getClassMetadata(SingleIntIdNoToStringEntity::class),
142153
$em->getClassMetadata(DoubleNameEntity::class),
143154
$em->getClassMetadata(DoubleNullableNameEntity::class),
@@ -194,6 +205,25 @@ public static function provideUniquenessConstraints(): iterable
194205
yield 'Named arguments' => [new UniqueEntity(message: 'myMessage', fields: ['name'], em: 'foo')];
195206
}
196207

208+
public function testValidateEntityWithPrivatePropertyAndProxyObject()
209+
{
210+
$entity = new SingleIntIdWithPrivateNameEntity(1, 'Foo');
211+
$this->em->persist($entity);
212+
$this->em->flush();
213+
214+
$this->em->clear();
215+
216+
// this will load a proxy object
217+
$entity = $this->em->getReference(SingleIntIdWithPrivateNameEntity::class, 1);
218+
219+
$this->validator->validate($entity, new UniqueEntity([
220+
'fields' => ['name'],
221+
'em' => self::EM_NAME,
222+
]));
223+
224+
$this->assertNoViolation();
225+
}
226+
197227
/**
198228
* @dataProvider provideConstraintsWithCustomErrorPath
199229
*/
@@ -387,7 +417,7 @@ public function testValidateUniquenessWithValidCustomErrorPath()
387417
*/
388418
public function testValidateUniquenessUsingCustomRepositoryMethod(UniqueEntity $constraint)
389419
{
390-
$repository = $this->createRepositoryMock();
420+
$repository = $this->createRepositoryMock(SingleIntIdEntity::class);
391421
$repository->expects($this->once())
392422
->method('findByCustom')
393423
->willReturn([])
@@ -411,7 +441,7 @@ public function testValidateUniquenessWithUnrewoundArray(UniqueEntity $constrain
411441
{
412442
$entity = new SingleIntIdEntity(1, 'foo');
413443

414-
$repository = $this->createRepositoryMock();
444+
$repository = $this->createRepositoryMock(SingleIntIdEntity::class);
415445
$repository->expects($this->once())
416446
->method('findByCustom')
417447
->willReturnCallback(
@@ -459,7 +489,7 @@ public function testValidateResultTypes($entity1, $result)
459489
'repositoryMethod' => 'findByCustom',
460490
]);
461491

462-
$repository = $this->createRepositoryMock();
492+
$repository = $this->createRepositoryMock($entity1::class);
463493
$repository->expects($this->once())
464494
->method('findByCustom')
465495
->willReturn($result)
@@ -581,7 +611,7 @@ public function testAssociatedEntityWithNull()
581611

582612
public function testValidateUniquenessWithArrayValue()
583613
{
584-
$repository = $this->createRepositoryMock();
614+
$repository = $this->createRepositoryMock(SingleIntIdEntity::class);
585615
$this->repositoryFactory->setRepository($this->em, SingleIntIdEntity::class, $repository);
586616

587617
$constraint = new UniqueEntity([
@@ -662,7 +692,7 @@ public function testEntityManagerNullObject()
662692

663693
public function testValidateUniquenessOnNullResult()
664694
{
665-
$repository = $this->createRepositoryMock();
695+
$repository = $this->createRepositoryMock(SingleIntIdEntity::class);
666696
$repository
667697
->method('find')
668698
->willReturn(null)
@@ -850,7 +880,7 @@ public function testValidateUniquenessWithEmptyIterator($entity, $result)
850880
'repositoryMethod' => 'findByCustom',
851881
]);
852882

853-
$repository = $this->createRepositoryMock();
883+
$repository = $this->createRepositoryMock($entity::class);
854884
$repository->expects($this->once())
855885
->method('findByCustom')
856886
->willReturn($result)

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

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -287,7 +287,9 @@ private function getFieldValues(mixed $object, ClassMetadata $class, array $fiel
287287
throw new ConstraintDefinitionException(sprintf('The field "%s" is not a property of class "%s".', $fieldName, $objectClass));
288288
}
289289

290-
$fieldValues[$entityFieldName] = $this->getPropertyValue($objectClass, $fieldName, $object);
290+
$fieldValues[$entityFieldName] = $isValueEntity && $object instanceof ($class->getName())
291+
? $class->reflFields[$fieldName]->getValue($object)
292+
: $this->getPropertyValue($objectClass, $fieldName, $object);
291293
}
292294

293295
return $fieldValues;

0 commit comments

Comments
 (0)
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