DataListTest   F
last analyzed

Complexity

Total Complexity 119

Size/Duplication

Total Lines 1860
Duplicated Lines 0 %

Importance

Changes 3
Bugs 1 Features 0
Metric Value
eloc 1002
dl 0
loc 1860
rs 1.592
c 3
b 1
f 0
wmc 119

106 Methods

Rating   Name   Duplication   Size   Complexity  
A getExtraDataObjects() 0 5 1
A testFilterDataObjectByCreatedDate() 0 38 1
A testSubtract() 0 7 1
A testSimpleSortASC() 0 6 1
A testDataListArrayAccess() 0 8 1
A testClone() 0 4 1
A testSortWithArraySyntaxSortDESC() 0 6 1
A testRemoveByID() 0 8 1
A testSortOneArgumentMultipleColumns() 0 6 1
A testMap() 0 18 1
A testDistinct() 0 21 1
A testSimpleSortDESC() 0 6 1
A testWhere() 0 13 1
A testAmbiguousAggregate() 0 17 1
A testLeftJoin() 0 25 1
A testDataClass() 0 4 1
A testToNestedArray() 0 31 1
A testSubtractBadDataclassThrowsException() 0 6 1
A testLimitAndOffset() 0 25 1
A testSimpleSortOneArgumentASC() 0 6 1
A testSimpleSortOneArgumentDESC() 0 6 1
A testInnerJoinParameterised() 0 29 1
A testEach() 0 13 1
A testSortWithArraySyntaxSortASC() 0 6 1
A testLeftJoinParameterised() 0 29 1
A testSortWithCompositeSyntax() 0 8 1
A testSortWithMultipleArraySyntaxSort() 0 6 1
A testFind() 0 5 1
A testByIDs() 0 8 2
A testCanSortBy() 0 11 1
A testSql() 0 14 1
A testFindById() 0 8 1
A testListCreationSortAndLimit() 0 21 2
A testDataClassCaseInsensitive() 0 4 1
A testByID() 0 14 1
A testInnerJoin() 0 26 1
A testSimpleSort() 0 6 1
A testSimpleFilterGreaterThanFilter() 0 6 1
A testFilterMultipleWithTwoMatches() 0 5 1
A testFilterOnImplicitJoinWithSharedInheritance() 0 12 1
A testMultipleFilterWithNoMatch() 0 5 1
A testSortMixedCase() 0 15 1
A testColumnFailureInvalidColumn() 0 5 1
A testColumnFromRelatedTable() 0 14 1
A testFilterOnInvalidRelation() 0 10 1
A testShuffle() 0 5 1
A testFilterMultipleWithArrayFilterAndModifiers() 0 8 1
A testMultipleExcludeWithTwoArrayOneTeam() 0 12 1
A testExcludeAnyMultiArrays() 0 8 1
A testSimpleFilterWithNonExistingComparisator() 0 6 1
A testMultipleExcludeMultipleMatches() 0 5 1
A testFilterOnJoin() 0 13 1
A testSimpleFilterEndsWith() 0 6 1
A testMultipleExclude() 0 5 1
B testFilterByNull() 0 180 1
A testFilterAny() 0 5 1
A testSimpleExclude() 0 8 1
A testReverse() 0 8 1
A testSimpleFilterExactMatchFilter() 0 6 1
A testFilterWithModifiers() 0 12 1
A testVeryComplicatedExcludeOnFilter() 0 29 1
A testFilterSharedRelationalClasses() 0 43 1
A testFilterOnImplicitJoin() 0 20 1
A testSimpleFilter() 0 7 1
A testSimpleFilterWithMultiple() 0 8 1
A testExcludeWithSearchFilter() 0 8 1
A testSimplePartialMatchFilter() 0 7 1
B testAggregateFilters() 0 96 8
A testMultipleExcludeArraysMultipleMatches() 0 11 1
A testSimpleFilterLessThanOrEqualFilter() 0 10 1
A testSimpleFilterGreaterThanOrEqualFilter() 0 10 1
A testSortNumeric() 0 15 1
A testFilterAnyMultipleArray() 0 6 1
A testFilterByCallback() 0 16 1
A testFilterAnyMultipleWithArrayFilter() 0 7 1
A testExcludeAny() 0 8 1
A testInvalidModifier() 0 6 1
A testSortInvalidParameters() 0 8 1
A testSortByRelation() 0 14 1
A testFilterAnyOnFilter() 0 46 1
A testAggregateDBName() 0 12 2
A testSortByComplexExpression() 0 18 1
A testMultipleExcludeWithNoExclusion() 0 9 1
A testCanFilterBy() 0 28 1
A testAggregateFilterExceptions() 0 22 3
A testFilterMultipleWithArrayFilter() 0 8 1
A testSimpleFilterStartsWith() 0 6 1
A testFilterArrayInArray() 0 10 1
A testMultipleExcludeArraysMultipleMatchesOneMiss() 0 17 1
A testSimpleExcludeWithMultiple() 0 6 1
A testFilterAnyArrayInArray() 0 24 1
A testMultipleExcludeWithMultipleThatCheersOnNonExistingTeam() 0 5 1
A testSimpleFilterLessThanFilter() 0 10 1
A testFilterAnyWithTwoGreaterThanFilters() 0 19 2
A testFilterAnyWithRelation() 0 8 1
A testComplicatedExcludeOnFilter() 0 15 1
A testFilterAndExcludeById() 0 9 1
A testExcludeAnyArrays() 0 8 1
A testColumnFailureInvalidTable() 0 8 1
A testMultipleExcludeWithMiss() 0 5 1
A testEmptyFilter() 0 6 1
A testExcludeOnFilter() 0 14 1
A testMultipleExcludeWithMultipleThatCheersEitherTeam() 0 13 1
A testFilterByNullCase() 0 20 1
A testMultipleExcludeWithTwoArray() 0 11 1
A testFilterMultipleArray() 0 7 1

How to fix   Complexity   

Complex Class

Complex classes like DataListTest often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use DataListTest, and based on these observations, apply Extract Interface, too.

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
    public function testSubtractBadDataclassThrowsException()
94
    {
95
        $this->expectException(\InvalidArgumentException::class);
96
        $teamsComments = TeamComment::get();
97
        $teams = Team::get();
98
        $teamsComments->subtract($teams);
99
    }
100
101
    public function testListCreationSortAndLimit()
102
    {
103
        // By default, a DataList will contain all items of that class
104
        $list = TeamComment::get()->sort('ID');
105
106
        // We can iterate on the DataList
107
        $names = array();
108
        foreach ($list as $item) {
109
            $names[] = $item->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...
110
        }
111
        $this->assertEquals(array('Joe', 'Bob', 'Phil'), $names);
112
113
        // If we don't want to iterate, we can extract a single column from the list with column()
114
        $this->assertEquals(array('Joe', 'Bob', 'Phil'), $list->column('Name'));
115
116
        // We can sort a list
117
        $list = $list->sort('Name');
118
        $this->assertEquals(array('Bob', 'Joe', 'Phil'), $list->column('Name'));
119
120
        // We can also restrict the output to a range
121
        $this->assertEquals(array('Joe', 'Phil'), $list->limit(2, 1)->column('Name'));
122
    }
123
124
    public function testLimitAndOffset()
125
    {
126
        $list = TeamComment::get();
127
        $check = $list->limit(3);
128
129
        $this->assertEquals(3, $check->count());
130
131
        $check = $list->limit(1);
132
        $this->assertEquals(1, $check->count());
133
134
        $check = $list->limit(1, 1);
135
        $this->assertEquals(1, $check->count());
136
137
        $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

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

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

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

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

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