Skip to content

Loading…

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

Closed
doctrinebot opened this Issue · 10 comments

1 participant

@doctrinebot

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::HINTFORCE_PARTIALLOAD 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.

@doctrinebot

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

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

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

Comment created by romanb:

Rescheduling for beta3.

@doctrinebot

Issue was closed with resolution "Fixed"

@doctrinebot

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

Comment created by mzach:

Possible fix in UoW, line 612:

$class->setIdentifierValues($entity, $this->entityIdentifiers[$oid]);
@doctrinebot

Comment created by @beberlei:

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

@doctrinebot

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)

@doctrinebot

Comment created by @beberlei:

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

@doctrinebot doctrinebot added this to the 2.0-BETA3 milestone
@doctrinebot doctrinebot closed this
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Something went wrong with that request. Please try again.