Completed
Push — 4.4 ( 26e3b6...8dcaed )
by
unknown
42s queued 20s
created

DataListTest::testSimpleExcludeWithMultiple()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
rs 10
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\Dev\SapphireTest;
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\ORM\Tests\DataObjectTest\Bracket;
14
use SilverStripe\ORM\Tests\DataObjectTest\EquipmentCompany;
15
use SilverStripe\ORM\Tests\DataObjectTest\Fan;
16
use SilverStripe\ORM\Tests\DataObjectTest\Fixture;
17
use SilverStripe\ORM\Tests\DataObjectTest\Player;
18
use SilverStripe\ORM\Tests\DataObjectTest\Sortable;
19
use SilverStripe\ORM\Tests\DataObjectTest\Staff;
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\ManyManyListTest\Category;
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
                'Title' => 'Team 1'
1066
            ]);
1067
1068
        $this->assertEquals(2, $list->count());
1069
        $values = $list->column('Name');
1070
        $this->assertEquals(array_intersect($values, array('Joe', 'Bob')), $values);
1071
    }
1072
1073
    public function testFilterOnImplicitJoin()
1074
    {
1075
        // Many to many
1076
        $list = Team::get()
1077
            ->filter('Players.FirstName', array('Captain', 'Captain 2'));
1078
1079
        $this->assertEquals(2, $list->count());
1080
1081
        // Has many
1082
        $list = Team::get()
1083
            ->filter('Comments.Name', array('Joe', 'Phil'));
1084
1085
        $this->assertEquals(2, $list->count());
1086
1087
        // Has one
1088
        $list = Player::get()
1089
            ->filter('FavouriteTeam.Title', 'Team 1');
1090
1091
        $this->assertEquals(1, $list->count());
1092
        $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...
1093
    }
1094
1095
    /**
1096
     * @expectedException \InvalidArgumentException
1097
     * @expectedExceptionMessage MascotAnimal is not a relation on model SilverStripe\ORM\Tests\DataObjectTest\Team
1098
     */
1099
    public function testFilterOnInvalidRelation()
1100
    {
1101
        // Filter on missing relation 'MascotAnimal'
1102
        Team::get()
1103
            ->filter('MascotAnimal.Name', 'Richard')
1104
            ->toArray();
1105
    }
1106
1107
    public function testFilterAndExcludeById()
1108
    {
1109
        $id = $this->idFromFixture(SubTeam::class, 'subteam1');
1110
        $list = SubTeam::get()->filter('ID', $id);
1111
        $this->assertEquals($id, $list->first()->ID);
1112
1113
        $list = SubTeam::get();
1114
        $this->assertEquals(3, count($list));
1115
        $this->assertEquals(2, count($list->exclude('ID', $id)));
1116
    }
1117
1118
    /**
1119
     * @skipUpgrade
1120
     */
1121
    public function testFilterByNull()
1122
    {
1123
        $list = Fan::get();
1124
        // Force DataObjectTest_Fan/fan5::Email to empty string
1125
        $fan5id = $this->idFromFixture(Fan::class, 'fan5');
1126
        DB::prepared_query("UPDATE \"DataObjectTest_Fan\" SET \"Email\" = '' WHERE \"ID\" = ?", array($fan5id));
1127
1128
        // Filter by null email
1129
        $nullEmails = $list->filter('Email', null);
1130
        $this->assertListEquals(
1131
            array(
1132
            array(
1133
                'Name' => 'Stephen',
1134
            ),
1135
            array(
1136
                'Name' => 'Mitch',
1137
            )
1138
            ),
1139
            $nullEmails
1140
        );
1141
1142
        // Filter by non-null
1143
        $nonNullEmails = $list->filter('Email:not', null);
1144
        $this->assertListEquals(
1145
            array(
1146
            array(
1147
                'Name' => 'Damian',
1148
                'Email' => '[email protected]',
1149
            ),
1150
            array(
1151
                'Name' => 'Richard',
1152
                'Email' => '[email protected]',
1153
            ),
1154
            array(
1155
                'Name' => 'Hamish',
1156
            )
1157
            ),
1158
            $nonNullEmails
1159
        );
1160
1161
        // Filter by empty only
1162
        $emptyOnly = $list->filter('Email', '');
1163
        $this->assertListEquals(
1164
            array(
1165
            array(
1166
                'Name' => 'Hamish',
1167
            )
1168
            ),
1169
            $emptyOnly
1170
        );
1171
1172
        // Non-empty only. This should include null values, since ExactMatchFilter works around
1173
        // the caveat that != '' also excludes null values in ANSI SQL-92 behaviour.
1174
        $nonEmptyOnly = $list->filter('Email:not', '');
1175
        $this->assertListEquals(
1176
            array(
1177
            array(
1178
                'Name' => 'Damian',
1179
                'Email' => '[email protected]',
1180
            ),
1181
            array(
1182
                'Name' => 'Richard',
1183
                'Email' => '[email protected]',
1184
            ),
1185
            array(
1186
                'Name' => 'Stephen',
1187
            ),
1188
            array(
1189
                'Name' => 'Mitch',
1190
            )
1191
            ),
1192
            $nonEmptyOnly
1193
        );
1194
1195
        // Filter by many including null, empty string, and non-empty
1196
        $items1 = $list->filter('Email', array(null, '', '[email protected]'));
1197
        $this->assertListEquals(
1198
            array(
1199
            array(
1200
                'Name' => 'Damian',
1201
                'Email' => '[email protected]',
1202
            ),
1203
            array(
1204
                'Name' => 'Stephen',
1205
            ),
1206
            array(
1207
                'Name' => 'Mitch',
1208
            ),
1209
            array(
1210
                'Name' => 'Hamish',
1211
            )
1212
            ),
1213
            $items1
1214
        );
1215
1216
        // Filter exclusion of above list
1217
        $items2 = $list->filter('Email:not', array(null, '', '[email protected]'));
1218
        $this->assertListEquals(
1219
            array(
1220
            array(
1221
                'Name' => 'Richard',
1222
                'Email' => '[email protected]',
1223
            ),
1224
            ),
1225
            $items2
1226
        );
1227
1228
        // Filter by many including empty string and non-empty
1229
        $items3 = $list->filter('Email', array('', '[email protected]'));
1230
        $this->assertListEquals(
1231
            array(
1232
            array(
1233
                'Name' => 'Damian',
1234
                'Email' => '[email protected]',
1235
            ),
1236
            array(
1237
                'Name' => 'Hamish',
1238
            )
1239
            ),
1240
            $items3
1241
        );
1242
1243
        // Filter by many including empty string and non-empty
1244
        // This also relies no the workaround for null comparison as in the $nonEmptyOnly test
1245
        $items4 = $list->filter('Email:not', array('', '[email protected]'));
1246
        $this->assertListEquals(
1247
            array(
1248
            array(
1249
                'Name' => 'Richard',
1250
                'Email' => '[email protected]',
1251
            ),
1252
            array(
1253
                'Name' => 'Stephen',
1254
            ),
1255
            array(
1256
                'Name' => 'Mitch',
1257
            )
1258
            ),
1259
            $items4
1260
        );
1261
1262
        // Filter by many including empty string and non-empty
1263
        // The extra null check isn't necessary, but check that this doesn't fail
1264
        $items5 = $list->filterAny(
1265
            array(
1266
            'Email:not' => array('', '[email protected]'),
1267
            'Email' => null
1268
            )
1269
        );
1270
        $this->assertListEquals(
1271
            array(
1272
            array(
1273
                'Name' => 'Richard',
1274
                'Email' => '[email protected]',
1275
            ),
1276
            array(
1277
                'Name' => 'Stephen',
1278
            ),
1279
            array(
1280
                'Name' => 'Mitch',
1281
            )
1282
            ),
1283
            $items5
1284
        );
1285
1286
        // Filter by null or empty values
1287
        $items6 = $list->filter('Email', array(null, ''));
1288
        $this->assertListEquals(
1289
            array(
1290
            array(
1291
                'Name' => 'Stephen',
1292
            ),
1293
            array(
1294
                'Name' => 'Mitch',
1295
            ),
1296
            array(
1297
                'Name' => 'Hamish',
1298
            )
1299
            ),
1300
            $items6
1301
        );
1302
    }
1303
1304
    /**
1305
     * Test null checks with case modifiers
1306
     */
1307
    public function testFilterByNullCase()
1308
    {
1309
        // Test with case (case/nocase both use same code path)
1310
        // Test with and without null, and with inclusion/exclusion permutations
1311
        $list = Fan::get();
1312
1313
        // Only an explicit NOT NULL should include null values
1314
        $items6 = $list->filter('Email:not:case', array(null, '', '[email protected]'));
1315
        $this->assertSQLContains(' AND "DataObjectTest_Fan"."Email" IS NOT NULL', $items6->sql());
1316
1317
        // These should all include values where Email IS NULL
1318
        $items7 = $list->filter('Email:nocase', array(null, '', '[email protected]'));
1319
        $this->assertSQLContains(' OR "DataObjectTest_Fan"."Email" IS NULL', $items7->sql());
1320
        $items8 = $list->filter('Email:not:case', array('', '[email protected]'));
1321
        $this->assertSQLContains(' OR "DataObjectTest_Fan"."Email" IS NULL', $items8->sql());
1322
1323
        // These should not contain any null checks at all
1324
        $items9 = $list->filter('Email:nocase', array('', '[email protected]'));
1325
        $this->assertSQLNotContains('"DataObjectTest_Fan"."Email" IS NULL', $items9->sql());
1326
        $this->assertSQLNotContains('"DataObjectTest_Fan"."Email" IS NOT NULL', $items9->sql());
1327
    }
1328
1329
    public function testAggregateDBName()
1330
    {
1331
        $filter = new ExactMatchFilter(
1332
            'Comments.Count()'
1333
        );
1334
        $filter->apply(new DataQuery(DataObjectTest\Team::class));
1335
        $this->assertEquals('COUNT("comments_DataObjectTest_TeamComment"."ID")', $filter->getDBName());
1336
1337
        foreach (['Comments.Max(ID)', 'Comments.Max( ID )', 'Comments.Max(  ID)'] as $name) {
1338
            $filter = new ExactMatchFilter($name);
1339
            $filter->apply(new DataQuery(DataObjectTest\Team::class));
1340
            $this->assertEquals('MAX("comments_DataObjectTest_TeamComment"."ID")', $filter->getDBName());
1341
        }
1342
    }
1343
1344
    public function testAggregateFilterExceptions()
1345
    {
1346
        $ex = null;
1347
        try {
1348
            $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...
1349
        } catch (\Exception $e) {
1350
            $ex = $e;
1351
        }
1352
        $this->assertInstanceOf(\InvalidArgumentException::class, $ex);
1353
        $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

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

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

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