Passed
Push — pulls/manymanylist-add-callbac... ( e094c2...9c86cd )
by Ingo
08:27
created

testRemoveCallbackOnRemoveAll()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

349
        /** @scrutinizer ignore-call */ 
350
        $fallbacks = $mexico->Fallbacks();
Loading history...
350
        $this->assertCount(2, $fallbacks);
351
352
        // Ensure the default sort is is correct
353
        list($first, $second) = $fallbacks;
354
        $this->assertSame('Argentina', $first->Title);
355
        $this->assertSame('International', $second->Title);
356
357
        // Ensure that we're respecting the default sort by reversing it
358
        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

358
        Config::inst()->/** @scrutinizer ignore-call */ update(FallbackLocale::class, 'default_sort', '"ManyManyThroughTest_FallbackLocale"."Sort" DESC');
Loading history...
359
360
        $reverse = $mexico->Fallbacks();
361
        list($firstReverse, $secondReverse) = $reverse;
362
        $this->assertSame('International', $firstReverse->Title);
363
        $this->assertSame('Argentina', $secondReverse->Title);
364
    }
365
366
    public function testRemoveCallbackOnRemove()
367
    {
368
        $removedIds = [];
369
370
        $base = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
371
        $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

371
        /** @scrutinizer ignore-call */ 
372
        $relation = $base->Items();
Loading history...
372
        $remove = $relation->First();
373
374
        $relation->setRemoveCallback(function ($list, $ids) use (&$removedIds) {
375
            $removedIds = $ids;
376
        });
377
378
        $relation->remove($remove);
379
        $this->assertEquals([$remove->ID], $removedIds);
380
    }
381
382
    public function testRemoveCallbackOnRemoveById()
383
    {
384
        $removedIds = [];
385
386
        $base = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
387
        $relation = $base->Items();
388
        $remove = $relation->First();
389
390
        $relation->setRemoveCallback(function ($list, $ids) use (&$removedIds) {
391
            $removedIds = $ids;
392
        });
393
394
        $relation->removeByID($remove->ID);
395
        $this->assertEquals([$remove->ID], $removedIds);
396
    }
397
398
    public function testRemoveCallbackOnRemoveAll()
399
    {
400
        $removedIds = [];
401
402
        $base = $this->objFromFixture(ManyManyThroughListTest\TestObject::class, 'parent1');
403
        $relation = $base->Items();
404
        $remove = $relation->column('ID');
405
406
        $relation->setRemoveCallback(function ($list, $ids) use (&$removedIds) {
407
            $removedIds = $ids;
408
        });
409
410
        $relation->removeAll();
411
        $this->assertEquals(sort($remove), sort($removedIds));
412
    }
413
}
414