Skip to content

Loading…

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

Closed
doctrinebot opened this Issue · 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
@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.