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

Closed
opened 2026-01-22 12:26:43 +01:00 by admin · 5 comments
Owner

Originally created by @doctrinebot on GitHub (Oct 29, 2009).

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

Originally created by @doctrinebot on GitHub (Oct 29, 2009). 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...
admin added the Bug label 2026-01-22 12:26:44 +01:00
admin closed this issue 2026-01-22 12:26:45 +01:00
Author
Owner

@doctrinebot commented on GitHub (Nov 2, 2009):

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 commented on GitHub (Nov 2, 2009): 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.
Author
Owner

@doctrinebot commented on GitHub (Nov 13, 2009):

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 commented on GitHub (Nov 13, 2009): 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(); } ```
Author
Owner

@doctrinebot commented on GitHub (Nov 13, 2009):

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 commented on GitHub (Nov 13, 2009): 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.
Author
Owner

@doctrinebot commented on GitHub (Nov 13, 2009):

Comment created by romanb:

Fixed now.

Thanks Nico for reporting and thanks Eric for the suggestion!

@doctrinebot commented on GitHub (Nov 13, 2009): Comment created by romanb: Fixed now. Thanks Nico for reporting and thanks Eric for the suggestion!
Author
Owner

@doctrinebot commented on GitHub (Nov 13, 2009):

Issue was closed with resolution "Fixed"

@doctrinebot commented on GitHub (Nov 13, 2009): Issue was closed with resolution "Fixed"
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: doctrine/archived-orm#90