UnitOfWorkTest.php 11KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. <?php
  2. namespace Doctrine\Tests\ORM;
  3. use Doctrine\ORM\UnitOfWork;
  4. use Doctrine\Tests\Mocks\ConnectionMock;
  5. use Doctrine\Tests\Mocks\EntityManagerMock;
  6. use Doctrine\Tests\Mocks\UnitOfWorkMock;
  7. use Doctrine\Tests\Mocks\EntityPersisterMock;
  8. use Doctrine\Tests\Mocks\IdentityIdGeneratorMock;
  9. use Doctrine\Tests\Models\Forum\ForumUser;
  10. use Doctrine\Tests\Models\Forum\ForumAvatar;
  11. require_once __DIR__ . '/../TestInit.php';
  12. /**
  13. * UnitOfWork tests.
  14. */
  15. class UnitOfWorkTest extends \Doctrine\Tests\OrmTestCase
  16. {
  17. // SUT
  18. private $_unitOfWork;
  19. // Provides a sequence mock to the UnitOfWork
  20. private $_connectionMock;
  21. // The EntityManager mock that provides the mock persisters
  22. private $_emMock;
  23. protected function setUp() {
  24. parent::setUp();
  25. $this->_connectionMock = new ConnectionMock(array(), new \Doctrine\Tests\Mocks\DriverMock());
  26. $this->_emMock = EntityManagerMock::create($this->_connectionMock);
  27. // SUT
  28. $this->_unitOfWork = new UnitOfWorkMock($this->_emMock);
  29. $this->_emMock->setUnitOfWork($this->_unitOfWork);
  30. }
  31. protected function tearDown() {
  32. }
  33. public function testRegisterRemovedOnNewEntityIsIgnored()
  34. {
  35. $user = new ForumUser();
  36. $user->username = 'romanb';
  37. $this->assertFalse($this->_unitOfWork->isScheduledForDelete($user));
  38. $this->_unitOfWork->scheduleForDelete($user);
  39. $this->assertFalse($this->_unitOfWork->isScheduledForDelete($user));
  40. }
  41. /* Operational tests */
  42. public function testSavingSingleEntityWithIdentityColumnForcesInsert()
  43. {
  44. // Setup fake persister and id generator for identity generation
  45. $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser"));
  46. $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
  47. //$idGeneratorMock = new IdentityIdGeneratorMock($this->_emMock);
  48. //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $idGeneratorMock);
  49. $userPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY);
  50. // Test
  51. $user = new ForumUser();
  52. $user->username = 'romanb';
  53. $this->_unitOfWork->persist($user);
  54. // Check
  55. $this->assertEquals(0, count($userPersister->getInserts()));
  56. $this->assertEquals(0, count($userPersister->getUpdates()));
  57. $this->assertEquals(0, count($userPersister->getDeletes()));
  58. $this->assertFalse($this->_unitOfWork->isInIdentityMap($user));
  59. // should no longer be scheduled for insert
  60. $this->assertTrue($this->_unitOfWork->isScheduledForInsert($user));
  61. // Now lets check whether a subsequent commit() does anything
  62. $userPersister->reset();
  63. // Test
  64. $this->_unitOfWork->commit();
  65. // Check.
  66. $this->assertEquals(1, count($userPersister->getInserts()));
  67. $this->assertEquals(0, count($userPersister->getUpdates()));
  68. $this->assertEquals(0, count($userPersister->getDeletes()));
  69. // should have an id
  70. $this->assertTrue(is_numeric($user->id));
  71. }
  72. /**
  73. * Tests a scenario where a save() operation is cascaded from a ForumUser
  74. * to its associated ForumAvatar, both entities using IDENTITY id generation.
  75. */
  76. public function testCascadedIdentityColumnInsert()
  77. {
  78. // Setup fake persister and id generator for identity generation
  79. //ForumUser
  80. $userPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumUser"));
  81. $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumUser', $userPersister);
  82. //$userIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock);
  83. //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumUser', $userIdGeneratorMock);
  84. $userPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY);
  85. // ForumAvatar
  86. $avatarPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\Forum\ForumAvatar"));
  87. $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarPersister);
  88. //$avatarIdGeneratorMock = new IdentityIdGeneratorMock($this->_emMock);
  89. //$this->_emMock->setIdGenerator('Doctrine\Tests\Models\Forum\ForumAvatar', $avatarIdGeneratorMock);
  90. $avatarPersister->setMockIdGeneratorType(\Doctrine\ORM\Mapping\ClassMetadata::GENERATOR_TYPE_IDENTITY);
  91. // Test
  92. $user = new ForumUser();
  93. $user->username = 'romanb';
  94. $avatar = new ForumAvatar();
  95. $user->avatar = $avatar;
  96. $this->_unitOfWork->persist($user); // save cascaded to avatar
  97. $this->_unitOfWork->commit();
  98. $this->assertTrue(is_numeric($user->id));
  99. $this->assertTrue(is_numeric($avatar->id));
  100. $this->assertEquals(1, count($userPersister->getInserts()));
  101. $this->assertEquals(0, count($userPersister->getUpdates()));
  102. $this->assertEquals(0, count($userPersister->getDeletes()));
  103. $this->assertEquals(1, count($avatarPersister->getInserts()));
  104. $this->assertEquals(0, count($avatarPersister->getUpdates()));
  105. $this->assertEquals(0, count($avatarPersister->getDeletes()));
  106. }
  107. public function testChangeTrackingNotify()
  108. {
  109. $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\ORM\NotifyChangedEntity"));
  110. $this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\NotifyChangedEntity', $persister);
  111. $itemPersister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\ORM\NotifyChangedRelatedItem"));
  112. $this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\NotifyChangedRelatedItem', $itemPersister);
  113. $entity = new NotifyChangedEntity;
  114. $entity->setData('thedata');
  115. $this->_unitOfWork->persist($entity);
  116. $this->_unitOfWork->commit();
  117. $this->assertEquals(1, count($persister->getInserts()));
  118. $persister->reset();
  119. $this->assertTrue($this->_unitOfWork->isInIdentityMap($entity));
  120. $entity->setData('newdata');
  121. $entity->setTransient('newtransientvalue');
  122. $this->assertTrue($this->_unitOfWork->isScheduledForDirtyCheck($entity));
  123. $this->assertEquals(array('data' => array('thedata', 'newdata')), $this->_unitOfWork->getEntityChangeSet($entity));
  124. $item = new NotifyChangedRelatedItem();
  125. $entity->getItems()->add($item);
  126. $item->setOwner($entity);
  127. $this->_unitOfWork->persist($item);
  128. $this->_unitOfWork->commit();
  129. $this->assertEquals(1, count($itemPersister->getInserts()));
  130. $persister->reset();
  131. $itemPersister->reset();
  132. $entity->getItems()->removeElement($item);
  133. $item->setOwner(null);
  134. $this->assertTrue($entity->getItems()->isDirty());
  135. $this->_unitOfWork->commit();
  136. $updates = $itemPersister->getUpdates();
  137. $this->assertEquals(1, count($updates));
  138. $this->assertTrue($updates[0] === $item);
  139. }
  140. public function testGetEntityStateOnVersionedEntityWithAssignedIdentifier()
  141. {
  142. $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\ORM\VersionedAssignedIdentifierEntity"));
  143. $this->_unitOfWork->setEntityPersister('Doctrine\Tests\ORM\VersionedAssignedIdentifierEntity', $persister);
  144. $e = new VersionedAssignedIdentifierEntity();
  145. $e->id = 42;
  146. $this->assertEquals(UnitOfWork::STATE_NEW, $this->_unitOfWork->getEntityState($e));
  147. $this->assertFalse($persister->isExistsCalled());
  148. }
  149. public function testGetEntityStateWithAssignedIdentity()
  150. {
  151. $persister = new EntityPersisterMock($this->_emMock, $this->_emMock->getClassMetadata("Doctrine\Tests\Models\CMS\CmsPhonenumber"));
  152. $this->_unitOfWork->setEntityPersister('Doctrine\Tests\Models\CMS\CmsPhonenumber', $persister);
  153. $ph = new \Doctrine\Tests\Models\CMS\CmsPhonenumber();
  154. $ph->phonenumber = '12345';
  155. $this->assertEquals(UnitOfWork::STATE_NEW, $this->_unitOfWork->getEntityState($ph));
  156. $this->assertTrue($persister->isExistsCalled());
  157. $persister->reset();
  158. // if the entity is already managed the exists() check should be skipped
  159. $this->_unitOfWork->registerManaged($ph, array('phonenumber' => '12345'), array());
  160. $this->assertEquals(UnitOfWork::STATE_MANAGED, $this->_unitOfWork->getEntityState($ph));
  161. $this->assertFalse($persister->isExistsCalled());
  162. $ph2 = new \Doctrine\Tests\Models\CMS\CmsPhonenumber();
  163. $ph2->phonenumber = '12345';
  164. $this->assertEquals(UnitOfWork::STATE_DETACHED, $this->_unitOfWork->getEntityState($ph2));
  165. $this->assertFalse($persister->isExistsCalled());
  166. }
  167. }
  168. /**
  169. * @Entity
  170. */
  171. class NotifyChangedEntity implements \Doctrine\Common\NotifyPropertyChanged
  172. {
  173. private $_listeners = array();
  174. /**
  175. * @Id
  176. * @Column(type="integer")
  177. * @GeneratedValue
  178. */
  179. private $id;
  180. /**
  181. * @Column(type="string")
  182. */
  183. private $data;
  184. private $transient; // not persisted
  185. /** @OneToMany(targetEntity="NotifyChangedRelatedItem", mappedBy="owner") */
  186. private $items;
  187. public function __construct() {
  188. $this->items = new \Doctrine\Common\Collections\ArrayCollection;
  189. }
  190. public function getId() {
  191. return $this->id;
  192. }
  193. public function getItems() {
  194. return $this->items;
  195. }
  196. public function setTransient($value) {
  197. if ($value != $this->transient) {
  198. $this->_onPropertyChanged('transient', $this->transient, $value);
  199. $this->transient = $value;
  200. }
  201. }
  202. public function getData() {
  203. return $this->data;
  204. }
  205. public function setData($data) {
  206. if ($data != $this->data) {
  207. $this->_onPropertyChanged('data', $this->data, $data);
  208. $this->data = $data;
  209. }
  210. }
  211. public function addPropertyChangedListener(\Doctrine\Common\PropertyChangedListener $listener)
  212. {
  213. $this->_listeners[] = $listener;
  214. }
  215. protected function _onPropertyChanged($propName, $oldValue, $newValue) {
  216. if ($this->_listeners) {
  217. foreach ($this->_listeners as $listener) {
  218. $listener->propertyChanged($this, $propName, $oldValue, $newValue);
  219. }
  220. }
  221. }
  222. }
  223. /** @Entity */
  224. class NotifyChangedRelatedItem
  225. {
  226. /**
  227. * @Id
  228. * @Column(type="integer")
  229. * @GeneratedValue
  230. */
  231. private $id;
  232. /** @ManyToOne(targetEntity="NotifyChangedEntity", inversedBy="items") */
  233. private $owner;
  234. public function getId() {
  235. return $this->id;
  236. }
  237. public function getOwner() {
  238. return $this->owner;
  239. }
  240. public function setOwner($owner) {
  241. $this->owner = $owner;
  242. }
  243. }
  244. /** @Entity */
  245. class VersionedAssignedIdentifierEntity
  246. {
  247. /**
  248. * @Id @Column(type="integer")
  249. */
  250. public $id;
  251. /**
  252. * @Version @Column(type="integer")
  253. */
  254. public $version;
  255. }