Completed
Push — master ( fc353d...f40ed0 )
by Ingo
27s
created

DataListTest   F

Complexity

Total Complexity 86

Size/Duplication

Total Lines 1250
Duplicated Lines 0 %

Coupling/Cohesion

Components 3
Dependencies 16

Importance

Changes 2
Bugs 1 Features 1
Metric Value
c 2
b 1
f 1
dl 0
loc 1250
rs 0.6314
wmc 86
lcom 3
cbo 16

84 Methods

Rating   Name   Duplication   Size   Complexity  
A testSubtract() 0 7 1
A testSubtractBadDataclassThrowsException() 0 6 1
A testListCreationSortAndLimit() 0 21 2
B testLimitAndOffset() 0 25 1
A testDistinct() 0 10 1
A testDataClass() 0 4 1
A testDataClassCaseInsensitive() 0 4 1
A testClone() 0 4 1
A testSql() 0 14 1
B testInnerJoin() 0 26 1
B testInnerJoinParameterised() 0 29 1
A testLeftJoin() 0 50 1
B testLeftJoinParameterised() 0 29 1
B testToNestedArray() 0 31 1
A testMap() 0 18 1
A testEach() 0 14 1
A testWhere() 0 13 1
A testByID() 0 14 1
A testByIDs() 0 9 2
A testRemoveByID() 0 8 1
A testCanSortBy() 0 11 1
A testDataListArrayAccess() 0 8 1
A testFind() 0 5 1
A testFindById() 0 8 1
A testSimpleSort() 0 6 1
A testSimpleSortOneArgumentASC() 0 6 1
A testSimpleSortOneArgumentDESC() 0 6 1
A testSortOneArgumentMultipleColumns() 0 6 1
A testSimpleSortASC() 0 6 1
A testSimpleSortDESC() 0 6 1
A testSortWithArraySyntaxSortASC() 0 6 1
A testSortWithArraySyntaxSortDESC() 0 6 1
A testSortWithMultipleArraySyntaxSort() 0 6 1
A testSortWithCompositeSyntax() 0 8 1
A testSortInvalidParameters() 0 8 1
A setUpOnce() 0 4 1
B testFilterDataObjectByCreatedDate() 0 39 1
B testCanFilterBy() 0 28 1
A testSimpleFilter() 0 7 1
A testSimpleFilterEndsWith() 0 6 1
A testSimpleFilterExactMatchFilter() 0 6 1
A testSimpleFilterGreaterThanFilter() 0 6 1
A testSimpleFilterGreaterThanOrEqualFilter() 0 8 1
A testSimpleFilterLessThanFilter() 0 8 1
A testSimpleFilterLessThanOrEqualFilter() 0 8 1
A testSimplePartialMatchFilter() 0 7 1
A testSimpleFilterStartsWith() 0 6 1
A testSimpleFilterWithNonExistingComparisator() 0 8 1
A testInvalidModifier() 0 9 1
A testSimpleFilterWithMultiple() 0 8 1
A testMultipleFilterWithNoMatch() 0 5 1
A testFilterMultipleArray() 0 7 1
A testFilterMultipleWithTwoMatches() 0 5 1
A testFilterMultipleWithArrayFilter() 0 8 1
A testFilterMultipleWithArrayFilterAndModifiers() 0 8 1
A testFilterArrayInArray() 0 8 1
A testFilterWithModifiers() 0 10 1
A testFilterAny() 0 5 1
A testFilterAnyMultipleArray() 0 6 1
B testFilterAnyOnFilter() 0 39 1
A testFilterAnyMultipleWithArrayFilter() 0 7 1
A testFilterAnyArrayInArray() 0 23 1
A testFilterOnJoin() 0 12 1
A testFilterOnImplicitJoin() 0 20 1
A testFilterOnInvalidRelation() 0 10 1
A testFilterAndExcludeById() 0 18 1
B testFilterByNull() 0 149 1
A testFilterByNullCase() 0 20 1
A testFilterByCallback() 0 14 1
A testSimpleExclude() 0 8 1
A testSimpleExcludeWithMultiple() 0 6 1
A testMultipleExcludeWithMiss() 0 5 1
A testMultipleExclude() 0 5 1
A testExcludeOnFilter() 0 12 1
A testExcludeWithSearchFilter() 0 8 1
A testEmptyFilter() 0 5 1
A testMultipleExcludeWithMultipleThatCheersEitherTeam() 0 11 1
A testMultipleExcludeWithMultipleThatCheersOnNonExistingTeam() 0 5 1
A testMultipleExcludeWithNoExclusion() 0 7 1
A testMultipleExcludeWithTwoArray() 0 9 1
A testMultipleExcludeWithTwoArrayOneTeam() 0 10 1
A testSortByRelation() 0 9 1
A testReverse() 0 8 1
A testSortByComplexExpression() 0 15 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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
use SilverStripe\ORM\DataList;
4
use SilverStripe\ORM\DB;
5
use SilverStripe\ORM\SS_Filterable;
6
7
/**
8
 * @package framework
9
 * @subpackage tests
10
 */
11
class DataListTest extends SapphireTest {
12
13
	// Borrow the model from DataObjectTest
14
	protected static $fixture_file = 'DataObjectTest.yml';
15
16
	public function setUpOnce() {
17
		$this->extraDataObjects = DataObjectTest::$extra_data_objects;
18
		parent::setUpOnce();
19
	}
20
21
	public function testFilterDataObjectByCreatedDate() {
22
		// create an object to test with
23
		$obj1 = new DataObjectTest_ValidatedObject();
24
		$obj1->Name = 'test obj 1';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<DataObjectTest_ValidatedObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
25
		$obj1->write();
26
		$this->assertTrue($obj1->isInDB());
27
28
		// reload the object from the database and reset its Created timestamp to a known value
29
		$obj1 = DataObjectTest_ValidatedObject::get()->filter(array('Name' => 'test obj 1'))->first();
30
		$this->assertTrue(is_object($obj1));
31
		$this->assertEquals('test obj 1', $obj1->Name);
32
		$obj1->Created = '2013-01-01 00:00:00';
33
		$obj1->write();
34
35
		// reload the object again and make sure that our Created date was properly persisted
36
		$obj1 = DataObjectTest_ValidatedObject::get()->filter(array('Name' => 'test obj 1'))->first();
37
		$this->assertTrue(is_object($obj1));
38
		$this->assertEquals('test obj 1', $obj1->Name);
39
		$this->assertEquals('2013-01-01 00:00:00', $obj1->Created);
40
41
		// now save a second object to the DB with an automatically-set Created value
42
		$obj2 = new DataObjectTest_ValidatedObject();
43
		$obj2->Name = 'test obj 2';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<DataObjectTest_ValidatedObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
44
		$obj2->write();
45
		$this->assertTrue($obj2->isInDB());
46
47
		// and a third object
48
		$obj3 = new DataObjectTest_ValidatedObject();
49
		$obj3->Name = 'test obj 3';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<DataObjectTest_ValidatedObject>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
50
		$obj3->write();
51
		$this->assertTrue($obj3->isInDB());
52
53
		// now test the filtering based on Created timestamp
54
		$list = DataObjectTest_ValidatedObject::get()
55
			->filter(array('Created:GreaterThan' => '2013-02-01 00:00:00'))
56
			->toArray();
57
		$this->assertEquals(2, count($list));
58
59
	}
60
61
	public function testSubtract(){
62
		$comment1 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment1');
63
		$subtractList = DataObjectTest_TeamComment::get()->filter('ID', $comment1->ID);
64
		$fullList = DataObjectTest_TeamComment::get();
65
		$newList = $fullList->subtract($subtractList);
66
		$this->assertEquals(2, $newList->Count(), 'List should only contain two objects after subtraction');
67
	}
68
69
	public function testSubtractBadDataclassThrowsException(){
70
		$this->setExpectedException('InvalidArgumentException');
71
		$teamsComments = DataObjectTest_TeamComment::get();
72
		$teams = DataObjectTest_Team::get();
73
		$teamsComments->subtract($teams);
74
	}
75
76
	public function testListCreationSortAndLimit() {
77
		// By default, a DataList will contain all items of that class
78
		$list = DataObjectTest_TeamComment::get()->sort('ID');
79
80
		// We can iterate on the DataList
81
		$names = array();
82
		foreach($list as $item) {
83
			$names[] = $item->Name;
84
		}
85
		$this->assertEquals(array('Joe', 'Bob', 'Phil'), $names);
86
87
		// If we don't want to iterate, we can extract a single column from the list with column()
88
		$this->assertEquals(array('Joe', 'Bob', 'Phil'), $list->column('Name'));
89
90
		// We can sort a list
91
		$list = $list->sort('Name');
92
		$this->assertEquals(array('Bob', 'Joe', 'Phil'), $list->column('Name'));
93
94
		// We can also restrict the output to a range
95
		$this->assertEquals(array('Joe', 'Phil'), $list->limit(2, 1)->column('Name'));
96
	}
97
98
	public function testLimitAndOffset() {
99
		$list = DataObjectTest_TeamComment::get();
100
		$check = $list->limit(3);
101
102
		$this->assertEquals(3, $check->count());
103
104
		$check = $list->limit(1);
105
		$this->assertEquals(1, $check->count());
106
107
		$check = $list->limit(1, 1);
108
		$this->assertEquals(1, $check->count());
109
110
		$check = $list->limit(false);
111
		$this->assertEquals(3, $check->count());
112
113
		$check = $list->limit(null);
114
		$this->assertEquals(3, $check->count());
115
116
		$check = $list->limit(null, 2);
117
		$this->assertEquals(1, $check->count());
118
119
		// count()/first()/last() methods may alter limit/offset, so run the query and manually check the count
120
		$check = $list->limit(null, 1)->toArray();
121
		$this->assertEquals(2, count($check));
122
	}
123
124
	public function testDistinct() {
125
		$list = DataObjectTest_TeamComment::get();
126
		$this->assertContains('SELECT DISTINCT', $list->dataQuery()->sql($params), 'Query is set as distinct by default');
127
128
		$list = $list->distinct(false);
129
		$this->assertNotContains('SELECT DISTINCT', $list->dataQuery()->sql($params), 'Query does not contain distinct');
130
131
		$list = $list->distinct(true);
132
		$this->assertContains('SELECT DISTINCT', $list->dataQuery()->sql($params), 'Query contains distinct');
133
	}
134
135
	public function testDataClass() {
136
		$list = DataObjectTest_TeamComment::get();
137
		$this->assertEquals('DataObjectTest_TeamComment',$list->dataClass());
138
	}
139
140
	public function testDataClassCaseInsensitive() {
141
		$list = DataList::create('dataobjecttest_teamcomment');
142
		$this->assertTrue($list->exists());
143
	}
144
145
	public function testClone() {
146
		$list = DataObjectTest_TeamComment::get();
147
		$this->assertEquals($list, clone($list));
148
	}
149
150
	public function testSql() {
151
		$db = DB::get_conn();
152
		$list = DataObjectTest_TeamComment::get();
153
		$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
154
			. '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
155
			. '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
156
			. '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
157
			. 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL '
158
			. 'THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
159
			. $db->quoteString('DataObjectTest_TeamComment')
160
			. ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment"'
161
			. ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
162
		$this->assertSQLEquals($expected, $list->sql($parameters));
163
	}
164
165
	public function testInnerJoin() {
166
		$db = DB::get_conn();
167
168
		$list = DataObjectTest_TeamComment::get();
169
		$list = $list->innerJoin(
170
			'DataObjectTest_Team',
171
			'"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"',
172
			'Team'
173
		);
174
175
		$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
176
			. '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
177
			. '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
178
			. '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
179
			. 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
180
			. ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
181
			. $db->quoteString('DataObjectTest_TeamComment')
182
			. ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" INNER JOIN '
183
			. '"DataObjectTest_Team" AS "Team" ON "DataObjectTest_Team"."ID" = '
184
			. '"DataObjectTest_TeamComment"."TeamID"'
185
			. ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
186
187
188
		$this->assertSQLEquals($expected, $list->sql($parameters));
189
		$this->assertEmpty($parameters);
190
	}
191
192
	public function testInnerJoinParameterised() {
193
		$db = DB::get_conn();
194
195
		$list = DataObjectTest_TeamComment::get();
196
		$list = $list->innerJoin(
197
			'DataObjectTest_Team',
198
			'"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID" '
199
			. 'AND "DataObjectTest_Team"."Title" LIKE ?',
200
			'Team',
201
			20,
202
			array('Team%')
203
		);
204
205
		$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
206
			. '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
207
			. '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
208
			. '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
209
			. 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
210
			. ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
211
			. $db->quoteString('DataObjectTest_TeamComment')
212
			. ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" INNER JOIN '
213
			. '"DataObjectTest_Team" AS "Team" ON "DataObjectTest_Team"."ID" = '
214
			. '"DataObjectTest_TeamComment"."TeamID" '
215
			. 'AND "DataObjectTest_Team"."Title" LIKE ?'
216
			. ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
217
218
		$this->assertSQLEquals($expected, $list->sql($parameters));
219
		$this->assertEquals(array('Team%'), $parameters);
220
	}
221
222
	public function testLeftJoin() {
223
		$db = DB::get_conn();
224
225
		$list = DataObjectTest_TeamComment::get();
226
		$list = $list->leftJoin(
227
			'DataObjectTest_Team',
228
			'"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"',
229
			'Team'
230
		);
231
232
		$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
233
			. '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
234
			. '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
235
			. '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
236
			. 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL '
237
			. 'THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
238
			. $db->quoteString('DataObjectTest_TeamComment')
239
			. ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" LEFT JOIN "DataObjectTest_Team" '
240
			. 'AS "Team" ON "DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"'
241
			. ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
242
243
244
		$this->assertSQLEquals($expected, $list->sql($parameters));
245
		$this->assertEmpty($parameters);
246
247
		// Test with namespaces (with non-sensical join, but good enough for testing)
248
		$list = DataObjectTest_TeamComment::get();
249
		$list = $list->leftJoin(
250
			'DataObjectTest\NamespacedClass',
251
			'"DataObjectTest\NamespacedClass"."ID" = "DataObjectTest_TeamComment"."ID"'
252
		);
253
254
		$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
255
			. '"DataObjectTest_TeamComment"."LastEdited", '
256
			. '"DataObjectTest_TeamComment"."Created", '
257
			. '"DataObjectTest_TeamComment"."Name", '
258
			. '"DataObjectTest_TeamComment"."Comment", '
259
			. '"DataObjectTest_TeamComment"."TeamID", '
260
			. '"DataObjectTest_TeamComment"."ID", '
261
			. 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL '
262
			. 'THEN "DataObjectTest_TeamComment"."ClassName" '
263
			. 'ELSE ' . $db->quoteString('DataObjectTest_TeamComment') . ' END AS "RecordClassName" '
264
			. 'FROM "DataObjectTest_TeamComment" '
265
			. 'LEFT JOIN "DataObjectTest\NamespacedClass" ON '
266
			. '"DataObjectTest\NamespacedClass"."ID" = "DataObjectTest_TeamComment"."ID"'
267
			. ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
268
		$this->assertSQLEquals($expected, $list->sql($parameters), 'Retains backslashes in namespaced classes');
269
		$this->assertEmpty($parameters);
270
271
	}
272
273
	public function testLeftJoinParameterised() {
274
		$db = DB::get_conn();
275
276
		$list = DataObjectTest_TeamComment::get();
277
		$list = $list->leftJoin(
278
			'DataObjectTest_Team',
279
			'"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID" '
280
			. 'AND "DataObjectTest_Team"."Title" LIKE ?',
281
			'Team',
282
			20,
283
			array('Team%')
284
		);
285
286
		$expected = 'SELECT DISTINCT "DataObjectTest_TeamComment"."ClassName", '
287
			. '"DataObjectTest_TeamComment"."LastEdited", "DataObjectTest_TeamComment"."Created", '
288
			. '"DataObjectTest_TeamComment"."Name", "DataObjectTest_TeamComment"."Comment", '
289
			. '"DataObjectTest_TeamComment"."TeamID", "DataObjectTest_TeamComment"."ID", '
290
			. 'CASE WHEN "DataObjectTest_TeamComment"."ClassName" IS NOT NULL'
291
			. ' THEN "DataObjectTest_TeamComment"."ClassName" ELSE '
292
			. $db->quoteString('DataObjectTest_TeamComment')
293
			. ' END AS "RecordClassName" FROM "DataObjectTest_TeamComment" LEFT JOIN '
294
			. '"DataObjectTest_Team" AS "Team" ON "DataObjectTest_Team"."ID" = '
295
			. '"DataObjectTest_TeamComment"."TeamID" '
296
			. 'AND "DataObjectTest_Team"."Title" LIKE ?'
297
			. ' ORDER BY "DataObjectTest_TeamComment"."Name" ASC';
298
299
		$this->assertSQLEquals($expected, $list->sql($parameters));
300
		$this->assertEquals(array('Team%'), $parameters);
301
	}
302
303
	public function testToNestedArray() {
304
		$list = DataObjectTest_TeamComment::get()->sort('ID');
305
		$nestedArray = $list->toNestedArray();
306
		$expected = array(
307
			0=>
308
			array(
309
				'ClassName'=>'DataObjectTest_TeamComment',
310
				'Name'=>'Joe',
311
				'Comment'=>'This is a team comment by Joe',
312
				'TeamID'=> $this->objFromFixture('DataObjectTest_TeamComment', 'comment1')->TeamID,
313
			),
314
			1=>
315
			array(
316
				'ClassName'=>'DataObjectTest_TeamComment',
317
				'Name'=>'Bob',
318
				'Comment'=>'This is a team comment by Bob',
319
				'TeamID'=> $this->objFromFixture('DataObjectTest_TeamComment', 'comment2')->TeamID,
320
			),
321
			2=>
322
			array(
323
				'ClassName'=>'DataObjectTest_TeamComment',
324
				'Name'=>'Phil',
325
				'Comment'=>'Phil is a unique guy, and comments on team2',
326
				'TeamID'=> $this->objFromFixture('DataObjectTest_TeamComment', 'comment3')->TeamID,
327
			),
328
		);
329
		$this->assertEquals(3, count($nestedArray));
330
		$this->assertEquals($expected[0]['Name'], $nestedArray[0]['Name']);
331
		$this->assertEquals($expected[1]['Comment'], $nestedArray[1]['Comment']);
332
		$this->assertEquals($expected[2]['TeamID'], $nestedArray[2]['TeamID']);
333
	}
334
335
	public function testMap() {
336
		$map = DataObjectTest_TeamComment::get()->map()->toArray();
337
		$expected = array(
338
			$this->idFromFixture('DataObjectTest_TeamComment', 'comment1') => 'Joe',
339
			$this->idFromFixture('DataObjectTest_TeamComment', 'comment2') => 'Bob',
340
			$this->idFromFixture('DataObjectTest_TeamComment', 'comment3') => 'Phil'
341
		);
342
343
		$this->assertEquals($expected, $map);
344
		$otherMap = DataObjectTest_TeamComment::get()->map('Name', 'TeamID')->toArray();
345
		$otherExpected = array(
346
			'Joe' => $this->objFromFixture('DataObjectTest_TeamComment', 'comment1')->TeamID,
347
			'Bob' => $this->objFromFixture('DataObjectTest_TeamComment', 'comment2')->TeamID,
348
			'Phil' => $this->objFromFixture('DataObjectTest_TeamComment', 'comment3')->TeamID
349
		);
350
351
		$this->assertEquals($otherExpected, $otherMap);
352
	}
353
354
	public function testEach() {
355
		$list = DataObjectTest_TeamComment::get();
356
357
		$count = 0;
358
		$test = $this;
359
360
		$list->each(function($item) use (&$count, $test) {
361
			$count++;
362
363
			$test->assertTrue(is_a($item, "DataObjectTest_TeamComment"));
364
		});
365
366
		$this->assertEquals($list->Count(), $count);
367
	}
368
369
	public function testWhere() {
370
		// We can use raw SQL queries with where.  This is only recommended for advanced uses;
371
		// if you can, you should use filter().
372
		$list = DataObjectTest_TeamComment::get();
373
374
		// where() returns a new DataList, like all the other modifiers, so it can be chained.
375
		$list2 = $list->where('"Name" = \'Joe\'');
376
		$this->assertEquals(array('This is a team comment by Joe'), $list2->column('Comment'));
377
378
		// The where() clauses are chained together with AND
379
		$list3 = $list2->where('"Name" = \'Bob\'');
380
		$this->assertEquals(array(), $list3->column('Comment'));
381
	}
382
383
	/**
384
	 * Test DataList->byID()
385
	 */
386
	public function testByID() {
387
		// We can get a single item by ID.
388
		$id = $this->idFromFixture('DataObjectTest_Team','team2');
389
		$team = DataObjectTest_Team::get()->byID($id);
390
391
		// byID() returns a DataObject, rather than a DataList
392
		$this->assertInstanceOf('DataObjectTest_Team', $team);
393
		$this->assertEquals('Team 2', $team->Title);
394
395
		// Assert that filtering on ID searches by the base table, not the child table field
396
		$query = DataObjectTest_SubTeam::get()->filter('ID', 4)->sql($parameters);
397
		$this->assertContains('WHERE ("DataObjectTest_Team"."ID" = ?)', $query);
398
		$this->assertNotContains('WHERE ("DataObjectTest_SubTeam"."ID" = ?)', $query);
399
	}
400
401
	public function testByIDs() {
402
		$knownIDs = $this->allFixtureIDs('DataObjectTest_Player');
403
		$removedID = array_pop($knownIDs);
404
		$filteredPlayers = DataObjectTest_Player::get()->byIDs($knownIDs);
405
		foreach ($filteredPlayers as $player) {
406
			$this->assertContains($player->ID, $knownIDs);
407
			$this->assertNotEquals($removedID, $player->ID);
408
		}
409
	}
410
411
	/**
412
	 * Test DataList->removeByID()
413
	 */
414
	public function testRemoveByID() {
415
		$list = DataObjectTest_Team::get();
416
		$id = $this->idFromFixture('DataObjectTest_Team','team2');
417
418
		$this->assertNotNull($list->byID($id));
419
		$list->removeByID($id);
420
		$this->assertNull($list->byID($id));
421
	}
422
423
	/**
424
	 * Test DataList->canSortBy()
425
	 */
426
	public function testCanSortBy() {
427
		// Basic check
428
		$team = DataObjectTest_Team::get();
429
		$this->assertTrue($team->canSortBy("Title"));
430
		$this->assertFalse($team->canSortBy("SomethingElse"));
431
432
		// Subclasses
433
		$subteam = DataObjectTest_SubTeam::get();
434
		$this->assertTrue($subteam->canSortBy("Title"));
435
		$this->assertTrue($subteam->canSortBy("SubclassDatabaseField"));
436
	}
437
438
	public function testDataListArrayAccess() {
439
		$list = DataObjectTest_Team::get()->sort('Title');
440
441
		// We can use array access to refer to single items in the DataList, as if it were an array
442
		$this->assertEquals("Subteam 1", $list[0]->Title);
443
		$this->assertEquals("Subteam 3", $list[2]->Title);
444
		$this->assertEquals("Team 2", $list[4]->Title);
445
	}
446
447
	public function testFind() {
448
		$list = DataObjectTest_Team::get();
449
		$record = $list->find('Title', 'Team 1');
450
		$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $record->ID);
451
	}
452
453
	public function testFindById() {
454
		$list = DataObjectTest_Team::get();
455
		$record = $list->find('ID', $this->idFromFixture('DataObjectTest_Team', 'team1'));
456
		$this->assertEquals('Team 1', $record->Title);
457
		// Test that you can call it twice on the same list
458
		$record = $list->find('ID', $this->idFromFixture('DataObjectTest_Team', 'team2'));
459
		$this->assertEquals('Team 2', $record->Title);
460
	}
461
462
	public function testSimpleSort() {
463
		$list = DataObjectTest_TeamComment::get();
464
		$list = $list->sort('Name');
465
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
466
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
467
	}
468
469
	public function testSimpleSortOneArgumentASC() {
470
		$list = DataObjectTest_TeamComment::get();
471
		$list = $list->sort('Name ASC');
472
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
473
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
474
	}
475
476
	public function testSimpleSortOneArgumentDESC() {
477
		$list = DataObjectTest_TeamComment::get();
478
		$list = $list->sort('Name DESC');
479
		$this->assertEquals('Phil', $list->first()->Name, 'Last comment should be from Phil');
480
		$this->assertEquals('Bob', $list->last()->Name, 'First comment should be from Bob');
481
	}
482
483
	public function testSortOneArgumentMultipleColumns() {
484
		$list = DataObjectTest_TeamComment::get();
485
		$list = $list->sort('TeamID ASC, Name DESC');
486
		$this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Bob');
487
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
488
	}
489
490
	public function testSimpleSortASC() {
491
		$list = DataObjectTest_TeamComment::get();
492
		$list = $list->sort('Name', 'asc');
493
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
494
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
495
	}
496
497
	public function testSimpleSortDESC() {
498
		$list = DataObjectTest_TeamComment::get();
499
		$list = $list->sort('Name', 'desc');
500
		$this->assertEquals('Phil', $list->first()->Name, 'Last comment should be from Phil');
501
		$this->assertEquals('Bob', $list->last()->Name, 'First comment should be from Bob');
502
	}
503
504
	public function testSortWithArraySyntaxSortASC() {
505
		$list = DataObjectTest_TeamComment::get();
506
		$list = $list->sort(array('Name'=>'asc'));
507
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
508
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
509
	}
510
511
	public function testSortWithArraySyntaxSortDESC() {
512
		$list = DataObjectTest_TeamComment::get();
513
		$list = $list->sort(array('Name'=>'desc'));
514
		$this->assertEquals('Phil', $list->first()->Name, 'Last comment should be from Phil');
515
		$this->assertEquals('Bob', $list->last()->Name, 'First comment should be from Bob');
516
	}
517
518
	public function testSortWithMultipleArraySyntaxSort() {
519
		$list = DataObjectTest_TeamComment::get();
520
		$list = $list->sort(array('TeamID'=>'asc','Name'=>'desc'));
521
		$this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Bob');
522
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
523
	}
524
525
	public function testSortWithCompositeSyntax() {
526
		// Phil commented on team with founder surname "Aaron"
527
		$list = DataObjectTest_TeamComment::get();
528
		$list = $list->sort('Team.Founder.Surname', 'asc');
529
		$this->assertEquals('Phil', $list->first()->Name);
530
		$list = $list->sort('Team.Founder.Surname', 'desc');
531
		$this->assertEquals('Phil', $list->last()->Name);
532
	}
533
534
	public function testSortInvalidParameters() {
535
		$this->setExpectedException(
536
			'InvalidArgumentException',
537
			'Fans is not a linear relation on model DataObjectTest_Player'
538
		);
539
		$list = DataObjectTest_Team::get();
540
		$list = $list->sort('Founder.Fans.Surname'); // Can't sort on has_many
541
	}
542
543
544
545
	/**
546
	 * Test DataList->canFilterBy()
547
	 */
548
	public function testCanFilterBy() {
549
		// Basic check
550
		$team = DataObjectTest_Team::get();
551
		$this->assertTrue($team->canFilterBy("Title"));
552
		$this->assertFalse($team->canFilterBy("SomethingElse"));
553
554
		// Has one
555
		$this->assertTrue($team->canFilterBy("CaptainID"));
556
		$this->assertTrue($team->canFilterBy("Captain.ShirtNumber"));
557
		$this->assertFalse($team->canFilterBy("SomethingElse.ShirtNumber"));
558
		$this->assertFalse($team->canFilterBy("Captain.SomethingElse"));
559
		$this->assertTrue($team->canFilterBy("Captain.FavouriteTeam.Captain.ShirtNumber"));
560
561
		// Has many
562
		$this->assertTrue($team->canFilterBy("Fans.Name"));
563
		$this->assertFalse($team->canFilterBy("SomethingElse.Name"));
564
		$this->assertFalse($team->canFilterBy("Fans.SomethingElse"));
565
566
		// Many many
567
		$this->assertTrue($team->canFilterBy("Players.FirstName"));
568
		$this->assertFalse($team->canFilterBy("SomethingElse.FirstName"));
569
		$this->assertFalse($team->canFilterBy("Players.SomethingElse"));
570
571
		// Subclasses
572
		$subteam = DataObjectTest_SubTeam::get();
573
		$this->assertTrue($subteam->canFilterBy("Title"));
574
		$this->assertTrue($subteam->canFilterBy("SubclassDatabaseField"));
575
	}
576
577
	/**
578
	 * $list->filter('Name', 'bob'); // only bob in the list
579
	 */
580
	public function testSimpleFilter() {
581
		$list = DataObjectTest_Team::get();
582
		$list = $list->filter('Title','Team 2');
583
		$this->assertEquals(1, $list->count());
584
		$this->assertEquals('Team 2', $list->first()->Title, 'List should only contain Team 2');
585
		$this->assertEquals('Team 2', $list->last()->Title, 'Last should only contain Team 2');
586
	}
587
588
	public function testSimpleFilterEndsWith() {
589
		$list = DataObjectTest_TeamComment::get();
590
		$list = $list->filter('Name:EndsWith', 'b');
591
		$this->assertEquals(1, $list->count());
592
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
593
	}
594
595
	public function testSimpleFilterExactMatchFilter() {
596
		$list = DataObjectTest_TeamComment::get();
597
		$list = $list->filter('Name:ExactMatch', 'Bob');
598
		$this->assertEquals(1, $list->count());
599
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
600
	}
601
602
	public function testSimpleFilterGreaterThanFilter() {
603
		$list = DataObjectTest_TeamComment::get();
604
		$list = $list->filter('TeamID:GreaterThan', $this->idFromFixture('DataObjectTest_Team', 'team1'));
605
		$this->assertEquals(1, $list->count());
606
		$this->assertEquals('Phil', $list->first()->Name, 'First comment should be from Phil');
607
	}
608
609
	public function testSimpleFilterGreaterThanOrEqualFilter() {
610
		$list = DataObjectTest_TeamComment::get();
611
		$list = $list->filter('TeamID:GreaterThanOrEqual',
612
			$this->idFromFixture('DataObjectTest_Team', 'team1'))->sort("ID");
613
		$this->assertEquals(3, $list->count());
614
		$this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
615
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
616
	}
617
618
	public function testSimpleFilterLessThanFilter() {
619
		$list = DataObjectTest_TeamComment::get();
620
		$list = $list->filter('TeamID:LessThan',
621
			$this->idFromFixture('DataObjectTest_Team', 'team2'))->sort('Name');
622
		$this->assertEquals(2, $list->count());
623
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
624
		$this->assertEquals('Joe', $list->Last()->Name, 'Last comment should be from Joe');
625
	}
626
627
	public function testSimpleFilterLessThanOrEqualFilter() {
628
		$list = DataObjectTest_TeamComment::get();
629
		$list = $list->filter('TeamID:LessThanOrEqual',
630
			$this->idFromFixture('DataObjectTest_Team', 'team1'))->sort('ID');
631
		$this->assertEquals(2, $list->count());
632
		$this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
633
		$this->assertEquals('Bob', $list->Last()->Name, 'Last comment should be from Bob');
634
	}
635
636
	public function testSimplePartialMatchFilter() {
637
		$list = DataObjectTest_TeamComment::get();
638
		$list = $list->filter('Name:PartialMatch', 'o')->sort('Name');
639
		$this->assertEquals(2, $list->count());
640
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
641
		$this->assertEquals('Joe', $list->last()->Name, 'First comment should be from Joe');
642
	}
643
644
	public function testSimpleFilterStartsWith() {
645
		$list = DataObjectTest_TeamComment::get();
646
		$list = $list->filter('Name:StartsWith', 'B');
647
		$this->assertEquals(1, $list->count());
648
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
649
	}
650
651
	public function testSimpleFilterWithNonExistingComparisator() {
652
		$this->setExpectedException(
653
			'ReflectionException',
654
			'Class DataListFilter.Bogus does not exist'
655
		);
656
		$list = DataObjectTest_TeamComment::get();
657
		$list->filter('Comment:Bogus', 'team comment');
658
	}
659
660
	public function testInvalidModifier() {
661
		// Invalid modifiers are treated as failed filter construction
662
		$this->setExpectedException(
663
			'ReflectionException',
664
			'Class DataListFilter.invalidmodifier does not exist'
665
		);
666
		$list = DataObjectTest_TeamComment::get();
667
		$list->filter('Comment:invalidmodifier', 'team comment');
668
	}
669
670
	/**
671
	 * $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
672
	 */
673
	public function testSimpleFilterWithMultiple() {
674
		$list = DataObjectTest_TeamComment::get();
675
		$list = $list->filter('Name', array('Bob','Phil'));
676
		$list = $list->sort('Name', 'ASC');
677
		$this->assertEquals(2, $list->count());
678
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
679
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
680
	}
681
682
	public function testMultipleFilterWithNoMatch() {
683
		$list = DataObjectTest_TeamComment::get();
684
		$list = $list->filter(array('Name'=>'Bob', 'Comment'=>'Phil is a unique guy, and comments on team2'));
685
		$this->assertEquals(0, $list->count());
686
	}
687
688
	/**
689
	 *  $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the age 21
690
	 */
691
	public function testFilterMultipleArray() {
692
		$list = DataObjectTest_TeamComment::get();
693
		$list = $list->filter(array('Name'=>'Bob', 'Comment'=>'This is a team comment by Bob'));
694
		$list = $list->sort('Name', 'ASC');
695
		$this->assertEquals(1, $list->count());
696
		$this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
697
	}
698
699
	public function testFilterMultipleWithTwoMatches() {
700
		$list = DataObjectTest_TeamComment::get();
701
		$list = $list->filter(array('TeamID'=>$this->idFromFixture('DataObjectTest_Team', 'team1')));
702
		$this->assertEquals(2, $list->count());
703
	}
704
705
	public function testFilterMultipleWithArrayFilter() {
706
		$list = DataObjectTest_TeamComment::get();
707
		$list = $list->filter(array('Name'=>array('Bob','Phil')));
708
		$list = $list->sort('Name', 'ASC');
709
		$this->assertEquals(2, $list->count(), 'There should be two comments');
710
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
711
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
712
	}
713
714
	public function testFilterMultipleWithArrayFilterAndModifiers() {
715
		$list = DataObjectTest_TeamComment::get();
716
		$list = $list->filter(array('Name:StartsWith'=>array('Bo', 'Jo')));
717
		$list = $list->sort('Name', 'ASC');
718
		$this->assertEquals(2, $list->count());
719
		$this->assertEquals('Bob', $list->first()->Name);
720
		$this->assertEquals('Joe', $list->last()->Name);
721
	}
722
723
	/**
724
	 * $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43)));
725
	 */
726
	public function testFilterArrayInArray() {
727
		$list = DataObjectTest_TeamComment::get();
728
		$list = $list->filter(array(
729
			'Name'=>array('Bob','Phil'),
730
			'TeamID'=>array($this->idFromFixture('DataObjectTest_Team', 'team1'))));
731
		$this->assertEquals(1, $list->count(), 'There should be one comment');
732
		$this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
733
	}
734
735
	public function testFilterWithModifiers() {
736
		$list = DataObjectTest_TeamComment::get();
737
		$nocaseList = $list->filter('Name:nocase', 'bob');
738
		$this->assertEquals(1, $nocaseList->count(), 'There should be one comment');
739
		$caseList = $list->filter('Name:case', 'bob');
740
		$this->assertEquals(0, $caseList->count(), 'There should be no comments');
741
		$gtList = $list->filter('TeamID:GreaterThan:not',
742
			$this->idFromFixture('DataObjectTest_Team', 'team1'));
743
		$this->assertEquals(2, $gtList->count());
744
	}
745
746
	public function testFilterAny() {
747
		$list = DataObjectTest_TeamComment::get();
748
		$list = $list->filterAny('Name', 'Bob');
749
		$this->assertEquals(1, $list->count());
750
	}
751
752
	public function testFilterAnyMultipleArray() {
753
		$list = DataObjectTest_TeamComment::get();
754
		$list = $list->filterAny(array('Name'=>'Bob', 'Comment'=>'This is a team comment by Bob'));
755
		$this->assertEquals(1, $list->count());
756
		$this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
757
	}
758
759
	public function testFilterAnyOnFilter() {
760
		$list = DataObjectTest_TeamComment::get();
761
		$list = $list->filter(array(
762
			'TeamID'=>$this->idFromFixture('DataObjectTest_Team', 'team1')
763
		));
764
		$list = $list->filterAny(array(
765
			'Name'=>array('Phil', 'Joe'),
766
			'Comment'=>'This is a team comment by Bob'
767
		));
768
		$list = $list->sort('Name');
769
		$this->assertEquals(2, $list->count());
770
		$this->assertEquals(
771
			'Bob',
772
			$list->offsetGet(0)->Name,
773
			'Results should include comments from Bob, matched by comment and team'
774
		);
775
		$this->assertEquals(
776
			'Joe',
777
			$list->offsetGet(1)->Name,
778
			'Results should include comments by Joe, matched by name and team (not by comment)'
779
		);
780
781
		$list = DataObjectTest_TeamComment::get();
782
		$list = $list->filter(array(
783
			'TeamID'=>$this->idFromFixture('DataObjectTest_Team', 'team1')
784
		));
785
		$list = $list->filterAny(array(
786
			'Name'=>array('Phil', 'Joe'),
787
			'Comment'=>'This is a team comment by Bob'
788
		));
789
		$list = $list->sort('Name');
790
		$list = $list->filter(array('Name' => 'Bob'));
791
		$this->assertEquals(1, $list->count());
792
		$this->assertEquals(
793
			'Bob',
794
			$list->offsetGet(0)->Name,
795
			'Results should include comments from Bob, matched by name and team'
796
		);
797
	}
798
799
	public function testFilterAnyMultipleWithArrayFilter() {
800
		$list = DataObjectTest_TeamComment::get();
801
		$list = $list->filterAny(array('Name'=>array('Bob','Phil')));
802
		$this->assertEquals(2, $list->count(), 'There should be two comments');
803
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
804
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
805
	}
806
807
	public function testFilterAnyArrayInArray() {
808
		$list = DataObjectTest_TeamComment::get();
809
		$list = $list->filterAny(array(
810
			'Name'=>array('Bob','Phil'),
811
			'TeamID'=>array($this->idFromFixture('DataObjectTest_Team', 'team1'))))
812
			->sort('Name');
813
		$this->assertEquals(3, $list->count());
814
		$this->assertEquals(
815
			'Bob',
816
			$list->offsetGet(0)->Name,
817
			'Results should include comments from Bob, matched by name and team'
818
		);
819
		$this->assertEquals(
820
			'Joe',
821
			$list->offsetGet(1)->Name,
822
			'Results should include comments by Joe, matched by team (not by name)'
823
		);
824
		$this->assertEquals(
825
			'Phil',
826
			$list->offsetGet(2)->Name,
827
			'Results should include comments from Phil, matched by name (even if he\'s not in Team1)'
828
		);
829
	}
830
831
	public function testFilterOnJoin() {
832
		$list = DataObjectTest_TeamComment::get()
833
			->leftJoin('DataObjectTest_Team',
834
				'"DataObjectTest_Team"."ID" = "DataObjectTest_TeamComment"."TeamID"'
835
			)->filter(array(
836
				'Title' => 'Team 1'
837
			));
838
839
		$this->assertEquals(2, $list->count());
840
		$values = $list->column('Name');
841
		$this->assertEquals(array_intersect($values, array('Joe', 'Bob')), $values);
842
	}
843
844
	public function testFilterOnImplicitJoin() {
845
		// Many to many
846
		$list = DataObjectTest_Team::get()
847
			->filter('Players.FirstName', array('Captain', 'Captain 2'));
848
849
		$this->assertEquals(2, $list->count());
850
851
		// Has many
852
		$list = DataObjectTest_Team::get()
853
			->filter('Comments.Name', array('Joe', 'Phil'));
854
855
		$this->assertEquals(2, $list->count());
856
857
		// Has one
858
		$list = DataObjectTest_Player::get()
859
			->filter('FavouriteTeam.Title', 'Team 1');
860
861
		$this->assertEquals(1, $list->count());
862
		$this->assertEquals('007', $list->first()->ShirtNumber);
863
	}
864
865
	public function testFilterOnInvalidRelation() {
866
		$this->setExpectedException(
867
			'InvalidArgumentException',
868
			"MascotAnimal is not a relation on model DataObjectTest_Team"
869
		);
870
		// Filter on missing relation 'MascotAnimal'
871
		DataObjectTest_Team::get()
872
			->filter('MascotAnimal.Name', 'Richard')
873
			->toArray();
874
	}
875
876
	public function testFilterAndExcludeById() {
877
		$id = $this->idFromFixture('DataObjectTest_SubTeam', 'subteam1');
878
		$list = DataObjectTest_SubTeam::get()->filter('ID', $id);
879
		$this->assertEquals($id, $list->first()->ID);
880
881
		$list = DataObjectTest_SubTeam::get();
882
		$this->assertEquals(3, count($list));
883
		$this->assertEquals(2, count($list->exclude('ID', $id)));
884
885
		// Check that classes with namespaces work.
886
		$obj = new DataObjectTest\NamespacedClass();
887
		$obj->Name = "Test";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<DataObjectTest\NamespacedClass>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
888
		$obj->write();
889
890
		$list = DataObjectTest\NamespacedClass::get()->filter('ID', $obj->ID);
891
		$this->assertEquals('Test', $list->First()->Name);
892
		$this->assertEquals(0, $list->exclude('ID', $obj->ID)->count());
893
	}
894
895
	public function testFilterByNull() {
896
		$list = DataObjectTest_Fan::get();
897
		// Force DataObjectTest_Fan/fan5::Email to empty string
898
		$fan5id = $this->idFromFixture('DataObjectTest_Fan', 'fan5');
899
		DB::prepared_query("UPDATE \"DataObjectTest_Fan\" SET \"Email\" = '' WHERE \"ID\" = ?", array($fan5id));
900
901
		// Filter by null email
902
		$nullEmails = $list->filter('Email', null);
903
		$this->assertDOSEquals(array(
904
			array(
905
				'Name' => 'Stephen',
906
			),
907
			array(
908
				'Name' => 'Mitch',
909
			)
910
		), $nullEmails);
911
912
		// Filter by non-null
913
		$nonNullEmails = $list->filter('Email:not', null);
914
		$this->assertDOSEquals(array(
915
			array(
916
				'Name' => 'Damian',
917
				'Email' => '[email protected]',
918
			),
919
			array(
920
				'Name' => 'Richard',
921
				'Email' => '[email protected]',
922
			),
923
			array(
924
				'Name' => 'Hamish',
925
			)
926
		), $nonNullEmails);
927
928
		// Filter by empty only
929
		$emptyOnly = $list->filter('Email', '');
930
		$this->assertDOSEquals(array(
931
			array(
932
				'Name' => 'Hamish',
933
			)
934
		), $emptyOnly);
935
936
		// Non-empty only. This should include null values, since ExactMatchFilter works around
937
		// the caveat that != '' also excludes null values in ANSI SQL-92 behaviour.
938
		$nonEmptyOnly = $list->filter('Email:not', '');
939
		$this->assertDOSEquals(array(
940
			array(
941
				'Name' => 'Damian',
942
				'Email' => '[email protected]',
943
			),
944
			array(
945
				'Name' => 'Richard',
946
				'Email' => '[email protected]',
947
			),
948
			array(
949
				'Name' => 'Stephen',
950
			),
951
			array(
952
				'Name' => 'Mitch',
953
			)
954
		), $nonEmptyOnly);
955
956
		// Filter by many including null, empty string, and non-empty
957
		$items1 = $list->filter('Email', array(null, '', '[email protected]'));
958
		$this->assertDOSEquals(array(
959
			array(
960
				'Name' => 'Damian',
961
				'Email' => '[email protected]',
962
			),
963
			array(
964
				'Name' => 'Stephen',
965
			),
966
			array(
967
				'Name' => 'Mitch',
968
			),
969
			array(
970
				'Name' => 'Hamish',
971
			)
972
		), $items1);
973
974
		// Filter exclusion of above list
975
		$items2 = $list->filter('Email:not', array(null, '', '[email protected]'));
976
		$this->assertDOSEquals(array(
977
			array(
978
				'Name' => 'Richard',
979
				'Email' => '[email protected]',
980
			),
981
		), $items2);
982
983
		// Filter by many including empty string and non-empty
984
		$items3 = $list->filter('Email', array('', '[email protected]'));
985
		$this->assertDOSEquals(array(
986
			array(
987
				'Name' => 'Damian',
988
				'Email' => '[email protected]',
989
			),
990
			array(
991
				'Name' => 'Hamish',
992
			)
993
		), $items3);
994
995
		// Filter by many including empty string and non-empty
996
		// This also relies no the workaround for null comparison as in the $nonEmptyOnly test
997
		$items4 = $list->filter('Email:not', array('', '[email protected]'));
998
		$this->assertDOSEquals(array(
999
			array(
1000
				'Name' => 'Richard',
1001
				'Email' => '[email protected]',
1002
			),
1003
			array(
1004
				'Name' => 'Stephen',
1005
			),
1006
			array(
1007
				'Name' => 'Mitch',
1008
			)
1009
		), $items4);
1010
1011
		// Filter by many including empty string and non-empty
1012
		// The extra null check isn't necessary, but check that this doesn't fail
1013
		$items5 = $list->filterAny(array(
1014
			'Email:not' => array('', '[email protected]'),
1015
			'Email' => null
1016
		));
1017
		$this->assertDOSEquals(array(
1018
			array(
1019
				'Name' => 'Richard',
1020
				'Email' => '[email protected]',
1021
			),
1022
			array(
1023
				'Name' => 'Stephen',
1024
			),
1025
			array(
1026
				'Name' => 'Mitch',
1027
			)
1028
		), $items5);
1029
1030
		// Filter by null or empty values
1031
		$items6 = $list->filter('Email', array(null, ''));
1032
		$this->assertDOSEquals(array(
1033
			array(
1034
				'Name' => 'Stephen',
1035
			),
1036
			array(
1037
				'Name' => 'Mitch',
1038
			),
1039
			array(
1040
				'Name' => 'Hamish',
1041
			)
1042
		), $items6);
1043
	}
1044
1045
	/**
1046
	 * Test null checks with case modifiers
1047
	 */
1048
	public function testFilterByNullCase() {
1049
		// Test with case (case/nocase both use same code path)
1050
		// Test with and without null, and with inclusion/exclusion permutations
1051
		$list = DataObjectTest_Fan::get();
1052
1053
		// Only an explicit NOT NULL should include null values
1054
		$items6 = $list->filter('Email:not:case', array(null, '', '[email protected]'));
1055
		$this->assertSQLContains(' AND "DataObjectTest_Fan"."Email" IS NOT NULL', $items6->sql());
1056
1057
		// These should all include values where Email IS NULL
1058
		$items7 = $list->filter('Email:nocase', array(null, '', '[email protected]'));
1059
		$this->assertSQLContains(' OR "DataObjectTest_Fan"."Email" IS NULL', $items7->sql());
1060
		$items8 = $list->filter('Email:not:case', array('', '[email protected]'));
1061
		$this->assertSQLContains(' OR "DataObjectTest_Fan"."Email" IS NULL', $items8->sql());
1062
1063
		// These should not contain any null checks at all
1064
		$items9 = $list->filter('Email:nocase', array('', '[email protected]'));
1065
		$this->assertSQLNotContains('"DataObjectTest_Fan"."Email" IS NULL', $items9->sql());
1066
		$this->assertSQLNotContains('"DataObjectTest_Fan"."Email" IS NOT NULL', $items9->sql());
1067
	}
1068
1069
	/**
1070
	 * $list = $list->filterByCallback(function($item, $list) { return $item->Age == 21; })
1071
	 */
1072
	public function testFilterByCallback() {
1073
		$team1ID = $this->idFromFixture('DataObjectTest_Team', 'team1');
1074
		$list = DataObjectTest_TeamComment::get();
1075
		$list = $list->filterByCallback(function ($item, $list) use ($team1ID) {
0 ignored issues
show
Unused Code introduced by
The parameter $list is not used and could be removed.

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

Loading history...
1076
			return $item->TeamID == $team1ID;
1077
		});
1078
1079
		$result = $list->column('Name');
1080
		$expected = array_intersect($result, array('Joe', 'Bob'));
1081
1082
		$this->assertEquals(2, $list->count());
1083
		$this->assertEquals($expected, $result, 'List should only contain comments from Team 1 (Joe and Bob)');
1084
		$this->assertTrue($list instanceof SS_Filterable, 'The List should be of type SS_Filterable');
1085
	}
1086
1087
	/**
1088
	 * $list->exclude('Name', 'bob'); // exclude bob from list
1089
	 */
1090
	public function testSimpleExclude() {
1091
		$list = DataObjectTest_TeamComment::get();
1092
		$list = $list->exclude('Name', 'Bob');
1093
		$list = $list->sort('Name');
1094
		$this->assertEquals(2, $list->count());
1095
		$this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
1096
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
1097
	}
1098
//
1099
	/**
1100
	 * $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
1101
	 */
1102
	public function testSimpleExcludeWithMultiple() {
1103
		$list = DataObjectTest_TeamComment::get();
1104
		$list = $list->exclude('Name', array('Joe','Phil'));
1105
		$this->assertEquals(1, $list->count());
1106
		$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
1107
	}
1108
1109
	/**
1110
	 * $list->exclude(array('Name'=>'bob, 'Age'=>21)); // negative version
1111
	 */
1112
	public function testMultipleExcludeWithMiss() {
1113
		$list = DataObjectTest_TeamComment::get();
1114
		$list = $list->exclude(array('Name'=>'Bob', 'Comment'=>'Does not match any comments'));
1115
		$this->assertEquals(3, $list->count());
1116
	}
1117
1118
	/**
1119
	 * $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
1120
	 */
1121
	public function testMultipleExclude() {
1122
		$list = DataObjectTest_TeamComment::get();
1123
		$list = $list->exclude(array('Name'=>'Bob', 'Comment'=>'This is a team comment by Bob'));
1124
		$this->assertEquals(2, $list->count());
1125
	}
1126
1127
	/**
1128
	 * Test that if an exclude() is applied to a filter(), the filter() is still preserved.
1129
	 */
1130
	public function testExcludeOnFilter() {
1131
		$list = DataObjectTest_TeamComment::get();
1132
		$list = $list->filter('Comment', 'Phil is a unique guy, and comments on team2');
1133
		$list = $list->exclude('Name', 'Bob');
1134
1135
		$sql = $list->sql($parameters);
1136
		$this->assertSQLContains(
1137
			'WHERE ("DataObjectTest_TeamComment"."Comment" = ?) AND (("DataObjectTest_TeamComment"."Name" != ? '
1138
			. 'OR "DataObjectTest_TeamComment"."Name" IS NULL))',
1139
			$sql);
1140
		$this->assertEquals(array('Phil is a unique guy, and comments on team2', 'Bob'), $parameters);
1141
	}
1142
1143
	public function testExcludeWithSearchFilter() {
1144
		$list = DataObjectTest_TeamComment::get();
1145
		$list = $list->exclude('Name:LessThan', 'Bob');
1146
1147
		$sql = $list->sql($parameters);
1148
		$this->assertSQLContains('WHERE (("DataObjectTest_TeamComment"."Name" >= ?))', $sql);
1149
		$this->assertEquals(array('Bob'), $parameters);
1150
	}
1151
1152
	/**
1153
	 * Test exact match filter with empty array items
1154
	 */
1155
	public function testEmptyFilter() {
1156
		$this->setExpectedException("InvalidArgumentException", 'Cannot filter "DataObjectTest_TeamComment"."Name" against an empty set');
1157
		$list = DataObjectTest_TeamComment::get();
1158
		$list->exclude('Name', array());
1159
	}
1160
1161
	/**
1162
	 * $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
1163
	 */
1164
	public function testMultipleExcludeWithMultipleThatCheersEitherTeam() {
1165
		$list = DataObjectTest_TeamComment::get();
1166
		$list = $list->exclude(array('Name'=>'Bob', 'TeamID'=>array(
1167
			$this->idFromFixture('DataObjectTest_Team', 'team1'),
1168
			$this->idFromFixture('DataObjectTest_Team', 'team2')
1169
		)));
1170
		$list = $list->sort('Name');
1171
		$this->assertEquals(2, $list->count());
1172
		$this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Phil');
1173
		$this->assertEquals('Phil', $list->last()->Name, 'First comment should be from Phil');
1174
	}
1175
1176
	/**
1177
	 * $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // negative version
1178
	 */
1179
	public function testMultipleExcludeWithMultipleThatCheersOnNonExistingTeam() {
1180
		$list = DataObjectTest_TeamComment::get();
1181
		$list = $list->exclude(array('Name'=>'Bob', 'TeamID'=>array(3)));
1182
		$this->assertEquals(3, $list->count());
1183
	}
1184
1185
	/**
1186
	 * $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43))); //negative version
1187
	 */
1188
	public function testMultipleExcludeWithNoExclusion() {
1189
		$list = DataObjectTest_TeamComment::get();
1190
		$list = $list->exclude(array(
1191
			'Name'=>array('Bob','Joe'),
1192
			'Comment' => 'Phil is a unique guy, and comments on team2'));
1193
		$this->assertEquals(3, $list->count());
1194
	}
1195
1196
	/**
1197
	 *  $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
1198
	 */
1199
	public function testMultipleExcludeWithTwoArray() {
1200
		$list = DataObjectTest_TeamComment::get();
1201
		$list = $list->exclude(array('Name' => array('Bob','Joe'), 'TeamID' => array(
1202
			$this->idFromFixture('DataObjectTest_Team', 'team1'),
1203
			$this->idFromFixture('DataObjectTest_Team', 'team2')
1204
		)));
1205
		$this->assertEquals(1, $list->count());
1206
		$this->assertEquals('Phil', $list->last()->Name, 'Only comment should be from Phil');
1207
	}
1208
1209
	/**
1210
	 *  $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
1211
	 */
1212
	public function testMultipleExcludeWithTwoArrayOneTeam() {
1213
		$list = DataObjectTest_TeamComment::get();
1214
		$list = $list->exclude(array(
1215
			'Name' => array('Bob', 'Phil'),
1216
			'TeamID' => array($this->idFromFixture('DataObjectTest_Team', 'team1'))));
1217
		$list = $list->sort('Name');
1218
		$this->assertEquals(2, $list->count());
1219
		$this->assertEquals('Joe', $list->first()->Name, 'First comment should be from Joe');
1220
		$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
1221
	}
1222
1223
	/**
1224
	 *
1225
	 */
1226
	public function testSortByRelation() {
1227
		$list = DataObjectTest_TeamComment::get();
1228
		$list = $list->sort(array('Team.Title' => 'DESC'));
1229
		$this->assertEquals(3, $list->count());
1230
		$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team2'), $list->first()->TeamID,
1231
			'First comment should be for Team 2');
1232
		$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $list->last()->TeamID,
1233
			'Last comment should be for Team 1');
1234
	}
1235
1236
	public function testReverse() {
1237
		$list = DataObjectTest_TeamComment::get();
1238
		$list = $list->sort('Name');
1239
		$list = $list->reverse();
1240
1241
		$this->assertEquals('Bob', $list->last()->Name, 'Last comment should be from Bob');
1242
		$this->assertEquals('Phil', $list->first()->Name, 'First comment should be from Phil');
1243
	}
1244
1245
	public function testSortByComplexExpression() {
1246
		// Test an expression with both spaces and commas. This test also tests that column() can be called
1247
		// with a complex sort expression, so keep using column() below
1248
		$list = DataObjectTest_Team::get()->sort(
1249
			'CASE WHEN "DataObjectTest_Team"."ClassName" = \'DataObjectTest_SubTeam\' THEN 0 ELSE 1 END, "Title" DESC'
1250
		);
1251
		$this->assertEquals(array(
1252
			'Subteam 3',
1253
			'Subteam 2',
1254
			'Subteam 1',
1255
			'Team 3',
1256
			'Team 2',
1257
			'Team 1',
1258
		), $list->column("Title"));
1259
	}
1260
}
1261