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 22, 2024
1 parent 74ef282 commit b2e4500
Show file tree
Hide file tree
Showing 9 changed files with 386 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 @@ -342,7 +342,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 @@ -533,6 +533,19 @@ public function getReference($entityName, $id)
$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) {
Expand Down
16 changes: 14 additions & 2 deletions src/Proxy/ProxyFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
use function array_combine;
use function array_flip;
use function array_intersect_key;
use function assert;
use function bin2hex;
use function chmod;
use function class_exists;
Expand Down Expand Up @@ -465,8 +466,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 @@ -476,7 +478,17 @@ private function getProxyFactory(string $className): Closure
throw ORMInvalidArgumentException::missingPrimaryKeyValue($className, $idField);
}

$reflector->setValue($proxy, $identifier[$idField]);
assert($reflector !== null);

$idValue = $identifier[$idField];
if ($class->hasAssociation($idField)) {
$idValue = $em->getReference(
$class->getAssociationTargetClass($idField),
$idValue,
);
}

$reflector->setValue($proxy, $idValue);
}

return $proxy;
Expand Down
25 changes: 25 additions & 0 deletions tests/Tests/Models/RelationAsId/Group.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\RelationAsId;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="relation_as_id_group")
*/
class Group
{
/**
* @ORM\Id
* @ORM\Column
*/
public int $id;

/**
* @ORM\Column
*/
public string $name;
}
31 changes: 31 additions & 0 deletions tests/Tests/Models/RelationAsId/Membership.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\RelationAsId;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="relation_as_id_membership")
*/
class Membership
{
/**
* @ORM\Id
* @ORM\ManyToOne
*/
public User $user;

/**
* @ORM\Id
* @ORM\ManyToOne
*/
public Group $group;

/**
* @ORM\Column
*/
public string $role;
}
25 changes: 25 additions & 0 deletions tests/Tests/Models/RelationAsId/Profile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\RelationAsId;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="relation_as_id_profile")
*/
class Profile
{
/**
* @ORM\Id
* @ORM\OneToOne
*/
public User $user;

/**
* @ORM\Column
*/
public string $url;
}
25 changes: 25 additions & 0 deletions tests/Tests/Models/RelationAsId/User.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php

declare(strict_types=1);

namespace Doctrine\Tests\Models\RelationAsId;

use Doctrine\ORM\Mapping as ORM;

/**
* @ORM\Entity
* @ORM\Table(name="relation_as_id_user")
*/
class User
{
/**
* @ORM\Id
* @ORM\Column
*/
public int $id;

/**
* @ORM\Column
*/
public string $name;
}
Loading

0 comments on commit b2e4500

Please sign in to comment.