Skip to content

Loading…

DDC-440: originalEntityData not initialized for proxy loaded via an association using EntityManaget->find() #4941

Closed
doctrinebot opened this Issue · 10 comments

2 participants

@doctrinebot

Jira issue originally created by user rickdt:

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

@doctrinebot

Comment created by rickdt:

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 splobjecthash 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

@doctrinebot

Comment created by rickdt:

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

@doctrinebot

Comment created by @beberlei:

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

@doctrinebot

Comment created by @beberlei:

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

@doctrinebot

Comment created by rickdt:

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

@doctrinebot

Comment created by @beberlei:

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

@doctrinebot

Comment created by rickdt:

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.

@doctrinebot

Comment created by @beberlei:

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

@doctrinebot

Issue was closed with resolution "Fixed"

@beberlei beberlei was assigned by doctrinebot
@doctrinebot doctrinebot added this to the 2.0-BETA2 milestone
@doctrinebot doctrinebot closed this
@doctrinebot doctrinebot added the Bug label
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.