Completed
Push — 3.7 ( 11b87a...bb5701 )
by
unknown
09:09 queued 01:14
created

testSetFieldWithArrayOnScalarOnlyField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 0
dl 0
loc 7
rs 10
c 0
b 0
f 0
1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 6 and the first side effect is on line 2055.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 * @package framework
4
 * @subpackage tests
5
 */
6
class DataObjectTest extends SapphireTest {
7
8
	protected static $fixture_file = 'DataObjectTest.yml';
9
10
	protected $extraDataObjects = array(
11
		'DataObjectTest_Team',
12
		'DataObjectTest_Fixture',
13
		'DataObjectTest_SubTeam',
14
		'OtherSubclassWithSameField',
15
		'DataObjectTest_FieldlessTable',
16
		'DataObjectTest_FieldlessSubTable',
17
		'DataObjectTest_ValidatedObject',
18
		'DataObjectTest_Player',
19
		'DataObjectTest_TeamComment',
20
		'DataObjectTest_EquipmentCompany',
21
		'DataObjectTest_SubEquipmentCompany',
22
		'DataObjectTest\NamespacedClass',
23
		'DataObjectTest\RelationClass',
24
		'DataObjectTest_ExtendedTeamComment',
25
		'DataObjectTest_Company',
26
		'DataObjectTest_Staff',
27
		'DataObjectTest_CEO',
28
		'DataObjectTest_Fan',
29
		'DataObjectTest_Play',
30
		'DataObjectTest_Ploy',
31
		'DataObjectTest_Bogey',
32
		'DataObjectTest_Sortable',
33
		'ManyManyListTest_Product',
34
		'ManyManyListTest_Category',
35
	);
36
37
	/**
38
	 * @dataProvider provideSingletons
39
	 */
40
	public function testSingleton($inst, $defaultValue, $altDefaultValue)
41
	{
42
		// Calls to scaffold the test database may have cached service specs for DataObjects
43
		// with the incorrect 'type' set (singleton instead of prototype)
44
		Injector::nest();
45
		$reflectionProp = new ReflectionProperty('Injector', 'specs');
46
		$reflectionProp->setAccessible(true);
47
		$reflectionProp->setValue(Injector::inst(), array());
48
49
		$inst = $inst();
50
		// Test that populateDefaults() isn't called on singletons
51
		// which can lead to SQL errors during build, and endless loops
52
		if ($defaultValue) {
53
			$this->assertEquals($defaultValue, $inst->MyFieldWithDefault);
54
		} else {
55
			$this->assertEmpty($inst->MyFieldWithDefault);
56
		}
57
58
		if ($altDefaultValue) {
59
			$this->assertEquals($altDefaultValue, $inst->MyFieldWithAltDefault);
60
		} else {
61
			$this->assertEmpty($inst->MyFieldWithAltDefault);
62
		}
63
64
		Injector::unnest();
65
	}
66
67
	public function provideSingletons()
68
	{
69
		// because PHPUnit evalutes test providers *before* setUp methods
70
		// any extensions added in the setUp methods won't be available
71
		// we must return closures to generate the arguments at run time
72
		return array(
73
			array(function () {
74
				return DataObjectTest_Fixture::create();
75
			}, 'Default Value', 'Default Value'),
76
			array(function () {
77
				return new DataObjectTest_Fixture();
78
			}, 'Default Value', 'Default Value'),
79
			array(function () {
80
				return singleton('DataObjectTest_Fixture');
81
			}, null, null),
82
			array(function () {
83
				return DataObjectTest_Fixture::singleton();
84
			}, null, null),
85
			array(function () {
86
				return new DataObjectTest_Fixture(null, true);
87
			}, null, null),
88
		);
89
	}
90
91
	public function testDb() {
92
		$obj = new DataObjectTest_TeamComment();
93
		$dbFields = $obj->db();
94
95
		// Assert fields are included
96
		$this->assertArrayHasKey('Name', $dbFields);
97
98
		// Assert the base fields are excluded
99
		$this->assertArrayNotHasKey('Created', $dbFields);
100
		$this->assertArrayNotHasKey('LastEdited', $dbFields);
101
		$this->assertArrayNotHasKey('ClassName', $dbFields);
102
		$this->assertArrayNotHasKey('ID', $dbFields);
103
104
		// Assert that the correct field type is returned when passing a field
105
		$this->assertEquals('Varchar', $obj->db('Name'));
106
		$this->assertEquals('Text', $obj->db('Comment'));
107
108
		$obj = new DataObjectTest_ExtendedTeamComment();
109
		$dbFields = $obj->db();
110
111
		// Assert overloaded fields have correct data type
112
		$this->assertEquals('HTMLText', $obj->db('Comment'));
113
		$this->assertEquals('HTMLText', $dbFields['Comment'],
114
			'Calls to DataObject::db without a field specified return correct data types');
115
116
		// assertEquals doesn't verify the order of array elements, so access keys manually to check order:
117
		// expected: array('Name' => 'Varchar', 'Comment' => 'HTMLText')
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
118
		reset($dbFields);
119
		$this->assertEquals('Name', key($dbFields), 'DataObject::db returns fields in correct order');
120
		next($dbFields);
121
		$this->assertEquals('Comment', key($dbFields), 'DataObject::db returns fields in correct order');
122
	}
123
124
	public function testConstructAcceptsValues() {
125
		// Values can be an array...
126
		$player = new DataObjectTest_Player(array(
127
			'FirstName' => 'James',
128
			'Surname' => 'Smith'
129
		));
130
131
		$this->assertEquals('James', $player->FirstName);
132
		$this->assertEquals('Smith', $player->Surname);
133
134
		// ... or a stdClass inst
135
		$data = new stdClass();
136
		$data->FirstName = 'John';
137
		$data->Surname = 'Doe';
138
		$player = new DataObjectTest_Player($data);
139
140
		$this->assertEquals('John', $player->FirstName);
141
		$this->assertEquals('Doe', $player->Surname);
142
143
		// IDs should be stored as integers, not strings
144
		$player = new DataObjectTest_Player(array('ID' => '5'));
145
		$this->assertSame(5, $player->ID);
146
	}
147
148
	public function testValidObjectsForBaseFields() {
149
		$obj = new DataObjectTest_ValidatedObject();
150
151
		foreach (array('Created', 'LastEdited', 'ClassName', 'ID') as $field) {
152
			$helper = $obj->dbObject($field);
153
			$this->assertTrue(
154
				($helper instanceof DBField),
155
				"for {$field} expected helper to be DBField, but was " .
156
				(is_object($helper) ? get_class($helper) : "null")
157
			);
158
		}
159
	}
160
161
	public function testDataIntegrityWhenTwoSubclassesHaveSameField() {
162
		// Save data into DataObjectTest_SubTeam.SubclassDatabaseField
163
		$obj = new DataObjectTest_SubTeam();
164
		$obj->SubclassDatabaseField = "obj-SubTeam";
0 ignored issues
show
Documentation introduced by
The property SubclassDatabaseField does not exist on object<DataObjectTest_SubTeam>. 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...
165
		$obj->write();
166
167
		// Change the class
168
		$obj->ClassName = 'OtherSubclassWithSameField';
169
		$obj->write();
170
		$obj->flushCache();
171
172
		// Re-fetch from the database and confirm that the data is sourced from
173
		// OtherSubclassWithSameField.SubclassDatabaseField
174
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
175
		$this->assertNull($obj->SubclassDatabaseField);
176
177
		// Confirm that save the object in the other direction.
178
		$obj->SubclassDatabaseField = 'obj-Other';
0 ignored issues
show
Documentation introduced by
The property SubclassDatabaseField does not exist on object<DataObject>. 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...
179
		$obj->write();
180
181
		$obj->ClassName = 'DataObjectTest_SubTeam';
182
		$obj->write();
183
		$obj->flushCache();
184
185
		// If we restore the class, the old value has been lying dormant and will be available again.
186
		// NOTE: This behaviour is volatile; we may change this in the future to clear fields that
187
		// are no longer relevant when changing ClassName
188
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
189
		$this->assertEquals('obj-SubTeam', $obj->SubclassDatabaseField);
190
	}
191
192
	/**
193
	 * Test deletion of DataObjects
194
	 *   - Deleting using delete() on the DataObject
195
	 *   - Deleting using DataObject::delete_by_id()
196
	 */
197
	public function testDelete() {
198
		// Test deleting using delete() on the DataObject
199
		// Get the first page
200
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
201
		$objID = $obj->ID;
202
		// Check the page exists before deleting
203
		$this->assertTrue(is_object($obj) && $obj->exists());
204
		// Delete the page
205
		$obj->delete();
206
		// Check that page does not exist after deleting
207
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
208
		$this->assertTrue(!$obj || !$obj->exists());
209
210
211
		// Test deleting using DataObject::delete_by_id()
212
		// Get the second page
213
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
214
		$objID = $obj->ID;
215
		// Check the page exists before deleting
216
		$this->assertTrue(is_object($obj) && $obj->exists());
217
		// Delete the page
218
		DataObject::delete_by_id('DataObjectTest_Player', $obj->ID);
219
		// Check that page does not exist after deleting
220
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
221
		$this->assertTrue(!$obj || !$obj->exists());
222
	}
223
224
	/**
225
	 * Test methods that get DataObjects
226
	 *   - DataObject::get()
227
	 *       - All records of a DataObject
228
	 *       - Filtering
229
	 *       - Sorting
230
	 *       - Joins
231
	 *       - Limit
232
	 *       - Container class
233
	 *   - DataObject::get_by_id()
234
	 *   - DataObject::get_one()
235
	 *        - With and without caching
236
	 *        - With and without ordering
237
	 */
238
	public function testGet() {
239
		// Test getting all records of a DataObject
240
		$comments = DataObject::get('DataObjectTest_TeamComment');
241
		$this->assertEquals(3, $comments->Count());
242
243
		// Test WHERE clause
244
		$comments = DataObject::get('DataObjectTest_TeamComment', "\"Name\"='Bob'");
245
		$this->assertEquals(1, $comments->Count());
246
		foreach($comments as $comment) {
247
			$this->assertEquals('Bob', $comment->Name);
248
		}
249
250
		// Test sorting
251
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC");
252
		$this->assertEquals(3, $comments->Count());
253
		$this->assertEquals('Bob', $comments->First()->Name);
254
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" DESC");
255
		$this->assertEquals(3, $comments->Count());
256
		$this->assertEquals('Phil', $comments->First()->Name);
257
258
		// Test limit
259
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC", '', '1,2');
260
		$this->assertEquals(2, $comments->Count());
261
		$this->assertEquals('Joe', $comments->First()->Name);
262
		$this->assertEquals('Phil', $comments->Last()->Name);
263
264
		// Test get_by_id()
265
		$captain1ID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
266
		$captain1 = DataObject::get_by_id('DataObjectTest_Player', $captain1ID);
267
		$this->assertEquals('Captain', $captain1->FirstName);
268
269
		// Test get_one() without caching
270
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
271
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
272
		), false);
273
		$comment1->Comment = "Something Else";
0 ignored issues
show
Documentation introduced by
The property Comment does not exist on object<DataObject>. 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...
274
275
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
276
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
277
		), false);
278
		$this->assertNotEquals($comment1->Comment, $comment2->Comment);
279
280
		// Test get_one() with caching
281
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
282
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
283
		), true);
284
		$comment1->Comment = "Something Else";
0 ignored issues
show
Documentation introduced by
The property Comment does not exist on object<DataObject>. 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...
285
286
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
287
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
288
		), true);
289
		$this->assertEquals((string)$comment1->Comment, (string)$comment2->Comment);
290
291
		// Test get_one() with order by without caching
292
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" ASC");
293
		$this->assertEquals('Bob', $comment->Name);
294
295
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" DESC");
296
		$this->assertEquals('Phil', $comment->Name);
297
298
		// Test get_one() with order by with caching
299
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" ASC');
300
		$this->assertEquals('Bob', $comment->Name);
301
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" DESC');
302
		$this->assertEquals('Phil', $comment->Name);
303
	}
304
305
	public function testGetCaseInsensitive() {
306
		// Test get_one() with bad case on the classname
307
		// Note: This will succeed only if the underlying DB server supports case-insensitive
308
		// table names (e.g. such as MySQL, but not SQLite3)
309
		if(!(DB::get_conn() instanceof MySQLDatabase)) {
310
			$this->markTestSkipped('MySQL only');
311
		}
312
313
		$subteam1 = DataObject::get_one('dataobjecttest_subteam', array(
314
			'"DataObjectTest_Team"."Title"' => 'Subteam 1'
315
		), true);
316
		$this->assertNotEmpty($subteam1);
317
		$this->assertEquals($subteam1->Title, "Subteam 1");
318
	}
319
320
	public function testGetSubclassFields() {
321
		/* Test that fields / has_one relations from the parent table and the subclass tables are extracted */
322
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
323
		// Base field
324
		$this->assertEquals('Captain', $captain1->FirstName);
325
		// Subclass field
326
		$this->assertEquals('007', $captain1->ShirtNumber);
327
		// Subclass has_one relation
328
		$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $captain1->FavouriteTeamID);
329
	}
330
331
	public function testGetRelationClass() {
332
		$obj = new DataObjectTest_Player();
333
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('FavouriteTeam'),
334
			'DataObjectTest_Team', 'has_one is properly inspected');
335
		$this->assertEquals(singleton('DataObjectTest_Company')->getRelationClass('CurrentStaff'),
336
			'DataObjectTest_Staff', 'has_many is properly inspected');
337
		$this->assertEquals(singleton('DataObjectTest_Team')->getRelationClass('Players'), 'DataObjectTest_Player',
338
			'many_many is properly inspected');
339
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('Teams'), 'DataObjectTest_Team',
340
			'belongs_many_many is properly inspected');
341
		$this->assertEquals(singleton('DataObjectTest_CEO')->getRelationClass('Company'), 'DataObjectTest_Company',
342
			'belongs_to is properly inspected');
343
		$this->assertEquals(singleton('DataObjectTest_Fan')->getRelationClass('Favourite'), 'DataObject',
344
			'polymorphic has_one is properly inspected');
345
	}
346
347
	/**
348
	 * Test that has_one relations can be retrieved
349
	 */
350
	public function testGetHasOneRelations() {
351
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
352
		$team1ID = $this->idFromFixture('DataObjectTest_Team', 'team1');
353
354
		// There will be a field called (relname)ID that contains the ID of the
355
		// object linked to via the has_one relation
356
		$this->assertEquals($team1ID, $captain1->FavouriteTeamID);
357
358
		// There will be a method called $obj->relname() that returns the object itself
359
		$this->assertEquals($team1ID, $captain1->FavouriteTeam()->ID);
360
361
		// Check entity with polymorphic has-one
362
		$fan1 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
363
		$this->assertTrue((bool)$fan1->hasValue('Favourite'));
364
365
		// There will be fields named (relname)ID and (relname)Class for polymorphic
366
		// entities
367
		$this->assertEquals($team1ID, $fan1->FavouriteID);
368
		$this->assertEquals('DataObjectTest_Team', $fan1->FavouriteClass);
369
370
		// There will be a method called $obj->relname() that returns the object itself
371
		$favourite = $fan1->Favourite();
372
		$this->assertEquals($team1ID, $favourite->ID);
373
		$this->assertInstanceOf('DataObjectTest_Team', $favourite);
374
375
		// check behaviour of dbObject with polymorphic relations
376
		$favouriteDBObject = $fan1->dbObject('Favourite');
377
		$favouriteValue = $favouriteDBObject->getValue();
378
		$this->assertInstanceOf('PolymorphicForeignKey', $favouriteDBObject);
379
		$this->assertEquals($favourite->ID, $favouriteValue->ID);
380
		$this->assertEquals($favourite->ClassName, $favouriteValue->ClassName);
381
	}
382
383
	/**
384
	 * Simple test to ensure that namespaced classes and polymorphic relations work together
385
	 */
386
	public function testPolymorphicNamespacedRelations() {
387
		$parent = new \DataObjectTest\NamespacedClass();
388
		$parent->Name = 'New Parent';
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...
389
		$parent->write();
390
391
		$child = new \DataObjectTest\RelationClass();
392
		$child->Title = 'New Child';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<DataObjectTest\RelationClass>. 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...
393
		$child->write();
394
		$parent->Relations()->add($child);
0 ignored issues
show
Bug introduced by
The method Relations() does not exist on DataObjectTest\NamespacedClass. Did you maybe mean duplicateManyManyRelations()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
395
396
		$this->assertEquals(1, $parent->Relations()->count());
0 ignored issues
show
Bug introduced by
The method Relations() does not exist on DataObjectTest\NamespacedClass. Did you maybe mean duplicateManyManyRelations()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
397
		$this->assertEquals(array('New Child'), $parent->Relations()->column('Title'));
0 ignored issues
show
Bug introduced by
The method Relations() does not exist on DataObjectTest\NamespacedClass. Did you maybe mean duplicateManyManyRelations()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
398
		$this->assertEquals('New Parent', $child->Parent()->Name);
0 ignored issues
show
Bug introduced by
The method Parent() does not exist on DataObjectTest\RelationClass. Did you maybe mean parentClass()?

This check marks calls to methods that do not seem to exist on an object.

This is most likely the result of a method being renamed without all references to it being renamed likewise.

Loading history...
399
	}
400
401
	public function testLimitAndCount() {
402
		$players = DataObject::get("DataObjectTest_Player");
403
404
		// There's 4 records in total
405
		$this->assertEquals(4, $players->count());
406
407
		// Testing "##, ##" syntax
408
		$this->assertEquals(4, $players->limit(20)->count());
409
		$this->assertEquals(4, $players->limit(20, 0)->count());
410
		$this->assertEquals(0, $players->limit(20, 20)->count());
411
		$this->assertEquals(2, $players->limit(2, 0)->count());
412
		$this->assertEquals(1, $players->limit(5, 3)->count());
413
	}
414
415
	public function testWriteNoChangesDoesntUpdateLastEdited() {
416
		// set mock now so we can be certain of LastEdited time for our test
417
		SS_Datetime::set_mock_now('2017-01-01 00:00:00');
418
		$obj = new DataObjectTest_Player();
419
		$obj->FirstName = 'Test';
420
		$obj->Surname = 'Plater';
421
		$obj->Email = '[email protected]';
422
		$obj->write();
423
		$writtenObj = DataObjectTest_Player::get()->byID($obj->ID);
424
		$this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
425
426
		// set mock now so we get a new LastEdited if, for some reason, it's updated
427
		SS_Datetime::set_mock_now('2017-02-01 00:00:00');
428
		$writtenObj->write();
429
		$this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
430
	}
431
432
	/**
433
	 * Test writing of database columns which don't correlate to a DBField,
434
	 * e.g. all relation fields on has_one/has_many like "ParentID".
435
	 *
436
	 */
437
	public function testWritePropertyWithoutDBField() {
438
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
439
		$obj->FavouriteTeamID = 99;
0 ignored issues
show
Documentation introduced by
The property FavouriteTeamID does not exist on object<DataObject>. 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...
440
		$obj->write();
441
442
		// reload the page from the database
443
		$savedObj = DataObject::get_by_id('DataObjectTest_Player', $obj->ID);
444
		$this->assertTrue($savedObj->FavouriteTeamID == 99);
445
446
		// Test with porymorphic relation
447
		$obj2 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
448
		$obj2->FavouriteID = 99;
0 ignored issues
show
Documentation introduced by
The property FavouriteID does not exist on object<DataObject>. 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...
449
		$obj2->FavouriteClass = 'DataObjectTest_Player';
0 ignored issues
show
Documentation introduced by
The property FavouriteClass does not exist on object<DataObject>. 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...
450
		$obj2->write();
451
452
		$savedObj2 = DataObject::get_by_id('DataObjectTest_Fan', $obj2->ID);
453
		$this->assertTrue($savedObj2->FavouriteID == 99);
454
		$this->assertTrue($savedObj2->FavouriteClass == 'DataObjectTest_Player');
455
	}
456
457
	/**
458
	 * Test has many relationships
459
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
460
	 *   - Test the IDs on the DataObjects are set correctly
461
	 */
462
	public function testHasManyRelationships() {
463
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
464
465
		// Test getComponents() gets the ComponentSet of the other side of the relation
466
		$this->assertTrue($team1->Comments()->Count() == 2);
467
468
		// Test the IDs on the DataObjects are set correctly
469
		foreach($team1->Comments() as $comment) {
470
			$this->assertEquals($team1->ID, $comment->TeamID);
471
		}
472
473
		// Test that we can add and remove items that already exist in the database
474
		$newComment = new DataObjectTest_TeamComment();
475
		$newComment->Name = "Automated commenter";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<DataObjectTest_TeamComment>. 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...
476
		$newComment->Comment = "This is a new comment";
0 ignored issues
show
Documentation introduced by
The property Comment does not exist on object<DataObjectTest_TeamComment>. 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...
477
		$newComment->write();
478
		$team1->Comments()->add($newComment);
479
		$this->assertEquals($team1->ID, $newComment->TeamID);
480
481
		$comment1 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment1');
482
		$comment2 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment2');
483
		$team1->Comments()->remove($comment2);
484
485
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
486
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
487
488
		// Test that removing an item from a list doesn't remove it from the same
489
		// relation belonging to a different object
490
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
491
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
492
		$team2->Comments()->remove($comment1);
493
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
494
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
495
	}
496
497
498
	/**
499
	 * Test has many relationships against polymorphic has_one fields
500
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
501
	 *   - Test the IDs on the DataObjects are set correctly
502
	 */
503
	public function testHasManyPolymorphicRelationships() {
504
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
505
506
		// Test getComponents() gets the ComponentSet of the other side of the relation
507
		$this->assertTrue($team1->Fans()->Count() == 2);
508
509
		// Test the IDs/Classes on the DataObjects are set correctly
510
		foreach($team1->Fans() as $fan) {
511
			$this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID');
512
			$this->assertEquals('DataObjectTest_Team', $fan->FavouriteClass, 'Fan has the correct FavouriteClass');
513
		}
514
515
		// Test that we can add and remove items that already exist in the database
516
		$newFan = new DataObjectTest_Fan();
517
		$newFan->Name = "New fan";
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<DataObjectTest_Fan>. 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...
518
		$newFan->write();
519
		$team1->Fans()->add($newFan);
520
		$this->assertEquals($team1->ID, $newFan->FavouriteID, 'Newly created fan has the correct FavouriteID');
521
		$this->assertEquals(
522
			'DataObjectTest_Team',
523
			$newFan->FavouriteClass,
524
			'Newly created fan has the correct FavouriteClass'
525
		);
526
527
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
528
		$fan3 = $this->objFromFixture('DataObjectTest_Fan', 'fan3');
529
		$team1->Fans()->remove($fan3);
530
531
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
532
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
533
534
		// Test that removing an item from a list doesn't remove it from the same
535
		// relation belonging to a different object
536
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
537
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
538
		$player1->Fans()->remove($fan1);
539
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
540
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
541
	}
542
543
544
	public function testHasOneRelationship() {
545
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
546
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
547
		$player2 = $this->objFromFixture('DataObjectTest_Player', 'player2');
548
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
549
550
		// Test relation probing
551
		$this->assertFalse((bool)$team1->hasValue('Captain', null, false));
552
		$this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
553
554
		// Add a captain to team 1
555
		$team1->setField('CaptainID', $player1->ID);
556
		$team1->write();
557
558
		$this->assertTrue((bool)$team1->hasValue('Captain', null, false));
559
		$this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
560
561
		$this->assertEquals($player1->ID, $team1->Captain()->ID,
562
			'The captain exists for team 1');
563
		$this->assertEquals($player1->ID, $team1->getComponent('Captain')->ID,
564
			'The captain exists through the component getter');
565
566
		$this->assertEquals($team1->Captain()->FirstName, 'Player 1',
567
			'Player 1 is the captain');
568
		$this->assertEquals($team1->getComponent('Captain')->FirstName, 'Player 1',
569
			'Player 1 is the captain');
570
571
		$team1->CaptainID = $player2->ID;
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<DataObject>. 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...
572
		$team1->write();
573
574
		$this->assertEquals($player2->ID, $team1->Captain()->ID);
575
		$this->assertEquals($player2->ID, $team1->getComponent('Captain')->ID);
576
		$this->assertEquals('Player 2', $team1->Captain()->FirstName);
577
		$this->assertEquals('Player 2', $team1->getComponent('Captain')->FirstName);
578
579
580
		// Set the favourite team for fan1
581
		$fan1->setField('FavouriteID', $team1->ID);
582
		$fan1->setField('FavouriteClass', $team1->class);
583
584
		$this->assertEquals($team1->ID, $fan1->Favourite()->ID, 'The team is assigned to fan 1');
585
		$this->assertInstanceOf($team1->class, $fan1->Favourite(), 'The team is assigned to fan 1');
586
		$this->assertEquals($team1->ID, $fan1->getComponent('Favourite')->ID,
587
			'The team exists through the component getter'
588
		);
589
		$this->assertInstanceOf($team1->class, $fan1->getComponent('Favourite'),
590
			'The team exists through the component getter'
591
		);
592
593
		$this->assertEquals($fan1->Favourite()->Title, 'Team 1',
594
			'Team 1 is the favourite');
595
		$this->assertEquals($fan1->getComponent('Favourite')->Title, 'Team 1',
596
			'Team 1 is the favourite');
597
	}
598
599
	/**
600
	 * @todo Extend type change tests (e.g. '0'==NULL)
601
	 */
602
	public function testChangedFields() {
603
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
604
		$obj->FirstName = 'Captain-changed';
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<DataObject>. 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...
605
		$obj->IsRetired = true;
0 ignored issues
show
Documentation introduced by
The property IsRetired does not exist on object<DataObject>. 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...
606
607
		$this->assertEquals(
608
			$obj->getChangedFields(true, DataObject::CHANGE_STRICT),
609
			array(
610
				'FirstName' => array(
611
					'before' => 'Captain',
612
					'after' => 'Captain-changed',
613
					'level' => DataObject::CHANGE_VALUE
614
				),
615
				'IsRetired' => array(
616
					'before' => 1,
617
					'after' => true,
618
					'level' => DataObject::CHANGE_STRICT
619
				)
620
			),
621
			'Changed fields are correctly detected with strict type changes (level=1)'
622
		);
623
624
		$this->assertEquals(
625
			$obj->getChangedFields(true, DataObject::CHANGE_VALUE),
626
			array(
627
				'FirstName' => array(
628
					'before'=>'Captain',
629
					'after'=>'Captain-changed',
630
					'level' => DataObject::CHANGE_VALUE
631
				)
632
			),
633
			'Changed fields are correctly detected while ignoring type changes (level=2)'
634
		);
635
636
		$newObj = new DataObjectTest_Player();
637
		$newObj->FirstName = "New Player";
638
		$this->assertEquals(
639
			array(
640
				'FirstName' => array(
641
					'before' => null,
642
					'after' => 'New Player',
643
					'level' => DataObject::CHANGE_VALUE
644
				)
645
			),
646
			$newObj->getChangedFields(true, DataObject::CHANGE_VALUE),
647
			'Initialised fields are correctly detected as full changes'
648
		);
649
	}
650
651
	public function testIsChanged() {
652
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
653
		$obj->NonDBField = 'bob';
0 ignored issues
show
Documentation introduced by
The property NonDBField does not exist on object<DataObject>. 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...
654
		$obj->FirstName = 'Captain-changed';
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<DataObject>. 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...
655
		$obj->IsRetired = true; // type change only, database stores "1"
0 ignored issues
show
Documentation introduced by
The property IsRetired does not exist on object<DataObject>. 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...
656
657
		// Now that DB fields are changed, isChanged is true
658
		$this->assertTrue($obj->isChanged('NonDBField'));
659
		$this->assertFalse($obj->isChanged('NonField'));
660
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
661
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
662
		$this->assertTrue($obj->isChanged('IsRetired', DataObject::CHANGE_STRICT));
663
		$this->assertFalse($obj->isChanged('IsRetired', DataObject::CHANGE_VALUE));
664
		$this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
665
		$this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
666
667
		$newObj = new DataObjectTest_Player();
668
		$newObj->FirstName = "New Player";
669
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
670
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
671
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
672
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
673
674
		$newObj->write();
675
		$this->assertFalse($newObj->ischanged());
676
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
677
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
678
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
679
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
680
681
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
682
		$obj->FirstName = null;
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<DataObject>. 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...
683
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
684
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
685
686
		/* Test when there's not field provided */
687
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
688
		$this->assertFalse($obj->isChanged());
689
		$obj->NonDBField = 'new value';
0 ignored issues
show
Documentation introduced by
The property NonDBField does not exist on object<DataObject>. 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...
690
		$this->assertFalse($obj->isChanged());
691
		$obj->FirstName = "New Player";
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<DataObject>. 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...
692
		$this->assertTrue($obj->isChanged());
693
694
		$obj->write();
695
		$this->assertFalse($obj->isChanged());
696
	}
697
698
	public function testRandomSort() {
699
		/* If we perform the same regularly sorted query twice, it should return the same results */
700
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", "ID");
701
		foreach($itemsA as $item) $keysA[] = $item->ID;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$keysA was never initialized. Although not strictly required by PHP, it is generally a good practice to add $keysA = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
702
703
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", "ID");
704
		foreach($itemsB as $item) $keysB[] = $item->ID;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$keysB was never initialized. Although not strictly required by PHP, it is generally a good practice to add $keysB = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
705
706
		/* Test when there's not field provided */
707
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
708
		$obj->FirstName = "New Player";
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<DataObject>. 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...
709
		$this->assertTrue($obj->isChanged());
710
711
		$obj->write();
712
		$this->assertFalse($obj->isChanged());
713
714
		/* If we perform the same random query twice, it shouldn't return the same results */
715
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
716
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
717
		$itemsC = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
718
		$itemsD = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
719
		foreach($itemsA as $item) $keysA[] = $item->ID;
0 ignored issues
show
Bug introduced by
The variable $keysA does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
720
		foreach($itemsB as $item) $keysB[] = $item->ID;
0 ignored issues
show
Bug introduced by
The variable $keysB does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
721
		foreach($itemsC as $item) $keysC[] = $item->ID;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$keysC was never initialized. Although not strictly required by PHP, it is generally a good practice to add $keysC = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
722
		foreach($itemsD as $item) $keysD[] = $item->ID;
0 ignored issues
show
Coding Style Comprehensibility introduced by
$keysD was never initialized. Although not strictly required by PHP, it is generally a good practice to add $keysD = array(); before regardless.

Adding an explicit array definition is generally preferable to implicit array definition as it guarantees a stable state of the code.

Let’s take a look at an example:

foreach ($collection as $item) {
    $myArray['foo'] = $item->getFoo();

    if ($item->hasBar()) {
        $myArray['bar'] = $item->getBar();
    }

    // do something with $myArray
}

As you can see in this example, the array $myArray is initialized the first time when the foreach loop is entered. You can also see that the value of the bar key is only written conditionally; thus, its value might result from a previous iteration.

This might or might not be intended. To make your intention clear, your code more readible and to avoid accidental bugs, we recommend to add an explicit initialization $myArray = array() either outside or inside the foreach loop.

Loading history...
723
724
		// These shouldn't all be the same (run it 4 times to minimise chance of an accidental collision)
725
		// There's about a 1 in a billion chance of an accidental collision
726
		$this->assertTrue($keysA != $keysB || $keysB != $keysC || $keysC != $keysD);
0 ignored issues
show
Bug introduced by
The variable $keysC does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
Bug introduced by
The variable $keysD does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
727
	}
728
729
	public function testWriteSavesToHasOneRelations() {
730
		/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
731
		$team = new DataObjectTest_Team();
732
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'player1');
733
		$team->CaptainID = $captainID;
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<DataObjectTest_Team>. 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...
734
		$team->write();
735
		$this->assertEquals($captainID,
736
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
737
738
		/* After giving it a value, you should also be able to set it back to null */
739
		$team->CaptainID = '';
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<DataObjectTest_Team>. 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...
740
		$team->write();
741
		$this->assertEquals(0,
742
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
743
744
		/* You should also be able to save a blank to it when it's first created */
745
		$team = new DataObjectTest_Team();
746
		$team->CaptainID = '';
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<DataObjectTest_Team>. 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...
747
		$team->write();
748
		$this->assertEquals(0,
749
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
750
751
		/* Ditto for existing records without a value */
752
		$existingTeam = $this->objFromFixture('DataObjectTest_Team', 'team1');
753
		$existingTeam->CaptainID = '';
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<DataObject>. 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...
754
		$existingTeam->write();
755
		$this->assertEquals(0,
756
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value());
757
	}
758
759
	public function testCanAccessHasOneObjectsAsMethods() {
760
		/* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the
761
		 * object itself should be accessible as $obj->Captain() */
762
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
763
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
764
765
		$team->CaptainID = $captainID;
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<DataObject>. 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...
766
		$this->assertNotNull($team->Captain());
767
		$this->assertEquals($captainID, $team->Captain()->ID);
768
769
		// Test for polymorphic has_one relations
770
		$fan = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
771
		$fan->FavouriteID = $team->ID;
0 ignored issues
show
Documentation introduced by
The property FavouriteID does not exist on object<DataObject>. 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...
772
		$fan->FavouriteClass = $team->class;
0 ignored issues
show
Documentation introduced by
The property FavouriteClass does not exist on object<DataObject>. 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...
773
		$this->assertNotNull($fan->Favourite());
774
		$this->assertEquals($team->ID, $fan->Favourite()->ID);
775
		$this->assertInstanceOf($team->class, $fan->Favourite());
776
	}
777
778
	public function testFieldNamesThatMatchMethodNamesWork() {
779
		/* Check that a field name that corresponds to a method on DataObject will still work */
780
		$obj = new DataObjectTest_Fixture();
781
		$obj->Data = "value1";
0 ignored issues
show
Documentation introduced by
The property Data does not exist on object<DataObjectTest_Fixture>. 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...
782
		$obj->DbObject = "value2";
0 ignored issues
show
Documentation introduced by
The property DbObject does not exist on object<DataObjectTest_Fixture>. 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...
783
		$obj->Duplicate = "value3";
0 ignored issues
show
Documentation introduced by
The property Duplicate does not exist on object<DataObjectTest_Fixture>. 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...
784
		$obj->write();
785
786
		$this->assertNotNull($obj->ID);
787
		$this->assertEquals('value1',
788
			DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
789
		$this->assertEquals('value2',
790
			DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
791
		$this->assertEquals('value3',
792
			DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
793
	}
794
795
	/**
796
	 * @todo Re-enable all test cases for field existence after behaviour has been fixed
797
	 */
798
	public function testFieldExistence() {
799
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
800
		$teamSingleton = singleton('DataObjectTest_Team');
801
802
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
803
		$subteamSingleton = singleton('DataObjectTest_SubTeam');
804
805
		/* hasField() singleton checks */
806
		$this->assertTrue($teamSingleton->hasField('ID'),
807
			'hasField() finds built-in fields in singletons');
808
		$this->assertTrue($teamSingleton->hasField('Title'),
809
			'hasField() finds custom fields in singletons');
810
811
		/* hasField() instance checks */
812
		$this->assertFalse($teamInstance->hasField('NonExistingField'),
813
			'hasField() doesnt find non-existing fields in instances');
814
		$this->assertTrue($teamInstance->hasField('ID'),
815
			'hasField() finds built-in fields in instances');
816
		$this->assertTrue($teamInstance->hasField('Created'),
817
			'hasField() finds built-in fields in instances');
818
		$this->assertTrue($teamInstance->hasField('DatabaseField'),
819
			'hasField() finds custom fields in instances');
820
		//$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
821
		//'hasField() doesnt find subclass fields in parentclass instances');
822
		$this->assertTrue($teamInstance->hasField('DynamicField'),
823
			'hasField() finds dynamic getters in instances');
824
		$this->assertTrue($teamInstance->hasField('HasOneRelationshipID'),
825
			'hasField() finds foreign keys in instances');
826
		$this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'),
827
			'hasField() finds extended fields in instances');
828
		$this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'),
829
			'hasField() finds extended foreign keys in instances');
830
		//$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
831
		//'hasField() includes extended dynamic getters in instances');
832
833
		/* hasField() subclass checks */
834
		$this->assertTrue($subteamInstance->hasField('ID'),
835
			'hasField() finds built-in fields in subclass instances');
836
		$this->assertTrue($subteamInstance->hasField('Created'),
837
			'hasField() finds built-in fields in subclass instances');
838
		$this->assertTrue($subteamInstance->hasField('DatabaseField'),
839
			'hasField() finds custom fields in subclass instances');
840
		$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'),
841
			'hasField() finds custom fields in subclass instances');
842
		$this->assertTrue($subteamInstance->hasField('DynamicField'),
843
			'hasField() finds dynamic getters in subclass instances');
844
		$this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'),
845
			'hasField() finds foreign keys in subclass instances');
846
		$this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'),
847
			'hasField() finds extended fields in subclass instances');
848
		$this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'),
849
			'hasField() finds extended foreign keys in subclass instances');
850
851
		/* hasDatabaseField() singleton checks */
852
		//$this->assertTrue($teamSingleton->hasDatabaseField('ID'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
853
		//'hasDatabaseField() finds built-in fields in singletons');
854
		$this->assertTrue($teamSingleton->hasDatabaseField('Title'),
855
			'hasDatabaseField() finds custom fields in singletons');
856
857
		/* hasDatabaseField() instance checks */
858
		$this->assertFalse($teamInstance->hasDatabaseField('NonExistingField'),
859
			'hasDatabaseField() doesnt find non-existing fields in instances');
860
		//$this->assertTrue($teamInstance->hasDatabaseField('ID'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
861
		//'hasDatabaseField() finds built-in fields in instances');
862
		$this->assertTrue($teamInstance->hasDatabaseField('Created'),
863
			'hasDatabaseField() finds built-in fields in instances');
864
		$this->assertTrue($teamInstance->hasDatabaseField('DatabaseField'),
865
			'hasDatabaseField() finds custom fields in instances');
866
		$this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'),
867
			'hasDatabaseField() doesnt find subclass fields in parentclass instances');
868
		//$this->assertFalse($teamInstance->hasDatabaseField('DynamicField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
869
		//'hasDatabaseField() doesnt dynamic getters in instances');
870
		$this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'),
871
			'hasDatabaseField() finds foreign keys in instances');
872
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'),
873
			'hasDatabaseField() finds extended fields in instances');
874
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'),
875
			'hasDatabaseField() finds extended foreign keys in instances');
876
		$this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'),
877
			'hasDatabaseField() doesnt include extended dynamic getters in instances');
878
879
		/* hasDatabaseField() subclass checks */
880
		$this->assertTrue($subteamInstance->hasDatabaseField('DatabaseField'),
881
			'hasField() finds custom fields in subclass instances');
882
		$this->assertTrue($subteamInstance->hasDatabaseField('SubclassDatabaseField'),
883
			'hasField() finds custom fields in subclass instances');
884
885
	}
886
887
	/**
888
	 * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed
889
	 */
890
	public function testFieldInheritance() {
891
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
892
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
893
894
		$this->assertEquals(
895
			array_keys($teamInstance->inheritedDatabaseFields()),
896
			array(
897
				//'ID',
898
				//'ClassName',
899
				//'Created',
900
				//'LastEdited',
901
				'Title',
902
				'DatabaseField',
903
				'ExtendedDatabaseField',
904
				'CaptainID',
905
				'HasOneRelationshipID',
906
				'ExtendedHasOneRelationshipID'
907
			),
908
			'inheritedDatabaseFields() contains all fields defined on instance: base, extended and foreign keys'
909
		);
910
911
		$this->assertEquals(
912
			array_keys(DataObject::database_fields('DataObjectTest_Team', false)),
913
			array(
914
				//'ID',
915
				'ClassName',
916
				'LastEdited',
917
				'Created',
918
				'Title',
919
				'DatabaseField',
920
				'ExtendedDatabaseField',
921
				'CaptainID',
922
				'HasOneRelationshipID',
923
				'ExtendedHasOneRelationshipID'
924
			),
925
			'databaseFields() contains only fields defined on instance, including base, extended and foreign keys'
926
		);
927
928
		$this->assertEquals(
929
			array_keys($subteamInstance->inheritedDatabaseFields()),
930
			array(
931
				//'ID',
932
				//'ClassName',
933
				//'Created',
934
				//'LastEdited',
935
				'SubclassDatabaseField',
936
				'ParentTeamID',
937
				'Title',
938
				'DatabaseField',
939
				'ExtendedDatabaseField',
940
				'CaptainID',
941
				'HasOneRelationshipID',
942
				'ExtendedHasOneRelationshipID',
943
			),
944
			'inheritedDatabaseFields() on subclass contains all fields, including base, extended  and foreign keys'
945
		);
946
947
		$this->assertEquals(
948
			array_keys(DataObject::database_fields('DataObjectTest_SubTeam', false)),
949
			array(
950
				'SubclassDatabaseField',
951
				'ParentTeamID',
952
			),
953
			'databaseFields() on subclass contains only fields defined on instance'
954
		);
955
	}
956
957
	public function testSearchableFields() {
958
		$player = $this->objFromFixture('DataObjectTest_Player', 'captain1');
959
		$fields = $player->searchableFields();
960
		$this->assertArrayHasKey(
961
			'IsRetired',
962
			$fields,
963
			'Fields defined by $searchable_fields static are correctly detected'
964
		);
965
		$this->assertArrayHasKey(
966
			'ShirtNumber',
967
			$fields,
968
			'Fields defined by $searchable_fields static are correctly detected'
969
		);
970
971
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
972
		$fields = $team->searchableFields();
973
		$this->assertArrayHasKey(
974
			'Title',
975
			$fields,
976
			'Fields can be inherited from the $summary_fields static, including methods called on fields'
977
		);
978
		$this->assertArrayHasKey(
979
			'Captain.ShirtNumber',
980
			$fields,
981
			'Fields on related objects can be inherited from the $summary_fields static'
982
		);
983
		$this->assertArrayHasKey(
984
			'Captain.FavouriteTeam.Title',
985
			$fields,
986
			'Fields on related objects can be inherited from the $summary_fields static'
987
		);
988
989
		$testObj = new DataObjectTest_Fixture();
990
		$fields = $testObj->searchableFields();
991
		$this->assertEmpty($fields);
992
	}
993
994
	public function testCastingHelper() {
995
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
996
997
		$this->assertEquals('Varchar', $team->castingHelper('Title'), 'db field wasn\'t casted correctly');
998
		$this->assertEquals('HTMLVarchar', $team->castingHelper('DatabaseField'), 'db field wasn\'t casted correctly');
999
1000
		$sponsor = $team->Sponsors()->first();
1001
		$this->assertEquals('Int', $sponsor->castingHelper('SponsorFee'), 'many_many_extraFields not casted correctly');
1002
	}
1003
1004
	public function testSummaryFieldsCustomLabels() {
1005
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
1006
		$summaryFields = $team->summaryFields();
1007
1008
		$this->assertEquals(
1009
			'Custom Title',
1010
			$summaryFields['Title'],
1011
			'Custom title is preserved'
1012
		);
1013
1014
		$this->assertEquals(
1015
			'Captain\'s shirt number',
1016
			$summaryFields['Captain.ShirtNumber'],
1017
			'Custom title on relation is preserved'
1018
		);
1019
	}
1020
1021
	public function testDataObjectUpdate() {
1022
		/* update() calls can use the dot syntax to reference has_one relations and other methods that return
1023
		 * objects */
1024
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1025
		$team1->CaptainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<DataObject>. 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...
1026
1027
		$team1->update(array(
1028
			'DatabaseField' => 'Something',
1029
			'Captain.FirstName' => 'Jim',
1030
			'Captain.Email' => '[email protected]',
1031
			'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1032
		));
1033
1034
		/* Test the simple case of updating fields on the object itself */
1035
		$this->assertEquals('Something', $team1->DatabaseField);
1036
1037
		/* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in
1038
		 * the database.  Although update() doesn't usually write, it does write related records automatically. */
1039
		$captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1040
		$this->assertEquals('Jim', $captain1->FirstName);
1041
		$this->assertEquals('[email protected]', $captain1->Email);
1042
1043
		/* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.
1044
		 * FavouriteTeam.Title made */
1045
		$reloadedTeam1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1046
		$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1047
	}
1048
1049
	public function testDataObjectUpdateNew() {
1050
		/* update() calls can use the dot syntax to reference has_one relations and other methods that return
1051
		 * objects */
1052
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1053
		$team1->CaptainID = 0;
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<DataObject>. 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...
1054
1055
		$team1->update(array(
1056
			'Captain.FirstName' => 'Jim',
1057
			'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1058
		));
1059
		/* Test that the captain ID has been updated */
1060
		$this->assertGreaterThan(0, $team1->CaptainID);
1061
1062
		/* Fetch the newly created captain */
1063
		$captain1 = DataObjectTest_Player::get()->byID($team1->CaptainID);
1064
		$this->assertEquals('Jim', $captain1->FirstName);
1065
1066
		/* Grab the favourite team and make sure it has the correct values */
1067
		$reloadedTeam1 = $captain1->FavouriteTeam();
1068
		$this->assertEquals($reloadedTeam1->ID, $captain1->FavouriteTeamID);
1069
		$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1070
	}
1071
1072
	public function testWritingInvalidDataObjectThrowsException() {
1073
		$validatedObject = new DataObjectTest_ValidatedObject();
1074
1075
		$this->setExpectedException('ValidationException');
1076
		$validatedObject->write();
1077
	}
1078
1079
	public function testWritingValidDataObjectDoesntThrowException() {
1080
		$validatedObject = new DataObjectTest_ValidatedObject();
1081
		$validatedObject->Name = "Mr. Jones";
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...
1082
1083
		$validatedObject->write();
1084
		$this->assertTrue($validatedObject->isInDB(), "Validated object was not saved to database");
1085
	}
1086
1087
	public function testSubclassCreation() {
1088
		/* Creating a new object of a subclass should set the ClassName field correctly */
1089
		$obj = new DataObjectTest_SubTeam();
1090
		$obj->write();
1091
		$this->assertEquals("DataObjectTest_SubTeam",
1092
			DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
1093
	}
1094
1095
	public function testForceInsert() {
1096
		/* If you set an ID on an object and pass forceInsert = true, then the object should be correctly created */
1097
		$conn = DB::get_conn();
1098
		if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', true);
1099
		$obj = new DataObjectTest_SubTeam();
1100
		$obj->ID = 1001;
1101
		$obj->Title = 'asdfasdf';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<DataObjectTest_SubTeam>. 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...
1102
		$obj->SubclassDatabaseField = 'asdfasdf';
0 ignored issues
show
Documentation introduced by
The property SubclassDatabaseField does not exist on object<DataObjectTest_SubTeam>. 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...
1103
		$obj->write(false, true);
1104
		if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', false);
1105
1106
		$this->assertEquals("DataObjectTest_SubTeam",
1107
			DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
1108
1109
		/* Check that it actually saves to the database with the correct ID */
1110
		$this->assertEquals("1001", DB::query(
1111
			"SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'")->value());
1112
		$this->assertEquals("1001",
1113
			DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value());
1114
	}
1115
1116
	public function TestHasOwnTable() {
1117
		/* Test DataObject::has_own_table() returns true if the object has $has_one or $db values */
1118
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Player"));
1119
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Team"));
1120
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Fixture"));
1121
1122
		/* Root DataObject that always have a table, even if they lack both $db and $has_one */
1123
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_FieldlessTable"));
1124
1125
		/* Subclasses without $db or $has_one don't have a table */
1126
		$this->assertFalse(DataObject::has_own_table("DataObjectTest_FieldlessSubTable"));
1127
1128
		/* Return false if you don't pass it a subclass of DataObject */
1129
		$this->assertFalse(DataObject::has_own_table("DataObject"));
1130
		$this->assertFalse(DataObject::has_own_table("ViewableData"));
1131
		$this->assertFalse(DataObject::has_own_table("ThisIsntADataObject"));
1132
	}
1133
1134
	public function testMerge() {
1135
		// test right merge of subclasses
1136
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1137
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1138
		$leftOrigID = $left->ID;
1139
		$left->merge($right, 'right', false, false);
1140
		$this->assertEquals(
1141
			$left->Title,
1142
			'Subteam 2',
1143
			'merge() with "right" priority overwrites fields with existing values on subclasses'
1144
		);
1145
		$this->assertEquals(
1146
			$left->ID,
1147
			$leftOrigID,
1148
			'merge() with "right" priority doesnt overwrite database ID'
1149
		);
1150
1151
		// test overwriteWithEmpty flag on existing left values
1152
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1153
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam3_with_empty_fields');
1154
		$left->merge($right, 'right', false, true);
1155
		$this->assertEquals(
1156
			$left->Title,
1157
			'Subteam 3',
1158
			'merge() with $overwriteWithEmpty overwrites non-empty fields on left object'
1159
		);
1160
1161
		// test overwriteWithEmpty flag on empty left values
1162
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1163
		// $SubclassDatabaseField is empty on here
1164
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1165
		$left->merge($right, 'right', false, true);
1166
		$this->assertEquals(
1167
			$left->SubclassDatabaseField,
1168
			NULL,
1169
			'merge() with $overwriteWithEmpty overwrites empty fields on left object'
1170
		);
1171
1172
		// @todo test "left" priority flag
1173
		// @todo test includeRelations flag
1174
		// @todo test includeRelations in combination with overwriteWithEmpty
1175
		// @todo test has_one relations
1176
		// @todo test has_many and many_many relations
1177
	}
1178
1179
	public function testPopulateDefaults() {
1180
		$obj = new DataObjectTest_Fixture();
1181
		$this->assertEquals(
1182
			$obj->MyFieldWithDefault,
1183
			'Default Value',
1184
			'Defaults are populated for in-memory object from $defaults array'
1185
		);
1186
1187
		$this->assertEquals(
1188
			$obj->MyFieldWithAltDefault,
1189
			'Default Value',
1190
			'Defaults are populated from overloaded populateDefaults() method'
1191
		);
1192
	}
1193
1194
	protected function makeAccessible($object, $method) {
1195
		$reflectionMethod = new ReflectionMethod($object, $method);
1196
		$reflectionMethod->setAccessible(true);
1197
		return $reflectionMethod;
1198
	}
1199
1200
	public function testValidateModelDefinitionsFailsWithArray() {
1201
1202
		$object = new DataObjectTest_Team;
1203
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1204
1205
		Config::inst()->update('DataObjectTest_Team', 'has_one', array('NotValid' => array('NoArraysAllowed')));
1206
		$this->setExpectedException('LogicException');
1207
1208
		$method->invoke($object);
1209
	}
1210
1211
	public function testValidateModelDefinitionsFailsWithIntKey() {
1212
		$object = new DataObjectTest_Team;
1213
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1214
1215
		Config::inst()->update('DataObjectTest_Team', 'has_many', array(12 => 'DataObjectTest_Player'));
1216
		$this->setExpectedException('LogicException');
1217
1218
		$method->invoke($object);
1219
	}
1220
1221
	public function testValidateModelDefinitionsFailsWithIntValue() {
1222
		$object = new DataObjectTest_Team;
1223
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1224
1225
		Config::inst()->update('DataObjectTest_Team', 'many_many', array('Players' => 12));
1226
		$this->setExpectedException('LogicException');
1227
1228
		$method->invoke($object);
1229
	}
1230
1231
	/**
1232
	 * many_many_extraFields is allowed to have an array value, so shouldn't throw an exception
1233
	 */
1234
	public function testValidateModelDefinitionsPassesWithExtraFields() {
1235
		$object = new DataObjectTest_Team;
1236
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1237
1238
		Config::inst()->update('DataObjectTest_Team', 'many_many_extraFields',
1239
			array('Relations' => array('Price' => 'Int')));
1240
1241
		try {
1242
			$method->invoke($object);
1243
		} catch(Exception $e) {
1244
			$this->fail('Exception should not be thrown');
1245
			throw $e;
1246
		}
1247
	}
1248
1249
	public function testNewClassInstance() {
1250
		$dataObject = $this->objFromFixture('DataObjectTest_Team', 'team1');
1251
		$changedDO = $dataObject->newClassInstance('DataObjectTest_SubTeam');
1252
		$changedFields = $changedDO->getChangedFields();
1253
1254
		// Don't write the record, it will reset changed fields
1255
		$this->assertInstanceOf('DataObjectTest_SubTeam', $changedDO);
1256
		$this->assertEquals($changedDO->ClassName, 'DataObjectTest_SubTeam');
1257
		$this->assertContains('ClassName', array_keys($changedFields));
1258
		$this->assertEquals($changedFields['ClassName']['before'], 'DataObjectTest_Team');
1259
		$this->assertEquals($changedFields['ClassName']['after'], 'DataObjectTest_SubTeam');
1260
1261
		$changedDO->write();
1262
1263
		$this->assertInstanceOf('DataObjectTest_SubTeam', $changedDO);
1264
		$this->assertEquals($changedDO->ClassName, 'DataObjectTest_SubTeam');
1265
	}
1266
1267
	public function testMultipleManyManyWithSameClass() {
1268
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
1269
		$sponsors = $team->Sponsors();
1270
		$equipmentSuppliers = $team->EquipmentSuppliers();
1271
1272
		// Check that DataObject::many_many() works as expected
1273
		list($class, $targetClass, $parentField, $childField, $joinTable) = $team->manyManyComponent('Sponsors');
0 ignored issues
show
Unused Code introduced by
The assignment to $parentField is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
Unused Code introduced by
The assignment to $childField is unused. Consider omitting it like so list($first,,$third).

This checks looks for assignemnts to variables using the list(...) function, where not all assigned variables are subsequently used.

Consider the following code example.

<?php

function returnThreeValues() {
    return array('a', 'b', 'c');
}

list($a, $b, $c) = returnThreeValues();

print $a . " - " . $c;

Only the variables $a and $c are used. There was no need to assign $b.

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1274
		$this->assertEquals('DataObjectTest_Team', $class,
1275
			'DataObject::many_many() didn\'t find the correct base class');
1276
		$this->assertEquals('DataObjectTest_EquipmentCompany', $targetClass,
1277
			'DataObject::many_many() didn\'t find the correct target class for the relation');
1278
		$this->assertEquals('DataObjectTest_EquipmentCompany_SponsoredTeams', $joinTable,
1279
			'DataObject::many_many() didn\'t find the correct relation table');
1280
1281
		// Check that ManyManyList still works
1282
		$this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation');
1283
		$this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation');
1284
1285
		// Check everything works when no relation is present
1286
		$teamWithoutSponsor = $this->objFromFixture('DataObjectTest_Team', 'team3');
1287
		$this->assertInstanceOf('ManyManyList', $teamWithoutSponsor->Sponsors());
1288
		$this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count());
1289
1290
		// Check many_many_extraFields still works
1291
		$equipmentCompany = $this->objFromFixture('DataObjectTest_EquipmentCompany', 'equipmentcompany1');
1292
		$equipmentCompany->SponsoredTeams()->add($teamWithoutSponsor, array('SponsorFee' => 1000));
1293
		$sponsoredTeams = $equipmentCompany->SponsoredTeams();
1294
		$this->assertEquals(1000, $sponsoredTeams->byID($teamWithoutSponsor->ID)->SponsorFee,
1295
			'Data from many_many_extraFields was not stored/extracted correctly');
1296
1297
		// Check subclasses correctly inherit multiple many_manys
1298
		$subTeam = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1299
		$this->assertEquals(2, $subTeam->Sponsors()->count(),
1300
			'Child class did not inherit multiple many_manys');
1301
		$this->assertEquals(1, $subTeam->EquipmentSuppliers()->count(),
1302
			'Child class did not inherit multiple many_manys');
1303
		// Team 2 has one EquipmentCompany sponsor and one SubEquipmentCompany
1304
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
1305
		$this->assertEquals(2, $team2->Sponsors()->count(),
1306
			'Child class did not inherit multiple belongs_many_manys');
1307
1308
		// Check many_many_extraFields also works from the belongs_many_many side
1309
		$sponsors = $team2->Sponsors();
1310
		$sponsors->add($equipmentCompany, array('SponsorFee' => 750));
1311
		$this->assertEquals(750, $sponsors->byID($equipmentCompany->ID)->SponsorFee,
1312
			'Data from many_many_extraFields was not stored/extracted correctly');
1313
1314
		$subEquipmentCompany = $this->objFromFixture('DataObjectTest_SubEquipmentCompany', 'subequipmentcompany1');
1315
		$subTeam->Sponsors()->add($subEquipmentCompany, array('SponsorFee' => 1200));
1316
		$this->assertEquals(1200, $subTeam->Sponsors()->byID($subEquipmentCompany->ID)->SponsorFee,
1317
			'Data from inherited many_many_extraFields was not stored/extracted correctly');
1318
	}
1319
1320
	public function testManyManyExtraFields() {
1321
		$player = $this->objFromFixture('DataObjectTest_Player', 'player1');
1322
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
1323
1324
		// Get all extra fields
1325
		$teamExtraFields = $team->manyManyExtraFields();
1326
		$this->assertEquals(array(
1327
			'Players' => array('Position' => 'Varchar(100)')
1328
		), $teamExtraFields);
1329
1330
		// Ensure fields from parent classes are included
1331
		$subTeam = singleton('DataObjectTest_SubTeam');
1332
		$teamExtraFields = $subTeam->manyManyExtraFields();
1333
		$this->assertEquals(array(
1334
			'Players' => array('Position' => 'Varchar(100)'),
1335
			'FormerPlayers' => array('Position' => 'Varchar(100)')
1336
		), $teamExtraFields);
1337
1338
		// Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
1339
		$teamExtraFields = $team->manyManyExtraFieldsForComponent('Players');
1340
		$this->assertEquals($teamExtraFields, array(
1341
			'Position' => 'Varchar(100)'
1342
		));
1343
1344
		// We'll have to go through the relation to get the extra fields on Player
1345
		$playerExtraFields = $player->manyManyExtraFieldsForComponent('Teams');
1346
		$this->assertEquals($playerExtraFields, array(
1347
			'Position' => 'Varchar(100)'
1348
		));
1349
1350
		// Iterate through a many-many relationship and confirm that extra fields are included
1351
		$newTeam = new DataObjectTest_Team();
1352
		$newTeam->Title = "New team";
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<DataObjectTest_Team>. 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...
1353
		$newTeam->write();
1354
		$newTeamID = $newTeam->ID;
1355
1356
		$newPlayer = new DataObjectTest_Player();
1357
		$newPlayer->FirstName = "Sam";
1358
		$newPlayer->Surname = "Minnee";
1359
		$newPlayer->write();
1360
1361
		// The idea of Sam as a prop is essentially humourous.
1362
		$newTeam->Players()->add($newPlayer, array("Position" => "Prop"));
1363
1364
		// Requery and uncache everything
1365
		$newTeam->flushCache();
1366
		$newTeam = DataObject::get_by_id('DataObjectTest_Team', $newTeamID);
1367
1368
		// Check that the Position many_many_extraField is extracted.
1369
		$player = $newTeam->Players()->First();
1370
		$this->assertEquals('Sam', $player->FirstName);
1371
		$this->assertEquals("Prop", $player->Position);
1372
1373
		// Check that ordering a many-many relation by an aggregate column doesn't fail
1374
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1375
		$player->Teams()->sort("count(DISTINCT \"DataObjectTest_Team_Players\".\"DataObjectTest_PlayerID\") DESC");
1376
	}
1377
1378
	/**
1379
	 * Check that the queries generated for many-many relation queries can have unlimitedRowCount
1380
	 * called on them.
1381
	 */
1382
	public function testManyManyUnlimitedRowCount() {
1383
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1384
		// TODO: What's going on here?
1385
		$this->assertEquals(2, $player->Teams()->dataQuery()->query()->unlimitedRowCount());
1386
	}
1387
1388
	/**
1389
	 * Tests that singular_name() generates sensible defaults.
1390
	 */
1391
	public function testSingularName() {
1392
		$assertions = array(
1393
			'DataObjectTest_Player'       => 'Data Object Test Player',
1394
			'DataObjectTest_Team'         => 'Data Object Test Team',
1395
			'DataObjectTest_Fixture'      => 'Data Object Test Fixture'
1396
		);
1397
1398
		foreach($assertions as $class => $expectedSingularName) {
1399
			$this->assertEquals(
1400
				$expectedSingularName,
1401
				singleton($class)->singular_name(),
1402
				"Assert that the singular_name for '$class' is correct."
1403
			);
1404
		}
1405
	}
1406
1407
	/**
1408
	 * Tests that plural_name() generates sensible defaults.
1409
	 */
1410
	public function testPluralName() {
1411
		$assertions = array(
1412
			'DataObjectTest_Player'       => 'Data Object Test Players',
1413
			'DataObjectTest_Team'         => 'Data Object Test Teams',
1414
			'DataObjectTest_Fixture'      => 'Data Object Test Fixtures',
1415
			'DataObjectTest_Play'         => 'Data Object Test Plays',
1416
			'DataObjectTest_Bogey'        => 'Data Object Test Bogeys',
1417
			'DataObjectTest_Ploy'         => 'Data Object Test Ploys',
1418
		);
1419
1420
		foreach($assertions as $class => $expectedPluralName) {
1421
			$this->assertEquals(
1422
				$expectedPluralName,
1423
				singleton($class)->plural_name(),
1424
				"Assert that the plural_name for '$class' is correct."
1425
			);
1426
		}
1427
	}
1428
1429
	public function testHasDatabaseField() {
1430
		$team = singleton('DataObjectTest_Team');
1431
		$subteam = singleton('DataObjectTest_SubTeam');
1432
1433
		$this->assertTrue(
1434
			$team->hasDatabaseField('Title'),
1435
			"hasOwnDatabaseField() works with \$db fields"
1436
		);
1437
		$this->assertTrue(
1438
			$team->hasDatabaseField('CaptainID'),
1439
			"hasOwnDatabaseField() works with \$has_one fields"
1440
		);
1441
		$this->assertFalse(
1442
			$team->hasDatabaseField('NonExistentField'),
1443
			"hasOwnDatabaseField() doesn't detect non-existend fields"
1444
		);
1445
		$this->assertTrue(
1446
			$team->hasDatabaseField('ExtendedDatabaseField'),
1447
			"hasOwnDatabaseField() works with extended fields"
1448
		);
1449
		$this->assertFalse(
1450
			$team->hasDatabaseField('SubclassDatabaseField'),
1451
			"hasOwnDatabaseField() doesn't pick up fields in subclasses on parent class"
1452
		);
1453
1454
		$this->assertTrue(
1455
			$subteam->hasDatabaseField('SubclassDatabaseField'),
1456
			"hasOwnDatabaseField() picks up fields in subclasses"
1457
		);
1458
1459
	}
1460
1461
	public function testFieldTypes() {
1462
		$obj = new DataObjectTest_Fixture();
1463
		$obj->DateField = '1988-01-02';
0 ignored issues
show
Documentation introduced by
The property DateField does not exist on object<DataObjectTest_Fixture>. 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...
1464
		$obj->DatetimeField = '1988-03-04 06:30';
0 ignored issues
show
Documentation introduced by
The property DatetimeField does not exist on object<DataObjectTest_Fixture>. 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...
1465
		$obj->write();
1466
		$obj->flushCache();
1467
1468
		$obj = DataObject::get_by_id('DataObjectTest_Fixture', $obj->ID);
1469
		$this->assertEquals('1988-01-02', $obj->DateField);
1470
		$this->assertEquals('1988-03-04 06:30:00', $obj->DatetimeField);
1471
	}
1472
1473
	public function testTwoSubclassesWithTheSameFieldNameWork() {
1474
		// Create two objects of different subclasses, setting the values of fields that are
1475
		// defined separately in each subclass
1476
		$obj1 = new DataObjectTest_SubTeam();
1477
		$obj1->SubclassDatabaseField = "obj1";
0 ignored issues
show
Documentation introduced by
The property SubclassDatabaseField does not exist on object<DataObjectTest_SubTeam>. 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...
1478
		$obj2 = new OtherSubclassWithSameField();
1479
		$obj2->SubclassDatabaseField = "obj2";
0 ignored issues
show
Documentation introduced by
The property SubclassDatabaseField does not exist on object<OtherSubclassWithSameField>. 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...
1480
1481
		// Write them to the database
1482
		$obj1->write();
1483
		$obj2->write();
1484
1485
		// Check that the values of those fields are properly read from the database
1486
		$values = DataObject::get("DataObjectTest_Team", "\"DataObjectTest_Team\".\"ID\" IN
1487
			($obj1->ID, $obj2->ID)")->column("SubclassDatabaseField");
1488
		$this->assertEquals(array_intersect($values, array('obj1', 'obj2')), $values);
1489
	}
1490
1491
	public function testClassNameSetForNewObjects() {
1492
		$d = new DataObjectTest_Player();
1493
		$this->assertEquals('DataObjectTest_Player', $d->ClassName);
1494
	}
1495
1496
	public function testHasValue() {
1497
		$team = new DataObjectTest_Team();
1498
		$this->assertFalse($team->hasValue('Title', null, false));
1499
		$this->assertFalse($team->hasValue('DatabaseField', null, false));
1500
1501
		$team->Title = 'hasValue';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<DataObjectTest_Team>. 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...
1502
		$this->assertTrue($team->hasValue('Title', null, false));
1503
		$this->assertFalse($team->hasValue('DatabaseField', null, false));
1504
1505
		$team->DatabaseField = '<p></p>';
0 ignored issues
show
Documentation introduced by
The property DatabaseField does not exist on object<DataObjectTest_Team>. 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...
1506
		$this->assertTrue($team->hasValue('Title', null, false));
1507
		$this->assertFalse (
1508
			$team->hasValue('DatabaseField', null, false),
1509
			'Test that a blank paragraph on a HTML field is not a valid value.'
1510
		);
1511
1512
		$team->Title = '<p></p>';
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<DataObjectTest_Team>. 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...
1513
		$this->assertTrue (
1514
			$team->hasValue('Title', null, false),
1515
			'Test that an empty paragraph is a value for non-HTML fields.'
1516
		);
1517
1518
		$team->DatabaseField = 'hasValue';
0 ignored issues
show
Documentation introduced by
The property DatabaseField does not exist on object<DataObjectTest_Team>. 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...
1519
		$this->assertTrue($team->hasValue('Title', null, false));
1520
		$this->assertTrue($team->hasValue('DatabaseField', null, false));
1521
	}
1522
1523
	public function testHasMany() {
1524
		$company = new DataObjectTest_Company();
1525
1526
		$this->assertEquals (
1527
			array (
1528
				'CurrentStaff'     => 'DataObjectTest_Staff',
1529
				'PreviousStaff'    => 'DataObjectTest_Staff'
1530
			),
1531
			$company->hasMany(),
1532
			'has_many strips field name data by default.'
1533
		);
1534
1535
		$this->assertEquals (
1536
			'DataObjectTest_Staff',
1537
			$company->hasManyComponent('CurrentStaff'),
1538
			'has_many strips field name data by default on single relationships.'
1539
		);
1540
1541
		$this->assertEquals (
1542
			array (
1543
				'CurrentStaff'     => 'DataObjectTest_Staff.CurrentCompany',
1544
				'PreviousStaff'    => 'DataObjectTest_Staff.PreviousCompany'
1545
			),
1546
			$company->hasMany(null, false),
1547
			'has_many returns field name data when $classOnly is false.'
1548
		);
1549
1550
		$this->assertEquals (
1551
			'DataObjectTest_Staff.CurrentCompany',
1552
			$company->hasManyComponent('CurrentStaff', false),
1553
			'has_many returns field name data on single records when $classOnly is false.'
1554
		);
1555
	}
1556
1557
	public function testGetRemoteJoinField() {
1558
		$company = new DataObjectTest_Company();
1559
1560
		$staffJoinField = $company->getRemoteJoinField('CurrentStaff', 'has_many', $polymorphic);
1561
		$this->assertEquals('CurrentCompanyID', $staffJoinField);
1562
		$this->assertFalse($polymorphic, 'DataObjectTest_Company->CurrentStaff is not polymorphic');
1563
		$previousStaffJoinField = $company->getRemoteJoinField('PreviousStaff', 'has_many', $polymorphic);
1564
		$this->assertEquals('PreviousCompanyID', $previousStaffJoinField);
1565
		$this->assertFalse($polymorphic, 'DataObjectTest_Company->PreviousStaff is not polymorphic');
1566
1567
		$ceo = new DataObjectTest_CEO();
1568
1569
		$this->assertEquals('CEOID', $ceo->getRemoteJoinField('Company', 'belongs_to', $polymorphic));
1570
		$this->assertFalse($polymorphic, 'DataObjectTest_CEO->Company is not polymorphic');
1571
		$this->assertEquals('PreviousCEOID', $ceo->getRemoteJoinField('PreviousCompany', 'belongs_to', $polymorphic));
1572
		$this->assertFalse($polymorphic, 'DataObjectTest_CEO->PreviousCompany is not polymorphic');
1573
1574
		$team = new DataObjectTest_Team();
1575
1576
		$this->assertEquals('Favourite', $team->getRemoteJoinField('Fans', 'has_many', $polymorphic));
1577
		$this->assertTrue($polymorphic, 'DataObjectTest_Team->Fans is polymorphic');
1578
		$this->assertEquals('TeamID', $team->getRemoteJoinField('Comments', 'has_many', $polymorphic));
1579
		$this->assertFalse($polymorphic, 'DataObjectTest_Team->Comments is not polymorphic');
1580
	}
1581
1582
	public function testBelongsTo() {
1583
		$company = new DataObjectTest_Company();
1584
		$ceo     = new DataObjectTest_CEO();
1585
1586
		$company->write();
1587
		$ceo->write();
1588
1589
		// Test belongs_to assignment
1590
		$company->CEOID = $ceo->ID;
0 ignored issues
show
Documentation introduced by
The property CEOID does not exist on object<DataObjectTest_Company>. 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...
1591
		$company->write();
1592
1593
		$this->assertEquals($company->ID, $ceo->Company()->ID, 'belongs_to returns the right results.');
1594
1595
		// Test automatic creation of class where no assigment exists
1596
		$ceo = new DataObjectTest_CEO();
1597
		$ceo->write();
1598
1599
		$this->assertTrue (
1600
			$ceo->Company() instanceof DataObjectTest_Company,
1601
			'DataObjects across belongs_to relations are automatically created.'
1602
		);
1603
		$this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
1604
1605
		// Write object with components
1606
		$ceo->Name = 'Edward Scissorhands';
0 ignored issues
show
Documentation introduced by
The property Name does not exist on object<DataObjectTest_CEO>. 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...
1607
		$ceo->write(false, false, false, true);
1608
		$this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
1609
1610
		$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
1611
		$this->assertEquals (
1612
			$ceo->Company()->ID, $newCEO->Company()->ID, 'belongs_to can be retrieved from the database.'
1613
		);
1614
	}
1615
1616
	public function testBelongsToPolymorphic() {
1617
		$company = new DataObjectTest_Company();
1618
		$ceo     = new DataObjectTest_CEO();
1619
1620
		$company->write();
1621
		$ceo->write();
1622
1623
		// Test belongs_to assignment
1624
		$company->OwnerID = $ceo->ID;
0 ignored issues
show
Documentation introduced by
The property OwnerID does not exist on object<DataObjectTest_Company>. 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...
1625
		$company->OwnerClass = $ceo->class;
0 ignored issues
show
Documentation introduced by
The property OwnerClass does not exist on object<DataObjectTest_Company>. 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...
1626
		$company->write();
1627
1628
		$this->assertEquals($company->ID, $ceo->CompanyOwned()->ID, 'belongs_to returns the right results.');
1629
		$this->assertEquals($company->class, $ceo->CompanyOwned()->class, 'belongs_to returns the right results.');
1630
1631
		// Test automatic creation of class where no assigment exists
1632
		$ceo = new DataObjectTest_CEO();
1633
		$ceo->write();
1634
1635
		$this->assertTrue (
1636
			$ceo->CompanyOwned() instanceof DataObjectTest_Company,
1637
			'DataObjects across polymorphic belongs_to relations are automatically created.'
1638
		);
1639
		$this->assertEquals($ceo->ID, $ceo->CompanyOwned()->OwnerID, 'Remote IDs are automatically set.');
1640
		$this->assertInstanceOf($ceo->CompanyOwned()->OwnerClass, $ceo, 'Remote class is automatically  set');
1641
1642
		// Write object with components
1643
		$ceo->write(false, false, false, true);
1644
		$this->assertTrue($ceo->CompanyOwned()->isInDB(), 'write() writes belongs_to components to the database.');
1645
1646
		$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
1647
		$this->assertEquals (
1648
			$ceo->CompanyOwned()->ID,
1649
			$newCEO->CompanyOwned()->ID,
1650
			'polymorphic belongs_to can be retrieved from the database.'
1651
		);
1652
	}
1653
1654
	/**
1655
	 * @expectedException LogicException
1656
	 */
1657
	public function testInvalidate() {
1658
		$do = new DataObjectTest_Fixture();
1659
		$do->write();
1660
1661
		$do->delete();
1662
1663
		$do->delete(); // Prohibit invalid object manipulation
1664
		$do->write();
1665
		$do->duplicate();
1666
	}
1667
1668
	public function testToMap() {
1669
		$obj = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1670
1671
		$map = $obj->toMap();
1672
1673
		$this->assertArrayHasKey('ID', $map, 'Contains base fields');
1674
		$this->assertArrayHasKey('Title', $map, 'Contains fields from parent class');
1675
		$this->assertArrayHasKey('SubclassDatabaseField', $map, 'Contains fields from concrete class');
1676
1677
		$this->assertEquals($obj->ID, $map['ID'],
1678
			'Contains values from base fields');
1679
		$this->assertEquals($obj->Title, $map['Title'],
1680
			'Contains values from parent class fields');
1681
		$this->assertEquals($obj->SubclassDatabaseField, $map['SubclassDatabaseField'],
1682
			'Contains values from concrete class fields');
1683
1684
		$newObj = new DataObjectTest_SubTeam();
1685
		$this->assertArrayHasKey('Title', $map, 'Contains null fields');
1686
	}
1687
1688
	public function testIsEmpty() {
1689
		$objEmpty = new DataObjectTest_Team();
1690
		$this->assertTrue($objEmpty->isEmpty(), 'New instance without populated defaults is empty');
1691
1692
		$objEmpty->Title = '0'; //
0 ignored issues
show
Documentation introduced by
The property Title does not exist on object<DataObjectTest_Team>. 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...
1693
		$this->assertFalse($objEmpty->isEmpty(), 'Zero value in attribute considered non-empty');
1694
	}
1695
1696
	public function testRelField() {
1697
		$captain = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1698
		// Test traversal of a single has_one
1699
		$this->assertEquals("Team 1", $captain->relField('FavouriteTeam.Title'));
1700
		// Test direct field access
1701
		$this->assertEquals("Captain", $captain->relField('FirstName'));
1702
1703
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1704
		// Test that we can traverse more than once, and that arbitrary methods are okay
1705
		$this->assertEquals("Team 1", $player->relField('Teams.First.Title'));
1706
1707
		$newPlayer = new DataObjectTest_Player();
1708
		$this->assertNull($newPlayer->relField('Teams.First.Title'));
1709
1710
		// Test that relField works on db field manipulations
1711
		$comment = $this->objFromFixture('DataObjectTest_TeamComment', 'comment3');
1712
		$this->assertEquals("PHIL IS A UNIQUE GUY, AND COMMENTS ON TEAM2" , $comment->relField('Comment.UpperCase'));
1713
	}
1714
1715
	public function testRelObject() {
1716
		$captain = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1717
1718
		// Test traversal of a single has_one
1719
		$this->assertInstanceOf("Varchar", $captain->relObject('FavouriteTeam.Title'));
1720
		$this->assertEquals("Team 1", $captain->relObject('FavouriteTeam.Title')->getValue());
1721
1722
		// Test direct field access
1723
		$this->assertInstanceOf("Boolean", $captain->relObject('IsRetired'));
1724
		$this->assertEquals(1, $captain->relObject('IsRetired')->getValue());
1725
1726
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1727
		// Test that we can traverse more than once, and that arbitrary methods are okay
1728
		$this->assertInstanceOf("Varchar", $player->relObject('Teams.First.Title'));
1729
		$this->assertEquals("Team 1", $player->relObject('Teams.First.Title')->getValue());
1730
	}
1731
1732
	public function testLateStaticBindingStyle() {
1733
		// Confirm that DataObjectTest_Player::get() operates as excepted
1734
		$this->assertEquals(4, DataObjectTest_Player::get()->Count());
1735
		$this->assertInstanceOf('DataObjectTest_Player', DataObjectTest_Player::get()->First());
1736
1737
		// You can't pass arguments to LSB syntax - use the DataList methods instead.
1738
		$this->setExpectedException('InvalidArgumentException');
1739
		DataObjectTest_Player::get(null, "\"ID\" = 1");
1740
1741
	}
1742
1743
	public function testBrokenLateStaticBindingStyle() {
1744
		// If you call DataObject::get() you have to pass a first argument
1745
		$this->setExpectedException('InvalidArgumentException');
1746
		DataObject::get();
1747
1748
	}
1749
1750
	public function testBigIntField() {
1751
		$staff = new DataObjectTest_Staff();
1752
		$staff->Salary = PHP_INT_MAX;
0 ignored issues
show
Documentation introduced by
The property Salary does not exist on object<DataObjectTest_Staff>. 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...
1753
		$staff->write();
1754
		$this->assertEquals(PHP_INT_MAX, DataObjectTest_Staff::get()->byID($staff->ID)->Salary);
1755
	}
1756
1757
1758
    /**
1759
     * @expectedException PHPUnit_Framework_Error_Warning
1760
     */
1761
	public function testSetFieldWithArrayOnScalarOnlyField()
1762
    {
1763
        $do = singleton('DataObjectTest_CompositeDBField');
1764
        $do->NonCompositeField = 'Some Value';
0 ignored issues
show
Bug introduced by
The property NonCompositeField does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1765
        $do->NonCompositeField = array('Amount' => 123, 'Currency' => 'CAD');
1766
        $this->assertEmpty($do->NonCompositeField);
1767
    }
1768
1769
    public function testSetFieldWithArrayOnCompositeField()
1770
    {
1771
        $do = singleton('DataObjectTest_CompositeDBField');
1772
        $do->CompositeMoneyField = array('Amount' => 123, 'Currency' => 'CAD');
0 ignored issues
show
Bug introduced by
The property CompositeMoneyField does not seem to exist in SS_Object.

An attempt at access to an undefined property has been detected. This may either be a typographical error or the property has been renamed but there are still references to its old name.

If you really want to allow access to undefined properties, you can define magic methods to allow access. See the php core documentation on Overloading.

Loading history...
1773
        $this->assertNotEmpty($do->CompositeMoneyField);
1774
    }
1775
1776
}
1777
1778
class DataObjectTest_Sortable extends DataObject implements TestOnly {
1779
	private static $db = array(
1780
		'Sort' => 'Int',
1781
		'Name' => 'Varchar',
1782
	);
1783
}
1784
1785
class DataObjectTest_Player extends Member implements TestOnly {
1786
	private static $db = array(
1787
		'IsRetired' => 'Boolean',
1788
		'ShirtNumber' => 'Varchar',
1789
	);
1790
1791
	private static $has_one = array(
1792
		'FavouriteTeam' => 'DataObjectTest_Team',
1793
	);
1794
1795
	private static $belongs_many_many = array(
1796
		'Teams' => 'DataObjectTest_Team'
1797
	);
1798
1799
	private static $has_many = array(
1800
		'Fans' => 'DataObjectTest_Fan.Favourite' // Polymorphic - Player fans
1801
	);
1802
1803
	private static $belongs_to = array (
1804
		'CompanyOwned'    => 'DataObjectTest_Company.Owner'
1805
	);
1806
1807
	private static $searchable_fields = array(
1808
		'IsRetired',
1809
		'ShirtNumber'
1810
	);
1811
}
1812
1813
class DataObjectTest_Team extends DataObject implements TestOnly {
1814
1815
	private static $db = array(
1816
		'Title' => 'Varchar',
1817
		'DatabaseField' => 'HTMLVarchar'
1818
	);
1819
1820
	private static $has_one = array(
1821
		"Captain" => 'DataObjectTest_Player',
1822
		'HasOneRelationship' => 'DataObjectTest_Player',
1823
	);
1824
1825
	private static $has_many = array(
1826
		'SubTeams' => 'DataObjectTest_SubTeam',
1827
		'Comments' => 'DataObjectTest_TeamComment',
1828
		'Fans' => 'DataObjectTest_Fan.Favourite' // Polymorphic - Team fans
1829
	);
1830
1831
	private static $many_many = array(
1832
		'Players' => 'DataObjectTest_Player'
1833
	);
1834
1835
	private static $many_many_extraFields = array(
1836
		'Players' => array(
1837
			'Position' => 'Varchar(100)'
1838
		)
1839
	);
1840
1841
	private static $belongs_many_many = array(
1842
		'Sponsors' => 'DataObjectTest_EquipmentCompany.SponsoredTeams',
1843
		'EquipmentSuppliers' => 'DataObjectTest_EquipmentCompany.EquipmentCustomers'
1844
	);
1845
1846
	private static $summary_fields = array(
1847
		'Title' => 'Custom Title',
1848
		'Title.UpperCase' => 'Title',
1849
		'Captain.ShirtNumber' => 'Captain\'s shirt number',
1850
		'Captain.FavouriteTeam.Title' => 'Captain\'s favourite team'
1851
	);
1852
1853
	private static $default_sort = '"Title"';
1854
1855
	public function MyTitle() {
1856
		return 'Team ' . $this->Title;
1857
	}
1858
1859
	public function getDynamicField() {
1860
		return 'dynamicfield';
1861
	}
1862
1863
}
1864
1865
class DataObjectTest_Fixture extends DataObject implements TestOnly {
1866
	private static $db = array(
1867
		// Funny field names
1868
		'Data' => 'Varchar',
1869
		'Duplicate' => 'Varchar',
1870
		'DbObject' => 'Varchar',
1871
1872
		// Field types
1873
		'DateField' => 'Date',
1874
		'DatetimeField' => 'Datetime',
1875
1876
		'MyFieldWithDefault' => 'Varchar',
1877
		'MyFieldWithAltDefault' => 'Varchar'
1878
	);
1879
1880
	private static $defaults = array(
1881
		'MyFieldWithDefault' => 'Default Value',
1882
	);
1883
1884
	private static $summary_fields = array(
1885
		'Data' => 'Data',
1886
		'DateField.Nice' => 'Date'
1887
	);
1888
1889
	private static $default_sort = '"DataObjectTest_Fixture"."ID" ASC';
1890
1891
	private static $searchable_fields = array();
1892
1893
	public function populateDefaults() {
1894
		parent::populateDefaults();
1895
1896
		$this->MyFieldWithAltDefault = 'Default Value';
0 ignored issues
show
Documentation introduced by
The property MyFieldWithAltDefault does not exist on object<DataObjectTest_Fixture>. 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...
1897
	}
1898
1899
}
1900
1901
class DataObjectTest_SubTeam extends DataObjectTest_Team implements TestOnly {
1902
	private static $db = array(
1903
		'SubclassDatabaseField' => 'Varchar'
1904
	);
1905
1906
	private static $has_one = array(
1907
		"ParentTeam" => 'DataObjectTest_Team',
1908
	);
1909
1910
	private static $many_many = array(
1911
		'FormerPlayers' => 'DataObjectTest_Player'
1912
	);
1913
1914
	private static $many_many_extraFields = array(
1915
		'FormerPlayers' => array(
1916
			'Position' => 'Varchar(100)'
1917
		)
1918
	);
1919
}
1920
class OtherSubclassWithSameField extends DataObjectTest_Team implements TestOnly {
1921
	private static $db = array(
1922
		'SubclassDatabaseField' => 'Varchar',
1923
	);
1924
}
1925
1926
1927
class DataObjectTest_FieldlessTable extends DataObject implements TestOnly {
1928
}
1929
1930
class DataObjectTest_FieldlessSubTable extends DataObjectTest_Team implements TestOnly {
1931
}
1932
1933
1934
class DataObjectTest_Team_Extension extends DataExtension implements TestOnly {
1935
1936
	private static $db = array(
1937
		'ExtendedDatabaseField' => 'Varchar'
1938
	);
1939
1940
	private static $has_one = array(
1941
		'ExtendedHasOneRelationship' => 'DataObjectTest_Player'
1942
	);
1943
1944
	public function getExtendedDynamicField() {
1945
		return "extended dynamic field";
1946
	}
1947
1948
}
1949
1950
class DataObjectTest_ValidatedObject extends DataObject implements TestOnly {
1951
1952
	private static $db = array(
1953
		'Name' => 'Varchar(50)'
1954
	);
1955
1956
	protected function validate() {
1957
		if(!empty($this->Name)) {
1958
			return new ValidationResult();
1959
		} else {
1960
			return new ValidationResult(false, "This object needs a name. Otherwise it will have an identity crisis!");
1961
		}
1962
	}
1963
}
1964
1965
class DataObjectTest_Company extends DataObject implements TestOnly {
1966
1967
	private static $db = array(
1968
		'Name' => 'Varchar'
1969
	);
1970
1971
	private static $has_one = array (
1972
		'CEO'         => 'DataObjectTest_CEO',
1973
		'PreviousCEO' => 'DataObjectTest_CEO',
1974
		'Owner'       => 'DataObject' // polymorphic
1975
	);
1976
1977
	private static $has_many = array (
1978
		'CurrentStaff'     => 'DataObjectTest_Staff.CurrentCompany',
1979
		'PreviousStaff'    => 'DataObjectTest_Staff.PreviousCompany'
1980
	);
1981
}
1982
1983
class DataObjectTest_EquipmentCompany extends DataObjectTest_Company implements TestOnly {
1984
	private static $many_many = array(
1985
		'SponsoredTeams' => 'DataObjectTest_Team',
1986
		'EquipmentCustomers' => 'DataObjectTest_Team'
1987
	);
1988
1989
	private static $many_many_extraFields = array(
1990
		'SponsoredTeams' => array(
1991
			'SponsorFee' => 'Int'
1992
		)
1993
	);
1994
}
1995
1996
class DataObjectTest_SubEquipmentCompany extends DataObjectTest_EquipmentCompany implements TestOnly {
1997
	private static $db = array(
1998
		'SubclassDatabaseField' => 'Varchar',
1999
	);
2000
}
2001
2002
class DataObjectTest_Staff extends DataObject implements TestOnly {
2003
	private static $db = array(
2004
		'Salary' => 'BigInt',
2005
	);
2006
	private static $has_one = array (
2007
		'CurrentCompany'  => 'DataObjectTest_Company',
2008
		'PreviousCompany' => 'DataObjectTest_Company'
2009
	);
2010
}
2011
2012
class DataObjectTest_CEO extends DataObjectTest_Staff {
2013
	private static $belongs_to = array (
2014
		'Company'         => 'DataObjectTest_Company.CEO',
2015
		'PreviousCompany' => 'DataObjectTest_Company.PreviousCEO',
2016
		'CompanyOwned'    => 'DataObjectTest_Company.Owner'
2017
	);
2018
}
2019
2020
class DataObjectTest_TeamComment extends DataObject implements TestOnly {
2021
	private static $db = array(
2022
		'Name' => 'Varchar',
2023
		'Comment' => 'Text'
2024
	);
2025
2026
	private static $has_one = array(
2027
		'Team' => 'DataObjectTest_Team'
2028
	);
2029
2030
	private static $default_sort = '"Name" ASC';
2031
}
2032
2033
class DataObjectTest_Fan extends DataObject implements TestOnly {
2034
2035
	private static $db = array(
2036
		'Name' => 'Varchar(255)'
2037
	);
2038
2039
	private static $has_one = array(
2040
		'Favourite' => 'DataObject', // Polymorphic relation
2041
		'SecondFavourite' => 'DataObject'
2042
	);
2043
}
2044
2045
class DataObjectTest_ExtendedTeamComment extends DataObjectTest_TeamComment {
2046
	private static $db = array(
2047
		'Comment' => 'HTMLText'
2048
	);
2049
}
2050
2051
class DataObjectTest_Play extends DataObject implements TestOnly {}
2052
class DataObjectTest_Ploy extends DataObject implements TestOnly {}
2053
class DataObjectTest_Bogey extends DataObject implements TestOnly {}
2054
2055
DataObjectTest_Team::add_extension('DataObjectTest_Team_Extension');
2056
2057
class DataObjectTest_CompositeDBField extends DataObject implements TestOnly {
2058
    private static $db = array(
2059
        'NonCompositeField' => 'Varchar',
2060
        'CompositeMoneyField' => 'Money',
2061
    );
2062
}