diff --git a/docs/en/reference/advanced-configuration.rst b/docs/en/reference/advanced-configuration.rst index 3282bbdb35..5e11057ed9 100644 --- a/docs/en/reference/advanced-configuration.rst +++ b/docs/en/reference/advanced-configuration.rst @@ -337,7 +337,7 @@ for the ``$identifier`` parameter is passed. ``$identifier`` values are not checked and there is no guarantee that the requested entity instance even exists – the method will still return a proxy object. -Its only when the proxy has to be fully initialized or associations cannot +It is only when the proxy has to be fully initialized or associations cannot be written to the database that invalid ``$identifier`` values may lead to exceptions. diff --git a/src/EntityManager.php b/src/EntityManager.php index 8045ac2f5e..418f7573d4 100644 --- a/src/EntityManager.php +++ b/src/EntityManager.php @@ -366,6 +366,19 @@ public function getReference(string $entityName, mixed $id): object|null $id = [$class->identifier[0] => $id]; } + foreach ($id as $i => $value) { + if (is_object($value)) { + $className = DefaultProxyClassNameResolver::getClass($value); + if ($this->metadataFactory->hasMetadataFor($className)) { + $id[$i] = $this->unitOfWork->getSingleIdentifierValue($value); + + if ($id[$i] === null) { + throw ORMInvalidArgumentException::invalidIdentifierBindingEntity($className); + } + } + } + } + $sortedId = []; foreach ($class->identifier as $identifier) { diff --git a/src/Proxy/ProxyFactory.php b/src/Proxy/ProxyFactory.php index b2d114a669..0139ffc797 100644 --- a/src/Proxy/ProxyFactory.php +++ b/src/Proxy/ProxyFactory.php @@ -280,8 +280,9 @@ private function getProxyFactory(string $className): Closure $initializer = $this->createLazyInitializer($class, $entityPersister, $this->identifierFlattener); $proxyClassName = $this->loadProxyClass($class); $identifierFields = array_intersect_key($class->getReflectionProperties(), $identifiers); + $em = $this->em; - $proxyFactory = Closure::bind(static function (array $identifier) use ($initializer, $skippedProperties, $identifierFields, $className): InternalProxy { + $proxyFactory = Closure::bind(static function (array $identifier) use ($initializer, $skippedProperties, $identifierFields, $className, $class, $em): InternalProxy { $proxy = self::createLazyGhost(static function (InternalProxy $object) use ($initializer, $identifier): void { $initializer($object, $identifier); }, $skippedProperties); @@ -292,7 +293,16 @@ private function getProxyFactory(string $className): Closure } assert($reflector !== null); - $reflector->setValue($proxy, $identifier[$idField]); + + $idValue = $identifier[$idField]; + if ($class->hasAssociation($idField)) { + $idValue = $em->getReference( + $class->getAssociationTargetClass($idField), + $idValue, + ); + } + + $reflector->setValue($proxy, $idValue); } return $proxy; diff --git a/tests/Tests/ORM/Functional/CompositePrimaryKeyWithAssociationsTest.php b/tests/Tests/ORM/Functional/CompositePrimaryKeyWithAssociationsTest.php index 25cab43261..aea3b669b0 100644 --- a/tests/Tests/ORM/Functional/CompositePrimaryKeyWithAssociationsTest.php +++ b/tests/Tests/ORM/Functional/CompositePrimaryKeyWithAssociationsTest.php @@ -8,6 +8,7 @@ use Doctrine\Tests\Models\GeoNames\Admin1AlternateName; use Doctrine\Tests\Models\GeoNames\Country; use Doctrine\Tests\OrmFunctionalTestCase; +use Doctrine\Tests\Proxies\__CG__\Doctrine\Tests\Models\GeoNames\Country as CountryProxy; class CompositePrimaryKeyWithAssociationsTest extends OrmFunctionalTestCase { @@ -61,4 +62,19 @@ public function testFindByAbleToGetCompositeEntitiesWithMixedTypeIdentifiers(): self::assertEquals(2, $name2->id); self::assertEquals('Rome', $name2->name); } + + public function testGetReferenceAbleToGetCompositeEntitiesFromId(): void + { + $admin1Rome = $this->_em->getReference(Admin1::class, ['country' => 'IT', 'id' => 1]); + self::assertInstanceOf(CountryProxy::class, $admin1Rome->country); + self::assertEquals('Italy', $admin1Rome->country->name); + } + + public function testGetReferenceAbleToGetCompositeEntitiesFromReference(): void + { + $countryRef = $this->_em->getReference(Country::class, 'IT'); + $admin1Rome = $this->_em->getReference(Admin1::class, ['country' => $countryRef, 'id' => 1]); + self::assertInstanceOf(CountryProxy::class, $admin1Rome->country); + self::assertEquals('Italy', $admin1Rome->country->name); + } }