Skip to content

Commit

Permalink
EntityManager::getReference() should handle a PK which is also a FK
Browse files Browse the repository at this point in the history
  • Loading branch information
le-yak committed Sep 17, 2024
1 parent cfc0655 commit 9dbc430
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 3 deletions.
2 changes: 1 addition & 1 deletion docs/en/reference/advanced-configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down
13 changes: 13 additions & 0 deletions src/EntityManager.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Check warning on line 376 in src/EntityManager.php

View check run for this annotation

Codecov / codecov/patch

src/EntityManager.php#L376

Added line #L376 was not covered by tests
}
}
}
}

$sortedId = [];

foreach ($class->identifier as $identifier) {
Expand Down
14 changes: 12 additions & 2 deletions src/Proxy/ProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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);
}
}

0 comments on commit 9dbc430

Please sign in to comment.