Passed
Push — pulls/manymanylist-add-callbac... ( aed064...34c05d )
by Ingo
13:26
created

testRemoveCallbackOnRemoveAll()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
cc 1
eloc 8
c 2
b 0
f 1
nc 1
nop 0
dl 0
loc 14
rs 10
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use SilverStripe\Core\Config\Config;
6
use SilverStripe\Dev\SapphireTest;
7
use SilverStripe\ORM\DataObject;
8
use SilverStripe\ORM\ManyManyThroughList;
9
use SilverStripe\ORM\Tests\DataObjectTest\Player;
10
use SilverStripe\ORM\Tests\DataObjectTest\Team;
11
use SilverStripe\ORM\Tests\ManyManyThroughListTest\Item;
12
use SilverStripe\ORM\Tests\ManyManyThroughListTest\PolyItem;
13
use SilverStripe\ORM\Tests\ManyManyThroughListTest\PolyJoinObject;
14
use SilverStripe\ORM\Tests\ManyManyThroughListTest\Locale;
15
use SilverStripe\ORM\Tests\ManyManyThroughListTest\FallbackLocale;
16
use SilverStripe\ORM\Tests\ManyManyThroughListTest\TestObject;
17
18
class ManyManyThroughListTest extends SapphireTest
19
{
20
    protected static $fixture_file = 'ManyManyThroughListTest.yml';
21
22
    protected static $extra_dataobjects = [
23
        ManyManyThroughListTest\Item::class,
24
        ManyManyThroughListTest\JoinObject::class,
25
        ManyManyThroughListTest\TestObject::class,
26
        ManyManyThroughListTest\PolyItem::class,
27
        ManyManyThroughListTest\PolyJoinObject::class,
28
        ManyManyThroughListTest\PolyObjectA::class,
29
        ManyManyThroughListTest\PolyObjectB::class,
30
        ManyManyThroughListTest\Locale::class,
31
        ManyManyThroughListTest\FallbackLocale::class,
32
    ];
33
34
    protected function setUp()
35
    {
36
        parent::setUp();
37
        DataObject::reset();
38
    }
39
40
    protected function tearDown()
41
    {
42
        DataObject::reset();
43
        parent::tearDown();
44
    }
45
46
    public function testSelectJoin()
47
    {
48
        /** @var ManyManyThroughListTest\TestObject $parent */
49
        $parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
50
        $this->assertListEquals(
51
            [
52
                ['Title' => 'item 1'],
53
                ['Title' => 'item 2']
54
            ],
55
            $parent->Items()
56
        );
57
        // Check filters on list work
58
        $item1 = $parent->Items()->filter('Title', 'item 1')->first();
59
        $this->assertNotNull($item1);
60
        $this->assertNotNull($item1->getJoin());
61
        $this->assertEquals('join 1', $item1->getJoin()->Title);
62
        $this->assertInstanceOf(
63
            ManyManyThroughListTest\JoinObject::class,
64
            $item1->ManyManyThroughListTest_JoinObject
0 ignored issues
show
Bug Best Practice introduced by
The property ManyManyThroughListTest_JoinObject does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
65
        );
66
        $this->assertEquals('join 1', $item1->ManyManyThroughListTest_JoinObject->Title);
67
68
        // Check filters on list work
69
        $item2 = $parent->Items()->filter('Title', 'item 2')->first();
70
        $this->assertNotNull($item2);
71
        $this->assertNotNull($item2->getJoin());
72
        $this->assertEquals('join 2', $item2->getJoin()->Title);
73
        $this->assertEquals('join 2', $item2->ManyManyThroughListTest_JoinObject->Title);
74
75
        // To filter on join table need to use some raw sql
76
        $item2 = $parent->Items()->where(['"ManyManyThroughListTest_JoinObject"."Title"' => 'join 2'])->first();
77
        $this->assertNotNull($item2);
78
        $this->assertEquals('item 2', $item2->Title);
79
        $this->assertNotNull($item2->getJoin());
80
        $this->assertEquals('join 2', $item2->getJoin()->Title);
81
        $this->assertEquals('join 2', $item2->ManyManyThroughListTest_JoinObject->Title);
82
83
        // Check that the join record is set for new records added
84
        $item3 = new Item;
85
        $this->assertNull($item3->getJoin());
86
        $parent->Items()->add($item3);
87
        $expectedJoinObject = ManyManyThroughListTest\JoinObject::get()->filter(['ParentID' => $parent->ID, 'ChildID' => $item3->ID ])->first();
88
        $this->assertEquals($expectedJoinObject->ID, $item3->getJoin()->ID);
89
        $this->assertEquals(get_class($expectedJoinObject), get_class($item3->getJoin()));
90
    }
91
92
    /**
93
     * @param string $sort
94
     * @param array $expected
95
     * @dataProvider sortingProvider
96
     */
97
    public function testSorting($sort, $expected)
98
    {
99
        /** @var ManyManyThroughListTest\TestObject $parent */
100
        $parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
101
102
        $items = $parent->Items();
103
        if ($sort) {
104
            $items = $items->sort($sort);
105
        }
106
        $this->assertSame($expected, $items->column('Title'));
107
    }
108
109
    /**
110
     * @return array[]
111
     */
112
    public function sortingProvider()
113
    {
114
        return [
115
            'nothing passed (default)' => [
116
                null,
117
                ['item 2', 'item 1'],
118
            ],
119
            'table with default column' => [
120
                '"ManyManyThroughListTest_JoinObject"."Sort"',
121
                ['item 2', 'item 1'],
122
            ],
123
            'table with default column ascending' => [
124
                '"ManyManyThroughListTest_JoinObject"."Sort" ASC',
125
                ['item 2', 'item 1'],
126
            ],
127
            'table with default column descending' => [
128
                '"ManyManyThroughListTest_JoinObject"."Sort" DESC',
129
                ['item 1', 'item 2'],
130
            ],
131
            'table with column descending' => [
132
                '"ManyManyThroughListTest_JoinObject"."Title" DESC',
133
                ['item 2', 'item 1'],
134
            ],
135
            'table with column ascending' => [
136
                '"ManyManyThroughListTest_JoinObject"."Title" ASC',
137
                ['item 1', 'item 2'],
138
            ],
139
            'default column' => [
140
                '"Sort"',
141
                ['item 2', 'item 1'],
142
            ],
143
            'default column ascending' => [
144
                '"Sort" ASC',
145
                ['item 2', 'item 1'],
146
            ],
147
            'default column descending' => [
148
                '"Sort" DESC',
149
                ['item 1', 'item 2'],
150
            ],
151
            'column descending' => [
152
                '"Title" DESC',
153
                ['item 2', 'item 1'],
154
            ],
155
            'column ascending' => [
156
                '"Title" ASC',
157
                ['item 1', 'item 2'],
158
            ],
159
        ];
160
    }
161
162
    public function testAdd()
163
    {
164
        /** @var ManyManyThroughListTest\TestObject $parent */
165
        $parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
166
        $newItem = new ManyManyThroughListTest\Item();
167
        $newItem->Title = 'my new item';
168
        $newItem->write();
169
        $parent->Items()->add($newItem, ['Title' => 'new join record']);
170
171
        // Check select
172
        $newItem = $parent->Items()->filter(['Title' => 'my new item'])->first();
173
        $this->assertNotNull($newItem);
174
        $this->assertEquals('my new item', $newItem->Title);
175
        $this->assertInstanceOf(
176
            ManyManyThroughListTest\JoinObject::class,
177
            $newItem->getJoin()
178
        );
179
        $this->assertInstanceOf(
180
            ManyManyThroughListTest\JoinObject::class,
181
            $newItem->ManyManyThroughListTest_JoinObject
0 ignored issues
show
Bug Best Practice introduced by
The property ManyManyThroughListTest_JoinObject does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
182
        );
183
        $this->assertEquals('new join record', $newItem->ManyManyThroughListTest_JoinObject->Title);
184
    }
185
186
    public function testRemove()
187
    {
188
        /** @var ManyManyThroughListTest\TestObject $parent */
189
        $parent = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
190
        $this->assertListEquals(
191
            [
192
                ['Title' => 'item 1'],
193
                ['Title' => 'item 2']
194
            ],
195
            $parent->Items()
196
        );
197
        $item1 = $parent->Items()->filter(['Title' => 'item 1'])->first();
198
        $parent->Items()->remove($item1);
199
        $this->assertListEquals(
200
            [['Title' => 'item 2']],
201
            $parent->Items()
202
        );
203
    }
204
205
    /**
206
     * Test validation
207
     *
208
     * @expectedException \InvalidArgumentException
209
     */
210
    public function testValidateModelValidatesJoinType()
211
    {
212
        DataObject::reset();
213
        ManyManyThroughListTest\Item::config()->update(
214
            'db',
215
            [
216
            ManyManyThroughListTest\JoinObject::class => 'Text'
217
            ]
218
        );
219
220
        DataObject::getSchema()->manyManyComponent(ManyManyThroughListTest\TestObject::class, 'Items');
221
    }
222
223
    public function testRelationParsing()
224
    {
225
        $schema = DataObject::getSchema();
226
227
        // Parent components
228
        $this->assertEquals(
229
            [
230
                'relationClass' => ManyManyThroughList::class,
231
                'parentClass' => ManyManyThroughListTest\TestObject::class,
232
                'childClass' => ManyManyThroughListTest\Item::class,
233
                'parentField' => 'ParentID',
234
                'childField' => 'ChildID',
235
                'join' => ManyManyThroughListTest\JoinObject::class
236
            ],
237
            $schema->manyManyComponent(ManyManyThroughListTest\TestObject::class, 'Items')
238
        );
239
240
        // Belongs_many_many is the same, but with parent/child substituted
241
        $this->assertEquals(
242
            [
243
                'relationClass' => ManyManyThroughList::class,
244
                'parentClass' => ManyManyThroughListTest\Item::class,
245
                'childClass' => ManyManyThroughListTest\TestObject::class,
246
                'parentField' => 'ChildID',
247
                'childField' => 'ParentID',
248
                'join' => ManyManyThroughListTest\JoinObject::class
249
            ],
250
            $schema->manyManyComponent(ManyManyThroughListTest\Item::class, 'Objects')
251
        );
252
    }
253
254
    /**
255
     * Note: polymorphic many_many support is currently experimental
256
     */
257
    public function testPolymorphicManyMany()
258
    {
259
        /** @var ManyManyThroughListTest\PolyObjectA $objA1 */
260
        $objA1 = $this->objFromFixture(ManyManyThroughListTest\PolyObjectA::class, 'obja1');
261
        /** @var ManyManyThroughListTest\PolyObjectB $objB1 */
262
        $objB1 = $this->objFromFixture(ManyManyThroughListTest\PolyObjectB::class, 'objb1');
263
        /** @var ManyManyThroughListTest\PolyObjectB $objB2 */
264
        $objB2 = $this->objFromFixture(ManyManyThroughListTest\PolyObjectB::class, 'objb2');
265
266
        // Test various parent class queries
267
        $this->assertListEquals([
268
            ['Title' => 'item 1'],
269
            ['Title' => 'item 2'],
270
        ], $objA1->Items());
271
        $this->assertListEquals([
272
            ['Title' => 'item 2'],
273
        ], $objB1->Items());
274
        $this->assertListEquals([
275
            ['Title' => 'item 2'],
276
        ], $objB2->Items());
277
278
        // Test adding items
279
        $newItem = new PolyItem();
280
        $newItem->Title = 'New Item';
281
        $objA1->Items()->add($newItem);
282
        $objB2->Items()->add($newItem);
283
        $this->assertListEquals([
284
            ['Title' => 'item 1'],
285
            ['Title' => 'item 2'],
286
            ['Title' => 'New Item'],
287
        ], $objA1->Items());
288
        $this->assertListEquals([
289
            ['Title' => 'item 2'],
290
        ], $objB1->Items());
291
        $this->assertListEquals([
292
            ['Title' => 'item 2'],
293
            ['Title' => 'New Item'],
294
        ], $objB2->Items());
295
296
        // Test removing items
297
        $item2 = $this->objFromFixture(ManyManyThroughListTest\PolyItem::class, 'child2');
298
        $objA1->Items()->remove($item2);
299
        $objB1->Items()->remove($item2);
300
        $this->assertListEquals([
301
            ['Title' => 'item 1'],
302
            ['Title' => 'New Item'],
303
        ], $objA1->Items());
304
        $this->assertListEquals([], $objB1->Items());
305
        $this->assertListEquals([
306
            ['Title' => 'item 2'],
307
            ['Title' => 'New Item'],
308
        ], $objB2->Items());
309
310
        // Test set-by-id-list
311
        $objB2->Items()->setByIDList([
312
            $newItem->ID,
313
            $this->idFromFixture(ManyManyThroughListTest\PolyItem::class, 'child1'),
314
        ]);
315
        $this->assertListEquals([
316
            ['Title' => 'item 1'],
317
            ['Title' => 'New Item'],
318
        ], $objA1->Items());
319
        $this->assertListEquals([], $objB1->Items());
320
        $this->assertListEquals([
321
            ['Title' => 'item 1'],
322
            ['Title' => 'New Item'],
323
        ], $objB2->Items());
324
    }
325
326
    public function testGetJoinTable()
327
    {
328
        $joinTable = DataObject::getSchema()->tableName(PolyJoinObject::class);
329
        /** @var ManyManyThroughListTest\PolyObjectA $objA1 */
330
        $objA1 = $this->objFromFixture(ManyManyThroughListTest\PolyObjectA::class, 'obja1');
331
        /** @var ManyManyThroughListTest\PolyObjectB $objB1 */
332
        $objB1 = $this->objFromFixture(ManyManyThroughListTest\PolyObjectB::class, 'objb1');
333
        /** @var ManyManyThroughListTest\PolyObjectB $objB2 */
334
        $objB2 = $this->objFromFixture(ManyManyThroughListTest\PolyObjectB::class, 'objb2');
335
336
        $this->assertEquals($joinTable, $objA1->Items()->getJoinTable());
337
        $this->assertEquals($joinTable, $objB1->Items()->getJoinTable());
338
        $this->assertEquals($joinTable, $objB2->Items()->getJoinTable());
339
    }
340
341
    /**
342
     * This tests that default sort works when the join table has a default sort set, and the main
343
     * dataobject has a default sort set.
344
     *
345
     * @return void
346
     */
347
    public function testDefaultSortOnJoinAndMain()
348
    {
349
        // We have spanish mexico with two fall back locales; argentina and international sorted in that order.
350
        $mexico = $this->objFromFixture(Locale::class, 'mexico');
351
352
        $fallbacks = $mexico->Fallbacks();
0 ignored issues
show
Bug introduced by
The method Fallbacks() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

352
        /** @scrutinizer ignore-call */ 
353
        $fallbacks = $mexico->Fallbacks();
Loading history...
353
        $this->assertCount(2, $fallbacks);
354
355
        // Ensure the default sort is is correct
356
        list($first, $second) = $fallbacks;
357
        $this->assertSame('Argentina', $first->Title);
358
        $this->assertSame('International', $second->Title);
359
360
        // Ensure that we're respecting the default sort by reversing it
361
        Config::inst()->update(FallbackLocale::class, 'default_sort', '"ManyManyThroughTest_FallbackLocale"."Sort" DESC');
0 ignored issues
show
Bug introduced by
The method update() does not exist on SilverStripe\Config\Coll...nfigCollectionInterface. It seems like you code against a sub-type of SilverStripe\Config\Coll...nfigCollectionInterface such as SilverStripe\Config\Coll...\MemoryConfigCollection. ( Ignorable by Annotation )

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

361
        Config::inst()->/** @scrutinizer ignore-call */ update(FallbackLocale::class, 'default_sort', '"ManyManyThroughTest_FallbackLocale"."Sort" DESC');
Loading history...
362
363
        $reverse = $mexico->Fallbacks();
364
        list($firstReverse, $secondReverse) = $reverse;
365
        $this->assertSame('International', $firstReverse->Title);
366
        $this->assertSame('Argentina', $secondReverse->Title);
367
    }
368
369
    public function testCallbackOnSetById()
370
    {
371
        $addedIds = [];
0 ignored issues
show
Unused Code introduced by
The assignment to $addedIds is dead and can be removed.
Loading history...
372
        $removedIds = [];
373
374
        $base = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
375
        $relation = $base->Items();
0 ignored issues
show
Bug introduced by
The method Items() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

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

375
        /** @scrutinizer ignore-call */ 
376
        $relation = $base->Items();
Loading history...
376
        $remove = $relation->First();
377
        $add = new Item();
378
        $add->write();
379
380
        $relation->addCallbacks()->add(function ($list, $item, $extraFields) use (&$removedIds) {
0 ignored issues
show
Unused Code introduced by
The parameter $extraFields is not used and could be removed. ( Ignorable by Annotation )

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

380
        $relation->addCallbacks()->add(function ($list, $item, /** @scrutinizer ignore-unused */ $extraFields) use (&$removedIds) {

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The import $removedIds is not used and could be removed.

This check looks for imports that have been defined, but are not used in the scope.

Loading history...
381
            $addedIds[] = $item;
0 ignored issues
show
Comprehensibility Best Practice introduced by
$addedIds was never initialized. Although not strictly required by PHP, it is generally a good practice to add $addedIds = array(); before regardless.
Loading history...
382
        });
383
384
        $relation->removeCallbacks()->add(function ($list, $ids) use (&$removedIds) {
385
            $removedIds = $ids;
386
        });
387
388
        $relation->setByIDList(array_merge(
389
            $base->Items()->exclude('ID', $remove->ID)->column('ID'),
390
            [$add->ID]
391
        ));
392
        $this->assertEquals([$remove->ID], $removedIds);
393
    }
394
395
    public function testAddCallbackWithExtraFields()
396
    {
397
        $added = [];
398
399
        $base = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
400
        $relation = $base->Items();
401
        $add = new Item();
402
        $add->write();
403
404
        $relation->addCallbacks()->add(function ($list, $item, $extraFields) use (&$added) {
405
            $added[] = [$item, $extraFields];
406
        });
407
408
        $relation->add($add, ['Sort' => '99']);
409
        $this->assertEquals([[$add, ['Sort' => '99']]], $added);
410
    }
411
412
    public function testRemoveCallbackOnRemove()
413
    {
414
        $removedIds = [];
415
416
        $base = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
417
        $relation = $base->Items();
418
        $remove = $relation->First();
419
420
        $relation->removeCallbacks()->add(function ($list, $ids) use (&$removedIds) {
421
            $removedIds = $ids;
422
        });
423
424
        $relation->remove($remove);
425
        $this->assertEquals([$remove->ID], $removedIds);
426
    }
427
428
    public function testRemoveCallbackOnRemoveById()
429
    {
430
        $removedIds = [];
431
432
        $base = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
433
        $relation = $base->Items();
434
        $remove = $relation->First();
435
436
        $relation->removeCallbacks()->add(function ($list, $ids) use (&$removedIds) {
437
            $removedIds = $ids;
438
        });
439
440
        $relation->removeByID($remove->ID);
441
        $this->assertEquals([$remove->ID], $removedIds);
442
    }
443
444
    public function testRemoveCallbackOnRemoveAll()
445
    {
446
        $removedIds = [];
447
448
        $base = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
449
        $relation = $base->Items();
450
        $remove = $relation->column('ID');
451
452
        $relation->removeCallbacks()->add(function ($list, $ids) use (&$removedIds) {
453
            $removedIds = $ids;
454
        });
455
456
        $relation->removeAll();
457
        $this->assertEquals(sort($remove), sort($removedIds));
458
    }
459
}
460