Doctrine 2 - ORM
  1. Doctrine 2 - ORM
  2. DDC-440

originalEntityData not initialized for proxy loaded via an association using EntityManaget->find()

    Details

      Description

      Here is the pseudo-code to reproduce.

      Let's say we have 2 entity, Person and Phone.
      There is a OneToMany relation between Person and Phone.

      You get a person entity with the find() method:

      $person = $em->find('Person', 1);
      $phones = $person->getPhones()
      $phone = $phones[0]; // Any would do
      
      $phone->setNumber('999-9999');
      
      $em->persist($phone);
      $em->persist($person);
      $em->flush();
      

      If you look at the resulting update query, you will see that all fields get updated and not only the "number" field. This is due to the fact that originalEntityData is not loaded and the change set cannot be calculated properly.

      I noticed this problem because I need the originalEntityData for an other purpose (creating history records) and it break when I use proxies.

      Look at UnitOfWork._createEntity()

      if (isset($this->_identityMap[$class->rootEntityName][$idHash])) {
                  $entity = $this->_identityMap[$class->rootEntityName][$idHash];
                  $oid = spl_object_hash($entity);
                  if ($entity instanceof Proxy && ! $entity->__isInitialized__) {
                      $entity->__isInitialized__ = true;
                      $overrideLocalValues = true;
                  } else {
                      $overrideLocalValues = isset($hints[Query::HINT_REFRESH]);
                  }
              } else {
                  $entity = $class->newInstance();
                  $oid = spl_object_hash($entity);
                  $this->_entityIdentifiers[$oid] = $id;
                  $this->_entityStates[$oid] = self::STATE_MANAGED;
                  $this->_originalEntityData[$oid] = $data;
                  $this->_identityMap[$class->rootEntityName][$idHash] = $entity;
                  if ($entity instanceof NotifyPropertyChanged) {
                      $entity->addPropertyChangedListener($this);
                  }
                  $overrideLocalValues = true;
              }
      

      In this case, the _identityMap is set so the originalEntityData is not initialized

      I realized that identityMap was set while looping into the parent entity associations
      Look again in UnitOfWork._createEntity()

       $newValue = $this->_em->getProxyFactory()->getProxy($assoc->targetEntityName, $associatedId);
      // PERF: Inlined & optimized code from UnitOfWork#registerManaged()
      $newValueOid = spl_object_hash($newValue);
      $this->_entityIdentifiers[$newValueOid] = $associatedId;
      $this->_identityMap[$targetClass->rootEntityName][$relatedIdHash] = $newValue;
      $this->_entityStates[$newValueOid] = self::STATE_MANAGED;
      

      I will try to build you a UnitTest

      1. DDC440Test.php
        5 kB
        Eric Durand-Tremblay
      2. DDC440Test.php
        2 kB
        Benjamin Eberlei
      3. DDC440Test.php
        1 kB
        Eric Durand-Tremblay

        Activity

        Hide
        Eric Durand-Tremblay added a comment -

        The attached UnitTest does not work for two reason

        1) I tried these steps to test the problem

        • Create test data
        • clear the EntityManager
        • Query for the data with find
        • Update the entity obtained by find

        I expected the entity to be a Proxy but it is a standard entity. Moreover, it have the same spl_object_hash than the object initially created (which have been cleared)

        2) I forgot one important detail in the pre-conditions. There is two relations between Person and Phone

        Person->phones (one to many)
        Person->main_phone (one to one)

        There is no such case in the provided test models

        Show
        Eric Durand-Tremblay added a comment - The attached UnitTest does not work for two reason 1) I tried these steps to test the problem Create test data clear the EntityManager Query for the data with find Update the entity obtained by find I expected the entity to be a Proxy but it is a standard entity. Moreover, it have the same spl_object_hash than the object initially created (which have been cleared) 2) I forgot one important detail in the pre-conditions. There is two relations between Person and Phone Person->phones (one to many) Person->main_phone (one to one) There is no such case in the provided test models
        Hide
        Eric Durand-Tremblay added a comment -

        I managed to bypass the problem using eager fetch (fetch="EAGER") in my associations.

        Show
        Eric Durand-Tremblay added a comment - I managed to bypass the problem using eager fetch (fetch="EAGER") in my associations.
        Hide
        Benjamin Eberlei added a comment -

        Attached a test myself, i can't reproduce the issue with both types of proxies. Both tests pass!

        Show
        Benjamin Eberlei added a comment - Attached a test myself, i can't reproduce the issue with both types of proxies. Both tests pass!
        Hide
        Benjamin Eberlei added a comment -

        Btw it is correct behaviour that originalEntityData is EMPTY so long the proxy is not loaded.

        Show
        Benjamin Eberlei added a comment - Btw it is correct behaviour that originalEntityData is EMPTY so long the proxy is not loaded.
        Hide
        Eric Durand-Tremblay added a comment -

        It's obvious that originalEntityData cannot be filled until the proxy is loaded.

        I was shure it was loaded when I saw the empty originalData. Maybe I did not check correctly.

        I'm not able for now to reproduce the problem in a simple unit test. The context in which it occurs in my application is fairly complex and as I said, work fine with eager fetching.

        I can't affort to spend more time on that just now. You can close the Case if you want, I will reopen it if I get more pertinent informations.

        Thank You

        Show
        Eric Durand-Tremblay added a comment - It's obvious that originalEntityData cannot be filled until the proxy is loaded. I was shure it was loaded when I saw the empty originalData. Maybe I did not check correctly. I'm not able for now to reproduce the problem in a simple unit test. The context in which it occurs in my application is fairly complex and as I said, work fine with eager fetching. I can't affort to spend more time on that just now. You can close the Case if you want, I will reopen it if I get more pertinent informations. Thank You
        Hide
        Benjamin Eberlei added a comment -

        Updated to Minor and removed scheduled version until we can find a reproduce case for this bug.

        Show
        Benjamin Eberlei added a comment - Updated to Minor and removed scheduled version until we can find a reproduce case for this bug.
        Hide
        Eric Durand-Tremblay added a comment -

        I finally managed to write a valid test case. The detail of the problem are written in comments in the test case file.

        I think this will be enough for you to work on the problem.

        BTW, Doctrine 2 beta 1 work great in our production environnement. Your great work is really appreciated.

        Show
        Eric Durand-Tremblay added a comment - I finally managed to write a valid test case. The detail of the problem are written in comments in the test case file. I think this will be enough for you to work on the problem. BTW, Doctrine 2 beta 1 work great in our production environnement. Your great work is really appreciated.
        Hide
        Benjamin Eberlei added a comment -

        Woah! thanks for the test-case, this was indeed a very nasty bug and it is now fixed.

        Show
        Benjamin Eberlei added a comment - Woah! thanks for the test-case, this was indeed a very nasty bug and it is now fixed.

          People

          • Assignee:
            Benjamin Eberlei
            Reporter:
            Eric Durand-Tremblay
          • Votes:
            0 Vote for this issue
            Watchers:
            0 Start watching this issue

            Dates

            • Created:
              Updated:
              Resolved: