Failed Conditions
Pull Request — master (#6143)
by Luís
10:34
created

testManyToManyOrderByIsNotIgnored()   B

Complexity

Conditions 1
Paths 1

Size

Total Lines 37
Code Lines 25

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 37
c 0
b 0
f 0
rs 8.8571
cc 1
eloc 25
nc 1
nop 0
1
<?php
2
3
namespace Doctrine\Tests\ORM\Functional;
4
5
use Doctrine\Common\Collections\ArrayCollection;
6
use Doctrine\Common\Collections\Criteria;
7
use Doctrine\ORM\PersistentCollection;
8
use Doctrine\ORM\UnitOfWork;
9
use Doctrine\Tests\Models\CMS\CmsGroup;
10
use Doctrine\Tests\Models\CMS\CmsTag;
11
use Doctrine\Tests\Models\CMS\CmsUser;
12
use Doctrine\Tests\OrmFunctionalTestCase;
13
14
/**
15
 * Basic many-to-many association tests.
16
 * ("Working with associations")
17
 *
18
 * @author robo
19
 */
20
class ManyToManyBasicAssociationTest extends OrmFunctionalTestCase
21
{
22
    protected function setUp()
23
    {
24
        $this->useModelSet('cms');
25
        parent::setUp();
26
    }
27
28
    public function testUnsetManyToMany()
29
    {
30
        $user = $this->addCmsUserGblancoWithGroups(1);
31
32
        unset($user->groups[0]->users[0]); // inverse side
33
        unset($user->groups[0]); // owning side!
34
35
        $this->_em->flush();
36
37
        // Check that the link in the association table has been deleted
38
        $this->assertGblancoGroupCountIs(0);
39
    }
40
41
    public function testBasicManyToManyJoin()
42
    {
43
        $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...
44
        $this->_em->clear();
45
46
        $this->assertEquals(0, $this->_em->getUnitOfWork()->size());
47
48
        $query = $this->_em->createQuery("select u, g from Doctrine\Tests\Models\CMS\CmsUser u join u.groups g");
49
50
        $result = $query->getResult();
51
52
        $this->assertEquals(2, $this->_em->getUnitOfWork()->size());
53
        $this->assertInstanceOf(CmsUser::class, $result[0]);
54
        $this->assertEquals('Guilherme', $result[0]->name);
55
        $this->assertEquals(1, $result[0]->getGroups()->count());
56
        $groups = $result[0]->getGroups();
57
        $this->assertEquals('Developers_0', $groups[0]->getName());
58
59
        $this->assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($result[0]));
60
        $this->assertEquals(UnitOfWork::STATE_MANAGED, $this->_em->getUnitOfWork()->getEntityState($groups[0]));
61
62
        $this->assertInstanceOf(PersistentCollection::class, $groups);
63
        $this->assertInstanceOf(PersistentCollection::class, $groups[0]->getUsers());
64
65
        $groups[0]->getUsers()->clear();
66
        $groups->clear();
67
68
        $this->_em->flush();
69
        $this->_em->clear();
70
71
        $query = $this->_em->createQuery("select u, g from Doctrine\Tests\Models\CMS\CmsUser u join u.groups g");
72
        $this->assertEquals(0, count($query->getResult()));
73
    }
74
75
    public function testManyToManyAddRemove()
76
    {
77
        $user = $this->addCmsUserGblancoWithGroups(2);
78
        $this->_em->clear();
79
80
        $uRep = $this->_em->getRepository(get_class($user));
81
82
        // Get user
83
        $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...
84
85
        $this->assertNotNull($user, "Has to return exactly one entry.");
86
87
        $this->assertFalse($user->getGroups()->isInitialized());
88
89
        // Check groups
90
        $this->assertEquals(2, $user->getGroups()->count());
91
92
        $this->assertTrue($user->getGroups()->isInitialized());
93
94
        // Remove first group
95
        unset($user->groups[0]);
96
        //$user->getGroups()->remove(0);
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
97
98
        $this->_em->flush();
99
        $this->_em->clear();
100
101
        // Reload same user
102
        $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...
103
104
        // Check groups
105
        $this->assertEquals(1, $user2->getGroups()->count());
106
    }
107
108
    public function testManyToManyInverseSideIgnored()
109
    {
110
        $user = $this->addCmsUserGblancoWithGroups(0);
111
112
        $group = new CmsGroup;
113
        $group->name = 'Humans';
114
115
        // modify directly, addUser() would also (properly) set the owning side
116
        $group->users[] = $user;
117
118
        $this->_em->persist($user);
119
        $this->_em->persist($group);
120
        $this->_em->flush();
121
        $this->_em->clear();
122
123
        // Association should not exist
124
        $user2 = $this->_em->find(get_class($user), $user->getId());
125
126
        $this->assertNotNull($user2, "Has to return exactly one entry.");
127
        $this->assertEquals(0, $user2->getGroups()->count());
128
    }
129
130
    public function testManyToManyCollectionClearing()
131
    {
132
        $user = $this->addCmsUserGblancoWithGroups($groupCount = 10);
133
134
        // Check that there are indeed 10 links in the association table
135
        $this->assertGblancoGroupCountIs($groupCount);
136
137
        $user->groups->clear();
138
139
        $this->_em->flush();
140
141
        // Check that the links in the association table have been deleted
142
        $this->assertGblancoGroupCountIs(0);
143
    }
144
145
    public function testManyToManyCollectionClearAndAdd()
146
    {
147
        $user = $this->addCmsUserGblancoWithGroups($groupCount = 10);
148
149
        $groups = $user->groups->toArray();
150
        $user->groups->clear();
151
152
        foreach ($groups AS $group) {
153
            $user->groups[] = $group;
154
        }
155
156
        $this->assertInstanceOf(PersistentCollection::class, $user->groups);
157
        $this->assertTrue($user->groups->isDirty());
158
159
        $this->assertEquals($groupCount, count($user->groups), "There should be 10 groups in the collection.");
160
161
        $this->_em->flush();
162
163
        $this->assertGblancoGroupCountIs($groupCount);
164
    }
165
166
    /**
167
     * @param int $expectedGroupCount
168
     */
169
    public function assertGblancoGroupCountIs($expectedGroupCount)
170
    {
171
        $countDql = "SELECT count(g.id) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.groups g WHERE u.username = 'gblanco'";
172
        $this->assertEquals(
173
            $expectedGroupCount,
174
            $this->_em->createQuery($countDql)->getSingleScalarResult(),
175
            "Failed to verify that CmsUser with username 'gblanco' has a group count of 10 with a DQL count query."
176
        );
177
    }
178
179
    public function testRetrieveManyToManyAndAddMore()
180
    {
181
        $user = $this->addCmsUserGblancoWithGroups(2);
182
183
        $group = new CmsGroup();
184
        $group->name = 'Developers_Fresh';
185
        $this->_em->persist($group);
186
        $this->_em->flush();
187
188
        $this->_em->clear();
189
190
        /* @var $freshUser CmsUser */
191
        $freshUser = $this->_em->find(CmsUser::class, $user->getId());
192
        $newGroup = new CmsGroup();
193
        $newGroup->setName('12Monkeys');
194
        $freshUser->addGroup($newGroup);
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
198
        $this->_em->flush();
199
200
        $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...
201
        $this->assertEquals(3, count($freshUser->getGroups()));
202
        $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...
203
204
        $this->_em->clear();
205
206
        $freshUser = $this->_em->find(CmsUser::class, $user->getId());
207
        $this->assertEquals(3, count($freshUser->getGroups()));
208
    }
209
210
    /**
211
     * @group DDC-130
212
     */
213
    public function testRemoveUserWithManyGroups()
214
    {
215
        $user = $this->addCmsUserGblancoWithGroups(2);
216
        $userId = $user->getId();
217
218
        $this->_em->remove($user);
219
        $this->_em->flush();
220
221
        $newUser = $this->_em->find(get_class($user), $userId);
222
        $this->assertNull($newUser);
223
    }
224
225
    /**
226
     * @group DDC-130
227
     */
228
    public function testRemoveGroupWithUser()
229
    {
230
        $user = $this->addCmsUserGblancoWithGroups(2);
231
232
        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...
233
            $this->_em->remove($group);
234
        }
235
        $this->_em->flush();
236
        $this->_em->clear();
237
238
        $newUser = $this->_em->find(get_class($user), $user->getId());
239
        $this->assertEquals(0, count($newUser->getGroups()));
240
    }
241
242
    public function testDereferenceCollectionDelete()
243
    {
244
        $user = $this->addCmsUserGblancoWithGroups(2);
245
        $user->groups = null;
246
247
        $this->_em->flush();
248
        $this->_em->clear();
249
250
        $newUser = $this->_em->find(get_class($user), $user->getId());
251
        $this->assertEquals(0, count($newUser->getGroups()));
252
    }
253
254
    /**
255
     * @group DDC-839
256
     */
257
    public function testWorkWithDqlHydratedEmptyCollection()
258
    {
259
        $user = $this->addCmsUserGblancoWithGroups(0);
260
        $group = new CmsGroup();
261
        $group->name = "Developers0";
262
        $this->_em->persist($group);
263
264
        $this->_em->flush();
265
        $this->_em->clear();
266
267
        $newUser = $this->_em->createQuery('SELECT u, g FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.groups g WHERE u.id = ?1')
268
                             ->setParameter(1, $user->getId())
269
                             ->getSingleResult();
270
        $this->assertEquals(0, count($newUser->groups));
271
        $this->assertInternalType('array', $newUser->groups->getMapping());
272
273
        $newUser->addGroup($group);
274
275
        $this->_em->flush();
276
        $this->_em->clear();
277
278
        $newUser = $this->_em->find(get_class($user), $user->getId());
279
        $this->assertEquals(1, count($newUser->groups));
280
    }
281
282
    /**
283
     * @param  int $groupCount
284
     * @return CmsUser
285
     */
286
    public function addCmsUserGblancoWithGroups($groupCount = 1)
287
    {
288
        $user = new CmsUser;
289
        $user->name = 'Guilherme';
290
        $user->username = 'gblanco';
291
        $user->status = 'developer';
292
293
        for ($i=0; $i < $groupCount; ++$i) {
294
            $group = new CmsGroup;
295
            $group->name = 'Developers_' . $i;
296
            $user->addGroup($group);
297
        }
298
299
        $this->_em->persist($user);
300
        $this->_em->flush();
301
302
        $this->assertNotNull($user->getId(), "User 'gblanco' should have an ID assigned after the persist()/flush() operation.");
303
304
        return $user;
305
    }
306
307
    /**
308
     * @group DDC-978
309
     */
310
    public function testClearAndResetCollection()
311
    {
312
        $user = $this->addCmsUserGblancoWithGroups(2);
313
        $group1 = new CmsGroup;
314
        $group1->name = 'Developers_New1';
315
        $group2 = new CmsGroup;
316
        $group2->name = 'Developers_New2';
317
318
        $this->_em->persist($group1);
319
        $this->_em->persist($group2);
320
        $this->_em->flush();
321
        $this->_em->clear();
322
323
        $user = $this->_em->find(get_class($user), $user->id);
324
325
        $coll = new ArrayCollection([$group1, $group2]);
326
        $user->groups = $coll;
327
        $this->_em->flush();
328
        $this->assertInstanceOf(PersistentCollection::class, $user->groups,
329
            "UnitOfWork should have replaced ArrayCollection with PersistentCollection.");
330
        $this->_em->flush();
331
332
        $this->_em->clear();
333
334
        $user = $this->_em->find(get_class($user), $user->id);
335
        $this->assertEquals(2, count($user->groups));
336
        $this->assertEquals('Developers_New1', $user->groups[0]->name);
337
        $this->assertEquals('Developers_New2', $user->groups[1]->name);
338
    }
339
340
    /**
341
     * @group DDC-733
342
     */
343
    public function testInitializePersistentCollection()
344
    {
345
        $user = $this->addCmsUserGblancoWithGroups(2);
346
        $this->_em->clear();
347
348
        $user = $this->_em->find(get_class($user), $user->id);
349
350
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
351
        $this->_em->getUnitOfWork()->initializeObject($user->groups);
352
        $this->assertTrue($user->groups->isInitialized(), "Collection should be initialized after calling UnitOfWork::initializeObject()");
353
    }
354
355
    /**
356
     * @group DDC-1189
357
     * @group DDC-956
358
     */
359
    public function testClearBeforeLazyLoad()
360
    {
361
        $user = $this->addCmsUserGblancoWithGroups(4);
362
363
        $this->_em->clear();
364
365
        $user = $this->_em->find(get_class($user), $user->id);
366
        $user->groups->clear();
367
        $this->assertEquals(0, count($user->groups));
368
369
        $this->_em->flush();
370
371
        $user = $this->_em->find(get_class($user), $user->id);
372
        $this->assertEquals(0, count($user->groups));
373
    }
374
375
    /**
376
     * @group DDC-3952
377
     */
378
    public function testManyToManyOrderByIsNotIgnored()
379
    {
380
        $user = $this->addCmsUserGblancoWithGroups(1);
381
382
        $group1 = new CmsGroup;
383
        $group2 = new CmsGroup;
384
        $group3 = new CmsGroup;
385
386
        $group1->name = 'C';
387
        $group2->name = 'A';
388
        $group3->name = 'B';
389
390
        $user->addGroup($group1);
391
        $user->addGroup($group2);
392
        $user->addGroup($group3);
393
394
        $this->_em->persist($user);
395
        $this->_em->flush();
396
397
        $this->_em->clear();
398
399
        $user = $this->_em->find(get_class($user), $user->id);
400
401
        $criteria = Criteria::create()
402
            ->orderBy(['name' => Criteria::ASC]);
403
404
        $this->assertEquals(
405
            ['A', 'B', 'C', 'Developers_0'],
406
            $user
407
                ->getGroups()
408
                ->matching($criteria)
409
                ->map(function (CmsGroup $group) {
410
                    return $group->getName();
411
                })
412
                ->toArray()
413
        );
414
    }
415
416
    /**
417
     * @group DDC-3952
418
     */
419
    public function testManyToManyOrderByHonorsFieldNameColumnNameAliases()
420
    {
421
        $user = new CmsUser;
422
        $user->name = 'Guilherme';
423
        $user->username = 'gblanco';
424
        $user->status = 'developer';
425
426
        $tag1 = new CmsTag;
427
        $tag2 = new CmsTag;
428
        $tag3 = new CmsTag;
429
430
        $tag1->name = 'C';
431
        $tag2->name = 'A';
432
        $tag3->name = 'B';
433
434
        $user->addTag($tag1);
435
        $user->addTag($tag2);
436
        $user->addTag($tag3);
437
438
        $this->_em->persist($user);
439
        $this->_em->flush();
440
441
        $this->_em->clear();
442
443
        $user = $this->_em->find(get_class($user), $user->id);
444
445
        $criteria = Criteria::create()
446
            ->orderBy(['name' => Criteria::ASC]);
447
448
        $this->assertEquals(
449
            ['A', 'B', 'C'],
450
            $user
451
                ->getTags()
452
                ->matching($criteria)
453
                ->map(function (CmsTag $tag) {
454
                    return $tag->getName();
455
                })
456
                ->toArray()
457
        );
458
    }
459
460
    public function testMatchingWithLimit()
461
    {
462
        $user = $this->addCmsUserGblancoWithGroups(2);
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()->setMaxResults(1);
471
        $result   = $groups->matching($criteria);
472
473
        $this->assertCount(1, $result);
474
475
        $this->assertFalse($user->groups->isInitialized(), "Post-condition: matching does not initialize collection");
476
    }
477
478 View Code Duplication
    public function testMatchingWithOffset()
479
    {
480
        $user = $this->addCmsUserGblancoWithGroups(2);
481
        $this->_em->clear();
482
483
        $user = $this->_em->find(get_class($user), $user->id);
484
485
        $groups = $user->groups;
486
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
487
488
        $criteria = Criteria::create()->setFirstResult(1);
489
        $result   = $groups->matching($criteria);
490
491
        $this->assertCount(1, $result);
492
493
        $firstGroup = $result->first();
494
        $this->assertEquals('Developers_1', $firstGroup->name);
495
496
        $this->assertFalse($user->groups->isInitialized(), "Post-condition: matching does not initialize collection");
497
    }
498
499
    public function testMatchingWithLimitAndOffset()
500
    {
501
        $user = $this->addCmsUserGblancoWithGroups(5);
502
        $this->_em->clear();
503
504
        $user = $this->_em->find(get_class($user), $user->id);
505
506
        $groups = $user->groups;
507
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
508
509
        $criteria = Criteria::create()->setFirstResult(1)->setMaxResults(3);
510
        $result   = $groups->matching($criteria);
511
512
        $this->assertCount(3, $result);
513
514
        $firstGroup = $result->first();
515
        $this->assertEquals('Developers_1', $firstGroup->name);
516
517
        $lastGroup = $result->last();
518
        $this->assertEquals('Developers_3', $lastGroup->name);
519
520
        $this->assertFalse($user->groups->isInitialized(), "Post-condition: matching does not initialize collection");
521
    }
522
523 View Code Duplication
    public function testMatching()
524
    {
525
        $user = $this->addCmsUserGblancoWithGroups(2);
526
        $this->_em->clear();
527
528
        $user = $this->_em->find(get_class($user), $user->id);
529
530
        $groups = $user->groups;
531
        $this->assertFalse($user->groups->isInitialized(), "Pre-condition: lazy collection");
532
533
        $criteria = Criteria::create()->where(Criteria::expr()->eq('name', (string) 'Developers_0'));
534
        $result   = $groups->matching($criteria);
535
536
        $this->assertCount(1, $result);
537
538
        $firstGroup = $result->first();
539
        $this->assertEquals('Developers_0', $firstGroup->name);
540
541
        $this->assertFalse($user->groups->isInitialized(), "Post-condition: matching does not initialize collection");
542
    }
543
}
544