Completed
Pull Request — master (#5662)
by Jeremy
10:23 queued 02:12
created

testMatchingWithOffset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 20
Code Lines 12

Duplication

Lines 0
Ratio 0 %
Metric Value
dl 0
loc 20
rs 9.4285
cc 1
eloc 12
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\ORM\Functional;
4
5
use Doctrine\Common\Collections\Criteria;
6
use Doctrine\Tests\Models\CMS\CmsUser,
7
    Doctrine\Tests\Models\CMS\CmsGroup,
8
    Doctrine\Common\Collections\ArrayCollection;
9
10
/**
11
 * Basic many-to-many association tests.
12
 * ("Working with associations")
13
 *
14
 * @author robo
15
 */
16
class ManyToManyBasicAssociationTest extends \Doctrine\Tests\OrmFunctionalTestCase
17
{
18
    protected function setUp()
19
    {
20
        $this->useModelSet('cms');
21
        parent::setUp();
22
    }
23
24
    public function testUnsetManyToMany()
25
    {
26
        $user = $this->addCmsUserGblancoWithGroups(1);
27
28
        unset($user->groups[0]->users[0]); // inverse side
29
        unset($user->groups[0]); // owning side!
30
31
        $this->_em->flush();
32
33
        // Check that the link in the association table has been deleted
34
        $this->assertGblancoGroupCountIs(0);
35
    }
36
37
    public function testBasicManyToManyJoin()
38
    {
39
        $user = $this->addCmsUserGblancoWithGroups(1);
0 ignored issues
show
Unused Code introduced by
$user is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
40
        $this->_em->clear();
41
42
        $this->assertEquals(0, $this->_em->getUnitOfWork()->size());
43
44
        $query = $this->_em->createQuery("select u, g from Doctrine\Tests\Models\CMS\CmsUser u join u.groups g");
45
46
        $result = $query->getResult();
47
48
        $this->assertEquals(2, $this->_em->getUnitOfWork()->size());
49
        $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]);
50
        $this->assertEquals('Guilherme', $result[0]->name);
51
        $this->assertEquals(1, $result[0]->getGroups()->count());
52
        $groups = $result[0]->getGroups();
53
        $this->assertEquals('Developers_0', $groups[0]->getName());
54
55
        $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($result[0]));
56
        $this->assertEquals(\Doctrine\ORM\UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($groups[0]));
57
58
        $this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $groups);
59
        $this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $groups[0]->getUsers());
60
61
        $groups[0]->getUsers()->clear();
62
        $groups->clear();
63
64
        $this->_em->flush();
65
        $this->_em->clear();
66
67
        $query = $this->_em->createQuery("select u, g from Doctrine\Tests\Models\CMS\CmsUser u join u.groups g");
68
        $this->assertEquals(0, count($query->getResult()));
69
    }
70
71
    public function testManyToManyAddRemove()
72
    {
73
        $user = $this->addCmsUserGblancoWithGroups(2);
74
        $this->_em->clear();
75
76
        $uRep = $this->_em->getRepository(get_class($user));
77
78
        // Get user
79
        $user = $uRep->findOneById($user->getId());
0 ignored issues
show
Documentation Bug introduced by
The method findOneById does not exist on object<Doctrine\ORM\EntityRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
80
81
        $this->assertNotNull($user, "Has to return exactly one entry.");
82
83
        $this->assertFalse($user->getGroups()->isInitialized());
84
85
        // Check groups
86
        $this->assertEquals(2, $user->getGroups()->count());
87
88
        $this->assertTrue($user->getGroups()->isInitialized());
89
90
        // Remove first group
91
        unset($user->groups[0]);
92
        //$user->getGroups()->remove(0);
93
94
        $this->_em->flush();
95
        $this->_em->clear();
96
97
        // Reload same user
98
        $user2 = $uRep->findOneById($user->getId());
0 ignored issues
show
Documentation Bug introduced by
The method findOneById does not exist on object<Doctrine\ORM\EntityRepository>? Since you implemented __call, maybe consider adding a @method annotation.

If you implement __call and you know which methods are available, you can improve IDE auto-completion and static analysis by adding a @method annotation to the class.

This is often the case, when __call is implemented by a parent class and only the child class knows which methods exist:

class ParentClass {
    private $data = array();

    public function __call($method, array $args) {
        if (0 === strpos($method, 'get')) {
            return $this->data[strtolower(substr($method, 3))];
        }

        throw new \LogicException(sprintf('Unsupported method: %s', $method));
    }
}

/**
 * If this class knows which fields exist, you can specify the methods here:
 *
 * @method string getName()
 */
class SomeClass extends ParentClass { }
Loading history...
99
100
        // Check groups
101
        $this->assertEquals(1, $user2->getGroups()->count());
102
    }
103
104
    public function testManyToManyInverseSideIgnored()
105
    {
106
        $user = $this->addCmsUserGblancoWithGroups(0);
107
108
        $group = new CmsGroup;
109
        $group->name = 'Humans';
110
111
        // modify directly, addUser() would also (properly) set the owning side
112
        $group->users[] = $user;
113
114
        $this->_em->persist($user);
115
        $this->_em->persist($group);
116
        $this->_em->flush();
117
        $this->_em->clear();
118
119
        // Association should not exist
120
        $user2 = $this->_em->find(get_class($user), $user->getId());
121
122
        $this->assertNotNull($user2, "Has to return exactly one entry.");
123
        $this->assertEquals(0, $user2->getGroups()->count());
124
    }
125
126
    public function testManyToManyCollectionClearing()
127
    {
128
        $user = $this->addCmsUserGblancoWithGroups($groupCount = 10);
129
130
        // Check that there are indeed 10 links in the association table
131
        $this->assertGblancoGroupCountIs($groupCount);
132
133
        $user->groups->clear();
134
135
        $this->_em->flush();
136
137
        // Check that the links in the association table have been deleted
138
        $this->assertGblancoGroupCountIs(0);
139
    }
140
141
    public function testManyToManyCollectionClearAndAdd()
142
    {
143
        $user = $this->addCmsUserGblancoWithGroups($groupCount = 10);
144
145
        $groups = $user->groups->toArray();
146
        $user->groups->clear();
147
148
        foreach ($groups AS $group) {
149
            $user->groups[] = $group;
150
        }
151
152
        $this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $user->groups);
153
        $this->assertTrue($user->groups->isDirty());
154
155
        $this->assertEquals($groupCount, count($user->groups), "There should be 10 groups in the collection.");
156
157
        $this->_em->flush();
158
159
        $this->assertGblancoGroupCountIs($groupCount);
160
    }
161
162
    /**
163
     * @param int $expectedGroupCount
164
     */
165
    public function assertGblancoGroupCountIs($expectedGroupCount)
166
    {
167
        $countDql = "SELECT count(g.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g WHERE u.username = 'gblanco'";
168
        $this->assertEquals(
169
            $expectedGroupCount,
170
            $this->_em->createQuery($countDql)->getSingleScalarResult(),
171
            "Failed to verify that CmsUser with username 'gblanco' has a group count of 10 with a DQL count query."
172
        );
173
    }
174
175
    public function testRetrieveManyToManyAndAddMore()
176
    {
177
        $user = $this->addCmsUserGblancoWithGroups(2);
178
179
        $group = new CmsGroup();
180
        $group->name = 'Developers_Fresh';
181
        $this->_em->persist($group);
182
        $this->_em->flush();
183
184
        $this->_em->clear();
185
186
        /* @var $freshUser CmsUser */
187
        $freshUser = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $user->getId());
188
        $newGroup = new CmsGroup();
189
        $newGroup->setName('12Monkeys');
190
        $freshUser->addGroup($newGroup);
191
192
        $this->assertFalse($freshUser->groups->isInitialized(), "CmsUser::groups Collection has to be uninitialized for this test.");
0 ignored issues
show
Bug introduced by
The method isInitialized() does not seem to exist on object<Doctrine\Common\C...ctions\ArrayCollection>.

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...
193
194
        $this->_em->flush();
195
196
        $this->assertFalse($freshUser->groups->isInitialized(), "CmsUser::groups Collection has to be uninitialized for this test.");
0 ignored issues
show
Bug introduced by
The method isInitialized() does not seem to exist on object<Doctrine\Common\C...ctions\ArrayCollection>.

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...
197
        $this->assertEquals(3, count($freshUser->getGroups()));
198
        $this->assertEquals(3, count($freshUser->getGroups()->getSnapshot()), "Snapshot of CmsUser::groups should contain 3 entries.");
0 ignored issues
show
Bug introduced by
The method getSnapshot() does not seem to exist on object<Doctrine\Common\C...ctions\ArrayCollection>.

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...
199
200
        $this->_em->clear();
201
202
        $freshUser = $this->_em->find('Doctrine\Tests\Models\CMS\CmsUser', $user->getId());
203
        $this->assertEquals(3, count($freshUser->getGroups()));
204
    }
205
206
    /**
207
     * @group DDC-130
208
     */
209
    public function testRemoveUserWithManyGroups()
210
    {
211
        $user = $this->addCmsUserGblancoWithGroups(2);
212
        $userId = $user->getId();
213
214
        $this->_em->remove($user);
215
        $this->_em->flush();
216
217
        $newUser = $this->_em->find(get_class($user), $userId);
218
        $this->assertNull($newUser);
219
    }
220
221
    /**
222
     * @group DDC-130
223
     */
224
    public function testRemoveGroupWithUser()
225
    {
226
        $user = $this->addCmsUserGblancoWithGroups(2);
227
228
        foreach ($user->getGroups() AS $group) {
0 ignored issues
show
Bug introduced by
The expression $user->getGroups() of type object<Doctrine\Common\C...yCollection>|array|null is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
229
            $this->_em->remove($group);
230
        }
231
        $this->_em->flush();
232
        $this->_em->clear();
233
234
        $newUser = $this->_em->find(get_class($user), $user->getId());
235
        $this->assertEquals(0, count($newUser->getGroups()));
236
    }
237
238
    public function testDereferenceCollectionDelete()
239
    {
240
        $user = $this->addCmsUserGblancoWithGroups(2);
241
        $user->groups = null;
242
243
        $this->_em->flush();
244
        $this->_em->clear();
245
246
        $newUser = $this->_em->find(get_class($user), $user->getId());
247
        $this->assertEquals(0, count($newUser->getGroups()));
248
    }
249
250
    /**
251
     * @group DDC-839
252
     */
253
    public function testWorkWithDqlHydratedEmptyCollection()
254
    {
255
        $user = $this->addCmsUserGblancoWithGroups(0);
256
        $group = new CmsGroup();
257
        $group->name = "Developers0";
258
        $this->_em->persist($group);
259
260
        $this->_em->flush();
261
        $this->_em->clear();
262
263
        $newUser = $this->_em->createQuery('SELECT u, g FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.groups g WHERE u.id = ?1')
264
                             ->setParameter(1, $user->getId())
265
                             ->getSingleResult();
266
        $this->assertEquals(0, count($newUser->groups));
267
        $this->assertInternalType('array', $newUser->groups->getMapping());
268
269
        $newUser->addGroup($group);
270
271
        $this->_em->flush();
272
        $this->_em->clear();
273
274
        $newUser = $this->_em->find(get_class($user), $user->getId());
275
        $this->assertEquals(1, count($newUser->groups));
276
    }
277
278
    /**
279
     * @param  int $groupCount
280
     * @return CmsUser
281
     */
282
    public function addCmsUserGblancoWithGroups($groupCount = 1)
283
    {
284
        $user = new CmsUser;
285
        $user->name = 'Guilherme';
286
        $user->username = 'gblanco';
287
        $user->status = 'developer';
288
289
        for ($i=0; $i < $groupCount; ++$i) {
290
            $group = new CmsGroup;
291
            $group->name = 'Developers_' . $i;
292
            $user->addGroup($group);
293
        }
294
295
        $this->_em->persist($user);
296
        $this->_em->flush();
297
298
        $this->assertNotNull($user->getId(), "User 'gblanco' should have an ID assigned after the persist()/flush() operation.");
299
300
        return $user;
301
    }
302
303
    /**
304
     * @group DDC-980
305
     */
306
    public function testUpdateDeleteSizeSubselectQueries()
307
    {
308
        $this->_em->createQuery("DELETE Doctrine\Tests\Models\CMS\CmsUser u WHERE SIZE(u.groups) = 10")->execute();
309
        $this->_em->createQuery("UPDATE Doctrine\Tests\Models\CMS\CmsUser u SET u.status = 'inactive' WHERE SIZE(u.groups) = 10")->execute();
310
    }
311
312
    /**
313
     * @group DDC-978
314
     */
315
    public function testClearAndResetCollection()
316
    {
317
        $user = $this->addCmsUserGblancoWithGroups(2);
318
        $group1 = new CmsGroup;
319
        $group1->name = 'Developers_New1';
320
        $group2 = new CmsGroup;
321
        $group2->name = 'Developers_New2';
322
323
        $this->_em->persist($group1);
324
        $this->_em->persist($group2);
325
        $this->_em->flush();
326
        $this->_em->clear();
327
328
        $user = $this->_em->find(get_class($user), $user->id);
329
330
        $coll = new ArrayCollection(array($group1, $group2));
331
        $user->groups = $coll;
332
        $this->_em->flush();
333
        $this->assertInstanceOf('Doctrine\ORM\PersistentCollection', $user->groups,
334
            "UnitOfWork should have replaced ArrayCollection with PersistentCollection.");
335
        $this->_em->flush();
336
337
        $this->_em->clear();
338
339
        $user = $this->_em->find(get_class($user), $user->id);
340
        $this->assertEquals(2, count($user->groups));
341
        $this->assertEquals('Developers_New1', $user->groups[0]->name);
342
        $this->assertEquals('Developers_New2', $user->groups[1]->name);
343
    }
344
345
    /**
346
     * @group DDC-733
347
     */
348
    public function testInitializePersistentCollection()
349
    {
350
        $user = $this->addCmsUserGblancoWithGroups(2);
351
        $this->_em->clear();
352
353
        $user = $this->_em->find(get_class($user), $user->id);
354
355
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
356
        $this->_em->getUnitOfWork()->initializeObject($user->groups);
357
        $this->assertTrue($user->groups->isInitialized(), "Collection should be initialized after calling UnitOfWork::initializeObject()");
358
    }
359
360
    /**
361
     * @group DDC-1189
362
     * @group DDC-956
363
     */
364
    public function testClearBeforeLazyLoad()
365
    {
366
        $user = $this->addCmsUserGblancoWithGroups(4);
367
368
        $this->_em->clear();
369
370
        $user = $this->_em->find(get_class($user), $user->id);
371
        $user->groups->clear();
372
        $this->assertEquals(0, count($user->groups));
373
374
        $this->_em->flush();
375
376
        $user = $this->_em->find(get_class($user), $user->id);
377
        $this->assertEquals(0, count($user->groups));
378
    }
379
380
    /**
381
     * @group DDC-3952
382
     */
383
    public function testManyToManyOrderByIsNotIgnored()
384
    {
385
        $user = $this->addCmsUserGblancoWithGroups(1);
386
387
        $group1 = new CmsGroup;
388
        $group2 = new CmsGroup;
389
        $group3 = new CmsGroup;
390
391
        $group1->name = 'C';
392
        $group2->name = 'A';
393
        $group3->name = 'B';
394
395
        $user->addGroup($group1);
396
        $user->addGroup($group2);
397
        $user->addGroup($group3);
398
399
        $this->_em->persist($user);
400
        $this->_em->flush();
401
402
        $this->_em->clear();
403
404
        $user = $this->_em->find(get_class($user), $user->id);
405
406
        $criteria = Criteria::create()
407
            ->orderBy(['name' => Criteria::ASC]);
408
409
        $this->assertEquals(
410
            ['A', 'B', 'C', 'Developers_0'],
411
            $user
412
                ->getGroups()
413
                ->matching($criteria)
414
                ->map(function (CmsGroup $group) {
415
                    return $group->getName();
416
                })
417
                ->toArray()
418
        );
419
    }
420
421
    public function testMatchingWithLimit()
422
    {
423
        $user = $this->addCmsUserGblancoWithGroups(2);
424
        $this->_em->clear();
425
426
        $user = $this->_em->find(get_class($user), $user->id);
427
428
        $groups = $user->groups;
429
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
430
431
        $criteria = Criteria::create()->setMaxResults(1);
432
        $result   = $groups->matching($criteria);
433
434
        $this->assertCount(1, $result);
435
436
        $this->assertFalse($user->groups->isInitialized(), "Post-condition: matching does not initialize collection");
437
    }
438
439
    public function testMatchingWithOffset()
440
    {
441
        $user = $this->addCmsUserGblancoWithGroups(2);
442
        $this->_em->clear();
443
444
        $user = $this->_em->find(get_class($user), $user->id);
445
446
        $groups = $user->groups;
447
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
448
449
        $criteria = Criteria::create()->setFirstResult(1);
450
        $result   = $groups->matching($criteria);
451
452
        $this->assertCount(1, $result);
453
454
        $firstGroup = $result->first();
455
        $this->assertEquals('Developers_1', $firstGroup->name);
456
457
        $this->assertFalse($user->groups->isInitialized(), "Post-condition: matching does not initialize collection");
458
    }
459
460
    public function testMatchingWithLimitAndOffset()
461
    {
462
        $user = $this->addCmsUserGblancoWithGroups(5);
463
        $this->_em->clear();
464
465
        $user = $this->_em->find(get_class($user), $user->id);
466
467
        $groups = $user->groups;
468
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
469
470
        $criteria = Criteria::create()->setFirstResult(1)->setMaxResults(3);
471
        $result   = $groups->matching($criteria);
472
473
        $this->assertCount(3, $result);
474
475
        $firstGroup = $result->first();
476
        $this->assertEquals('Developers_1', $firstGroup->name);
477
478
        $lastGroup = $result->last();
479
        $this->assertEquals('Developers_3', $lastGroup->name);
480
481
        $this->assertFalse($user->groups->isInitialized(), "Post-condition: matching does not initialize collection");
482
    }
483
484
    public function testMatching()
485
    {
486
        $user = $this->addCmsUserGblancoWithGroups(2);
487
        $this->_em->clear();
488
489
        $user = $this->_em->find(get_class($user), $user->id);
490
491
        $groups = $user->groups;
492
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
493
494
        $criteria = Criteria::create()->where(Criteria::expr()->eq('name', (string) 'Developers_0'));
495
        $result   = $groups->matching($criteria);
496
497
        $this->assertCount(1, $result);
498
499
        $firstGroup = $result->first();
500
        $this->assertEquals('Developers_0', $firstGroup->name);
501
502
        $this->assertFalse($user->groups->isInitialized(), "Post-condition: matching does not initialize collection");
503
    }
504
}
505