Commit 5a00a947 authored by romanb's avatar romanb

Implemented three change tracking policys. First test for usage of NotifyPropertyChanged.

parent 832f355a
...@@ -44,21 +44,10 @@ class Configuration extends \Doctrine\DBAL\Configuration ...@@ -44,21 +44,10 @@ class Configuration extends \Doctrine\DBAL\Configuration
'resultCacheImpl' => null, 'resultCacheImpl' => null,
'queryCacheImpl' => null, 'queryCacheImpl' => null,
'metadataCacheImpl' => null, 'metadataCacheImpl' => null,
'metadataDriverImpl' => new AnnotationDriver(), 'metadataDriverImpl' => new AnnotationDriver()
'automaticDirtyChecking' => true
)); ));
} }
public function setAutomaticDirtyChecking($bool)
{
$this->_attributes['automaticDirtyChecking'] = $bool;
}
public function getAutomaticDirtyChecking()
{
return $this->_attributes['automaticDirtyChecking'];
}
public function setMetadataDriverImpl($driverImpl) public function setMetadataDriverImpl($driverImpl)
{ {
$this->_attributes['metadataDriverImpl'] = $driverImpl; $this->_attributes['metadataDriverImpl'] = $driverImpl;
......
...@@ -383,7 +383,7 @@ final class ClassMetadata ...@@ -383,7 +383,7 @@ final class ClassMetadata
* *
* @var integer * @var integer
*/ */
//private $_changeTrackingPolicy; private $_changeTrackingPolicy = self::CHANGETRACKING_DEFERRED_IMPLICIT;
/** /**
* Initializes a new ClassMetadata instance that will hold the object-relational mapping * Initializes a new ClassMetadata instance that will hold the object-relational mapping
...@@ -420,6 +420,51 @@ final class ClassMetadata ...@@ -420,6 +420,51 @@ final class ClassMetadata
return $this->_reflectionProperties; return $this->_reflectionProperties;
} }
/**
*
* @return integer
*/
public function getChangeTrackingPolicy()
{
return $this->_changeTrackingPolicy;
}
/**
*
* @param integer $policy
*/
public function setChangeTrackingPolicy($policy)
{
$this->_changeTrackingPolicy = $policy;
}
/**
*
* @return boolean
*/
public function isChangeTrackingDeferredExplicit()
{
return $this->_changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_EXPLICIT;
}
/**
*
* @return boolean
*/
public function isChangeTrackingPolicyDeferredImplicit()
{
return $this->_changeTrackingPolicy == self::CHANGETRACKING_DEFERRED_IMPLICIT;
}
/**
*
* @return boolean
*/
public function isChangeTrackingNotify()
{
return $this->_changeTrackingPolicy == self::CHANGETRACKING_NOTIFY;
}
/** /**
* INTERNAL: * INTERNAL:
* Adds a reflection property. Usually only used by the ClassMetadataFactory * Adds a reflection property. Usually only used by the ClassMetadataFactory
......
...@@ -309,27 +309,27 @@ class UnitOfWork implements PropertyChangedListener ...@@ -309,27 +309,27 @@ class UnitOfWork implements PropertyChangedListener
foreach ($entities as $entity) { foreach ($entities as $entity) {
$entitySet[get_class($entity)][] = $entity; $entitySet[get_class($entity)][] = $entity;
} }
} else if ( ! $this->_em->getConfiguration()->getAutomaticDirtyChecking()) {
$entitySet = $this->_scheduledForDirtyCheck;
} else { } else {
$entitySet = $this->_identityMap; $entitySet = $this->_identityMap;
} }
foreach ($entitySet as $className => $entities) { foreach ($entitySet as $className => $entities) {
$class = $this->_em->getClassMetadata($className); $class = $this->_em->getClassMetadata($className);
if ( ! $class->isInheritanceTypeNone() && count($entities) > 0) {
$class = $this->_em->getClassMetadata(get_class($entities[key($entities)]));
}
/* // Skip class if change tracking happens through notification
if ($class->isChangeTrackingNotify()) { if ($class->isChangeTrackingNotify()) {
continue; continue;
} }
// If change tracking is explicit, then only compute changes on explicitly saved entities
$entitiesToProcess = $class->isChangeTrackingDeferredExplicit() ? $entitiesToProcess = $class->isChangeTrackingDeferredExplicit() ?
$this->_scheduledForDirtyCheck[$className] : $entities; $this->_scheduledForDirtyCheck[$className] : $entities;
*/
foreach ($entities as $entity) { if ( ! $class->isInheritanceTypeNone() && count($entitiesToProcess) > 0) {
$class = $this->_em->getClassMetadata(get_class($entitiesToProcess[key($entitiesToProcess)]));
}
foreach ($entitiesToProcess as $entity) {
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
$state = $this->getEntityState($entity); $state = $this->getEntityState($entity);
...@@ -970,11 +970,8 @@ class UnitOfWork implements PropertyChangedListener ...@@ -970,11 +970,8 @@ class UnitOfWork implements PropertyChangedListener
$class = $this->_em->getClassMetadata(get_class($entity)); $class = $this->_em->getClassMetadata(get_class($entity));
switch ($this->getEntityState($entity)) { switch ($this->getEntityState($entity)) {
case self::STATE_MANAGED: case self::STATE_MANAGED:
// nothing to do, except if automatic dirty checking is disabled // nothing to do, except if policy is "deferred explicit"
/*if ($class->isChangeTrackingDeferredExplicit()) { if ($class->isChangeTrackingDeferredExplicit()) {
$this->scheduleForDirtyCheck($entity);
}*/
if ( ! $this->_em->getConfiguration()->getAutomaticDirtyChecking()) {
$this->scheduleForDirtyCheck($entity); $this->scheduleForDirtyCheck($entity);
} }
break; break;
...@@ -1399,7 +1396,7 @@ class UnitOfWork implements PropertyChangedListener ...@@ -1399,7 +1396,7 @@ class UnitOfWork implements PropertyChangedListener
$oid = spl_object_hash($entity); $oid = spl_object_hash($entity);
$class = $this->_em->getClassMetadata(get_class($entity)); $class = $this->_em->getClassMetadata(get_class($entity));
$this->_entityChangeSets[$oid][$propertyName] = array($oldValue => $newValue); $this->_entityChangeSets[$oid][$propertyName] = array($oldValue, $newValue);
if ($class->hasAssociation($propertyName)) { if ($class->hasAssociation($propertyName)) {
$assoc = $class->getAssociationMapping($name); $assoc = $class->getAssociationMapping($name);
......
...@@ -7,7 +7,6 @@ use Doctrine\Tests\Mocks\EntityManagerMock; ...@@ -7,7 +7,6 @@ use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Mocks\UnitOfWorkMock; use Doctrine\Tests\Mocks\UnitOfWorkMock;
use Doctrine\Tests\Mocks\EntityPersisterMock; use Doctrine\Tests\Mocks\EntityPersisterMock;
use Doctrine\Tests\Mocks\IdentityIdGeneratorMock; use Doctrine\Tests\Mocks\IdentityIdGeneratorMock;
use Doctrine\Tests\Models\Forum\ForumUser; use Doctrine\Tests\Models\Forum\ForumUser;
use Doctrine\Tests\Models\Forum\ForumAvatar; use Doctrine\Tests\Models\Forum\ForumAvatar;
...@@ -124,54 +123,21 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase ...@@ -124,54 +123,21 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
$this->assertEquals(0, count($avatarPersister->getDeletes())); $this->assertEquals(0, count($avatarPersister->getDeletes()));
} }
/*public function testComputeEntityChangeSets() public function testChangeTrackingNotify()
{ {
// We need an ID generator for ForumAvatar, because we attach a NEW ForumAvatar $entity = new NotifyChangedEntity;
// to a (faked) MANAGED instance. During changeset computation this will result $entity->setData('thedata');
// in the UnitOfWork requesting the Id generator of ForumAvatar. $this->_unitOfWork->save($entity);
$avatarIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock);
$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarIdGeneratorMock); $this->assertTrue($this->_unitOfWork->isInIdentityMap($entity));
$user1 = new ForumUser(); $entity->setData('newdata');
$user1->id = 1;
$user1->username = "romanb"; $this->assertTrue($this->_unitOfWork->isRegisteredDirty($entity));
$user1->avatar = new ForumAvatar();
// Fake managed state $this->assertEquals(array('data' => array('thedata', 'newdata')), $this->_unitOfWork->getEntityChangeSet($entity));
$this->_unitOfWork->setEntityState($user1, \Doctrine\ORM\UnitOfWork::STATE_MANAGED);
$user2 = new ForumUser();
$user2->id = 2;
$user2->username = "jwage";
// Fake managed state
$this->_unitOfWork->setEntityState($user2, \Doctrine\ORM\UnitOfWork::STATE_MANAGED);
// Fake original entity date
$this->_unitOfWork->setOriginalEntityData($user1, array(
'id' => 1, 'username' => 'roman'
));
$this->_unitOfWork->setOriginalEntityData($user2, array(
'id' => 2, 'username' => 'jon'
));
// Go
$this->_unitOfWork->computeChangeSets(array($user1, $user2));
// Verify
$user1ChangeSet = $this->_unitOfWork->getEntityChangeSet($user1);
$this->assertTrue(is_array($user1ChangeSet));
$this->assertEquals(2, count($user1ChangeSet));
$this->assertTrue(isset($user1ChangeSet['username']));
$this->assertEquals(array('roman' => 'romanb'), $user1ChangeSet['username']);
$this->assertTrue(isset($user1ChangeSet['avatar']));
$this->assertSame(array(null => $user1->avatar), $user1ChangeSet['avatar']);
$user2ChangeSet = $this->_unitOfWork->getEntityChangeSet($user2);
$this->assertTrue(is_array($user2ChangeSet));
$this->assertEquals(1, count($user2ChangeSet));
$this->assertTrue(isset($user2ChangeSet['username']));
$this->assertEquals(array('jon' => 'jwage'), $user2ChangeSet['username']);
} }
*/
/* /*
public function testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert() public function testSavingSingleEntityWithSequenceIdGeneratorSchedulesInsert()
{ {
...@@ -214,3 +180,49 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase ...@@ -214,3 +180,49 @@ class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
} }
*/ */
} }
/**
* @DoctrineEntity
*/
class NotifyChangedEntity implements \Doctrine\Common\NotifyPropertyChanged
{
private $_listeners = array();
/**
* @DoctrineId
* @DoctrineColumn(type="integer")
* @DoctrineGeneratedValue(strategy="auto")
*/
private $id;
/**
* @DoctrineColumn(type="varchar")
*/
private $data;
public function getId() {
return $this->id;
}
public function getData() {
return $this->data;
}
public function setData($data) {
if ($data != $this->data) {
$this->_onPropertyChanged('data', $this->data, $data);
$this->data = $data;
}
}
public function addPropertyChangedListener(\Doctrine\Common\PropertyChangedListener $listener)
{
$this->_listeners[] = $listener;
}
protected function _onPropertyChanged($propName, $oldValue, $newValue) {
if ($this->_listeners) {
foreach ($this->_listeners as $listener) {
$listener->propertyChanged($this, $propName, $oldValue, $newValue);
}
}
}
}
\ 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