DDC-74: Updates get lost when Lifecycle Events (@PreUpdate) are invoked #5252

Closed
doctrinebot opened this Issue Oct 29, 2009 · 5 comments

1 participant

@doctrinebot

Jira issue originally created by user nicokaiser:

When Lifecycle Events are invoked on entity update (@PreUpdate), the entity is not updated properly in the database.

This code creates a User* object, sets its name to "Bob" and its value to empty, then updates the object and updates the *name to "Alice".

{quote}
$user = new User;
$user->setName('Bob');
$user->setValue('');
$em->persist($user);
$em->flush();
$user->setName('Alice');
$em->flush();
{quote}

However, when the User class has a @PreUpdate event that e.g. sets the value* to "Hello World", the name change gets lost and only the *value is updated by the second flush() call.

This is a critical bug which prevents creation of entities that simulate the "Timestampable" behaviour of Doctrine 1.x...

@doctrinebot

Comment created by @beberlei:

I think this might be a hen-egg problem.

@PreUpdate is only invoked after it is calculated that a change occured on all those entities that have updates. After a change in @PreUpdate events there would have to be another calculation of changes on all those entities, which would probably mean a significant performance hit.

@doctrinebot

Comment created by rickdt:

I may not know all the consequences of this, but I think I have a fix for this bug.

In the class ORM\UnitOfWork

As I understand it, computeSingleEntityChangeSet() is called to update the changeset and _originalEntityData is set to the current values.

But, I see that the old changes are lost.

If i modify the function computeSingleEntityChangeSet to merge the changeset, it works.

At line 656 replace

$this->_entityChangeSets[$oid] = $changeSet;

By :

if($this->_entityChangeSets[$oid]){
    $this->_entityChangeSets[$oid] += $changeSet;
}
else {
    $this->_entityChangeSets[$oid] = $changeSet;
}

Here is my test case :

$qb = new \Doctrine\ORM\QueryBuilder($em);
$qb->select('fna')
            ->from('Entity\FNA', 'fna')
            ->andwhere($qb->expr()->eq('fna.id', ':fna_id'));
$qb->setParameter('fna_id', 1);
$query = $qb->getQuery();

$fna = $query->getSingleResult();

$fna->setStatus('COMPLETED');
$em->persist($fna);
$em->flush();

AND The preUdate :

/**** 
 * @PreUpdate
*/
public function onPreUpdate($args=false)
 {
        $this->modified_at = new \DateTime();
}
@doctrinebot

Comment created by romanb:

Indeed this looks like a good fix except that the addition has to be the other way around so that when the same field is changed twice, first before the flush and then in a lifecycle callback/event the change from the callback prevails.

I will work on this and write a test for it.

Thanks Eric.

@doctrinebot

Comment created by romanb:

Fixed now.

Thanks Nico for reporting and thanks Eric for the suggestion!

@doctrinebot

Issue was closed with resolution "Fixed"

@doctrinebot doctrinebot added this to the 2.0-ALPHA4 milestone Dec 6, 2015
@doctrinebot doctrinebot closed this Dec 6, 2015
@doctrinebot doctrinebot added the Bug label Dec 7, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment