Rajout de doctrine/orm
[zf2.biz/galerie.git] / vendor / doctrine / orm / tests / Doctrine / Tests / ORM / Functional / IdentityMapTest.php
1 <?php
2
3 namespace Doctrine\Tests\ORM\Functional;
4
5 use Doctrine\Tests\Models\CMS\CmsUser,
6     Doctrine\Tests\Models\CMS\CmsAddress,
7     Doctrine\Tests\Models\CMS\CmsPhonenumber,
8     Doctrine\ORM\Query;
9
10 require_once __DIR__ . '/../../TestInit.php';
11
12 /**
13  * IdentityMapTest
14  *
15  * Tests correct behavior and usage of the identity map. Local values and associations
16  * that are already fetched always prevail, unless explicitly refreshed.
17  *
18  * @author Roman Borschel <roman@code-factory.org>
19  */
20 class IdentityMapTest extends \Doctrine\Tests\OrmFunctionalTestCase
21 {
22     protected function setUp() {
23         $this->useModelSet('cms');
24         parent::setUp();
25     }
26
27     public function testBasicIdentityManagement()
28     {
29         $user = new CmsUser;
30         $user->status = 'dev';
31         $user->username = 'romanb';
32         $user->name = 'Roman B.';
33
34         $address = new CmsAddress;
35         $address->country = 'de';
36         $address->zip = 1234;
37         $address->city = 'Berlin';
38
39         $user->setAddress($address);
40
41         $this->_em->persist($user);
42         $this->_em->flush();
43         $this->_em->clear();
44
45         $user2 = $this->_em->find(get_class($user), $user->getId());
46         $this->assertTrue($user2 !== $user);
47         $user3 = $this->_em->find(get_class($user), $user->getId());
48         $this->assertTrue($user2 === $user3);
49
50         $address2 = $this->_em->find(get_class($address), $address->getId());
51         $this->assertTrue($address2 !== $address);
52         $address3 = $this->_em->find(get_class($address), $address->getId());
53         $this->assertTrue($address2 === $address3);
54
55         $this->assertTrue($user2->getAddress() === $address2); // !!!
56     }
57
58     public function testSingleValuedAssociationIdentityMapBehaviorWithRefresh()
59     {
60         $address = new CmsAddress;
61         $address->country = 'de';
62         $address->zip = '12345';
63         $address->city = 'Berlin';
64
65         $user1 = new CmsUser;
66         $user1->status = 'dev';
67         $user1->username = 'romanb';
68         $user1->name = 'Roman B.';
69
70         $user2 = new CmsUser;
71         $user2->status = 'dev';
72         $user2->username = 'gblanco';
73         $user2->name = 'Guilherme Blanco';
74
75         $address->setUser($user1);
76
77         $this->_em->persist($address);
78         $this->_em->persist($user1);
79         $this->_em->persist($user2);
80         $this->_em->flush();
81
82         $this->assertSame($user1, $address->user);
83
84         //external update to CmsAddress
85         $this->_em->getConnection()->executeUpdate('update cms_addresses set user_id = ?', array($user2->getId()));
86
87         // But we want to have this external change!
88         // Solution 1: refresh(), broken atm!
89         $this->_em->refresh($address);
90
91         // Now the association should be "correct", referencing $user2
92         $this->assertSame($user2, $address->user);
93         $this->assertSame($user2->address, $address); // check back reference also
94
95         // Attention! refreshes can result in broken bidirectional associations! this is currently expected!
96         // $user1 still points to $address!
97         $this->assertSame($user1->address, $address);
98     }
99
100     public function testSingleValuedAssociationIdentityMapBehaviorWithRefreshQuery()
101     {
102         $address = new CmsAddress;
103         $address->country = 'de';
104         $address->zip = '12345';
105         $address->city = 'Berlin';
106
107         $user1 = new CmsUser;
108         $user1->status = 'dev';
109         $user1->username = 'romanb';
110         $user1->name = 'Roman B.';
111
112         $user2 = new CmsUser;
113         $user2->status = 'dev';
114         $user2->username = 'gblanco';
115         $user2->name = 'Guilherme Blanco';
116
117         $address->setUser($user1);
118
119         $this->_em->persist($address);
120         $this->_em->persist($user1);
121         $this->_em->persist($user2);
122         $this->_em->flush();
123
124
125         $this->assertSame($user1, $address->user);
126
127         //external update to CmsAddress
128         $this->_em->getConnection()->executeUpdate('update cms_addresses set user_id = ?', array($user2->getId()));
129
130         //select
131         $q = $this->_em->createQuery('select a, u from Doctrine\Tests\Models\CMS\CmsAddress a join a.user u');
132         $address2 = $q->getSingleResult();
133
134         $this->assertSame($address, $address2);
135
136         // Should still be $user1
137         $this->assertSame($user1, $address2->user);
138         $this->assertTrue($user2->address === null);
139
140         // But we want to have this external change!
141         // Solution 2: Alternatively, a refresh query should work
142         $q = $this->_em->createQuery('select a, u from Doctrine\Tests\Models\CMS\CmsAddress a join a.user u');
143         $q->setHint(Query::HINT_REFRESH, true);
144         $address3 = $q->getSingleResult();
145
146         $this->assertSame($address, $address3); // should still be the same, always from identity map
147
148         // Now the association should be "correct", referencing $user2
149         $this->assertSame($user2, $address2->user);
150         $this->assertSame($user2->address, $address2); // check back reference also
151
152         // Attention! refreshes can result in broken bidirectional associations! this is currently expected!
153         // $user1 still points to $address2!
154         $this->assertSame($user1->address, $address2);
155     }
156
157     public function testCollectionValuedAssociationIdentityMapBehaviorWithRefreshQuery()
158     {
159         $user = new CmsUser;
160         $user->status = 'dev';
161         $user->username = 'romanb';
162         $user->name = 'Roman B.';
163
164         $phone1 = new CmsPhonenumber;
165         $phone1->phonenumber = 123;
166
167         $phone2 = new CmsPhonenumber;
168         $phone2->phonenumber = 234;
169
170         $phone3 = new CmsPhonenumber;
171         $phone3->phonenumber = 345;
172
173         $user->addPhonenumber($phone1);
174         $user->addPhonenumber($phone2);
175         $user->addPhonenumber($phone3);
176
177         $this->_em->persist($user); // cascaded to phone numbers
178         $this->_em->flush();
179
180         $this->assertEquals(3, count($user->getPhonenumbers()));
181         $this->assertFalse($user->getPhonenumbers()->isDirty());
182
183         //external update to CmsAddress
184         $this->_em->getConnection()->executeUpdate('insert into cms_phonenumbers (phonenumber, user_id) VALUES (?,?)', array(999, $user->getId()));
185
186         //select
187         $q = $this->_em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
188         $user2 = $q->getSingleResult();
189
190         $this->assertSame($user, $user2);
191
192         // Should still be the same 3 phonenumbers
193         $this->assertEquals(3, count($user2->getPhonenumbers()));
194
195         // But we want to have this external change!
196         // Solution 1: refresh().
197         //$this->_em->refresh($user2); broken atm!
198         // Solution 2: Alternatively, a refresh query should work
199         $q = $this->_em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
200         $q->setHint(Query::HINT_REFRESH, true);
201         $user3 = $q->getSingleResult();
202
203         $this->assertSame($user, $user3); // should still be the same, always from identity map
204
205         // Now the collection should be refreshed with correct count
206         $this->assertEquals(4, count($user3->getPhonenumbers()));
207     }
208
209     public function testCollectionValuedAssociationIdentityMapBehaviorWithRefresh()
210     {
211         $user = new CmsUser;
212         $user->status = 'dev';
213         $user->username = 'romanb';
214         $user->name = 'Roman B.';
215
216         $phone1 = new CmsPhonenumber;
217         $phone1->phonenumber = 123;
218
219         $phone2 = new CmsPhonenumber;
220         $phone2->phonenumber = 234;
221
222         $phone3 = new CmsPhonenumber;
223         $phone3->phonenumber = 345;
224
225         $user->addPhonenumber($phone1);
226         $user->addPhonenumber($phone2);
227         $user->addPhonenumber($phone3);
228
229         $this->_em->persist($user); // cascaded to phone numbers
230         $this->_em->flush();
231
232         $this->assertEquals(3, count($user->getPhonenumbers()));
233
234         //external update to CmsAddress
235         $this->_em->getConnection()->executeUpdate('insert into cms_phonenumbers (phonenumber, user_id) VALUES (?,?)', array(999, $user->getId()));
236
237         //select
238         $q = $this->_em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
239         $user2 = $q->getSingleResult();
240
241         $this->assertSame($user, $user2);
242
243         // Should still be the same 3 phonenumbers
244         $this->assertEquals(3, count($user2->getPhonenumbers()));
245
246         // But we want to have this external change!
247         // Solution 1: refresh().
248         $this->_em->refresh($user2);
249
250         $this->assertSame($user, $user2); // should still be the same, always from identity map
251
252         // Now the collection should be refreshed with correct count
253         $this->assertEquals(4, count($user2->getPhonenumbers()));
254     }
255
256     public function testReusedSplObjectHashDoesNotConfuseUnitOfWork()
257     {
258         $hash1 = $this->subRoutine($this->_em);
259         // Make sure cycles are collected NOW, because a PersistentCollection references
260         // its owner, hence without forcing gc on cycles now the object will not (yet)
261         // be garbage collected and thus the object hash is not reused.
262         // This is not a memory leak!
263         gc_collect_cycles();
264
265         $user1 = new CmsUser;
266         $user1->status = 'dev';
267         $user1->username = 'jwage';
268         $user1->name = 'Jonathan W.';
269         $hash2 = spl_object_hash($user1);
270         $this->assertEquals($hash1, $hash2); // Hash reused!
271         $this->_em->persist($user1);
272         $this->_em->flush();
273     }
274
275     private function subRoutine($em) {
276         $user = new CmsUser;
277         $user->status = 'dev';
278         $user->username = 'romanb';
279         $user->name = 'Roman B.';
280         $em->persist($user);
281         $em->flush();
282         $em->remove($user);
283         $em->flush();
284
285         return spl_object_hash($user);
286     }
287 }
288