[DDC-74] Updates get lost when Lifecycle Events (@PreUpdate) are invoked Created: 29/Oct/09  Updated: 13/Nov/09  Resolved: 13/Nov/09

Status: Closed
Project: Doctrine 2 - ORM
Component/s: Mapping Drivers
Affects Version/s: None
Fix Version/s: 2.0-ALPHA4
Security Level: All

Type: Bug Priority: Critical
Reporter: Nico Kaiser Assignee: Roman S. Borschel
Resolution: Fixed Votes: 1
Labels: None


 Description   

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

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

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



 Comments   
Comment by Benjamin Eberlei [ 02/Nov/09 ]

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.

Comment by Eric Durand-Tremblay [ 13/Nov/09 ]

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();
}
Comment by Roman S. Borschel [ 13/Nov/09 ]

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.

Comment by Roman S. Borschel [ 13/Nov/09 ]

Fixed now.

Thanks Nico for reporting and thanks Eric for the suggestion!

Generated at Fri Oct 24 12:49:40 UTC 2014 using JIRA 6.2.3#6260-sha1:63ef1d6dac3f4f4d7db4c1effd405ba38ccdc558.