diff --git a/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdWithPrivateNameEntity.php b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdWithPrivateNameEntity.php new file mode 100644 index 0000000000000..bbc019e8a9fb4 --- /dev/null +++ b/src/Symfony/Bridge/Doctrine/Tests/Fixtures/SingleIntIdWithPrivateNameEntity.php @@ -0,0 +1,39 @@ + + * + * 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\Column; +use Doctrine\ORM\Mapping\Entity; +use Doctrine\ORM\Mapping\Id; + +#[Entity] +class SingleIntIdWithPrivateNameEntity +{ + public function __construct( + #[Id, Column(type: 'integer')] + protected int $id, + + #[Column(type: 'string', nullable: true)] + private ?string $name, + ) { + } + + public function getName(): ?string + { + return $this->name; + } + + public function __toString(): string + { + 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 4cde094d0e2df..f554acb70d0fb 100644 --- a/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php +++ b/src/Symfony/Bridge/Doctrine/Tests/Validator/Constraints/UniqueEntityValidatorTest.php @@ -36,6 +36,7 @@ use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdNoToStringEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdStringWrapperNameEntity; +use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleIntIdWithPrivateNameEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\SingleStringIdEntity; use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapper; use Symfony\Bridge\Doctrine\Tests\Fixtures\Type\StringWrapperType; @@ -90,12 +91,17 @@ protected function createRegistryMock($em = null) return $registry; } - protected function createRepositoryMock() + protected function createRepositoryMock(string $className) { - return $this->getMockBuilder(MockableRepository::class) + $repositoryMock = $this->getMockBuilder(MockableRepository::class) ->disableOriginalConstructor() ->onlyMethods(['find', 'findAll', 'findOneBy', 'findBy', 'getClassName', 'findByCustom']) ->getMock(); + + $repositoryMock->method('getClassName') + ->willReturn($className); + + return $repositoryMock; } protected function createEntityManagerMock($repositoryMock) @@ -109,6 +115,10 @@ protected function createEntityManagerMock($repositoryMock) $classMetadata = $this->createMock( class_exists(ClassMetadataInfo::class) ? ClassMetadataInfo::class : ClassMetadata::class ); + $classMetadata + ->method('getName') + ->willReturn($repositoryMock->getClassName()) + ; $classMetadata ->expects($this->any()) ->method('hasField') @@ -138,6 +148,7 @@ private function createSchema($em) $schemaTool = new SchemaTool($em); $schemaTool->createSchema([ $em->getClassMetadata(SingleIntIdEntity::class), + $em->getClassMetadata(SingleIntIdWithPrivateNameEntity::class), $em->getClassMetadata(SingleIntIdNoToStringEntity::class), $em->getClassMetadata(DoubleNameEntity::class), $em->getClassMetadata(DoubleNullableNameEntity::class), @@ -194,6 +205,25 @@ public static function provideUniquenessConstraints(): iterable yield 'Named arguments' => [new UniqueEntity(message: 'myMessage', fields: ['name'], em: 'foo')]; } + public function testValidateEntityWithPrivatePropertyAndProxyObject() + { + $entity = new SingleIntIdWithPrivateNameEntity(1, 'Foo'); + $this->em->persist($entity); + $this->em->flush(); + + $this->em->clear(); + + // this will load a proxy object + $entity = $this->em->getReference(SingleIntIdWithPrivateNameEntity::class, 1); + + $this->validator->validate($entity, new UniqueEntity([ + 'fields' => ['name'], + 'em' => self::EM_NAME, + ])); + + $this->assertNoViolation(); + } + /** * @dataProvider provideConstraintsWithCustomErrorPath */ @@ -387,7 +417,7 @@ public function testValidateUniquenessWithValidCustomErrorPath() */ public function testValidateUniquenessUsingCustomRepositoryMethod(UniqueEntity $constraint) { - $repository = $this->createRepositoryMock(); + $repository = $this->createRepositoryMock(SingleIntIdEntity::class); $repository->expects($this->once()) ->method('findByCustom') ->willReturn([]) @@ -411,7 +441,7 @@ public function testValidateUniquenessWithUnrewoundArray(UniqueEntity $constrain { $entity = new SingleIntIdEntity(1, 'foo'); - $repository = $this->createRepositoryMock(); + $repository = $this->createRepositoryMock(SingleIntIdEntity::class); $repository->expects($this->once()) ->method('findByCustom') ->willReturnCallback( @@ -459,7 +489,7 @@ public function testValidateResultTypes($entity1, $result) 'repositoryMethod' => 'findByCustom', ]); - $repository = $this->createRepositoryMock(); + $repository = $this->createRepositoryMock($entity1::class); $repository->expects($this->once()) ->method('findByCustom') ->willReturn($result) @@ -581,7 +611,7 @@ public function testAssociatedEntityWithNull() public function testValidateUniquenessWithArrayValue() { - $repository = $this->createRepositoryMock(); + $repository = $this->createRepositoryMock(SingleIntIdEntity::class); $this->repositoryFactory->setRepository($this->em, SingleIntIdEntity::class, $repository); $constraint = new UniqueEntity([ @@ -662,7 +692,7 @@ public function testEntityManagerNullObject() public function testValidateUniquenessOnNullResult() { - $repository = $this->createRepositoryMock(); + $repository = $this->createRepositoryMock(SingleIntIdEntity::class); $repository ->method('find') ->willReturn(null) @@ -850,7 +880,7 @@ public function testValidateUniquenessWithEmptyIterator($entity, $result) 'repositoryMethod' => 'findByCustom', ]); - $repository = $this->createRepositoryMock(); + $repository = $this->createRepositoryMock($entity::class); $repository->expects($this->once()) ->method('findByCustom') ->willReturn($result) diff --git a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php index 5591170140d01..ec5aac9e84d43 100644 --- a/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php +++ b/src/Symfony/Bridge/Doctrine/Validator/Constraints/UniqueEntityValidator.php @@ -287,7 +287,9 @@ private function getFieldValues(mixed $object, ClassMetadata $class, array $fiel throw new ConstraintDefinitionException(sprintf('The field "%s" is not a property of class "%s".', $fieldName, $objectClass)); } - $fieldValues[$entityFieldName] = $this->getPropertyValue($objectClass, $fieldName, $object); + $fieldValues[$entityFieldName] = $isValueEntity && $object instanceof ($class->getName()) + ? $class->reflFields[$fieldName]->getValue($object) + : $this->getPropertyValue($objectClass, $fieldName, $object); } return $fieldValues;