1
0
mirror of synced 2025-01-18 22:41:43 +03:00

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

This commit is contained in:
beberlei 2010-03-14 22:16:15 +00:00
parent 0a9101d79e
commit bc6714c2c8
4 changed files with 146 additions and 8 deletions

View File

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

View File

@ -0,0 +1,98 @@
<?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."
);
}
}
}

View File

@ -752,16 +752,14 @@ 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);
}
}
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);
}
if ($hasPreUpdateListeners) {
$this->_evm->dispatchEvent(Events::preUpdate, new Event\PreUpdateEventArgs(
$entity, $this->_em, $this->_entityChangeSets[$oid])
);
}
$persister->update($entity);
unset($this->_entityUpdates[$oid]);

View File

@ -1,6 +1,7 @@
<?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');
}
}