DDC-167: New method: EntityManager#getPartialReference #205

Closed
opened 2026-01-22 12:30:36 +01:00 by admin · 10 comments
Owner

Originally created by @doctrinebot on GitHub (Nov 20, 2009).

Jira issue originally created by user romanb:

Currently, EntityManager#getReference can be used to retrieve cheap references to "persistent" objects without actually loading them. The returned proxies only initialize themselves automatically when one of their methods is invoked. This can lead to unnecessary/unwanted initialization in cases of bidirectional associations where the "association management methods" (setFoo) properly set the other side of the association. Example (one-one, User-Address, User owning side):

class User {
    private $address;
    public function setAddress(Address $address) {
        if ($address !== $this->address) {
            $this->address = $address;
            $address->setUser($this);
        }
    }
}

class Address {
    private $user;
    public function setUser(User $user) {
        if ($user !== $this->user) {
            $this->user = $user;
            $user->setAddress($this);
        }
    }
}

Now, assuming the following code that makes use of getReference to associate a user to an address but actually wants to avoid loading the address.

// assuming $user is already loaded and now we want to associate it to an existing address
// but without actually loading the address

$addressId = 42; // from request, $*POST/$*GET/...
$address = $em->getReference('Address', $addressId);

$user->setAddress($address); // calls $address->setUser which initializes the proxy!
$em->flush();

A possible solution could be to allow obtaining single references to partial objects through the EntityManager. Partial objects can already be fetched through querying with the Query::HINT_FORCE_PARTIAL_LOAD query hint so the possibility to get a single partial object by ID seems to make sense.

A proposed method name is: getPartialReference($className, $id). It would return a managed (partial) instance of $className where only the identifier is populated and that will not initialize itself (hence "partial"). In the example above, usage of a partial reference object would avoid the extra initialization.

Originally created by @doctrinebot on GitHub (Nov 20, 2009). Jira issue originally created by user romanb: Currently, EntityManager#getReference can be used to retrieve cheap references to "persistent" objects without actually loading them. The returned proxies only initialize themselves automatically when one of their methods is invoked. This can lead to unnecessary/unwanted initialization in cases of bidirectional associations where the "association management methods" (setFoo) properly set the other side of the association. Example (one-one, User-Address, User owning side): ``` class User { private $address; public function setAddress(Address $address) { if ($address !== $this->address) { $this->address = $address; $address->setUser($this); } } } class Address { private $user; public function setUser(User $user) { if ($user !== $this->user) { $this->user = $user; $user->setAddress($this); } } } ``` Now, assuming the following code that makes use of getReference to associate a user to an address but actually wants to avoid loading the address. ``` // assuming $user is already loaded and now we want to associate it to an existing address // but without actually loading the address $addressId = 42; // from request, $*POST/$*GET/... $address = $em->getReference('Address', $addressId); $user->setAddress($address); // calls $address->setUser which initializes the proxy! $em->flush(); ``` A possible solution could be to allow obtaining single references to partial objects through the EntityManager. Partial objects can already be fetched through querying with the Query::HINT_FORCE_PARTIAL_LOAD query hint so the possibility to get a single partial object by ID seems to make sense. A proposed method name is: getPartialReference($className, $id). It would return a managed (partial) instance of $className where only the identifier is populated and that will not initialize itself (hence "partial"). In the example above, usage of a partial reference object would avoid the extra initialization.
admin added the Improvement label 2026-01-22 12:30:36 +01:00
admin closed this issue 2026-01-22 12:30:37 +01:00
Author
Owner

@doctrinebot commented on GitHub (Nov 20, 2009):

Comment created by @beberlei:

Sounds good to me, the method name is also very clear about that the retrieved object is partial, the only negative is that its really the only use-case for this method.

@doctrinebot commented on GitHub (Nov 20, 2009): Comment created by @beberlei: Sounds good to me, the method name is also very clear about that the retrieved object is partial, the only negative is that its really the only use-case for this method.
Author
Owner

@doctrinebot commented on GitHub (Nov 21, 2009):

Comment created by romanb:

I think this has some more use-cases. For example if you only want a single object and you have its ID and all you really need is the ID during that request. Proxy objects are initialized even if you call getId() because we (the proxy) cant know what happens inside that method.

Of course, partial objects are always dangerous and I think this is made pretty clear in the manual but the choice is up to the user in the end.

@doctrinebot commented on GitHub (Nov 21, 2009): Comment created by romanb: I think this has some more use-cases. For example if you only want a single object and you have its ID and all you really need is the ID during that request. Proxy objects are initialized even if you call getId() because we (the proxy) cant know what happens inside that method. Of course, partial objects are always dangerous and I think this is made pretty clear in the manual but the choice is up to the user in the end.
Author
Owner

@doctrinebot commented on GitHub (Mar 12, 2010):

Comment created by romanb:

Another interesting use-case: updating an object without loading it (as an alternative to a DQL bulk UPDATE):

$user = $em->getPartialReference('User', $userId);
$user->setName('newname');
$em->flush();

Of course one needs to be aware that the original entity data in such an update is not "correct", i.e. when using event listeners.

@doctrinebot commented on GitHub (Mar 12, 2010): Comment created by romanb: Another interesting use-case: updating an object without loading it (as an alternative to a DQL bulk UPDATE): ``` $user = $em->getPartialReference('User', $userId); $user->setName('newname'); $em->flush(); ``` Of course one needs to be aware that the original entity data in such an update is not "correct", i.e. when using event listeners.
Author
Owner

@doctrinebot commented on GitHub (May 19, 2010):

Comment created by romanb:

Rescheduling for beta3.

@doctrinebot commented on GitHub (May 19, 2010): Comment created by romanb: Rescheduling for beta3.
Author
Owner

@doctrinebot commented on GitHub (Jul 20, 2010):

Issue was closed with resolution "Fixed"

@doctrinebot commented on GitHub (Jul 20, 2010): Issue was closed with resolution "Fixed"
Author
Owner

@doctrinebot commented on GitHub (Jul 21, 2010):

Comment created by mzach:

Hello,

this fix needs to be implemented in UnitOfWork on line 612 (function persistNew()) as well - when I try to save an entity, I get an Exception from my error handler:

ErrorException with Argument 2 passed to Doctrine\ORM\Mapping\ClassMetadata::setIdentifierValues() must be an array, integer given, called in /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php on line 612 and defined
Backtrace: #0: Doctrine\ORM\Mapping\ClassMetadata->setIdentifierValues at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:612
#1: Doctrine\ORM\UnitOfWork->persistNew at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:1247
#2: Doctrine\ORM\UnitOfWork->doPersist at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:1210
#3: Doctrine\ORM\UnitOfWork->persist at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/EntityManager.php:438

The relevant code in UoW is:

            $idValue = $idGen->generate($this->em, $entity);
            if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) {
                $this->entityIdentifiers[$oid] = array($class->identifier[0] => $idValue);
                $class->setIdentifierValues($entity, $idValue);

We're using the SequenceGenerator

@SequenceGenerator(allocationSize=1,sequenceName="address*id*seq")

which doesn't return an array, so the array typehint fails and generates an error.

@doctrinebot commented on GitHub (Jul 21, 2010): Comment created by mzach: Hello, this fix needs to be implemented in UnitOfWork on line 612 (function persistNew()) as well - when I try to save an entity, I get an Exception from my error handler: ``` ErrorException with Argument 2 passed to Doctrine\ORM\Mapping\ClassMetadata::setIdentifierValues() must be an array, integer given, called in /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php on line 612 and defined Backtrace: #0: Doctrine\ORM\Mapping\ClassMetadata->setIdentifierValues at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:612 #1: Doctrine\ORM\UnitOfWork->persistNew at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:1247 #2: Doctrine\ORM\UnitOfWork->doPersist at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/UnitOfWork.php:1210 #3: Doctrine\ORM\UnitOfWork->persist at /var/www/svn/cWorld_ZF/branches/devel-trunk/library/Doctrine/ORM/EntityManager.php:438 ``` The relevant code in UoW is: ``` $idValue = $idGen->generate($this->em, $entity); if ( ! $idGen instanceof \Doctrine\ORM\Id\AssignedGenerator) { $this->entityIdentifiers[$oid] = array($class->identifier[0] => $idValue); $class->setIdentifierValues($entity, $idValue); ``` We're using the SequenceGenerator ``` @SequenceGenerator(allocationSize=1,sequenceName="address*id*seq") ``` which doesn't return an array, so the array typehint fails and generates an error.
Author
Owner

@doctrinebot commented on GitHub (Jul 21, 2010):

Comment created by mzach:

Possible fix in UoW, line 612:

$class->setIdentifierValues($entity, $this->entityIdentifiers[$oid]);
@doctrinebot commented on GitHub (Jul 21, 2010): Comment created by mzach: Possible fix in UoW, line 612: ``` $class->setIdentifierValues($entity, $this->entityIdentifiers[$oid]); ```
Author
Owner

@doctrinebot commented on GitHub (Jul 21, 2010):

Comment created by @beberlei:

please open a new issue, or reopen this one. posting into closed issues does more harm than good ;)

@doctrinebot commented on GitHub (Jul 21, 2010): Comment created by @beberlei: please open a new issue, or reopen this one. posting into closed issues does more harm than good ;)
Author
Owner

@doctrinebot commented on GitHub (Jul 22, 2010):

Comment created by mzach:

Hello Benjamin,

since I've no rights to re-open this entry I had to create a new one: http://www.doctrine-project.org/jira/browse/DDC-714

@doctrinebot commented on GitHub (Jul 22, 2010): Comment created by mzach: Hello Benjamin, since I've no rights to re-open this entry I had to create a new one: http://www.doctrine-project.org/jira/browse/[DDC-714](http://www.doctrine-project.org/jira/browse/DDC-714)
Author
Owner

@doctrinebot commented on GitHub (Jul 22, 2010):

Comment created by @beberlei:

hm, maybe our user-rirghts arre strange. I look into it later

@doctrinebot commented on GitHub (Jul 22, 2010): Comment created by @beberlei: hm, maybe our user-rirghts arre strange. I look into it later
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#205