Completed
Pull Request — 2.7 (#7407)
by COLE
64:31
created

testCollectionValuedAssociationIdentityMapBehaviorWithRefreshQuery()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 51
Code Lines 27

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 27
nc 1
nop 0
dl 0
loc 51
rs 9.488
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

182
        $this->assertFalse($user->getPhonenumbers()->/** @scrutinizer ignore-call */ isDirty());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
183
184
        //external update to CmsAddress
185
        $this->_em->getConnection()->executeUpdate('insert into cms_phonenumbers (phonenumber, user_id) VALUES (?,?)', [999, $user->getId()]
186
        );
187
188
        //select
189
        $q = $this->_em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
190
        $user2 = $q->getSingleResult();
191
192
        $this->assertSame($user, $user2);
193
194
        // Should still be the same 3 phonenumbers
195
        $this->assertEquals(3, count($user2->getPhonenumbers()));
196
197
        // But we want to have this external change!
198
        // Solution 1: refresh().
199
        //$this->_em->refresh($user2); broken atm!
200
        // Solution 2: Alternatively, a refresh query should work
201
        $q = $this->_em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
202
        $q->setHint(Query::HINT_REFRESH, true);
203
        $user3 = $q->getSingleResult();
204
205
        $this->assertSame($user, $user3); // should still be the same, always from identity map
206
207
        // Now the collection should be refreshed with correct count
208
        $this->assertEquals(4, count($user3->getPhonenumbers()));
209
    }
210
211
    public function testCollectionValuedAssociationIdentityMapBehaviorWithRefresh()
212
    {
213
        $user = new CmsUser;
214
        $user->status = 'dev';
215
        $user->username = 'romanb';
216
        $user->name = 'Roman B.';
217
218
        $phone1 = new CmsPhonenumber;
219
        $phone1->phonenumber = 123;
220
221
        $phone2 = new CmsPhonenumber;
222
        $phone2->phonenumber = 234;
223
224
        $phone3 = new CmsPhonenumber;
225
        $phone3->phonenumber = 345;
226
227
        $user->addPhonenumber($phone1);
228
        $user->addPhonenumber($phone2);
229
        $user->addPhonenumber($phone3);
230
231
        $this->_em->persist($user); // cascaded to phone numbers
232
        $this->_em->flush();
233
234
        $this->assertEquals(3, count($user->getPhonenumbers()));
235
236
        //external update to CmsAddress
237
        $this->_em->getConnection()->executeUpdate('insert into cms_phonenumbers (phonenumber, user_id) VALUES (?,?)', [999, $user->getId()]
238
        );
239
240
        //select
241
        $q = $this->_em->createQuery('select u, p from Doctrine\Tests\Models\CMS\CmsUser u join u.phonenumbers p');
242
        $user2 = $q->getSingleResult();
243
244
        $this->assertSame($user, $user2);
245
246
        // Should still be the same 3 phonenumbers
247
        $this->assertEquals(3, count($user2->getPhonenumbers()));
248
249
        // But we want to have this external change!
250
        // Solution 1: refresh().
251
        $this->_em->refresh($user2);
252
253
        $this->assertSame($user, $user2); // should still be the same, always from identity map
254
255
        // Now the collection should be refreshed with correct count
256
        $this->assertEquals(4, count($user2->getPhonenumbers()));
257
    }
258
259
    /**
260
     * @group HashCollision
261
     */
262
    public function testHashCollision() {
263
        $user = new CmsUser();
264
        $user->username = "Test";
265
        $user->name     = "Test";
266
        $this->_em->persist($user);
267
        $this->_em->flush();
268
269
        $articles = [];
270
        for($i=0;$i<100;$i++) {
271
            $article = new CmsArticle();
272
            $article->topic = "Test";
273
            $article->text  = "Test";
274
            $article->setAuthor($this->_em->merge($user));
275
            $this->_em->persist($article);
276
            $this->_em->flush();
277
            $this->_em->clear();
278
            $articles [] = $article;
279
        }
280
281
        $user = $this->_em->merge($user);
282
        foreach($articles as $article) {
283
            $article = $this->_em->merge($article);
284
            $article->setAuthor($user);
285
        }
286
        unset($article);
287
288
        gc_collect_cycles();
289
290
        $keep = [];
291
        for($x=0;$x<1000;$x++) {
292
            $keep[] = $article = new CmsArticle();
293
294
            $article->topic = "Test";
295
            $article->text  = "Test";
296
            $article->setAuthor($this->_em->merge($user));
297
298
            $this->_em->persist($article);
299
            $this->_em->flush();
300
            $this->assertNotNull($article->id, "Article wasn't persisted on iteration $x");
301
        }
302
    }
303
}
304
305