Passed
Push — partial-fix-4622 ( 983d36 )
by Sam
07:19
created

DataListTest::testAmbiguousAggregate()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 17
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 11
nc 1
nop 0
dl 0
loc 17
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use InvalidArgumentException;
6
use SilverStripe\Core\Convert;
7
use SilverStripe\Core\Injector\InjectorNotFoundException;
8
use SilverStripe\ORM\DataList;
9
use SilverStripe\ORM\DataQuery;
10
use SilverStripe\ORM\DB;
11
use SilverStripe\ORM\Filterable;
12
use SilverStripe\ORM\Filters\ExactMatchFilter;
13
use SilverStripe\Dev\SapphireTest;
14
use SilverStripe\ORM\Tests\DataObjectTest\Fixture;
15
use SilverStripe\ORM\Tests\DataObjectTest\Bracket;
16
use SilverStripe\ORM\Tests\DataObjectTest\EquipmentCompany;
17
use SilverStripe\ORM\Tests\DataObjectTest\Fan;
18
use SilverStripe\ORM\Tests\DataObjectTest\Player;
19
use SilverStripe\ORM\Tests\DataObjectTest\Sortable;
20
use SilverStripe\ORM\Tests\DataObjectTest\SubTeam;
21
use SilverStripe\ORM\Tests\DataObjectTest\Team;
22
use SilverStripe\ORM\Tests\DataObjectTest\TeamComment;
23
use SilverStripe\ORM\Tests\DataObjectTest\ValidatedObject;
24
use SilverStripe\ORM\Tests\DataObjectTest\Staff;
25
26
/**
27
 * @skipUpgrade
28
 */
29
class DataListTest extends SapphireTest
30
{
31
32
    // Borrow the model from DataObjectTest
33
    protected static $fixture_file = 'DataObjectTest.yml';
34
35
    public static function getExtraDataObjects()
36
    {
37
        return array_merge(
38
            DataObjectTest::$extra_data_objects,
39
            ManyManyListTest::$extra_data_objects
40
        );
41
    }
42
43
44
    public function testFilterDataObjectByCreatedDate()
45
    {
46
        // create an object to test with
47
        $obj1 = new ValidatedObject();
48
        $obj1->Name = 'test obj 1';
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\Tests\D...ectTest\ValidatedObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
49
        $obj1->write();
50
        $this->assertTrue($obj1->isInDB());
51
52
        // reload the object from the database and reset its Created timestamp to a known value
53
        $obj1 = ValidatedObject::get()->filter(array('Name' => 'test obj 1'))->first();
54
        $this->assertTrue(is_object($obj1));
55
        $this->assertEquals('test obj 1', $obj1->Name);
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
56
        $obj1->Created = '2013-01-01 00:00:00';
57
        $obj1->write();
58
59
        // reload the object again and make sure that our Created date was properly persisted
60
        $obj1 = ValidatedObject::get()->filter(array('Name' => 'test obj 1'))->first();
61
        $this->assertTrue(is_object($obj1));
62
        $this->assertEquals('test obj 1', $obj1->Name);
63
        $this->assertEquals('2013-01-01 00:00:00', $obj1->Created);
64
65
        // now save a second object to the DB with an automatically-set Created value
66
        $obj2 = new ValidatedObject();
67
        $obj2->Name = 'test obj 2';
68
        $obj2->write();
69
        $this->assertTrue($obj2->isInDB());
70
71
        // and a third object
72
        $obj3 = new ValidatedObject();
73
        $obj3->Name = 'test obj 3';
74
        $obj3->write();
75
        $this->assertTrue($obj3->isInDB());
76
77
        // now test the filtering based on Created timestamp
78
        $list = ValidatedObject::get()
79
            ->filter(array('Created:GreaterThan' => '2013-02-01 00:00:00'))
80
            ->toArray();
81
        $this->assertEquals(2, count($list));
82
    }
83
84
    public function testSubtract()
85
    {
86
        $comment1 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment1');
87
        $subtractList = TeamComment::get()->filter('ID', $comment1->ID);
88
        $fullList = TeamComment::get();
89
        $newList = $fullList->subtract($subtractList);
90
        $this->assertEquals(2, $newList->Count(), 'List should only contain two objects after subtraction');
91
    }
92
93
    /**
94
     * @expectedException \InvalidArgumentException
95
     */
96
    public function testSubtractBadDataclassThrowsException()
97
    {
98
        $teamsComments = TeamComment::get();
99
        $teams = Team::get();
100
        $teamsComments->subtract($teams);
101
    }
102
103
    public function testListCreationSortAndLimit()
104
    {
105
        // By default, a DataList will contain all items of that class
106
        $list = TeamComment::get()->sort('ID');
107
108
        // We can iterate on the DataList
109
        $names = array();
110
        foreach ($list as $item) {
111
            $names[] = $item->Name;
112
        }
113
        $this->assertEquals(array('Joe', 'Bob', 'Phil'), $names);
114
115
        // If we don't want to iterate, we can extract a single column from the list with column()
116
        $this->assertEquals(array('Joe', 'Bob', 'Phil'), $list->column('Name'));
117
118
        // We can sort a list
119
        $list = $list->sort('Name');
120
        $this->assertEquals(array('Bob', 'Joe', 'Phil'), $list->column('Name'));
121
122
        // We can also restrict the output to a range
123
        $this->assertEquals(array('Joe', 'Phil'), $list->limit(2, 1)->column('Name'));
124
    }
125
126
    public function testLimitAndOffset()
127
    {
128
        $list = TeamComment::get();
129
        $check = $list->limit(3);
130
131
        $this->assertEquals(3, $check->count());
132
133
        $check = $list->limit(1);
134
        $this->assertEquals(1, $check->count());
135
136
        $check = $list->limit(1, 1);
137
        $this->assertEquals(1, $check->count());
138
139
        $check = $list->limit(false);
0 ignored issues
show
Bug introduced by
false of type false is incompatible with the type integer expected by parameter $limit of SilverStripe\ORM\DataList::limit(). ( Ignorable by Annotation )

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

139
        $check = $list->limit(/** @scrutinizer ignore-type */ false);
Loading history...
140
        $this->assertEquals(3, $check->count());
141
142
        $check = $list->limit(null);
143
        $this->assertEquals(3, $check->count());
144
145
        $check = $list->limit(null, 2);
146
        $this->assertEquals(1, $check->count());
147
148
        // count()/first()/last() methods may alter limit/offset, so run the query and manually check the count
149
        $check = $list->limit(null, 1)->toArray();
150
        $this->assertEquals(2, count($check));
151
    }
152
153
    public function testDistinct()
154
    {
155
        $list = TeamComment::get();
156
        $this->assertContains('SELECT DISTINCT', $list->dataQuery()->sql($params), 'Query is set as distinct by default');
157
158
        $list = $list->distinct(false);
159
        $this->assertNotContains('SELECT DISTINCT', $list->dataQuery()->sql($params), 'Query does not contain distinct');
160
161
        $list = $list->distinct(true);
162
        $this->assertContains('SELECT DISTINCT', $list->dataQuery()->sql($params), 'Query contains distinct');
163
    }
164
165
    public function testDataClass()
166
    {
167
        $list = TeamComment::get();
168
        $this->assertEquals(DataObjectTest\TeamComment::class, $list->dataClass());
169
    }
170
171
    public function testDataClassCaseInsensitive()
172
    {
173
        $list = DataList::create(strtolower(DataObjectTest\TeamComment::class));
174
        $this->assertTrue($list->exists());
175
    }
176
177
    public function testClone()
178
    {
179
        $list = TeamComment::get();
180
        $this->assertEquals($list, clone($list));
181
    }
182
183
    public function testSql()
184
    {
185
        $db = DB::get_conn();
186
        $list = TeamComment::get();
187
        $expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
188
            . '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
189
            . '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
190
            . '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
191
            . 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL '
192
            . 'THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
193
            . $db->quoteString(DataObjectTest\TeamComment::class)
194
            . ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment"'
195
            . ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
196
        $this->assertSQLEquals($expected, $list->sql($parameters));
197
    }
198
199
    public function testInnerJoin()
200
    {
201
        $db = DB::get_conn();
202
203
        $list = TeamComment::get();
204
        $list = $list->innerJoin(
205
            'DataObjectTest_Team',
206
            '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"',
207
            'Team'
208
        );
209
210
        $expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
211
            . '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
212
            . '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
213
            . '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
214
            . 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
215
            . ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
216
            . $db->quoteString(DataObjectTest\TeamComment::class)
217
            . ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" INNER JOIN '
218
            . '"DataObjectTest_Team" AS "Team" ON "DataObjectTest_Team"."ID" = '
219
            . '"DataObjectTest_TeamComment"."TeamID"'
220
            . ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
221
222
223
        $this->assertSQLEquals($expected, $list->sql($parameters));
224
        $this->assertEmpty($parameters);
225
    }
226
227
    public function testInnerJoinParameterised()
228
    {
229
        $db = DB::get_conn();
230
231
        $list = TeamComment::get();
232
        $list = $list->innerJoin(
233
            'DataObjectTest_Team',
234
            '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID" '
235
            . 'AND "DataObjectTest_Team"."Title" LIKE ?',
236
            'Team',
237
            20,
238
            array('Team%')
239
        );
240
241
        $expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
242
            . '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
243
            . '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
244
            . '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
245
            . 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
246
            . ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
247
            . $db->quoteString(DataObjectTest\TeamComment::class)
248
            . ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" INNER JOIN '
249
            . '"DataObjectTest_Team" AS "Team" ON "DataObjectTest_Team"."ID" = '
250
            . '"DataObjectTest_TeamComment"."TeamID" '
251
            . 'AND "DataObjectTest_Team"."Title" LIKE ?'
252
            . ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
253
254
        $this->assertSQLEquals($expected, $list->sql($parameters));
255
        $this->assertEquals(array('Team%'), $parameters);
256
    }
257
258
    public function testLeftJoin()
259
    {
260
        $db = DB::get_conn();
261
262
        $list = TeamComment::get();
263
        $list = $list->leftJoin(
264
            'DataObjectTest_Team',
265
            '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"',
266
            'Team'
267
        );
268
269
        $expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
270
            . '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
271
            . '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
272
            . '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
273
            . 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL '
274
            . 'THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
275
            . $db->quoteString(DataObjectTest\TeamComment::class)
276
            . ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" LEFT JOIN "DataObjectTest_Team" '
277
            . 'AS "Team" ON "DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"'
278
            . ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
279
280
281
        $this->assertSQLEquals($expected, $list->sql($parameters));
282
        $this->assertEmpty($parameters);
283
    }
284
285
    public function testLeftJoinParameterised()
286
    {
287
        $db = DB::get_conn();
288
289
        $list = TeamComment::get();
290
        $list = $list->leftJoin(
291
            'DataObjectTest_Team',
292
            '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID" '
293
            . 'AND "DataObjectTest_Team"."Title" LIKE ?',
294
            'Team',
295
            20,
296
            array('Team%')
297
        );
298
299
        $expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
300
            . '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
301
            . '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
302
            . '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
303
            . 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
304
            . ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
305
            . $db->quoteString(DataObjectTest\TeamComment::class)
306
            . ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" LEFT JOIN '
307
            . '"DataObjectTest_Team" AS "Team" ON "DataObjectTest_Team"."ID" = '
308
            . '"DataObjectTest_TeamComment"."TeamID" '
309
            . 'AND "DataObjectTest_Team"."Title" LIKE ?'
310
            . ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
311
312
        $this->assertSQLEquals($expected, $list->sql($parameters));
313
        $this->assertEquals(array('Team%'), $parameters);
314
    }
315
316
    public function testToNestedArray()
317
    {
318
        $list = TeamComment::get()->sort('ID');
319
        $nestedArray = $list->toNestedArray();
320
        $expected = array(
321
            0=>
322
            array(
323
                'ClassName'=>DataObjectTest\TeamComment::class,
324
                'Name'=>'Joe',
325
                'Comment'=>'This is a team comment by Joe',
326
                'TeamID'=> $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment1')->TeamID,
0 ignored issues
show
Bug Best Practice introduced by
The property TeamID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
327
            ),
328
            1=>
329
            array(
330
                'ClassName'=>DataObjectTest\TeamComment::class,
331
                'Name'=>'Bob',
332
                'Comment'=>'This is a team comment by Bob',
333
                'TeamID'=> $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment2')->TeamID,
334
            ),
335
            2=>
336
            array(
337
                'ClassName'=>DataObjectTest\TeamComment::class,
338
                'Name'=>'Phil',
339
                'Comment'=>'Phil is a unique guy, and comments on team2',
340
                'TeamID'=> $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment3')->TeamID,
341
            ),
342
        );
343
        $this->assertEquals(3, count($nestedArray));
344
        $this->assertEquals($expected[0]['Name'], $nestedArray[0]['Name']);
345
        $this->assertEquals($expected[1]['Comment'], $nestedArray[1]['Comment']);
346
        $this->assertEquals($expected[2]['TeamID'], $nestedArray[2]['TeamID']);
347
    }
348
349
    public function testMap()
350
    {
351
        $map = TeamComment::get()->map()->toArray();
352
        $expected = array(
353
            $this->idFromFixture(DataObjectTest\TeamComment::class, 'comment1') => 'Joe',
354
            $this->idFromFixture(DataObjectTest\TeamComment::class, 'comment2') => 'Bob',
355
            $this->idFromFixture(DataObjectTest\TeamComment::class, 'comment3') => 'Phil'
356
        );
357
358
        $this->assertEquals($expected, $map);
359
        $otherMap = TeamComment::get()->map('Name', 'TeamID')->toArray();
360
        $otherExpected = array(
361
            'Joe' => $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment1')->TeamID,
0 ignored issues
show
Bug Best Practice introduced by
The property TeamID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
362
            'Bob' => $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment2')->TeamID,
363
            'Phil' => $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment3')->TeamID
364
        );
365
366
        $this->assertEquals($otherExpected, $otherMap);
367
    }
368
369
    public function testAmbiguousAggregate()
370
    {
371
        // Test that we avoid ambiguity error when a field exists on two joined tables
372
        // Fetch the sponsors in a round-about way to simulate this
373
        $teamID = $this->idFromFixture(DataObjectTest\Team::class, 'team2');
374
        $sponsors = EquipmentCompany::get()->filter('SponsoredTeams.ID', $teamID);
375
        $this->assertNotNull($sponsors->Max('ID'));
376
        $this->assertNotNull($sponsors->Min('ID'));
377
        $this->assertNotNull($sponsors->Avg('ID'));
378
        $this->assertNotNull($sponsors->Sum('ID'));
379
380
        // Test non-orm many_many_extraFields
381
        $company = $this->objFromFixture(EquipmentCompany::class, 'equipmentcompany1');
382
        $this->assertNotNull($company->SponsoredTeams()->Max('SponsorFee'));
0 ignored issues
show
Bug introduced by
The method SponsoredTeams() 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

382
        $this->assertNotNull($company->/** @scrutinizer ignore-call */ SponsoredTeams()->Max('SponsorFee'));
Loading history...
383
        $this->assertNotNull($company->SponsoredTeams()->Min('SponsorFee'));
384
        $this->assertNotNull($company->SponsoredTeams()->Avg('SponsorFee'));
385
        $this->assertNotNull($company->SponsoredTeams()->Sum('SponsorFee'));
386
    }
387
388
    public function testEach()
389
    {
390
        $list = TeamComment::get();
391
392
        $count = 0;
393
        $list->each(
394
            function ($item) use (&$count) {
395
                $count++;
396
                $this->assertInstanceOf(TeamComment::class, $item);
397
            }
398
        );
399
400
        $this->assertEquals($count, $list->count());
401
    }
402
403
    public function testWhere()
404
    {
405
        // We can use raw SQL queries with where.  This is only recommended for advanced uses;
406
        // if you can, you should use filter().
407
        $list = TeamComment::get();
408
409
        // where() returns a new DataList, like all the other modifiers, so it can be chained.
410
        $list2 = $list->where('"Name" = \'Joe\'');
411
        $this->assertEquals(array('This is a team comment by Joe'), $list2->column('Comment'));
412
413
        // The where() clauses are chained together with AND
414
        $list3 = $list2->where('"Name" = \'Bob\'');
415
        $this->assertEquals(array(), $list3->column('Comment'));
416
    }
417
418
    /**
419
     * Test DataList->byID()
420
     */
421
    public function testByID()
422
    {
423
        // We can get a single item by ID.
424
        $id = $this->idFromFixture(DataObjectTest\Team::class, 'team2');
425
        $team = Team::get()->byID($id);
426
427
        // byID() returns a DataObject, rather than a DataList
428
        $this->assertInstanceOf(DataObjectTest\Team::class, $team);
429
        $this->assertEquals('Team 2', $team->Title);
430
431
        // Assert that filtering on ID searches by the base table, not the child table field
432
        $query = SubTeam::get()->filter('ID', 4)->sql($parameters);
433
        $this->assertContains('WHERE ("DataObjectTest_Team"."ID" = ?)', $query);
434
        $this->assertNotContains('WHERE ("DataObjectTest_SubTeam"."ID" = ?)', $query);
435
    }
436
437
    public function testByIDs()
438
    {
439
        $knownIDs = $this->allFixtureIDs(DataObjectTest\Player::class);
440
        $removedID = array_pop($knownIDs);
441
        $filteredPlayers = Player::get()->byIDs($knownIDs);
442
        foreach ($filteredPlayers as $player) {
443
            $this->assertContains($player->ID, $knownIDs);
444
            $this->assertNotEquals($removedID, $player->ID);
445
        }
446
    }
447
448
    /**
449
     * Test DataList->removeByID()
450
     */
451
    public function testRemoveByID()
452
    {
453
        $list = Team::get();
454
        $id = $this->idFromFixture(DataObjectTest\Team::class, 'team2');
455
456
        $this->assertNotNull($list->byID($id));
457
        $list->removeByID($id);
458
        $this->assertNull($list->byID($id));
459
    }
460
461
    /**
462
     * Test DataList->canSortBy()
463
     */
464
    public function testCanSortBy()
465
    {
466
        // Basic check
467
        $team = Team::get();
468
        $this->assertTrue($team->canSortBy("Title"));
469
        $this->assertFalse($team->canSortBy("SomethingElse"));
470
471
        // Subclasses
472
        $subteam = SubTeam::get();
473
        $this->assertTrue($subteam->canSortBy("Title"));
474
        $this->assertTrue($subteam->canSortBy("SubclassDatabaseField"));
475
    }
476
477
    public function testDataListArrayAccess()
478
    {
479
        $list = Team::get()->sort('Title');
480
481
        // We can use array access to refer to single items in the DataList, as if it were an array
482
        $this->assertEquals("Subteam 1", $list[0]->Title);
483
        $this->assertEquals("Subteam 3", $list[2]->Title);
484
        $this->assertEquals("Team 2", $list[4]->Title);
485
    }
486
487
    public function testFind()
488
    {
489
        $list = Team::get();
490
        $record = $list->find('Title', 'Team 1');
491
        $this->assertEquals($this->idFromFixture(DataObjectTest\Team::class, 'team1'), $record->ID);
492
    }
493
494
    public function testFindById()
495
    {
496
        $list = Team::get();
497
        $record = $list->find('ID', $this->idFromFixture(DataObjectTest\Team::class, 'team1'));
498
        $this->assertEquals('Team 1', $record->Title);
499
        // Test that you can call it twice on the same list
500
        $record = $list->find('ID', $this->idFromFixture(DataObjectTest\Team::class, 'team2'));
501
        $this->assertEquals('Team 2', $record->Title);
502
    }
503
504
    public function testSimpleSort()
505
    {
506
        $list = TeamComment::get();
507
        $list = $list->sort('Name');
508
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
509
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
510
    }
511
512
    public function testSimpleSortOneArgumentASC()
513
    {
514
        $list = TeamComment::get();
515
        $list = $list->sort('Name ASC');
516
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
517
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
518
    }
519
520
    public function testSimpleSortOneArgumentDESC()
521
    {
522
        $list = TeamComment::get();
523
        $list = $list->sort('Name DESC');
524
        $this->assertEquals('Phil', $list->first()->Name, 'Last comment should be from Phil');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
525
        $this->assertEquals('Bob', $list->last()->Name, 'First comment should be from Bob');
526
    }
527
528
    public function testSortOneArgumentMultipleColumns()
529
    {
530
        $list = TeamComment::get();
531
        $list = $list->sort('TeamID ASC, Name DESC');
532
        $this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
533
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
534
    }
535
536
    public function testSimpleSortASC()
537
    {
538
        $list = TeamComment::get();
539
        $list = $list->sort('Name', 'asc');
540
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
541
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
542
    }
543
544
    public function testSimpleSortDESC()
545
    {
546
        $list = TeamComment::get();
547
        $list = $list->sort('Name', 'desc');
548
        $this->assertEquals('Phil', $list->first()->Name, 'Last comment should be from Phil');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
549
        $this->assertEquals('Bob', $list->last()->Name, 'First comment should be from Bob');
550
    }
551
552
    public function testSortWithArraySyntaxSortASC()
553
    {
554
        $list = TeamComment::get();
555
        $list = $list->sort(array('Name'=>'asc'));
556
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
557
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
558
    }
559
560
    public function testSortWithArraySyntaxSortDESC()
561
    {
562
        $list = TeamComment::get();
563
        $list = $list->sort(array('Name'=>'desc'));
564
        $this->assertEquals('Phil', $list->first()->Name, 'Last comment should be from Phil');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
565
        $this->assertEquals('Bob', $list->last()->Name, 'First comment should be from Bob');
566
    }
567
568
    public function testSortWithMultipleArraySyntaxSort()
569
    {
570
        $list = TeamComment::get();
571
        $list = $list->sort(array('TeamID'=>'asc','Name'=>'desc'));
572
        $this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
573
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
574
    }
575
576
    public function testSortWithCompositeSyntax()
577
    {
578
        // Phil commented on team with founder surname "Aaron"
579
        $list = TeamComment::get();
580
        $list = $list->sort('Team.Founder.Surname', 'asc');
581
        $this->assertEquals('Phil', $list->first()->Name);
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
582
        $list = $list->sort('Team.Founder.Surname', 'desc');
583
        $this->assertEquals('Phil', $list->last()->Name);
584
    }
585
586
    /**
587
     * @expectedException \InvalidArgumentException
588
     * @expectedExceptionMessage Fans is not a linear relation on model SilverStripe\ORM\Tests\DataObjectTest\Player
589
     */
590
    public function testSortInvalidParameters()
591
    {
592
        $list = Team::get();
593
        $list->sort('Founder.Fans.Surname'); // Can't sort on has_many
594
    }
595
596
    public function testSortNumeric()
597
    {
598
        $list = Sortable::get();
599
        $list1 = $list->sort('Sort', 'ASC');
600
        $this->assertEquals(
601
            array(
602
            -10,
603
            -2,
604
            -1,
605
            0,
606
            1,
607
            2,
608
            10
609
            ),
610
            $list1->column('Sort')
611
        );
612
    }
613
614
    public function testSortMixedCase()
615
    {
616
        $list = Sortable::get();
617
        $list1 = $list->sort('Name', 'ASC');
618
        $this->assertEquals(
619
            array(
620
            'Bob',
621
            'bonny',
622
            'jane',
623
            'John',
624
            'sam',
625
            'Steve',
626
            'steven'
627
            ),
628
            $list1->column('Name')
629
        );
630
    }
631
632
    /**
633
     * Test DataList->canFilterBy()
634
     */
635
    public function testCanFilterBy()
636
    {
637
        // Basic check
638
        $team = Team::get();
639
        $this->assertTrue($team->canFilterBy("Title"));
640
        $this->assertFalse($team->canFilterBy("SomethingElse"));
641
642
        // Has one
643
        $this->assertTrue($team->canFilterBy("CaptainID"));
644
        $this->assertTrue($team->canFilterBy("Captain.ShirtNumber"));
645
        $this->assertFalse($team->canFilterBy("SomethingElse.ShirtNumber"));
646
        $this->assertFalse($team->canFilterBy("Captain.SomethingElse"));
647
        $this->assertTrue($team->canFilterBy("Captain.FavouriteTeam.Captain.ShirtNumber"));
648
649
        // Has many
650
        $this->assertTrue($team->canFilterBy("Fans.Name"));
651
        $this->assertFalse($team->canFilterBy("SomethingElse.Name"));
652
        $this->assertFalse($team->canFilterBy("Fans.SomethingElse"));
653
654
        // Many many
655
        $this->assertTrue($team->canFilterBy("Players.FirstName"));
656
        $this->assertFalse($team->canFilterBy("SomethingElse.FirstName"));
657
        $this->assertFalse($team->canFilterBy("Players.SomethingElse"));
658
659
        // Subclasses
660
        $subteam = SubTeam::get();
661
        $this->assertTrue($subteam->canFilterBy("Title"));
662
        $this->assertTrue($subteam->canFilterBy("SubclassDatabaseField"));
663
    }
664
665
    /**
666
     * $list->filter('Name', 'bob'); // only bob in the list
667
     */
668
    public function testSimpleFilter()
669
    {
670
        $list = Team::get();
671
        $list = $list->filter('Title', 'Team 2');
672
        $this->assertEquals(1, $list->count());
673
        $this->assertEquals('Team 2', $list->first()->Title, 'List should only contain Team 2');
674
        $this->assertEquals('Team 2', $list->last()->Title, 'Last should only contain Team 2');
675
    }
676
677
    public function testSimpleFilterEndsWith()
678
    {
679
        $list = TeamComment::get();
680
        $list = $list->filter('Name:EndsWith', 'b');
681
        $this->assertEquals(1, $list->count());
682
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
683
    }
684
685
    public function testSimpleFilterExactMatchFilter()
686
    {
687
        $list = TeamComment::get();
688
        $list = $list->filter('Name:ExactMatch', 'Bob');
689
        $this->assertEquals(1, $list->count());
690
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
691
    }
692
693
    public function testSimpleFilterGreaterThanFilter()
694
    {
695
        $list = TeamComment::get();
696
        $list = $list->filter('TeamID:GreaterThan', $this->idFromFixture(DataObjectTest\Team::class, 'team1'));
697
        $this->assertEquals(1, $list->count());
698
        $this->assertEquals('Phil', $list->first()->Name, 'First comment should be from Phil');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
699
    }
700
701
    public function testSimpleFilterGreaterThanOrEqualFilter()
702
    {
703
        $list = TeamComment::get();
704
        $list = $list->filter(
705
            'TeamID:GreaterThanOrEqual',
706
            $this->idFromFixture(DataObjectTest\Team::class, 'team1')
707
        )->sort("ID");
708
        $this->assertEquals(3, $list->count());
709
        $this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
710
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
711
    }
712
713
    public function testSimpleFilterLessThanFilter()
714
    {
715
        $list = TeamComment::get();
716
        $list = $list->filter(
717
            'TeamID:LessThan',
718
            $this->idFromFixture(DataObjectTest\Team::class, 'team2')
719
        )->sort('Name');
720
        $this->assertEquals(2, $list->count());
721
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
722
        $this->assertEquals('Joe', $list->Last()->Name, 'Last comment should be from Joe');
723
    }
724
725
    public function testSimpleFilterLessThanOrEqualFilter()
726
    {
727
        $list = TeamComment::get();
728
        $list = $list->filter(
729
            'TeamID:LessThanOrEqual',
730
            $this->idFromFixture(DataObjectTest\Team::class, 'team1')
731
        )->sort('ID');
732
        $this->assertEquals(2, $list->count());
733
        $this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
734
        $this->assertEquals('Bob', $list->Last()->Name, 'Last comment should be from Bob');
735
    }
736
737
    public function testSimplePartialMatchFilter()
738
    {
739
        $list = TeamComment::get();
740
        $list = $list->filter('Name:PartialMatch', 'o')->sort('Name');
741
        $this->assertEquals(2, $list->count());
742
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
743
        $this->assertEquals('Joe', $list->last()->Name, 'First comment should be from Joe');
744
    }
745
746
    public function testSimpleFilterStartsWith()
747
    {
748
        $list = TeamComment::get();
749
        $list = $list->filter('Name:StartsWith', 'B');
750
        $this->assertEquals(1, $list->count());
751
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
752
    }
753
754
    /**
755
     * @expectedException \SilverStripe\Core\Injector\InjectorNotFoundException
756
     * @expectedExceptionMessage Class DataListFilter.Bogus does not exist
757
     */
758
    public function testSimpleFilterWithNonExistingComparisator()
759
    {
760
        $list = TeamComment::get();
761
        $list->filter('Comment:Bogus', 'team comment');
762
    }
763
764
    /**
765
     * Invalid modifiers are treated as failed filter construction
766
     *
767
     * @expectedException \SilverStripe\Core\Injector\InjectorNotFoundException
768
     * @expectedExceptionMessage Class DataListFilter.invalidmodifier does not exist
769
     */
770
    public function testInvalidModifier()
771
    {
772
        $list = TeamComment::get();
773
        $list->filter('Comment:invalidmodifier', 'team comment');
774
    }
775
776
    /**
777
     * $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
778
     */
779
    public function testSimpleFilterWithMultiple()
780
    {
781
        $list = TeamComment::get();
782
        $list = $list->filter('Name', array('Bob','Phil'));
783
        $list = $list->sort('Name', 'ASC');
784
        $this->assertEquals(2, $list->count());
785
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
786
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
787
    }
788
789
    public function testMultipleFilterWithNoMatch()
790
    {
791
        $list = TeamComment::get();
792
        $list = $list->filter(array('Name'=>'Bob', 'Comment'=>'Phil is a unique guy, and comments on team2'));
793
        $this->assertEquals(0, $list->count());
794
    }
795
796
    /**
797
     *  $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the age 21
798
     */
799
    public function testFilterMultipleArray()
800
    {
801
        $list = TeamComment::get();
802
        $list = $list->filter(array('Name'=>'Bob', 'Comment'=>'This is a team comment by Bob'));
803
        $list = $list->sort('Name', 'ASC');
804
        $this->assertEquals(1, $list->count());
805
        $this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
806
    }
807
808
    public function testFilterMultipleWithTwoMatches()
809
    {
810
        $list = TeamComment::get();
811
        $list = $list->filter(array('TeamID'=>$this->idFromFixture(DataObjectTest\Team::class, 'team1')));
812
        $this->assertEquals(2, $list->count());
813
    }
814
815
    public function testFilterMultipleWithArrayFilter()
816
    {
817
        $list = TeamComment::get();
818
        $list = $list->filter(array('Name'=>array('Bob','Phil')));
819
        $list = $list->sort('Name', 'ASC');
820
        $this->assertEquals(2, $list->count(), 'There should be two comments');
821
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
822
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
823
    }
824
825
    public function testFilterMultipleWithArrayFilterAndModifiers()
826
    {
827
        $list = TeamComment::get();
828
        $list = $list->filter(array('Name:StartsWith'=>array('Bo', 'Jo')));
829
        $list = $list->sort('Name', 'ASC');
830
        $this->assertEquals(2, $list->count());
831
        $this->assertEquals('Bob', $list->first()->Name);
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
832
        $this->assertEquals('Joe', $list->last()->Name);
833
    }
834
835
    /**
836
     * $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43)));
837
     */
838
    public function testFilterArrayInArray()
839
    {
840
        $list = TeamComment::get();
841
        $list = $list->filter(
842
            array(
843
            'Name'=>array('Bob','Phil'),
844
            'TeamID'=>array($this->idFromFixture(DataObjectTest\Team::class, 'team1')))
845
        );
846
        $this->assertEquals(1, $list->count(), 'There should be one comment');
847
        $this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
848
    }
849
850
    public function testFilterWithModifiers()
851
    {
852
        $list = TeamComment::get();
853
        $nocaseList = $list->filter('Name:nocase', 'bob');
854
        $this->assertEquals(1, $nocaseList->count(), 'There should be one comment');
855
        $caseList = $list->filter('Name:case', 'bob');
856
        $this->assertEquals(0, $caseList->count(), 'There should be no comments');
857
        $gtList = $list->filter(
858
            'TeamID:GreaterThan:not',
859
            $this->idFromFixture(DataObjectTest\Team::class, 'team1')
860
        );
861
        $this->assertEquals(2, $gtList->count());
862
    }
863
864
    /**
865
     * Test that a filter correctly aliases relationships that share common classes
866
     */
867
    public function testFilterSharedRelationalClasses()
868
    {
869
        /** @var Bracket $final1 */
870
        $final1 = $this->objFromFixture(Bracket::class, 'final');
871
        $prefinal1 = $this->objFromFixture(Bracket::class, 'prefinal1');
872
        $prefinal2 = $this->objFromFixture(Bracket::class, 'prefinal2');
873
        $semifinal1 = $this->objFromFixture(Bracket::class, 'semifinal1');
874
        $team2 = $this->objFromFixture(Team::class, 'team2');
875
876
        // grand child can be found from parent
877
        $found = Bracket::get()->filter('Next.Next.Title', $final1->Title);
878
        $this->assertListEquals(
879
            [['Title' => $semifinal1->Title]],
880
            $found
881
        );
882
883
        // grand child can be found from child
884
        $found = Bracket::get()->filter('Next.Title', $prefinal1->Title);
885
        $this->assertListEquals(
886
            [['Title' => $semifinal1->Title]],
887
            $found
888
        );
889
890
        // child can be found from parent
891
        $found = Bracket::get()->filter('Next.Title', $final1->Title);
892
        $this->assertListEquals(
893
            [
894
                ['Title' => $prefinal1->Title],
895
                ['Title' => $prefinal2->Title]
896
            ],
897
            $found
898
        );
899
900
        // Complex filter, get brackets where the following bracket was won by team 1
901
        // Note: Includes results from multiple levels
902
        $found = Bracket::get()->filter('Next.Winner.Title', $team2->Title);
903
        $this->assertListEquals(
904
            [
905
                ['Title' => $prefinal1->Title],
906
                ['Title' => $prefinal2->Title],
907
                ['Title' => $semifinal1->Title]
908
            ],
909
            $found
910
        );
911
    }
912
913
    public function testFilterOnImplicitJoinWithSharedInheritance()
914
    {
915
        $list = DataObjectTest\RelationChildFirst::get()->filter(array(
916
            'ManyNext.ID' => array(
917
                $this->idFromFixture(DataObjectTest\RelationChildSecond::class, 'test1'),
918
                $this->idFromFixture(DataObjectTest\RelationChildSecond::class, 'test2'),
919
            ),
920
        ));
921
        $this->assertEquals(2, $list->count());
922
        $ids = $list->column('ID');
923
        $this->assertContains($this->idFromFixture(DataObjectTest\RelationChildFirst::class, 'test1'), $ids);
924
        $this->assertContains($this->idFromFixture(DataObjectTest\RelationChildFirst::class, 'test2'), $ids);
925
    }
926
927
    public function testFilterAny()
928
    {
929
        $list = TeamComment::get();
930
        $list = $list->filterAny('Name', 'Bob');
931
        $this->assertEquals(1, $list->count());
932
    }
933
934
    public function testFilterAnyWithRelation()
935
    {
936
        $list = Player::get();
937
        $list = $list->filterAny(array(
938
            'Teams.Title:StartsWith' => 'Team',
939
            'ID:GreaterThan' => 0,
940
        ));
941
        $this->assertCount(4, $list);
942
    }
943
944
    public function testFilterAnyWithTwoGreaterThanFilters()
945
    {
946
947
        for ($i=1; $i<=3; $i++) {
948
            $f = new Fixture();
949
            $f->MyDecimal = $i;
0 ignored issues
show
Bug Best Practice introduced by
The property MyDecimal does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
950
            $f->write();
951
952
            $f = new Fixture();
953
            $f->MyInt = $i;
0 ignored issues
show
Bug Best Practice introduced by
The property MyInt does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
954
            $f->write();
955
        }
956
957
        $list = Fixture::get()->filterAny([
958
            'MyDecimal:GreaterThan' => 1, // 2 records
959
            'MyInt:GreaterThan' => 2, // 1 record
960
        ]);
961
962
        $this->assertCount(3, $list);
963
    }
964
965
    public function testFilterAnyMultipleArray()
966
    {
967
        $list = TeamComment::get();
968
        $list = $list->filterAny(array('Name'=>'Bob', 'Comment'=>'This is a team comment by Bob'));
969
        $this->assertEquals(1, $list->count());
970
        $this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
971
    }
972
973
    public function testFilterAnyOnFilter()
974
    {
975
        $list = TeamComment::get();
976
        $list = $list->filter(
977
            array(
978
            'TeamID'=>$this->idFromFixture(DataObjectTest\Team::class, 'team1')
979
            )
980
        );
981
        $list = $list->filterAny(
982
            array(
983
            'Name'=>array('Phil', 'Joe'),
984
            'Comment'=>'This is a team comment by Bob'
985
            )
986
        );
987
        $list = $list->sort('Name');
988
        $this->assertEquals(2, $list->count());
989
        $this->assertEquals(
990
            'Bob',
991
            $list->offsetGet(0)->Name,
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
992
            'Results should include comments from Bob, matched by comment and team'
993
        );
994
        $this->assertEquals(
995
            'Joe',
996
            $list->offsetGet(1)->Name,
997
            'Results should include comments by Joe, matched by name and team (not by comment)'
998
        );
999
1000
        $list = TeamComment::get();
1001
        $list = $list->filter(
1002
            array(
1003
            'TeamID'=>$this->idFromFixture(DataObjectTest\Team::class, 'team1')
1004
            )
1005
        );
1006
        $list = $list->filterAny(
1007
            array(
1008
            'Name'=>array('Phil', 'Joe'),
1009
            'Comment'=>'This is a team comment by Bob'
1010
            )
1011
        );
1012
        $list = $list->sort('Name');
1013
        $list = $list->filter(array('Name' => 'Bob'));
1014
        $this->assertEquals(1, $list->count());
1015
        $this->assertEquals(
1016
            'Bob',
1017
            $list->offsetGet(0)->Name,
1018
            'Results should include comments from Bob, matched by name and team'
1019
        );
1020
    }
1021
1022
    public function testFilterAnyMultipleWithArrayFilter()
1023
    {
1024
        $list = TeamComment::get();
1025
        $list = $list->filterAny(array('Name'=>array('Bob','Phil')));
1026
        $this->assertEquals(2, $list->count(), 'There should be two comments');
1027
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1028
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
1029
    }
1030
1031
    public function testFilterAnyArrayInArray()
1032
    {
1033
        $list = TeamComment::get();
1034
        $list = $list->filterAny(
1035
            array(
1036
            'Name'=>array('Bob','Phil'),
1037
            'TeamID'=>array($this->idFromFixture(DataObjectTest\Team::class, 'team1')))
1038
        )
1039
            ->sort('Name');
1040
        $this->assertEquals(3, $list->count());
1041
        $this->assertEquals(
1042
            'Bob',
1043
            $list->offsetGet(0)->Name,
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1044
            'Results should include comments from Bob, matched by name and team'
1045
        );
1046
        $this->assertEquals(
1047
            'Joe',
1048
            $list->offsetGet(1)->Name,
1049
            'Results should include comments by Joe, matched by team (not by name)'
1050
        );
1051
        $this->assertEquals(
1052
            'Phil',
1053
            $list->offsetGet(2)->Name,
1054
            'Results should include comments from Phil, matched by name (even if he\'s not in Team1)'
1055
        );
1056
    }
1057
1058
    public function testFilterOnJoin()
1059
    {
1060
        $list = TeamComment::get()
1061
            ->leftJoin(
1062
                'DataObjectTest_Team',
1063
                '"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"'
1064
            )->filter(
1065
                array(
1066
                'Title' => 'Team 1'
1067
                )
1068
            );
1069
1070
        $this->assertEquals(2, $list->count());
1071
        $values = $list->column('Name');
1072
        $this->assertEquals(array_intersect($values, array('Joe', 'Bob')), $values);
1073
    }
1074
1075
    public function testFilterOnImplicitJoin()
1076
    {
1077
        // Many to many
1078
        $list = Team::get()
1079
            ->filter('Players.FirstName', array('Captain', 'Captain 2'));
1080
1081
        $this->assertEquals(2, $list->count());
1082
1083
        // Has many
1084
        $list = Team::get()
1085
            ->filter('Comments.Name', array('Joe', 'Phil'));
1086
1087
        $this->assertEquals(2, $list->count());
1088
1089
        // Has one
1090
        $list = Player::get()
1091
            ->filter('FavouriteTeam.Title', 'Team 1');
1092
1093
        $this->assertEquals(1, $list->count());
1094
        $this->assertEquals('007', $list->first()->ShirtNumber);
0 ignored issues
show
Bug Best Practice introduced by
The property ShirtNumber does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1095
    }
1096
1097
    /**
1098
     * @expectedException \InvalidArgumentException
1099
     * @expectedExceptionMessage MascotAnimal is not a relation on model SilverStripe\ORM\Tests\DataObjectTest\Team
1100
     */
1101
    public function testFilterOnInvalidRelation()
1102
    {
1103
        // Filter on missing relation 'MascotAnimal'
1104
        Team::get()
1105
            ->filter('MascotAnimal.Name', 'Richard')
1106
            ->toArray();
1107
    }
1108
1109
    public function testFilterAndExcludeById()
1110
    {
1111
        $id = $this->idFromFixture(SubTeam::class, 'subteam1');
1112
        $list = SubTeam::get()->filter('ID', $id);
1113
        $this->assertEquals($id, $list->first()->ID);
1114
1115
        $list = SubTeam::get();
1116
        $this->assertEquals(3, count($list));
1117
        $this->assertEquals(2, count($list->exclude('ID', $id)));
1118
    }
1119
1120
    /**
1121
     * @skipUpgrade
1122
     */
1123
    public function testFilterByNull()
1124
    {
1125
        $list = Fan::get();
1126
        // Force DataObjectTest_Fan/fan5::Email to empty string
1127
        $fan5id = $this->idFromFixture(Fan::class, 'fan5');
1128
        DB::prepared_query("UPDATE \"DataObjectTest_Fan\" SET \"Email\" = '' WHERE \"ID\" = ?", array($fan5id));
1129
1130
        // Filter by null email
1131
        $nullEmails = $list->filter('Email', null);
1132
        $this->assertListEquals(
1133
            array(
1134
            array(
1135
                'Name' => 'Stephen',
1136
            ),
1137
            array(
1138
                'Name' => 'Mitch',
1139
            )
1140
            ),
1141
            $nullEmails
1142
        );
1143
1144
        // Filter by non-null
1145
        $nonNullEmails = $list->filter('Email:not', null);
1146
        $this->assertListEquals(
1147
            array(
1148
            array(
1149
                'Name' => 'Damian',
1150
                'Email' => '[email protected]',
1151
            ),
1152
            array(
1153
                'Name' => 'Richard',
1154
                'Email' => '[email protected]',
1155
            ),
1156
            array(
1157
                'Name' => 'Hamish',
1158
            )
1159
            ),
1160
            $nonNullEmails
1161
        );
1162
1163
        // Filter by empty only
1164
        $emptyOnly = $list->filter('Email', '');
1165
        $this->assertListEquals(
1166
            array(
1167
            array(
1168
                'Name' => 'Hamish',
1169
            )
1170
            ),
1171
            $emptyOnly
1172
        );
1173
1174
        // Non-empty only. This should include null values, since ExactMatchFilter works around
1175
        // the caveat that != '' also excludes null values in ANSI SQL-92 behaviour.
1176
        $nonEmptyOnly = $list->filter('Email:not', '');
1177
        $this->assertListEquals(
1178
            array(
1179
            array(
1180
                'Name' => 'Damian',
1181
                'Email' => '[email protected]',
1182
            ),
1183
            array(
1184
                'Name' => 'Richard',
1185
                'Email' => '[email protected]',
1186
            ),
1187
            array(
1188
                'Name' => 'Stephen',
1189
            ),
1190
            array(
1191
                'Name' => 'Mitch',
1192
            )
1193
            ),
1194
            $nonEmptyOnly
1195
        );
1196
1197
        // Filter by many including null, empty string, and non-empty
1198
        $items1 = $list->filter('Email', array(null, '', '[email protected]'));
1199
        $this->assertListEquals(
1200
            array(
1201
            array(
1202
                'Name' => 'Damian',
1203
                'Email' => '[email protected]',
1204
            ),
1205
            array(
1206
                'Name' => 'Stephen',
1207
            ),
1208
            array(
1209
                'Name' => 'Mitch',
1210
            ),
1211
            array(
1212
                'Name' => 'Hamish',
1213
            )
1214
            ),
1215
            $items1
1216
        );
1217
1218
        // Filter exclusion of above list
1219
        $items2 = $list->filter('Email:not', array(null, '', '[email protected]'));
1220
        $this->assertListEquals(
1221
            array(
1222
            array(
1223
                'Name' => 'Richard',
1224
                'Email' => '[email protected]',
1225
            ),
1226
            ),
1227
            $items2
1228
        );
1229
1230
        // Filter by many including empty string and non-empty
1231
        $items3 = $list->filter('Email', array('', '[email protected]'));
1232
        $this->assertListEquals(
1233
            array(
1234
            array(
1235
                'Name' => 'Damian',
1236
                'Email' => '[email protected]',
1237
            ),
1238
            array(
1239
                'Name' => 'Hamish',
1240
            )
1241
            ),
1242
            $items3
1243
        );
1244
1245
        // Filter by many including empty string and non-empty
1246
        // This also relies no the workaround for null comparison as in the $nonEmptyOnly test
1247
        $items4 = $list->filter('Email:not', array('', '[email protected]'));
1248
        $this->assertListEquals(
1249
            array(
1250
            array(
1251
                'Name' => 'Richard',
1252
                'Email' => '[email protected]',
1253
            ),
1254
            array(
1255
                'Name' => 'Stephen',
1256
            ),
1257
            array(
1258
                'Name' => 'Mitch',
1259
            )
1260
            ),
1261
            $items4
1262
        );
1263
1264
        // Filter by many including empty string and non-empty
1265
        // The extra null check isn't necessary, but check that this doesn't fail
1266
        $items5 = $list->filterAny(
1267
            array(
1268
            'Email:not' => array('', '[email protected]'),
1269
            'Email' => null
1270
            )
1271
        );
1272
        $this->assertListEquals(
1273
            array(
1274
            array(
1275
                'Name' => 'Richard',
1276
                'Email' => '[email protected]',
1277
            ),
1278
            array(
1279
                'Name' => 'Stephen',
1280
            ),
1281
            array(
1282
                'Name' => 'Mitch',
1283
            )
1284
            ),
1285
            $items5
1286
        );
1287
1288
        // Filter by null or empty values
1289
        $items6 = $list->filter('Email', array(null, ''));
1290
        $this->assertListEquals(
1291
            array(
1292
            array(
1293
                'Name' => 'Stephen',
1294
            ),
1295
            array(
1296
                'Name' => 'Mitch',
1297
            ),
1298
            array(
1299
                'Name' => 'Hamish',
1300
            )
1301
            ),
1302
            $items6
1303
        );
1304
    }
1305
1306
    /**
1307
     * Test null checks with case modifiers
1308
     */
1309
    public function testFilterByNullCase()
1310
    {
1311
        // Test with case (case/nocase both use same code path)
1312
        // Test with and without null, and with inclusion/exclusion permutations
1313
        $list = Fan::get();
1314
1315
        // Only an explicit NOT NULL should include null values
1316
        $items6 = $list->filter('Email:not:case', array(null, '', '[email protected]'));
1317
        $this->assertSQLContains(' AND "DataObjectTest_Fan"."Email" IS NOT NULL', $items6->sql());
1318
1319
        // These should all include values where Email IS NULL
1320
        $items7 = $list->filter('Email:nocase', array(null, '', '[email protected]'));
1321
        $this->assertSQLContains(' OR "DataObjectTest_Fan"."Email" IS NULL', $items7->sql());
1322
        $items8 = $list->filter('Email:not:case', array('', '[email protected]'));
1323
        $this->assertSQLContains(' OR "DataObjectTest_Fan"."Email" IS NULL', $items8->sql());
1324
1325
        // These should not contain any null checks at all
1326
        $items9 = $list->filter('Email:nocase', array('', '[email protected]'));
1327
        $this->assertSQLNotContains('"DataObjectTest_Fan"."Email" IS NULL', $items9->sql());
1328
        $this->assertSQLNotContains('"DataObjectTest_Fan"."Email" IS NOT NULL', $items9->sql());
1329
    }
1330
1331
    public function testAggregateDBName()
1332
    {
1333
        $filter = new ExactMatchFilter(
1334
            'Comments.Count()'
1335
        );
1336
        $filter->apply(new DataQuery(DataObjectTest\Team::class));
1337
        $this->assertEquals('COUNT("comments_DataObjectTest_TeamComment"."ID")', $filter->getDBName());
1338
1339
        foreach (['Comments.Max(ID)', 'Comments.Max( ID )', 'Comments.Max(  ID)'] as $name) {
1340
            $filter = new ExactMatchFilter($name);
1341
            $filter->apply(new DataQuery(DataObjectTest\Team::class));
1342
            $this->assertEquals('MAX("comments_DataObjectTest_TeamComment"."ID")', $filter->getDBName());
1343
        }
1344
    }
1345
1346
    public function testAggregateFilterExceptions()
1347
    {
1348
        $ex = null;
1349
        try {
1350
            $filter = new ExactMatchFilter('Comments.Max( This will not parse! )');
0 ignored issues
show
Unused Code introduced by
The assignment to $filter is dead and can be removed.
Loading history...
1351
        } catch (\Exception $e) {
1352
            $ex = $e;
1353
        }
1354
        $this->assertInstanceOf(\InvalidArgumentException::class, $ex);
1355
        $this->assertRegExp('/Malformed/', $ex->getMessage());
0 ignored issues
show
Bug introduced by
The method getMessage() does not exist on null. ( Ignorable by Annotation )

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

1355
        $this->assertRegExp('/Malformed/', $ex->/** @scrutinizer ignore-call */ getMessage());

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...
1356
1357
1358
        $filter = new ExactMatchFilter('Comments.Max(NonExistentColumn)');
1359
        $filter->setModel(new DataObjectTest\Team());
1360
        $ex = null;
1361
        try {
1362
            $name = $filter->getDBName();
0 ignored issues
show
Unused Code introduced by
The assignment to $name is dead and can be removed.
Loading history...
1363
        } catch (\Exception $e) {
1364
            $ex = $e;
1365
        }
1366
        $this->assertInstanceOf(\InvalidArgumentException::class, $ex);
1367
        $this->assertRegExp('/Invalid column/', $ex->getMessage());
1368
    }
1369
1370
    public function testAggregateFilters()
1371
    {
1372
        $teams = Team::get()->filter('Comments.Count()', 2);
1373
1374
        $team1 = $this->objFromFixture(Team::class, 'team1');
1375
        $team2 = $this->objFromFixture(Team::class, 'team2');
1376
        $team3 = $this->objFromFixture(Team::class, 'team3');
1377
        $team4 = $this->objFromFixture(SubTeam::class, 'subteam1');
1378
        $team5 = $this->objFromFixture(SubTeam::class, 'subteam2_with_player_relation');
1379
        $team6 = $this->objFromFixture(SubTeam::class, 'subteam3_with_empty_fields');
1380
1381
        $company1 = $this->objFromFixture(EquipmentCompany::class, 'equipmentcompany1');
1382
        $company2 = $this->objFromFixture(EquipmentCompany::class, 'equipmentcompany2');
1383
1384
        $company1->CurrentStaff()->add(Staff::create(['Salary' => 3])->write());
0 ignored issues
show
Bug introduced by
The method CurrentStaff() 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

1384
        $company1->/** @scrutinizer ignore-call */ 
1385
                   CurrentStaff()->add(Staff::create(['Salary' => 3])->write());
Loading history...
1385
        $company1->CurrentStaff()->add(Staff::create(['Salary' => 5])->write());
1386
        $company2->CurrentStaff()->add(Staff::create(['Salary' => 4])->write());
1387
1388
        $this->assertCount(1, $teams);
1389
        $this->assertEquals($team1->ID, $teams->first()->ID);
1390
1391
        $teams = Team::get()->filter('Comments.Count()', [1,2]);
1392
1393
        $this->assertCount(2, $teams);
1394
        foreach ([$team1, $team2] as $expectedTeam) {
1395
            $this->assertContains($expectedTeam->ID, $teams->column('ID'));
1396
        }
1397
1398
        $teams = Team::get()->filter('Comments.Count():GreaterThan', 1);
1399
1400
        $this->assertCount(1, $teams);
1401
        $this->assertContains(
1402
            $this->objFromFixture(Team::class, 'team1')->ID,
1403
            $teams->column('ID')
1404
        );
1405
1406
        $teams = Team::get()->filter('Comments.Count():LessThan', 2);
1407
1408
        $this->assertCount(5, $teams);
1409
        foreach ([$team2, $team3, $team4, $team5, $team6] as $expectedTeam) {
1410
            $this->assertContains($expectedTeam->ID, $teams->column('ID'));
1411
        }
1412
1413
        $teams = Team::get()->filter('Comments.Count():GreaterThanOrEqual', 1);
1414
1415
        $this->assertCount(2, $teams);
1416
        foreach ([$team1, $team2] as $expectedTeam) {
1417
            $this->assertContains($expectedTeam->ID, $teams->column('ID'));
1418
        }
1419
1420
        $teams = Team::get()->filter('Comments.Count():LessThanOrEqual', 1);
1421
1422
        $this->assertCount(5, $teams);
1423
        foreach ([$team2, $team3, $team4, $team5, $team6] as $expectedTeam) {
1424
            $this->assertContains($expectedTeam->ID, $teams->column('ID'));
1425
        }
1426
1427
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Max(Salary)', 5);
1428
        $this->assertCount(1, $companies);
1429
        $this->assertEquals($company1->ID, $companies->first()->ID);
1430
1431
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Min(Salary)', 3);
1432
        $this->assertCount(1, $companies);
1433
        $this->assertEquals($company1->ID, $companies->first()->ID);
1434
1435
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Max(Salary):GreaterThan', 3);
1436
        $this->assertCount(2, $companies);
1437
        foreach ([$company1, $company2] as $expectedTeam) {
1438
            $this->assertContains($expectedTeam->ID, $companies->column('ID'));
1439
        }
1440
1441
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Sum(Salary)', 8);
1442
        $this->assertCount(1, $companies);
1443
        $this->assertEquals($company1->ID, $companies->first()->ID);
1444
1445
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Sum(Salary):LessThan', 7);
1446
        $this->assertCount(1, $companies);
1447
        $this->assertEquals($company2->ID, $companies->first()->ID);
1448
1449
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Sum(Salary):GreaterThan', 100);
1450
        $this->assertCount(0, $companies);
1451
1452
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Sum(Salary):GreaterThan', 7);
1453
        $this->assertCount(1, $companies);
1454
        $this->assertEquals($company1->ID, $companies->first()->ID);
1455
1456
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Avg(Salary)', 4);
1457
        $this->assertCount(2, $companies);
1458
        foreach ([$company1, $company2] as $expectedTeam) {
1459
            $this->assertContains($expectedTeam->ID, $companies->column('ID'));
1460
        }
1461
1462
        $companies = EquipmentCompany::get()->filter('CurrentStaff.Avg(Salary):LessThan', 10);
1463
        $this->assertCount(2, $companies);
1464
        foreach ([$company1, $company2] as $expectedTeam) {
1465
            $this->assertContains($expectedTeam->ID, $companies->column('ID'));
1466
        }
1467
    }
1468
1469
    /**
1470
     * $list = $list->filterByCallback(function($item, $list) { return $item->Age == 21; })
1471
     */
1472
    public function testFilterByCallback()
1473
    {
1474
        $team1ID = $this->idFromFixture(DataObjectTest\Team::class, 'team1');
1475
        $list = TeamComment::get();
1476
        $list = $list->filterByCallback(
1477
            function ($item, $list) use ($team1ID) {
0 ignored issues
show
Unused Code introduced by
The parameter $list 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

1477
            function ($item, /** @scrutinizer ignore-unused */ $list) use ($team1ID) {

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...
1478
                return $item->TeamID == $team1ID;
1479
            }
1480
        );
1481
1482
        $result = $list->column('Name');
1483
        $expected = array_intersect($result, array('Joe', 'Bob'));
1484
1485
        $this->assertEquals(2, $list->count());
1486
        $this->assertEquals($expected, $result, 'List should only contain comments from Team 1 (Joe and Bob)');
1487
        $this->assertTrue($list instanceof Filterable, 'The List should be of type SS_Filterable');
1488
    }
1489
1490
    /**
1491
     * $list->exclude('Name', 'bob'); // exclude bob from list
1492
     */
1493
    public function testSimpleExclude()
1494
    {
1495
        $list = TeamComment::get();
1496
        $list = $list->exclude('Name', 'Bob');
1497
        $list = $list->sort('Name');
1498
        $this->assertEquals(2, $list->count());
1499
        $this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1500
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
1501
    }
1502
    //
1503
    /**
1504
     * $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
1505
     */
1506
    public function testSimpleExcludeWithMultiple()
1507
    {
1508
        $list = TeamComment::get();
1509
        $list = $list->exclude('Name', array('Joe','Phil'));
1510
        $this->assertEquals(1, $list->count());
1511
        $this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1512
    }
1513
1514
    /**
1515
     * $list->exclude(array('Name'=>'bob, 'Age'=>21)); // negative version
1516
     */
1517
    public function testMultipleExcludeWithMiss()
1518
    {
1519
        $list = TeamComment::get();
1520
        $list = $list->exclude(array('Name'=>'Bob', 'Comment'=>'Does not match any comments'));
1521
        $this->assertEquals(3, $list->count());
1522
    }
1523
1524
    /**
1525
     * $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
1526
     */
1527
    public function testMultipleExclude()
1528
    {
1529
        $list = TeamComment::get();
1530
        $list = $list->exclude(array('Name'=>'Bob', 'Comment'=>'This is a team comment by Bob'));
1531
        $this->assertEquals(2, $list->count());
1532
    }
1533
1534
    /**
1535
     * Test doesn't exclude if only matches one
1536
     * $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
1537
     */
1538
    public function testMultipleExcludeMultipleMatches()
1539
    {
1540
        $list = TeamComment::get();
1541
        $list = $list->exclude(array('Name'=>'Bob', 'Comment'=>'Phil is a unique guy, and comments on team2'));
1542
        $this->assertCount(3, $list);
1543
    }
1544
1545
    /**
1546
     * // exclude only those that match both
1547
     */
1548
    public function testMultipleExcludeArraysMultipleMatches()
1549
    {
1550
        $list = TeamComment::get();
1551
        $list = $list->exclude(array(
1552
            'Name'=> array('Bob', 'Phil'),
1553
            'Comment'=> array(
1554
                'This is a team comment by Bob',
1555
                'Phil is a unique guy, and comments on team2'
1556
            )
1557
        ));
1558
        $this->assertListEquals([['Name' => 'Joe']], $list);
1559
    }
1560
1561
    /**
1562
     * Exclude only which matches both params
1563
     */
1564
    public function testMultipleExcludeArraysMultipleMatchesOneMiss()
1565
    {
1566
        $list = TeamComment::get();
1567
        $list = $list->exclude(array(
1568
            'Name' => array('Bob', 'Phil'),
1569
            'Comment' => array(
1570
                'Does not match any comments',
1571
                'Phil is a unique guy, and comments on team2'
1572
            )
1573
        ));
1574
        $list = $list->sort('Name');
1575
        $this->assertListEquals(
1576
            [
1577
                ['Name' => 'Bob'],
1578
                ['Name' => 'Joe'],
1579
            ],
1580
            $list
1581
        );
1582
    }
1583
1584
    /**
1585
     * Test that if an exclude() is applied to a filter(), the filter() is still preserved.
1586
     */
1587
    public function testExcludeOnFilter()
1588
    {
1589
        $list = TeamComment::get();
1590
        $list = $list->filter('Comment', 'Phil is a unique guy, and comments on team2');
1591
        $list = $list->exclude('Name', 'Bob');
1592
1593
        $sql = $list->sql($parameters);
1594
        $this->assertSQLContains(
1595
            'WHERE ("DataObjectTest_TeamComment"."Comment" = ?) AND (("DataObjectTest_TeamComment"."Name" != ? '
1596
            . 'OR "DataObjectTest_TeamComment"."Name" IS NULL))',
1597
            $sql
1598
        );
1599
        $this->assertEquals(array('Phil is a unique guy, and comments on team2', 'Bob'), $parameters);
1600
        $this->assertListEquals([['Name' => 'Phil']], $list);
1601
    }
1602
1603
    /**
1604
     * Test that if a complicated exclude() is applied to a filter(), the filter() is still preserved.
1605
     */
1606
    public function testComplicatedExcludeOnFilter()
1607
    {
1608
        $list = TeamComment::get();
1609
        $list = $list->filter('Name', array('Phil', 'Bob'));
1610
        $list = $list->exclude('Name', array('Bob', 'Joe'));
1611
1612
        $sql = $list->sql($parameters);
1613
        $this->assertSQLContains(
1614
            'WHERE ("DataObjectTest_TeamComment"."Name" IN (?, ?)) AND (("DataObjectTest_TeamComment"."Name" NOT IN (?, ?) '
1615
            . 'OR "DataObjectTest_TeamComment"."Name" IS NULL))',
1616
            $sql
1617
        );
1618
        $this->assertEquals(array('Phil', 'Bob', 'Bob', 'Joe'), $parameters);
1619
        $this->assertListEquals([['Name' => 'Phil']], $list);
1620
    }
1621
1622
    /**
1623
     * Test that if a very complicated exclude() is applied to a filter(), the filter() is still preserved.
1624
     */
1625
    public function testVeryComplicatedExcludeOnFilter()
1626
    {
1627
        $list = TeamComment::get();
1628
        $list = $list->filter('Name', array('Phil', 'Bob'));
1629
        $list = $list->exclude(array(
1630
            'Name' => array('Joe', 'Phil'),
1631
            'Comment' => array('Matches no comments', 'Not a matching comment')
1632
        ));
1633
1634
        $sql = $list->sql($parameters);
1635
        $this->assertSQLContains(
1636
            'WHERE ("DataObjectTest_TeamComment"."Name" IN (?, ?)) '
1637
            . 'AND (("DataObjectTest_TeamComment"."Name" NOT IN (?, ?) '
1638
            . 'OR "DataObjectTest_TeamComment"."Name" IS NULL) '
1639
            . 'OR ("DataObjectTest_TeamComment"."Comment" NOT IN (?, ?) '
1640
            . 'OR "DataObjectTest_TeamComment"."Comment" IS NULL))',
1641
            $sql
1642
        );
1643
        $this->assertEquals(array('Phil', 'Bob', 'Joe', 'Phil', 'Matches no comments', 'Not a matching comment'), $parameters);
1644
        $list = $list->sort('Name');
1645
        $this->assertListEquals(
1646
            [
1647
                ['Name' => 'Bob'],
1648
                ['Name' => 'Phil'],
1649
            ],
1650
            $list
1651
        );
1652
    }
1653
1654
    public function testExcludeWithSearchFilter()
1655
    {
1656
        $list = TeamComment::get();
1657
        $list = $list->exclude('Name:LessThan', 'Bob');
1658
1659
        $sql = $list->sql($parameters);
1660
        $this->assertSQLContains('WHERE (("DataObjectTest_TeamComment"."Name" >= ?))', $sql);
1661
        $this->assertEquals(array('Bob'), $parameters);
1662
    }
1663
1664
    /**
1665
     * Test that Bob and Phil are excluded (one match each)
1666
     */
1667
    public function testExcludeAny()
1668
    {
1669
        $list = TeamComment::get();
1670
        $list = $list->excludeAny(array(
1671
            'Name' => 'Bob',
1672
            'Comment' => 'Phil is a unique guy, and comments on team2'
1673
        ));
1674
        $this->assertListEquals([['Name' => 'Joe']], $list);
1675
    }
1676
1677
    /**
1678
     * Test that Bob and Phil are excluded by Name
1679
     */
1680
    public function testExcludeAnyArrays()
1681
    {
1682
        $list = TeamComment::get();
1683
        $list = $list->excludeAny(array(
1684
            'Name' => array('Bob', 'Phil'),
1685
            'Comment' => 'No matching comments'
1686
        ));
1687
        $this->assertListEquals([['Name' => 'Joe']], $list);
1688
    }
1689
1690
    /**
1691
     * Test that Bob is excluded by Name, Phil by comment
1692
     */
1693
    public function testExcludeAnyMultiArrays()
1694
    {
1695
        $list = TeamComment::get();
1696
        $list = $list->excludeAny(array(
1697
            'Name' => array('Bob', 'Fred'),
1698
            'Comment' => array('No matching comments', 'Phil is a unique guy, and comments on team2')
1699
        ));
1700
        $this->assertListEquals([['Name' => 'Joe']], $list);
1701
    }
1702
1703
    /**
1704
     * Test exact match filter with empty array items
1705
     *
1706
     * @expectedException \InvalidArgumentException
1707
     * @expectedExceptionMessage Cannot filter "DataObjectTest_TeamComment"."Name" against an empty set
1708
     */
1709
    public function testEmptyFilter()
1710
    {
1711
        $list = TeamComment::get();
1712
        $list->exclude('Name', array());
1713
    }
1714
1715
    /**
1716
     * $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
1717
     */
1718
    public function testMultipleExcludeWithMultipleThatCheersEitherTeam()
1719
    {
1720
        $list = TeamComment::get();
1721
        $list = $list->exclude(
1722
            array('Name'=>'Bob', 'TeamID'=>array(
1723
            $this->idFromFixture(DataObjectTest\Team::class, 'team1'),
1724
            $this->idFromFixture(DataObjectTest\Team::class, 'team2')
1725
            ))
1726
        );
1727
        $list = $list->sort('Name');
1728
        $this->assertEquals(2, $list->count());
1729
        $this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Phil');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1730
        $this->assertEquals('Phil', $list->last()->Name, 'First comment should be from Phil');
1731
    }
1732
1733
    /**
1734
     * $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // negative version
1735
     */
1736
    public function testMultipleExcludeWithMultipleThatCheersOnNonExistingTeam()
1737
    {
1738
        $list = TeamComment::get();
1739
        $list = $list->exclude(array('Name'=>'Bob', 'TeamID'=>array(3)));
1740
        $this->assertEquals(3, $list->count());
1741
    }
1742
1743
    /**
1744
     * $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43))); //negative version
1745
     */
1746
    public function testMultipleExcludeWithNoExclusion()
1747
    {
1748
        $list = TeamComment::get();
1749
        $list = $list->exclude(
1750
            array(
1751
            'Name'=>array('Bob','Joe'),
1752
            'Comment' => 'Phil is a unique guy, and comments on team2')
1753
        );
1754
        $this->assertEquals(3, $list->count());
1755
    }
1756
1757
    /**
1758
     *  $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
1759
     */
1760
    public function testMultipleExcludeWithTwoArray()
1761
    {
1762
        $list = TeamComment::get();
1763
        $list = $list->exclude(
1764
            array('Name' => array('Bob','Joe'), 'TeamID' => array(
1765
            $this->idFromFixture(DataObjectTest\Team::class, 'team1'),
1766
            $this->idFromFixture(DataObjectTest\Team::class, 'team2')
1767
            ))
1768
        );
1769
        $this->assertEquals(1, $list->count());
1770
        $this->assertEquals('Phil', $list->last()->Name, 'Only comment should be from Phil');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1771
    }
1772
1773
    /**
1774
     *  $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
1775
     */
1776
    public function testMultipleExcludeWithTwoArrayOneTeam()
1777
    {
1778
        $list = TeamComment::get();
1779
        $list = $list->exclude(
1780
            array(
1781
            'Name' => array('Bob', 'Phil'),
1782
            'TeamID' => array($this->idFromFixture(DataObjectTest\Team::class, 'team1')))
1783
        );
1784
        $list = $list->sort('Name');
1785
        $this->assertEquals(2, $list->count());
1786
        $this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1787
        $this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
1788
    }
1789
1790
    /**
1791
     *
1792
     */
1793
    public function testSortByRelation()
1794
    {
1795
        $list = TeamComment::get();
1796
        $list = $list->sort(array('Team.Title' => 'DESC'));
1797
        $this->assertEquals(3, $list->count());
1798
        $this->assertEquals(
1799
            $this->idFromFixture(DataObjectTest\Team::class, 'team2'),
1800
            $list->first()->TeamID,
0 ignored issues
show
Bug Best Practice introduced by
The property TeamID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1801
            'First comment should be for Team 2'
1802
        );
1803
        $this->assertEquals(
1804
            $this->idFromFixture(DataObjectTest\Team::class, 'team1'),
1805
            $list->last()->TeamID,
1806
            'Last comment should be for Team 1'
1807
        );
1808
    }
1809
1810
    public function testReverse()
1811
    {
1812
        $list = TeamComment::get();
1813
        $list = $list->sort('Name');
1814
        $list = $list->reverse();
1815
1816
        $this->assertEquals('Bob', $list->last()->Name, 'Last comment should be from Bob');
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1817
        $this->assertEquals('Phil', $list->first()->Name, 'First comment should be from Phil');
1818
    }
1819
1820
    public function testSortByComplexExpression()
1821
    {
1822
        // Test an expression with both spaces and commas. This test also tests that column() can be called
1823
        // with a complex sort expression, so keep using column() below
1824
        $teamClass = Convert::raw2sql(SubTeam::class);
1825
        $list = Team::get()->sort(
1826
            'CASE WHEN "DataObjectTest_Team"."ClassName" = \'' . $teamClass . '\' THEN 0 ELSE 1 END, "Title" DESC'
1827
        );
1828
        $this->assertEquals(
1829
            array(
1830
            'Subteam 3',
1831
            'Subteam 2',
1832
            'Subteam 1',
1833
            'Team 3',
1834
            'Team 2',
1835
            'Team 1',
1836
            ),
1837
            $list->column("Title")
1838
        );
1839
    }
1840
1841
    public function testShuffle()
1842
    {
1843
        $list = Team::get()->shuffle();
1844
1845
        $this->assertSQLContains(DB::get_conn()->random() . ' AS "_SortColumn', $list->dataQuery()->sql());
1846
    }
1847
}
1848