Doctrine 2 - ORM
  1. Doctrine 2 - ORM
  2. DDC-167

New method: EntityManager#getPartialReference

    Details

    • Type: Improvement Improvement
    • Status: Closed
    • Priority: Major Major
    • Resolution: Fixed
    • Affects Version/s: None
    • Fix Version/s: 2.0-BETA3
    • Component/s: ORM
    • Security Level: All
    • Labels:
      None

      Description

      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.

        Activity

        Hide
        Benjamin Eberlei added a comment -

        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.

        Show
        Benjamin Eberlei added a comment - 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.
        Hide
        Roman S. Borschel added a comment -

        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.

        Show
        Roman S. Borschel added a comment - 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.
        Hide
        Roman S. Borschel added a comment -

        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.

        Show
        Roman S. Borschel added a comment - 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.
        Hide
        Roman S. Borschel added a comment -

        Rescheduling for beta3.

        Show
        Roman S. Borschel added a comment - Rescheduling for beta3.
        Hide
        Michael Zach added a comment -

        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.

        Show
        Michael Zach added a comment - 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.
        Hide
        Michael Zach added a comment -

        Possible fix in UoW, line 612:

        $class->setIdentifierValues($entity, $this->entityIdentifiers[$oid]);
        
        Show
        Michael Zach added a comment - Possible fix in UoW, line 612: $class->setIdentifierValues($entity, $ this ->entityIdentifiers[$oid]);
        Hide
        Benjamin Eberlei added a comment -

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

        Show
        Benjamin Eberlei added a comment - please open a new issue, or reopen this one. posting into closed issues does more harm than good
        Hide
        Michael Zach added a comment -

        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

        Show
        Michael Zach added a comment - 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
        Hide
        Benjamin Eberlei added a comment -

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

        Show
        Benjamin Eberlei added a comment - hm, maybe our user-rirghts arre strange. I look into it later

          People

          • Assignee:
            Roman S. Borschel
            Reporter:
            Roman S. Borschel
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: