1
0
mirror of synced 2025-01-19 15:01:40 +03:00
doctrine2/tests/Doctrine/Tests/ORM/UnitOfWorkTest.php

427 lines
14 KiB
PHP
Raw Normal View History

<?php
2009-01-04 16:15:32 +00:00
namespace Doctrine\Tests\ORM;
2009-01-04 16:15:32 +00:00
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\NotifyPropertyChanged;
use Doctrine\Common\PropertyChangedListener;
use Doctrine\ORM\Mapping\ClassMetadata;
2010-07-04 18:55:49 +02:00
use Doctrine\ORM\UnitOfWork;
use Doctrine\Tests\Mocks\ConnectionMock;
use Doctrine\Tests\Mocks\DriverMock;
use Doctrine\Tests\Mocks\EntityManagerMock;
use Doctrine\Tests\Mocks\EntityPersisterMock;
use Doctrine\Tests\Mocks\UnitOfWorkMock;
use Doctrine\Tests\Models\Forum\ForumAvatar;
use Doctrine\Tests\Models\Forum\ForumUser;
use stdClass;
/**
* UnitOfWork tests.
*/
class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
{
/**
* SUT
*
* @var UnitOfWorkMock
*/
private $_unitOfWork;
/**
* Provides a sequence mock to the UnitOfWork
*
* @var ConnectionMock
*/
private $_connectionMock;
/**
* The EntityManager mock that provides the mock persisters
*
* @var EntityManagerMock
*/
private $_emMock;
2011-12-19 22:56:19 +01:00
protected function setUp() {
parent::setUp();
$this->_connectionMock = new ConnectionMock(array(), new DriverMock());
$this->_emMock = EntityManagerMock::create($this->_connectionMock);
2008-09-07 13:48:40 +00:00
// SUT
$this->_unitOfWork = new UnitOfWorkMock($this->_emMock);
$this->_emMock->setUnitOfWork($this->_unitOfWork);
}
2011-12-19 22:56:19 +01:00
protected function tearDown() {
}
2011-12-19 22:56:19 +01:00
public function testRegisterRemovedOnNewEntityIsIgnored()
{
$user = new ForumUser();
$user->username = 'romanb';
2009-07-19 16:54:53 +00:00
$this->assertFalse($this->_unitOfWork->isScheduledForDelete($user));
$this->_unitOfWork->scheduleForDelete($user);
2011-12-19 22:56:19 +01:00
$this->assertFalse($this->_unitOfWork->isScheduledForDelete($user));
}
2011-12-19 22:56:19 +01:00
/* Operational tests */
2011-12-19 22:56:19 +01:00
public function testSavingSingleEntityWithIdentityColumnForcesInsert()
{
// Setup fake persister and id generator for identity generation
$userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\Models\Forum\ForumUser'));
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
$userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
// Test
$user = new ForumUser();
$user->username = 'romanb';
2009-07-19 16:54:53 +00:00
$this->_unitOfWork->persist($user);
// Check
2009-07-19 16:54:53 +00:00
$this->assertEquals(0, count($userPersister->getInserts()));
$this->assertEquals(0, count($userPersister->getUpdates()));
2011-12-19 22:56:19 +01:00
$this->assertEquals(0, count($userPersister->getDeletes()));
2009-07-19 16:54:53 +00:00
$this->assertFalse($this->_unitOfWork->isInIdentityMap($user));
// should no longer be scheduled for insert
2009-07-19 16:54:53 +00:00
$this->assertTrue($this->_unitOfWork->isScheduledForInsert($user));
2011-12-19 22:56:19 +01:00
// Now lets check whether a subsequent commit() does anything
$userPersister->reset();
// Test
2009-07-19 16:54:53 +00:00
$this->_unitOfWork->commit();
2011-12-19 22:56:19 +01:00
2009-07-19 16:54:53 +00:00
// Check.
$this->assertEquals(1, count($userPersister->getInserts()));
$this->assertEquals(0, count($userPersister->getUpdates()));
$this->assertEquals(0, count($userPersister->getDeletes()));
2011-12-19 22:56:19 +01:00
2009-07-19 16:54:53 +00:00
// should have an id
$this->assertTrue(is_numeric($user->id));
}
/**
* Tests a scenario where a save() operation is cascaded from a ForumUser
* to its associated ForumAvatar, both entities using IDENTITY id generation.
*/
public function testCascadedIdentityColumnInsert()
{
// Setup fake persister and id generator for identity generation
//ForumUser
$userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\Models\Forum\ForumUser'));
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
$userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
// ForumAvatar
$avatarPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\Models\Forum\ForumAvatar'));
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarPersister);
$avatarPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
// Test
$user = new ForumUser();
$user->username = 'romanb';
$avatar = new ForumAvatar();
$user->avatar = $avatar;
2009-07-19 16:54:53 +00:00
$this->_unitOfWork->persist($user); // save cascaded to avatar
2011-12-19 22:56:19 +01:00
2009-07-19 16:54:53 +00:00
$this->_unitOfWork->commit();
$this->assertTrue(is_numeric($user->id));
$this->assertTrue(is_numeric($avatar->id));
2009-07-19 16:54:53 +00:00
$this->assertEquals(1, count($userPersister->getInserts()));
$this->assertEquals(0, count($userPersister->getUpdates()));
$this->assertEquals(0, count($userPersister->getDeletes()));
2009-07-19 16:54:53 +00:00
$this->assertEquals(1, count($avatarPersister->getInserts()));
$this->assertEquals(0, count($avatarPersister->getUpdates()));
$this->assertEquals(0, count($avatarPersister->getDeletes()));
}
public function testChangeTrackingNotify()
{
$persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\ORM\NotifyChangedEntity'));
2009-05-19 16:11:08 +00:00
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\NotifyChangedEntity', $persister);
$itemPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\ORM\NotifyChangedRelatedItem'));
2010-07-15 15:52:42 +02:00
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\NotifyChangedRelatedItem', $itemPersister);
2009-05-19 16:11:08 +00:00
$entity = new NotifyChangedEntity;
$entity->setData('thedata');
2009-07-19 16:54:53 +00:00
$this->_unitOfWork->persist($entity);
2011-12-19 22:56:19 +01:00
2009-07-19 16:54:53 +00:00
$this->_unitOfWork->commit();
2010-07-15 15:52:42 +02:00
$this->assertEquals(1, count($persister->getInserts()));
$persister->reset();
$this->assertTrue($this->_unitOfWork->isInIdentityMap($entity));
$entity->setData('newdata');
2010-05-01 12:14:16 +02:00
$entity->setTransient('newtransientvalue');
2010-07-15 15:52:42 +02:00
$this->assertTrue($this->_unitOfWork->isScheduledForDirtyCheck($entity));
$this->assertEquals(array('data' => array('thedata', 'newdata')), $this->_unitOfWork->getEntityChangeSet($entity));
2010-07-15 15:52:42 +02:00
$item = new NotifyChangedRelatedItem();
$entity->getItems()->add($item);
$item->setOwner($entity);
$this->_unitOfWork->persist($item);
$this->_unitOfWork->commit();
$this->assertEquals(1, count($itemPersister->getInserts()));
$persister->reset();
$itemPersister->reset();
2011-12-19 22:56:19 +01:00
2010-07-15 15:52:42 +02:00
$entity->getItems()->removeElement($item);
$item->setOwner(null);
$this->assertTrue($entity->getItems()->isDirty());
$this->_unitOfWork->commit();
$updates = $itemPersister->getUpdates();
$this->assertEquals(1, count($updates));
$this->assertTrue($updates[0] === $item);
}
2010-07-04 18:55:49 +02:00
public function testGetEntityStateOnVersionedEntityWithAssignedIdentifier()
{
$persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\ORM\VersionedAssignedIdentifierEntity'));
2010-07-04 18:55:49 +02:00
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\VersionedAssignedIdentifierEntity', $persister);
$e = new VersionedAssignedIdentifierEntity();
$e->id = 42;
$this->assertEquals(UnitOfWork::STATE_NEW, $this->_unitOfWork->getEntityState($e));
$this->assertFalse($persister->isExistsCalled());
}
2010-07-04 18:55:49 +02:00
public function testGetEntityStateWithAssignedIdentity()
{
$persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\Models\CMS\CmsPhonenumber'));
2010-07-04 18:55:49 +02:00
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\CMS\CmsPhonenumber', $persister);
$ph = new \Doctrine\Tests\Models\CMS\CmsPhonenumber();
$ph->phonenumber = '12345';
$this->assertEquals(UnitOfWork::STATE_NEW, $this->_unitOfWork->getEntityState($ph));
$this->assertTrue($persister->isExistsCalled());
$persister->reset();
// if the entity is already managed the exists() check should be skipped
2010-07-04 18:55:49 +02:00
$this->_unitOfWork->registerManaged($ph, array('phonenumber' => '12345'), array());
$this->assertEquals(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($ph));
$this->assertFalse($persister->isExistsCalled());
$ph2 = new \Doctrine\Tests\Models\CMS\CmsPhonenumber();
$ph2->phonenumber = '12345';
$this->assertEquals(UnitOfWork::STATE_DETACHED, $this->_unitOfWork->getEntityState($ph2));
$this->assertFalse($persister->isExistsCalled());
}
2012-10-19 09:15:07 +02:00
/**
* DDC-2086 [GH-484] Prevented 'Undefined index' notice when updating.
2012-10-19 09:15:07 +02:00
*/
public function testNoUndefinedIndexNoticeOnScheduleForUpdateWithoutChanges()
{
// Setup fake persister and id generator
$userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata('Doctrine\Tests\Models\Forum\ForumUser'));
$userPersister->setMockIdGeneratorType(ClassMetadata::GENERATOR_TYPE_IDENTITY);
2012-10-19 09:15:07 +02:00
$this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
// Create a test user
$user = new ForumUser();
$user->name = 'Jasper';
$this->_unitOfWork->persist($user);
$this->_unitOfWork->commit();
// Schedule user for update without changes
$this->_unitOfWork->scheduleForUpdate($user);
// This commit should not raise an E_NOTICE
$this->_unitOfWork->commit();
}
/**
* @group DDC-1984
*/
public function testLockWithoutEntityThrowsException()
{
$this->setExpectedException('InvalidArgumentException');
$this->_unitOfWork->lock(null, null, null);
}
/**
* @group DDC-3490
*
* @dataProvider invalidAssociationValuesDataProvider
*
* @param mixed $invalidValue
*/
public function testRejectsPersistenceOfObjectsWithInvalidAssociationValue($invalidValue)
{
$this->_unitOfWork->setEntityPersister(
'Doctrine\Tests\Models\Forum\ForumUser',
new EntityPersisterMock(
$this->_emMock,
$this->_emMock->getClassMetadata('Doctrine\Tests\Models\Forum\ForumUser')
)
);
$user = new ForumUser();
$user->username = 'John';
$user->avatar = $invalidValue;
$this->setExpectedException('Doctrine\ORM\ORMInvalidArgumentException');
$this->_unitOfWork->persist($user);
}
/**
* @group DDC-3490
*
* @dataProvider invalidAssociationValuesDataProvider
*
* @param mixed $invalidValue
*/
public function testRejectsChangeSetComputationForObjectsWithInvalidAssociationValue($invalidValue)
{
$metadata = $this->_emMock->getClassMetadata('Doctrine\Tests\Models\Forum\ForumUser');
$this->_unitOfWork->setEntityPersister(
'Doctrine\Tests\Models\Forum\ForumUser',
new EntityPersisterMock($this->_emMock, $metadata)
);
$user = new ForumUser();
$this->_unitOfWork->persist($user);
$user->username = 'John';
$user->avatar = $invalidValue;
$this->setExpectedException('Doctrine\ORM\ORMInvalidArgumentException');
$this->_unitOfWork->computeChangeSet($metadata, $user);
}
/**
* Data Provider
*
* @return mixed[][]
*/
public function invalidAssociationValuesDataProvider()
{
return [
['foo'],
[['foo']],
[''],
[[]],
[new stdClass()],
[new ArrayCollection()],
];
}
}
/**
* @Entity
*/
class NotifyChangedEntity implements NotifyPropertyChanged
{
private $_listeners = array();
/**
* @Id
* @Column(type="integer")
2010-07-15 15:52:42 +02:00
* @GeneratedValue
*/
private $id;
/**
* @Column(type="string")
*/
private $data;
2010-05-01 12:14:16 +02:00
private $transient; // not persisted
2010-07-15 15:52:42 +02:00
/** @OneToMany(targetEntity="NotifyChangedRelatedItem", mappedBy="owner") */
private $items;
public function __construct() {
$this->items = new ArrayCollection;
2010-07-15 15:52:42 +02:00
}
public function getId() {
return $this->id;
}
2010-07-15 15:52:42 +02:00
public function getItems() {
return $this->items;
}
2010-05-01 12:14:16 +02:00
public function setTransient($value) {
if ($value != $this->transient) {
$this->_onPropertyChanged('transient', $this->transient, $value);
$this->transient = $value;
}
}
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(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);
}
}
}
2010-07-04 18:55:49 +02:00
}
2010-07-15 15:52:42 +02:00
/** @Entity */
class NotifyChangedRelatedItem
{
/**
* @Id
* @Column(type="integer")
* @GeneratedValue
*/
private $id;
/** @ManyToOne(targetEntity="NotifyChangedEntity", inversedBy="items") */
private $owner;
public function getId() {
return $this->id;
}
public function getOwner() {
return $this->owner;
}
public function setOwner($owner) {
$this->owner = $owner;
}
}
2010-07-04 18:55:49 +02:00
/** @Entity */
class VersionedAssignedIdentifierEntity
{
/**
* @Id @Column(type="integer")
*/
public $id;
/**
* @Version @Column(type="integer")
*/
public $version;
2012-10-19 09:15:07 +02:00
}