Commit bc6714c2 authored by beberlei's avatar beberlei

[2.0] DDC-414 - Changed semantics of preUpdate Event to allow only changes to...

[2.0] DDC-414 - Changed semantics of preUpdate Event to allow only changes to the entity changeset, not the internal state of the entity anymore.
parent 0a9101d7
......@@ -19,6 +19,13 @@ mapped fields inside PHP easily. Upon persist() invocation these values are save
The 'inheritance-type' attribute changed to take last bit of ClassMetadata constant names, i.e.
NONE, SINGLE_TABLE, INHERITANCE_TYPE_JOINED
## Change of PreUpdate Event Listener
Event Listeners listening to the 'preUpdate' event can only affect the primitive values of entity changesets
by using the API on the `PreUpdateEventArgs` instance passed to the preUpdate listener method. Any changes
to the state of the entitys properties won't affect the database UPDATE statement anymore. This gives drastic
performance benefits for the preUpdate event.
# Upgrade from 2.0-ALPHA3 to 2.0-ALPHA4
## CLI Controller changes
......
<?php
namespace Doctrine\ORM\Event;
use Doctrine\Common\EventArgs,
Doctrine\ORM\EntityManager;
/**
* Class that holds event arguments for a preInsert/preUpdate event.
*
* @author Roman Borschel <roman@code-factory.org>
* @author Benjamin Eberlei <kontakt@beberlei.de>
* @since 2.0
*/
class PreUpdateEventArgs extends LifecycleEventArgs
{
/**
* @var array
*/
private $_entityChangeSet;
/**
*
* @param object $entity
* @param EntityManager $em
* @param array $changeSet
*/
public function __construct($entity, $em, array &$changeSet)
{
parent::__construct($entity, $em);
$this->_entityChangeSet = &$changeSet;
}
public function getEntityChangeSet()
{
return $this->_entityChangeSet;
}
/**
* Field has a changeset?
*
* @return bool
*/
public function hasChangedField($field)
{
return isset($this->_entityChangeSet[$field]);
}
/**
* Get the old value of the changeset of the changed field.
*
* @param string $field
* @return mixed
*/
public function getOldValue($field)
{
$this->_assertValidField($field);
return $this->_entityChangeSet[$field][0];
}
/**
* Get the new value of the changeset of the changed field.
*
* @param string $field
* @return mixed
*/
public function getNewValue($field)
{
$this->_assertValidField($field);
return $this->_entityChangeSet[$field][1];
}
/**
* Set the new value of this field.
*
* @param string $field
* @param mixed $value
*/
public function setNewValue($field, $value)
{
$this->_assertValidField($field);
$this->_entityChangeSet[$field][1] = $value;
}
private function _assertValidField($field)
{
if (!isset($this->_entityChangeSet[$field])) {
throw new \InvalidArgumentException(
"Field '".$field."' is not a valid field of the entity ".
"'".get_class($this->getEntity())."' in PreInsertUpdateEventArgs."
);
}
}
}
......@@ -752,15 +752,13 @@ class UnitOfWork implements PropertyChangedListener
if ($hasPreUpdateLifecycleCallbacks) {
$class->invokeLifecycleCallbacks(Events::preUpdate, $entity);
if ( ! $hasPreUpdateListeners) {
// Need to recompute entity changeset to detect changes made in the callback.
$this->recomputeSingleEntityChangeSet($class, $entity);
}
$this->recomputeSingleEntityChangeSet($class, $entity);
}
if ($hasPreUpdateListeners) {
$this->_evm->dispatchEvent(Events::preUpdate, new LifecycleEventArgs($entity, $this->_em));
// Need to recompute entity changeset to detect changes made in the listener.
$this->recomputeSingleEntityChangeSet($class, $entity);
$this->_evm->dispatchEvent(Events::preUpdate, new Event\PreUpdateEventArgs(
$entity, $this->_em, $this->_entityChangeSets[$oid])
);
}
$persister->update($entity);
......
<?php
namespace Doctrine\Tests\ORM\Functional;
use Doctrine\ORM\Event\PreUpdateEventArgs;
require_once __DIR__ . '/../../TestInit.php';
......@@ -111,6 +112,32 @@ class LifecycleCallbackTest extends \Doctrine\Tests\OrmFunctionalTestCase
$childMeta = $this->_em->getClassMetadata(__NAMESPACE__ . '\LifecycleCallbackChildEntity');
$this->assertEquals(array('prePersist' => array(0 => 'doStuff')), $childMeta->lifecycleCallbacks);
}
public function testLifecycleListener_ChangeUpdateChangeSet()
{
$listener = new LifecycleListenerPreUpdate;
$this->_em->getEventManager()->addEventListener(array('preUpdate'), $listener);
$user = new LifecycleCallbackTestUser;
$user->setName('Bob');
$user->setValue('value');
$this->_em->persist($user);
$this->_em->flush();
$this->_em->clear();
$dql = "SELECT u FROM Doctrine\Tests\ORM\Functional\LifecycleCallbackTestUser u WHERE u.name = 'Bob'";
$bob = $this->_em->createQuery($dql)->getSingleResult();
$bob->setName('Alice');
$this->_em->flush(); // preUpdate reverts Alice to Bob
$this->_em->clear();
$this->_em->getEventManager()->removeEventListener(array('preUpdate'), $listener);
$bob = $this->_em->createQuery($dql)->getSingleResult();
$this->assertEquals('Bob', $bob->getName());
}
}
/** @Entity @HasLifecycleCallbacks */
......@@ -219,3 +246,11 @@ class LifecycleCallbackChildEntity extends LifecycleCallbackParentEntity {
/** @Id @Column(type="integer") @GeneratedValue */
private $id;
}
class LifecycleListenerPreUpdate
{
public function preUpdate(PreUpdateEventArgs $eventArgs)
{
$eventArgs->setNewValue('name', 'Bob');
}
}
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment