/tests/Doctrine/Tests/ORM/Functional/IdentityMapTest.php

https://github.com/jaikdean/doctrine2 · PHP · 255 lines · 158 code · 63 blank · 34 comment · 0 complexity · 076fc8d12cb07c79c0192004dc5d2d70 MD5 · raw file

  1. <?php
  2. declare(strict_types=1);
  3. namespace Doctrine\Tests\ORM\Functional;
  4. use Doctrine\ORM\Query;
  5. use Doctrine\Tests\Models\CMS\CmsAddress;
  6. use Doctrine\Tests\Models\CMS\CmsPhonenumber;
  7. use Doctrine\Tests\Models\CMS\CmsUser;
  8. use Doctrine\Tests\OrmFunctionalTestCase;
  9. use function get_class;
  10. /**
  11. * IdentityMapTest
  12. *
  13. * Tests correct behavior and usage of the identity map. Local values and associations
  14. * that are already fetched always prevail, unless explicitly refreshed.
  15. */
  16. class IdentityMapTest extends OrmFunctionalTestCase
  17. {
  18. protected function setUp() : void
  19. {
  20. $this->useModelSet('cms');
  21. parent::setUp();
  22. }
  23. public function testBasicIdentityManagement() : void
  24. {
  25. $user = new CmsUser();
  26. $user->status = 'dev';
  27. $user->username = 'romanb';
  28. $user->name = 'Roman B.';
  29. $address = new CmsAddress();
  30. $address->country = 'de';
  31. $address->zip = 1234;
  32. $address->city = 'Berlin';
  33. $user->setAddress($address);
  34. $this->em->persist($user);
  35. $this->em->flush();
  36. $this->em->clear();
  37. $user2 = $this->em->find(get_class($user), $user->getId());
  38. self::assertNotSame($user2, $user);
  39. $user3 = $this->em->find(get_class($user), $user->getId());
  40. self::assertSame($user2, $user3);
  41. $address2 = $this->em->find(get_class($address), $address->getId());
  42. self::assertNotSame($address2, $address);
  43. $address3 = $this->em->find(get_class($address), $address->getId());
  44. self::assertSame($address2, $address3);
  45. self::assertSame($user2->getAddress(), $address2); // !!!
  46. }
  47. public function testSingleValuedAssociationIdentityMapBehaviorWithRefresh() : void
  48. {
  49. $address = new CmsAddress();
  50. $address->country = 'de';
  51. $address->zip = '12345';
  52. $address->city = 'Berlin';
  53. $user1 = new CmsUser();
  54. $user1->status = 'dev';
  55. $user1->username = 'romanb';
  56. $user1->name = 'Roman B.';
  57. $user2 = new CmsUser();
  58. $user2->status = 'dev';
  59. $user2->username = 'gblanco';
  60. $user2->name = 'Guilherme Blanco';
  61. $address->setUser($user1);
  62. $this->em->persist($address);
  63. $this->em->persist($user1);
  64. $this->em->persist($user2);
  65. $this->em->flush();
  66. self::assertSame($user1, $address->user);
  67. //external update to CmsAddress
  68. $this->em->getConnection()->executeUpdate('update cms_addresses set user_id = ?', [$user2->getId()]);
  69. // But we want to have this external change!
  70. // Solution 1: refresh(), broken atm!
  71. $this->em->refresh($address);
  72. // Now the association should be "correct", referencing $user2
  73. self::assertSame($user2, $address->user);
  74. self::assertSame($user2->address, $address); // check back reference also
  75. // Attention! refreshes can result in broken bidirectional associations! this is currently expected!
  76. // $user1 still points to $address!
  77. self::assertSame($user1->address, $address);
  78. }
  79. public function testSingleValuedAssociationIdentityMapBehaviorWithRefreshQuery() : void
  80. {
  81. $address = new CmsAddress();
  82. $address->country = 'de';
  83. $address->zip = '12345';
  84. $address->city = 'Berlin';
  85. $user1 = new CmsUser();
  86. $user1->status = 'dev';
  87. $user1->username = 'romanb';
  88. $user1->name = 'Roman B.';
  89. $user2 = new CmsUser();
  90. $user2->status = 'dev';
  91. $user2->username = 'gblanco';
  92. $user2->name = 'Guilherme Blanco';
  93. $address->setUser($user1);
  94. $this->em->persist($address);
  95. $this->em->persist($user1);
  96. $this->em->persist($user2);
  97. $this->em->flush();
  98. self::assertSame($user1, $address->user);
  99. //external update to CmsAddress
  100. $this->em->getConnection()->executeUpdate('update cms_addresses set user_id = ?', [$user2->getId()]);
  101. //select
  102. $q = $this->em->createQuery('select a, u from Doctrine\Tests\Models\CMS\CmsAddress a join a.user u');
  103. $address2 = $q->getSingleResult();
  104. self::assertSame($address, $address2);
  105. // Should still be $user1
  106. self::assertSame($user1, $address2->user);
  107. self::assertNull($user2->address);
  108. // But we want to have this external change!
  109. // Solution 2: Alternatively, a refresh query should work
  110. $q = $this->em->createQuery('select a, u from Doctrine\Tests\Models\CMS\CmsAddress a join a.user u');
  111. $q->setHint(Query::HINT_REFRESH, true);
  112. $address3 = $q->getSingleResult();
  113. self::assertSame($address, $address3); // should still be the same, always from identity map
  114. // Now the association should be "correct", referencing $user2
  115. self::assertSame($user2, $address2->user);
  116. self::assertSame($user2->address, $address2); // check back reference also
  117. // Attention! refreshes can result in broken bidirectional associations! this is currently expected!
  118. // $user1 still points to $address2!
  119. self::assertSame($user1->address, $address2);
  120. }
  121. public function testCollectionValuedAssociationIdentityMapBehaviorWithRefreshQuery() : void
  122. {
  123. $user = new CmsUser();
  124. $user->status = 'dev';
  125. $user->username = 'romanb';
  126. $user->name = 'Roman B.';
  127. $phone1 = new CmsPhonenumber();
  128. $phone1->phonenumber = 123;
  129. $phone2 = new CmsPhonenumber();
  130. $phone2->phonenumber = 234;
  131. $phone3 = new CmsPhonenumber();
  132. $phone3->phonenumber = 345;
  133. $user->addPhonenumber($phone1);
  134. $user->addPhonenumber($phone2);
  135. $user->addPhonenumber($phone3);
  136. $this->em->persist($user); // cascaded to phone numbers
  137. $this->em->flush();
  138. self::assertCount(3, $user->getPhonenumbers());
  139. self::assertFalse($user->getPhonenumbers()->isDirty());
  140. //external update to CmsAddress
  141. $this->em->getConnection()->executeUpdate('insert into cms_phonenumbers (phonenumber, user_id) VALUES (?,?)', [999, $user->getId()]);
  142. //select
  143. $q = $this->em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
  144. $user2 = $q->getSingleResult();
  145. self::assertSame($user, $user2);
  146. // Should still be the same 3 phonenumbers
  147. self::assertCount(3, $user2->getPhonenumbers());
  148. // But we want to have this external change!
  149. // Solution 1: refresh().
  150. //$this->em->refresh($user2); broken atm!
  151. // Solution 2: Alternatively, a refresh query should work
  152. $q = $this->em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
  153. $q->setHint(Query::HINT_REFRESH, true);
  154. $user3 = $q->getSingleResult();
  155. self::assertSame($user, $user3); // should still be the same, always from identity map
  156. // Now the collection should be refreshed with correct count
  157. self::assertCount(4, $user3->getPhonenumbers());
  158. }
  159. public function testCollectionValuedAssociationIdentityMapBehaviorWithRefresh() : void
  160. {
  161. $user = new CmsUser();
  162. $user->status = 'dev';
  163. $user->username = 'romanb';
  164. $user->name = 'Roman B.';
  165. $phone1 = new CmsPhonenumber();
  166. $phone1->phonenumber = 123;
  167. $phone2 = new CmsPhonenumber();
  168. $phone2->phonenumber = 234;
  169. $phone3 = new CmsPhonenumber();
  170. $phone3->phonenumber = 345;
  171. $user->addPhonenumber($phone1);
  172. $user->addPhonenumber($phone2);
  173. $user->addPhonenumber($phone3);
  174. $this->em->persist($user); // cascaded to phone numbers
  175. $this->em->flush();
  176. self::assertCount(3, $user->getPhonenumbers());
  177. //external update to CmsAddress
  178. $this->em->getConnection()->executeUpdate('insert into cms_phonenumbers (phonenumber, user_id) VALUES (?,?)', [999, $user->getId()]);
  179. //select
  180. $q = $this->em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
  181. $user2 = $q->getSingleResult();
  182. self::assertSame($user, $user2);
  183. // Should still be the same 3 phonenumbers
  184. self::assertCount(3, $user2->getPhonenumbers());
  185. // But we want to have this external change!
  186. // Solution 1: refresh().
  187. $this->em->refresh($user2);
  188. self::assertSame($user, $user2); // should still be the same, always from identity map
  189. // Now the collection should be refreshed with correct count
  190. self::assertCount(4, $user2->getPhonenumbers());
  191. }
  192. }