Completed
Push — 3.5 ( 1a9180...1bec8a )
by Daniel
24s
created

DataObjectTest::provideSingletons()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 23
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 17
nc 1
nop 0
dl 0
loc 23
rs 9.0856
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 2024.

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
		'ManyManyListTest_Product',
33
		'ManyManyListTest_Category',
34
	);
35
36
	/**
37
	 * @dataProvider provideSingletons
38
	 */
39
	public function testSingleton($inst, $defaultValue, $altDefaultValue)
40
	{
41
		$inst = $inst();
42
		// Test that populateDefaults() isn't called on singletons
43
		// which can lead to SQL errors during build, and endless loops
44
		if ($defaultValue) {
45
			$this->assertEquals($defaultValue, $inst->MyFieldWithDefault);
46
		} else {
47
			$this->assertEmpty($inst->MyFieldWithDefault);
48
		}
49
50
		if ($altDefaultValue) {
51
			$this->assertEquals($altDefaultValue, $inst->MyFieldWithAltDefault);
52
		} else {
53
			$this->assertEmpty($inst->MyFieldWithAltDefault);
54
		}
55
	}
56
57
	public function provideSingletons()
58
	{
59
		// because PHPUnit evalutes test providers *before* setUp methods
60
		// any extensions added in the setUp methods won't be available
61
		// we must return closures to generate the arguments at run time
62
		return array(
63
			array(function () {
64
				return DataObjectTest_Fixture::create();
65
			}, 'Default Value', 'Default Value'),
66
			array(function () {
67
				return new DataObjectTest_Fixture();
68
			}, 'Default Value', 'Default Value'),
69
			array(function () {
70
				return singleton('DataObjectTest_Fixture');
71
			}, null, null),
72
			array(function () {
73
				return DataObjectTest_Fixture::singleton();
74
			}, null, null),
75
			array(function () {
76
				return new DataObjectTest_Fixture(null, true);
77
			}, null, null),
78
		);
79
	}
80
81
	public function testDb() {
82
		$obj = new DataObjectTest_TeamComment();
83
		$dbFields = $obj->db();
84
85
		// Assert fields are included
86
		$this->assertArrayHasKey('Name', $dbFields);
87
88
		// Assert the base fields are excluded
89
		$this->assertArrayNotHasKey('Created', $dbFields);
90
		$this->assertArrayNotHasKey('LastEdited', $dbFields);
91
		$this->assertArrayNotHasKey('ClassName', $dbFields);
92
		$this->assertArrayNotHasKey('ID', $dbFields);
93
94
		// Assert that the correct field type is returned when passing a field
95
		$this->assertEquals('Varchar', $obj->db('Name'));
96
		$this->assertEquals('Text', $obj->db('Comment'));
97
98
		$obj = new DataObjectTest_ExtendedTeamComment();
99
		$dbFields = $obj->db();
100
101
		// Assert overloaded fields have correct data type
102
		$this->assertEquals('HTMLText', $obj->db('Comment'));
103
		$this->assertEquals('HTMLText', $dbFields['Comment'],
104
			'Calls to DataObject::db without a field specified return correct data types');
105
106
		// assertEquals doesn't verify the order of array elements, so access keys manually to check order:
107
		// 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...
108
		reset($dbFields);
109
		$this->assertEquals('Name', key($dbFields), 'DataObject::db returns fields in correct order');
110
		next($dbFields);
111
		$this->assertEquals('Comment', key($dbFields), 'DataObject::db returns fields in correct order');
112
	}
113
114
	public function testConstructAcceptsValues() {
115
		// Values can be an array...
116
		$player = new DataObjectTest_Player(array(
117
			'FirstName' => 'James',
118
			'Surname' => 'Smith'
119
		));
120
121
		$this->assertEquals('James', $player->FirstName);
122
		$this->assertEquals('Smith', $player->Surname);
123
124
		// ... or a stdClass inst
125
		$data = new stdClass();
126
		$data->FirstName = 'John';
127
		$data->Surname = 'Doe';
128
		$player = new DataObjectTest_Player($data);
129
130
		$this->assertEquals('John', $player->FirstName);
131
		$this->assertEquals('Doe', $player->Surname);
132
133
		// IDs should be stored as integers, not strings
134
		$player = new DataObjectTest_Player(array('ID' => '5'));
135
		$this->assertSame(5, $player->ID);
136
	}
137
138
	public function testValidObjectsForBaseFields() {
139
		$obj = new DataObjectTest_ValidatedObject();
140
141
		foreach (array('Created', 'LastEdited', 'ClassName', 'ID') as $field) {
142
			$helper = $obj->dbObject($field);
143
			$this->assertTrue(
144
				($helper instanceof DBField),
145
				"for {$field} expected helper to be DBField, but was " .
146
				(is_object($helper) ? get_class($helper) : "null")
147
			);
148
		}
149
	}
150
151
	public function testDataIntegrityWhenTwoSubclassesHaveSameField() {
152
		// Save data into DataObjectTest_SubTeam.SubclassDatabaseField
153
		$obj = new DataObjectTest_SubTeam();
154
		$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...
155
		$obj->write();
156
157
		// Change the class
158
		$obj->ClassName = 'OtherSubclassWithSameField';
159
		$obj->write();
160
		$obj->flushCache();
161
162
		// Re-fetch from the database and confirm that the data is sourced from
163
		// OtherSubclassWithSameField.SubclassDatabaseField
164
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
165
		$this->assertNull($obj->SubclassDatabaseField);
166
167
		// Confirm that save the object in the other direction.
168
		$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...
169
		$obj->write();
170
171
		$obj->ClassName = 'DataObjectTest_SubTeam';
172
		$obj->write();
173
		$obj->flushCache();
174
175
		// If we restore the class, the old value has been lying dormant and will be available again.
176
		// NOTE: This behaviour is volatile; we may change this in the future to clear fields that
177
		// are no longer relevant when changing ClassName
178
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
179
		$this->assertEquals('obj-SubTeam', $obj->SubclassDatabaseField);
180
	}
181
182
	/**
183
	 * Test deletion of DataObjects
184
	 *   - Deleting using delete() on the DataObject
185
	 *   - Deleting using DataObject::delete_by_id()
186
	 */
187
	public function testDelete() {
188
		// Test deleting using delete() on the DataObject
189
		// Get the first page
190
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
191
		$objID = $obj->ID;
192
		// Check the page exists before deleting
193
		$this->assertTrue(is_object($obj) && $obj->exists());
194
		// Delete the page
195
		$obj->delete();
196
		// Check that page does not exist after deleting
197
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
198
		$this->assertTrue(!$obj || !$obj->exists());
199
200
201
		// Test deleting using DataObject::delete_by_id()
202
		// Get the second page
203
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
204
		$objID = $obj->ID;
205
		// Check the page exists before deleting
206
		$this->assertTrue(is_object($obj) && $obj->exists());
207
		// Delete the page
208
		DataObject::delete_by_id('DataObjectTest_Player', $obj->ID);
209
		// Check that page does not exist after deleting
210
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
211
		$this->assertTrue(!$obj || !$obj->exists());
212
	}
213
214
	/**
215
	 * Test methods that get DataObjects
216
	 *   - DataObject::get()
217
	 *       - All records of a DataObject
218
	 *       - Filtering
219
	 *       - Sorting
220
	 *       - Joins
221
	 *       - Limit
222
	 *       - Container class
223
	 *   - DataObject::get_by_id()
224
	 *   - DataObject::get_one()
225
	 *        - With and without caching
226
	 *        - With and without ordering
227
	 */
228
	public function testGet() {
229
		// Test getting all records of a DataObject
230
		$comments = DataObject::get('DataObjectTest_TeamComment');
231
		$this->assertEquals(3, $comments->Count());
232
233
		// Test WHERE clause
234
		$comments = DataObject::get('DataObjectTest_TeamComment', "\"Name\"='Bob'");
235
		$this->assertEquals(1, $comments->Count());
236
		foreach($comments as $comment) {
237
			$this->assertEquals('Bob', $comment->Name);
238
		}
239
240
		// Test sorting
241
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC");
242
		$this->assertEquals(3, $comments->Count());
243
		$this->assertEquals('Bob', $comments->First()->Name);
244
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" DESC");
245
		$this->assertEquals(3, $comments->Count());
246
		$this->assertEquals('Phil', $comments->First()->Name);
247
248
		// Test limit
249
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC", '', '1,2');
250
		$this->assertEquals(2, $comments->Count());
251
		$this->assertEquals('Joe', $comments->First()->Name);
252
		$this->assertEquals('Phil', $comments->Last()->Name);
253
254
		// Test get_by_id()
255
		$captain1ID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
256
		$captain1 = DataObject::get_by_id('DataObjectTest_Player', $captain1ID);
257
		$this->assertEquals('Captain', $captain1->FirstName);
258
259
		// Test get_one() without caching
260
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
261
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
262
		), false);
263
		$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...
264
265
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
266
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
267
		), false);
268
		$this->assertNotEquals($comment1->Comment, $comment2->Comment);
269
270
		// Test get_one() with caching
271
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
272
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
273
		), true);
274
		$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...
275
276
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
277
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
278
		), true);
279
		$this->assertEquals((string)$comment1->Comment, (string)$comment2->Comment);
280
281
		// Test get_one() with order by without caching
282
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" ASC");
283
		$this->assertEquals('Bob', $comment->Name);
284
285
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" DESC");
286
		$this->assertEquals('Phil', $comment->Name);
287
288
		// Test get_one() with order by with caching
289
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" ASC');
290
		$this->assertEquals('Bob', $comment->Name);
291
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" DESC');
292
		$this->assertEquals('Phil', $comment->Name);
293
	}
294
295
	public function testGetCaseInsensitive() {
296
		// Test get_one() with bad case on the classname
297
		// Note: This will succeed only if the underlying DB server supports case-insensitive
298
		// table names (e.g. such as MySQL, but not SQLite3)
299
		if(!(DB::get_conn() instanceof MySQLDatabase)) {
300
			$this->markTestSkipped('MySQL only');
301
		}
302
303
		$subteam1 = DataObject::get_one('dataobjecttest_subteam', array(
304
			'"DataObjectTest_Team"."Title"' => 'Subteam 1'
305
		), true);
306
		$this->assertNotEmpty($subteam1);
307
		$this->assertEquals($subteam1->Title, "Subteam 1");
308
	}
309
310
	public function testGetSubclassFields() {
311
		/* Test that fields / has_one relations from the parent table and the subclass tables are extracted */
312
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
313
		// Base field
314
		$this->assertEquals('Captain', $captain1->FirstName);
315
		// Subclass field
316
		$this->assertEquals('007', $captain1->ShirtNumber);
317
		// Subclass has_one relation
318
		$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $captain1->FavouriteTeamID);
319
	}
320
321
	public function testGetRelationClass() {
322
		$obj = new DataObjectTest_Player();
323
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('FavouriteTeam'),
324
			'DataObjectTest_Team', 'has_one is properly inspected');
325
		$this->assertEquals(singleton('DataObjectTest_Company')->getRelationClass('CurrentStaff'),
326
			'DataObjectTest_Staff', 'has_many is properly inspected');
327
		$this->assertEquals(singleton('DataObjectTest_Team')->getRelationClass('Players'), 'DataObjectTest_Player',
328
			'many_many is properly inspected');
329
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('Teams'), 'DataObjectTest_Team',
330
			'belongs_many_many is properly inspected');
331
		$this->assertEquals(singleton('DataObjectTest_CEO')->getRelationClass('Company'), 'DataObjectTest_Company',
332
			'belongs_to is properly inspected');
333
		$this->assertEquals(singleton('DataObjectTest_Fan')->getRelationClass('Favourite'), 'DataObject',
334
			'polymorphic has_one is properly inspected');
335
	}
336
337
	/**
338
	 * Test that has_one relations can be retrieved
339
	 */
340
	public function testGetHasOneRelations() {
341
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
342
		$team1ID = $this->idFromFixture('DataObjectTest_Team', 'team1');
343
344
		// There will be a field called (relname)ID that contains the ID of the
345
		// object linked to via the has_one relation
346
		$this->assertEquals($team1ID, $captain1->FavouriteTeamID);
347
348
		// There will be a method called $obj->relname() that returns the object itself
349
		$this->assertEquals($team1ID, $captain1->FavouriteTeam()->ID);
350
351
		// Check entity with polymorphic has-one
352
		$fan1 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
353
		$this->assertTrue((bool)$fan1->hasValue('Favourite'));
354
355
		// There will be fields named (relname)ID and (relname)Class for polymorphic
356
		// entities
357
		$this->assertEquals($team1ID, $fan1->FavouriteID);
358
		$this->assertEquals('DataObjectTest_Team', $fan1->FavouriteClass);
359
360
		// There will be a method called $obj->relname() that returns the object itself
361
		$favourite = $fan1->Favourite();
362
		$this->assertEquals($team1ID, $favourite->ID);
363
		$this->assertInstanceOf('DataObjectTest_Team', $favourite);
364
365
		// check behaviour of dbObject with polymorphic relations
366
		$favouriteDBObject = $fan1->dbObject('Favourite');
367
		$favouriteValue = $favouriteDBObject->getValue();
368
		$this->assertInstanceOf('PolymorphicForeignKey', $favouriteDBObject);
369
		$this->assertEquals($favourite->ID, $favouriteValue->ID);
370
		$this->assertEquals($favourite->ClassName, $favouriteValue->ClassName);
371
	}
372
373
	/**
374
	 * Simple test to ensure that namespaced classes and polymorphic relations work together
375
	 */
376
	public function testPolymorphicNamespacedRelations() {
377
		$parent = new \DataObjectTest\NamespacedClass();
378
		$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...
379
		$parent->write();
380
381
		$child = new \DataObjectTest\RelationClass();
382
		$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...
383
		$child->write();
384
		$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...
385
386
		$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...
387
		$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...
388
		$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...
389
	}
390
391
	public function testLimitAndCount() {
392
		$players = DataObject::get("DataObjectTest_Player");
393
394
		// There's 4 records in total
395
		$this->assertEquals(4, $players->count());
396
397
		// Testing "##, ##" syntax
398
		$this->assertEquals(4, $players->limit(20)->count());
399
		$this->assertEquals(4, $players->limit(20, 0)->count());
400
		$this->assertEquals(0, $players->limit(20, 20)->count());
401
		$this->assertEquals(2, $players->limit(2, 0)->count());
402
		$this->assertEquals(1, $players->limit(5, 3)->count());
403
	}
404
405
	public function testWriteNoChangesDoesntUpdateLastEdited() {
406
		// set mock now so we can be certain of LastEdited time for our test
407
		SS_Datetime::set_mock_now('2017-01-01 00:00:00');
408
		$obj = new DataObjectTest_Player();
409
		$obj->FirstName = 'Test';
410
		$obj->Surname = 'Plater';
411
		$obj->Email = '[email protected]';
412
		$obj->write();
413
		$writtenObj = DataObjectTest_Player::get()->byID($obj->ID);
414
		$this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
415
416
		// set mock now so we get a new LastEdited if, for some reason, it's updated
417
		SS_Datetime::set_mock_now('2017-02-01 00:00:00');
418
		$writtenObj->write();
419
		$this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
420
	}
421
422
	/**
423
	 * Test writing of database columns which don't correlate to a DBField,
424
	 * e.g. all relation fields on has_one/has_many like "ParentID".
425
	 *
426
	 */
427
	public function testWritePropertyWithoutDBField() {
428
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
429
		$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...
430
		$obj->write();
431
432
		// reload the page from the database
433
		$savedObj = DataObject::get_by_id('DataObjectTest_Player', $obj->ID);
434
		$this->assertTrue($savedObj->FavouriteTeamID == 99);
435
436
		// Test with porymorphic relation
437
		$obj2 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
438
		$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...
439
		$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...
440
		$obj2->write();
441
442
		$savedObj2 = DataObject::get_by_id('DataObjectTest_Fan', $obj2->ID);
443
		$this->assertTrue($savedObj2->FavouriteID == 99);
444
		$this->assertTrue($savedObj2->FavouriteClass == 'DataObjectTest_Player');
445
	}
446
447
	/**
448
	 * Test has many relationships
449
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
450
	 *   - Test the IDs on the DataObjects are set correctly
451
	 */
452
	public function testHasManyRelationships() {
453
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
454
455
		// Test getComponents() gets the ComponentSet of the other side of the relation
456
		$this->assertTrue($team1->Comments()->Count() == 2);
457
458
		// Test the IDs on the DataObjects are set correctly
459
		foreach($team1->Comments() as $comment) {
460
			$this->assertEquals($team1->ID, $comment->TeamID);
461
		}
462
463
		// Test that we can add and remove items that already exist in the database
464
		$newComment = new DataObjectTest_TeamComment();
465
		$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...
466
		$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...
467
		$newComment->write();
468
		$team1->Comments()->add($newComment);
469
		$this->assertEquals($team1->ID, $newComment->TeamID);
470
471
		$comment1 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment1');
472
		$comment2 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment2');
473
		$team1->Comments()->remove($comment2);
474
475
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
476
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
477
478
		// Test that removing an item from a list doesn't remove it from the same
479
		// relation belonging to a different object
480
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
481
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
482
		$team2->Comments()->remove($comment1);
483
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
484
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
485
	}
486
487
488
	/**
489
	 * Test has many relationships against polymorphic has_one fields
490
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
491
	 *   - Test the IDs on the DataObjects are set correctly
492
	 */
493
	public function testHasManyPolymorphicRelationships() {
494
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
495
496
		// Test getComponents() gets the ComponentSet of the other side of the relation
497
		$this->assertTrue($team1->Fans()->Count() == 2);
498
499
		// Test the IDs/Classes on the DataObjects are set correctly
500
		foreach($team1->Fans() as $fan) {
501
			$this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID');
502
			$this->assertEquals('DataObjectTest_Team', $fan->FavouriteClass, 'Fan has the correct FavouriteClass');
503
		}
504
505
		// Test that we can add and remove items that already exist in the database
506
		$newFan = new DataObjectTest_Fan();
507
		$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...
508
		$newFan->write();
509
		$team1->Fans()->add($newFan);
510
		$this->assertEquals($team1->ID, $newFan->FavouriteID, 'Newly created fan has the correct FavouriteID');
511
		$this->assertEquals(
512
			'DataObjectTest_Team',
513
			$newFan->FavouriteClass,
514
			'Newly created fan has the correct FavouriteClass'
515
		);
516
517
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
518
		$fan3 = $this->objFromFixture('DataObjectTest_Fan', 'fan3');
519
		$team1->Fans()->remove($fan3);
520
521
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
522
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
523
524
		// Test that removing an item from a list doesn't remove it from the same
525
		// relation belonging to a different object
526
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
527
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
528
		$player1->Fans()->remove($fan1);
529
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
530
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
531
	}
532
533
534
	public function testHasOneRelationship() {
535
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
536
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
537
		$player2 = $this->objFromFixture('DataObjectTest_Player', 'player2');
538
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
539
540
		// Test relation probing
541
		$this->assertFalse((bool)$team1->hasValue('Captain', null, false));
542
		$this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
543
544
		// Add a captain to team 1
545
		$team1->setField('CaptainID', $player1->ID);
546
		$team1->write();
547
548
		$this->assertTrue((bool)$team1->hasValue('Captain', null, false));
549
		$this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
550
551
		$this->assertEquals($player1->ID, $team1->Captain()->ID,
552
			'The captain exists for team 1');
553
		$this->assertEquals($player1->ID, $team1->getComponent('Captain')->ID,
554
			'The captain exists through the component getter');
555
556
		$this->assertEquals($team1->Captain()->FirstName, 'Player 1',
557
			'Player 1 is the captain');
558
		$this->assertEquals($team1->getComponent('Captain')->FirstName, 'Player 1',
559
			'Player 1 is the captain');
560
561
		$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...
562
		$team1->write();
563
564
		$this->assertEquals($player2->ID, $team1->Captain()->ID);
565
		$this->assertEquals($player2->ID, $team1->getComponent('Captain')->ID);
566
		$this->assertEquals('Player 2', $team1->Captain()->FirstName);
567
		$this->assertEquals('Player 2', $team1->getComponent('Captain')->FirstName);
568
569
570
		// Set the favourite team for fan1
571
		$fan1->setField('FavouriteID', $team1->ID);
572
		$fan1->setField('FavouriteClass', $team1->class);
573
574
		$this->assertEquals($team1->ID, $fan1->Favourite()->ID, 'The team is assigned to fan 1');
575
		$this->assertInstanceOf($team1->class, $fan1->Favourite(), 'The team is assigned to fan 1');
576
		$this->assertEquals($team1->ID, $fan1->getComponent('Favourite')->ID,
577
			'The team exists through the component getter'
578
		);
579
		$this->assertInstanceOf($team1->class, $fan1->getComponent('Favourite'),
580
			'The team exists through the component getter'
581
		);
582
583
		$this->assertEquals($fan1->Favourite()->Title, 'Team 1',
584
			'Team 1 is the favourite');
585
		$this->assertEquals($fan1->getComponent('Favourite')->Title, 'Team 1',
586
			'Team 1 is the favourite');
587
	}
588
589
	/**
590
	 * @todo Extend type change tests (e.g. '0'==NULL)
591
	 */
592
	public function testChangedFields() {
593
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
594
		$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...
595
		$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...
596
597
		$this->assertEquals(
598
			$obj->getChangedFields(true, DataObject::CHANGE_STRICT),
599
			array(
600
				'FirstName' => array(
601
					'before' => 'Captain',
602
					'after' => 'Captain-changed',
603
					'level' => DataObject::CHANGE_VALUE
604
				),
605
				'IsRetired' => array(
606
					'before' => 1,
607
					'after' => true,
608
					'level' => DataObject::CHANGE_STRICT
609
				)
610
			),
611
			'Changed fields are correctly detected with strict type changes (level=1)'
612
		);
613
614
		$this->assertEquals(
615
			$obj->getChangedFields(true, DataObject::CHANGE_VALUE),
616
			array(
617
				'FirstName' => array(
618
					'before'=>'Captain',
619
					'after'=>'Captain-changed',
620
					'level' => DataObject::CHANGE_VALUE
621
				)
622
			),
623
			'Changed fields are correctly detected while ignoring type changes (level=2)'
624
		);
625
626
		$newObj = new DataObjectTest_Player();
627
		$newObj->FirstName = "New Player";
628
		$this->assertEquals(
629
			array(
630
				'FirstName' => array(
631
					'before' => null,
632
					'after' => 'New Player',
633
					'level' => DataObject::CHANGE_VALUE
634
				)
635
			),
636
			$newObj->getChangedFields(true, DataObject::CHANGE_VALUE),
637
			'Initialised fields are correctly detected as full changes'
638
		);
639
	}
640
641
	public function testIsChanged() {
642
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
643
		$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...
644
		$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...
645
		$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...
646
647
		// Now that DB fields are changed, isChanged is true
648
		$this->assertTrue($obj->isChanged('NonDBField'));
649
		$this->assertFalse($obj->isChanged('NonField'));
650
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
651
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
652
		$this->assertTrue($obj->isChanged('IsRetired', DataObject::CHANGE_STRICT));
653
		$this->assertFalse($obj->isChanged('IsRetired', DataObject::CHANGE_VALUE));
654
		$this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
655
		$this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
656
657
		$newObj = new DataObjectTest_Player();
658
		$newObj->FirstName = "New Player";
659
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
660
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
661
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
662
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
663
664
		$newObj->write();
665
		$this->assertFalse($newObj->ischanged());
666
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
667
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
668
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
669
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
670
671
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
672
		$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...
673
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
674
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
675
676
		/* Test when there's not field provided */
677
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
678
		$this->assertFalse($obj->isChanged());
679
		$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...
680
		$this->assertFalse($obj->isChanged());
681
		$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...
682
		$this->assertTrue($obj->isChanged());
683
684
		$obj->write();
685
		$this->assertFalse($obj->isChanged());
686
	}
687
688
	public function testRandomSort() {
689
		/* If we perform the same regularly sorted query twice, it should return the same results */
690
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", "ID");
691
		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...
692
693
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", "ID");
694
		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...
695
696
		/* Test when there's not field provided */
697
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
698
		$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...
699
		$this->assertTrue($obj->isChanged());
700
701
		$obj->write();
702
		$this->assertFalse($obj->isChanged());
703
704
		/* If we perform the same random query twice, it shouldn't return the same results */
705
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
706
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
707
		$itemsC = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
708
		$itemsD = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
709
		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...
710
		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...
711
		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...
712
		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...
713
714
		// These shouldn't all be the same (run it 4 times to minimise chance of an accidental collision)
715
		// There's about a 1 in a billion chance of an accidental collision
716
		$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...
717
	}
718
719
	public function testWriteSavesToHasOneRelations() {
720
		/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
721
		$team = new DataObjectTest_Team();
722
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'player1');
723
		$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...
724
		$team->write();
725
		$this->assertEquals($captainID,
726
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
727
728
		/* After giving it a value, you should also be able to set it back to null */
729
		$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...
730
		$team->write();
731
		$this->assertEquals(0,
732
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
733
734
		/* You should also be able to save a blank to it when it's first created */
735
		$team = new DataObjectTest_Team();
736
		$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...
737
		$team->write();
738
		$this->assertEquals(0,
739
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
740
741
		/* Ditto for existing records without a value */
742
		$existingTeam = $this->objFromFixture('DataObjectTest_Team', 'team1');
743
		$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...
744
		$existingTeam->write();
745
		$this->assertEquals(0,
746
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value());
747
	}
748
749
	public function testCanAccessHasOneObjectsAsMethods() {
750
		/* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the
751
		 * object itself should be accessible as $obj->Captain() */
752
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
753
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
754
755
		$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...
756
		$this->assertNotNull($team->Captain());
757
		$this->assertEquals($captainID, $team->Captain()->ID);
758
759
		// Test for polymorphic has_one relations
760
		$fan = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
761
		$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...
762
		$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...
763
		$this->assertNotNull($fan->Favourite());
764
		$this->assertEquals($team->ID, $fan->Favourite()->ID);
765
		$this->assertInstanceOf($team->class, $fan->Favourite());
766
	}
767
768
	public function testFieldNamesThatMatchMethodNamesWork() {
769
		/* Check that a field name that corresponds to a method on DataObject will still work */
770
		$obj = new DataObjectTest_Fixture();
771
		$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...
772
		$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...
773
		$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...
774
		$obj->write();
775
776
		$this->assertNotNull($obj->ID);
777
		$this->assertEquals('value1',
778
			DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
779
		$this->assertEquals('value2',
780
			DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
781
		$this->assertEquals('value3',
782
			DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
783
	}
784
785
	/**
786
	 * @todo Re-enable all test cases for field existence after behaviour has been fixed
787
	 */
788
	public function testFieldExistence() {
789
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
790
		$teamSingleton = singleton('DataObjectTest_Team');
791
792
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
793
		$subteamSingleton = singleton('DataObjectTest_SubTeam');
794
795
		/* hasField() singleton checks */
796
		$this->assertTrue($teamSingleton->hasField('ID'),
797
			'hasField() finds built-in fields in singletons');
798
		$this->assertTrue($teamSingleton->hasField('Title'),
799
			'hasField() finds custom fields in singletons');
800
801
		/* hasField() instance checks */
802
		$this->assertFalse($teamInstance->hasField('NonExistingField'),
803
			'hasField() doesnt find non-existing fields in instances');
804
		$this->assertTrue($teamInstance->hasField('ID'),
805
			'hasField() finds built-in fields in instances');
806
		$this->assertTrue($teamInstance->hasField('Created'),
807
			'hasField() finds built-in fields in instances');
808
		$this->assertTrue($teamInstance->hasField('DatabaseField'),
809
			'hasField() finds custom fields in instances');
810
		//$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...
811
		//'hasField() doesnt find subclass fields in parentclass instances');
812
		$this->assertTrue($teamInstance->hasField('DynamicField'),
813
			'hasField() finds dynamic getters in instances');
814
		$this->assertTrue($teamInstance->hasField('HasOneRelationshipID'),
815
			'hasField() finds foreign keys in instances');
816
		$this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'),
817
			'hasField() finds extended fields in instances');
818
		$this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'),
819
			'hasField() finds extended foreign keys in instances');
820
		//$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...
821
		//'hasField() includes extended dynamic getters in instances');
822
823
		/* hasField() subclass checks */
824
		$this->assertTrue($subteamInstance->hasField('ID'),
825
			'hasField() finds built-in fields in subclass instances');
826
		$this->assertTrue($subteamInstance->hasField('Created'),
827
			'hasField() finds built-in fields in subclass instances');
828
		$this->assertTrue($subteamInstance->hasField('DatabaseField'),
829
			'hasField() finds custom fields in subclass instances');
830
		$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'),
831
			'hasField() finds custom fields in subclass instances');
832
		$this->assertTrue($subteamInstance->hasField('DynamicField'),
833
			'hasField() finds dynamic getters in subclass instances');
834
		$this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'),
835
			'hasField() finds foreign keys in subclass instances');
836
		$this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'),
837
			'hasField() finds extended fields in subclass instances');
838
		$this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'),
839
			'hasField() finds extended foreign keys in subclass instances');
840
841
		/* hasDatabaseField() singleton checks */
842
		//$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...
843
		//'hasDatabaseField() finds built-in fields in singletons');
844
		$this->assertTrue($teamSingleton->hasDatabaseField('Title'),
845
			'hasDatabaseField() finds custom fields in singletons');
846
847
		/* hasDatabaseField() instance checks */
848
		$this->assertFalse($teamInstance->hasDatabaseField('NonExistingField'),
849
			'hasDatabaseField() doesnt find non-existing fields in instances');
850
		//$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...
851
		//'hasDatabaseField() finds built-in fields in instances');
852
		$this->assertTrue($teamInstance->hasDatabaseField('Created'),
853
			'hasDatabaseField() finds built-in fields in instances');
854
		$this->assertTrue($teamInstance->hasDatabaseField('DatabaseField'),
855
			'hasDatabaseField() finds custom fields in instances');
856
		$this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'),
857
			'hasDatabaseField() doesnt find subclass fields in parentclass instances');
858
		//$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...
859
		//'hasDatabaseField() doesnt dynamic getters in instances');
860
		$this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'),
861
			'hasDatabaseField() finds foreign keys in instances');
862
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'),
863
			'hasDatabaseField() finds extended fields in instances');
864
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'),
865
			'hasDatabaseField() finds extended foreign keys in instances');
866
		$this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'),
867
			'hasDatabaseField() doesnt include extended dynamic getters in instances');
868
869
		/* hasDatabaseField() subclass checks */
870
		$this->assertTrue($subteamInstance->hasDatabaseField('DatabaseField'),
871
			'hasField() finds custom fields in subclass instances');
872
		$this->assertTrue($subteamInstance->hasDatabaseField('SubclassDatabaseField'),
873
			'hasField() finds custom fields in subclass instances');
874
875
	}
876
877
	/**
878
	 * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed
879
	 */
880
	public function testFieldInheritance() {
881
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
882
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
883
884
		$this->assertEquals(
885
			array_keys($teamInstance->inheritedDatabaseFields()),
886
			array(
887
				//'ID',
888
				//'ClassName',
889
				//'Created',
890
				//'LastEdited',
891
				'Title',
892
				'DatabaseField',
893
				'ExtendedDatabaseField',
894
				'CaptainID',
895
				'HasOneRelationshipID',
896
				'ExtendedHasOneRelationshipID'
897
			),
898
			'inheritedDatabaseFields() contains all fields defined on instance: base, extended and foreign keys'
899
		);
900
901
		$this->assertEquals(
902
			array_keys(DataObject::database_fields('DataObjectTest_Team', false)),
903
			array(
904
				//'ID',
905
				'ClassName',
906
				'LastEdited',
907
				'Created',
908
				'Title',
909
				'DatabaseField',
910
				'ExtendedDatabaseField',
911
				'CaptainID',
912
				'HasOneRelationshipID',
913
				'ExtendedHasOneRelationshipID'
914
			),
915
			'databaseFields() contains only fields defined on instance, including base, extended and foreign keys'
916
		);
917
918
		$this->assertEquals(
919
			array_keys($subteamInstance->inheritedDatabaseFields()),
920
			array(
921
				//'ID',
922
				//'ClassName',
923
				//'Created',
924
				//'LastEdited',
925
				'SubclassDatabaseField',
926
				'ParentTeamID',
927
				'Title',
928
				'DatabaseField',
929
				'ExtendedDatabaseField',
930
				'CaptainID',
931
				'HasOneRelationshipID',
932
				'ExtendedHasOneRelationshipID',
933
			),
934
			'inheritedDatabaseFields() on subclass contains all fields, including base, extended  and foreign keys'
935
		);
936
937
		$this->assertEquals(
938
			array_keys(DataObject::database_fields('DataObjectTest_SubTeam', false)),
939
			array(
940
				'SubclassDatabaseField',
941
				'ParentTeamID',
942
			),
943
			'databaseFields() on subclass contains only fields defined on instance'
944
		);
945
	}
946
947
	public function testSearchableFields() {
948
		$player = $this->objFromFixture('DataObjectTest_Player', 'captain1');
949
		$fields = $player->searchableFields();
950
		$this->assertArrayHasKey(
951
			'IsRetired',
952
			$fields,
953
			'Fields defined by $searchable_fields static are correctly detected'
954
		);
955
		$this->assertArrayHasKey(
956
			'ShirtNumber',
957
			$fields,
958
			'Fields defined by $searchable_fields static are correctly detected'
959
		);
960
961
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
962
		$fields = $team->searchableFields();
963
		$this->assertArrayHasKey(
964
			'Title',
965
			$fields,
966
			'Fields can be inherited from the $summary_fields static, including methods called on fields'
967
		);
968
		$this->assertArrayHasKey(
969
			'Captain.ShirtNumber',
970
			$fields,
971
			'Fields on related objects can be inherited from the $summary_fields static'
972
		);
973
		$this->assertArrayHasKey(
974
			'Captain.FavouriteTeam.Title',
975
			$fields,
976
			'Fields on related objects can be inherited from the $summary_fields static'
977
		);
978
979
		$testObj = new DataObjectTest_Fixture();
980
		$fields = $testObj->searchableFields();
981
		$this->assertEmpty($fields);
982
	}
983
984
	public function testCastingHelper() {
985
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
986
987
		$this->assertEquals('Varchar', $team->castingHelper('Title'), 'db field wasn\'t casted correctly');
988
		$this->assertEquals('HTMLVarchar', $team->castingHelper('DatabaseField'), 'db field wasn\'t casted correctly');
989
990
		$sponsor = $team->Sponsors()->first();
991
		$this->assertEquals('Int', $sponsor->castingHelper('SponsorFee'), 'many_many_extraFields not casted correctly');
992
	}
993
994
	public function testSummaryFieldsCustomLabels() {
995
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
996
		$summaryFields = $team->summaryFields();
997
998
		$this->assertEquals(
999
			'Custom Title',
1000
			$summaryFields['Title'],
1001
			'Custom title is preserved'
1002
		);
1003
1004
		$this->assertEquals(
1005
			'Captain\'s shirt number',
1006
			$summaryFields['Captain.ShirtNumber'],
1007
			'Custom title on relation is preserved'
1008
		);
1009
	}
1010
1011
	public function testDataObjectUpdate() {
1012
		/* update() calls can use the dot syntax to reference has_one relations and other methods that return
1013
		 * objects */
1014
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1015
		$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...
1016
1017
		$team1->update(array(
1018
			'DatabaseField' => 'Something',
1019
			'Captain.FirstName' => 'Jim',
1020
			'Captain.Email' => '[email protected]',
1021
			'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1022
		));
1023
1024
		/* Test the simple case of updating fields on the object itself */
1025
		$this->assertEquals('Something', $team1->DatabaseField);
1026
1027
		/* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in
1028
		 * the database.  Although update() doesn't usually write, it does write related records automatically. */
1029
		$captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1030
		$this->assertEquals('Jim', $captain1->FirstName);
1031
		$this->assertEquals('[email protected]', $captain1->Email);
1032
1033
		/* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.
1034
		 * FavouriteTeam.Title made */
1035
		$reloadedTeam1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1036
		$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1037
	}
1038
1039
	public function testDataObjectUpdateNew() {
1040
		/* update() calls can use the dot syntax to reference has_one relations and other methods that return
1041
		 * objects */
1042
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1043
		$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...
1044
1045
		$team1->update(array(
1046
			'Captain.FirstName' => 'Jim',
1047
			'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1048
		));
1049
		/* Test that the captain ID has been updated */
1050
		$this->assertGreaterThan(0, $team1->CaptainID);
1051
1052
		/* Fetch the newly created captain */
1053
		$captain1 = DataObjectTest_Player::get()->byID($team1->CaptainID);
1054
		$this->assertEquals('Jim', $captain1->FirstName);
1055
1056
		/* Grab the favourite team and make sure it has the correct values */
1057
		$reloadedTeam1 = $captain1->FavouriteTeam();
1058
		$this->assertEquals($reloadedTeam1->ID, $captain1->FavouriteTeamID);
1059
		$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1060
	}
1061
1062
	public function testWritingInvalidDataObjectThrowsException() {
1063
		$validatedObject = new DataObjectTest_ValidatedObject();
1064
1065
		$this->setExpectedException('ValidationException');
1066
		$validatedObject->write();
1067
	}
1068
1069
	public function testWritingValidDataObjectDoesntThrowException() {
1070
		$validatedObject = new DataObjectTest_ValidatedObject();
1071
		$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...
1072
1073
		$validatedObject->write();
1074
		$this->assertTrue($validatedObject->isInDB(), "Validated object was not saved to database");
1075
	}
1076
1077
	public function testSubclassCreation() {
1078
		/* Creating a new object of a subclass should set the ClassName field correctly */
1079
		$obj = new DataObjectTest_SubTeam();
1080
		$obj->write();
1081
		$this->assertEquals("DataObjectTest_SubTeam",
1082
			DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
1083
	}
1084
1085
	public function testForceInsert() {
1086
		/* If you set an ID on an object and pass forceInsert = true, then the object should be correctly created */
1087
		$conn = DB::get_conn();
1088
		if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', true);
1089
		$obj = new DataObjectTest_SubTeam();
1090
		$obj->ID = 1001;
1091
		$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...
1092
		$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...
1093
		$obj->write(false, true);
1094
		if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', false);
1095
1096
		$this->assertEquals("DataObjectTest_SubTeam",
1097
			DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
1098
1099
		/* Check that it actually saves to the database with the correct ID */
1100
		$this->assertEquals("1001", DB::query(
1101
			"SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'")->value());
1102
		$this->assertEquals("1001",
1103
			DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value());
1104
	}
1105
1106
	public function TestHasOwnTable() {
1107
		/* Test DataObject::has_own_table() returns true if the object has $has_one or $db values */
1108
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Player"));
1109
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Team"));
1110
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Fixture"));
1111
1112
		/* Root DataObject that always have a table, even if they lack both $db and $has_one */
1113
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_FieldlessTable"));
1114
1115
		/* Subclasses without $db or $has_one don't have a table */
1116
		$this->assertFalse(DataObject::has_own_table("DataObjectTest_FieldlessSubTable"));
1117
1118
		/* Return false if you don't pass it a subclass of DataObject */
1119
		$this->assertFalse(DataObject::has_own_table("DataObject"));
1120
		$this->assertFalse(DataObject::has_own_table("ViewableData"));
1121
		$this->assertFalse(DataObject::has_own_table("ThisIsntADataObject"));
1122
	}
1123
1124
	public function testMerge() {
1125
		// test right merge of subclasses
1126
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1127
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1128
		$leftOrigID = $left->ID;
1129
		$left->merge($right, 'right', false, false);
1130
		$this->assertEquals(
1131
			$left->Title,
1132
			'Subteam 2',
1133
			'merge() with "right" priority overwrites fields with existing values on subclasses'
1134
		);
1135
		$this->assertEquals(
1136
			$left->ID,
1137
			$leftOrigID,
1138
			'merge() with "right" priority doesnt overwrite database ID'
1139
		);
1140
1141
		// test overwriteWithEmpty flag on existing left values
1142
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1143
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam3_with_empty_fields');
1144
		$left->merge($right, 'right', false, true);
1145
		$this->assertEquals(
1146
			$left->Title,
1147
			'Subteam 3',
1148
			'merge() with $overwriteWithEmpty overwrites non-empty fields on left object'
1149
		);
1150
1151
		// test overwriteWithEmpty flag on empty left values
1152
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1153
		// $SubclassDatabaseField is empty on here
1154
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1155
		$left->merge($right, 'right', false, true);
1156
		$this->assertEquals(
1157
			$left->SubclassDatabaseField,
1158
			NULL,
1159
			'merge() with $overwriteWithEmpty overwrites empty fields on left object'
1160
		);
1161
1162
		// @todo test "left" priority flag
1163
		// @todo test includeRelations flag
1164
		// @todo test includeRelations in combination with overwriteWithEmpty
1165
		// @todo test has_one relations
1166
		// @todo test has_many and many_many relations
1167
	}
1168
1169
	public function testPopulateDefaults() {
1170
		$obj = new DataObjectTest_Fixture();
1171
		$this->assertEquals(
1172
			$obj->MyFieldWithDefault,
1173
			'Default Value',
1174
			'Defaults are populated for in-memory object from $defaults array'
1175
		);
1176
1177
		$this->assertEquals(
1178
			$obj->MyFieldWithAltDefault,
1179
			'Default Value',
1180
			'Defaults are populated from overloaded populateDefaults() method'
1181
		);
1182
	}
1183
1184
	protected function makeAccessible($object, $method) {
1185
		$reflectionMethod = new ReflectionMethod($object, $method);
1186
		$reflectionMethod->setAccessible(true);
1187
		return $reflectionMethod;
1188
	}
1189
1190
	public function testValidateModelDefinitionsFailsWithArray() {
1191
1192
		$object = new DataObjectTest_Team;
1193
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1194
1195
		Config::inst()->update('DataObjectTest_Team', 'has_one', array('NotValid' => array('NoArraysAllowed')));
1196
		$this->setExpectedException('LogicException');
1197
1198
		$method->invoke($object);
1199
	}
1200
1201
	public function testValidateModelDefinitionsFailsWithIntKey() {
1202
		$object = new DataObjectTest_Team;
1203
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1204
1205
		Config::inst()->update('DataObjectTest_Team', 'has_many', array(12 => 'DataObjectTest_Player'));
1206
		$this->setExpectedException('LogicException');
1207
1208
		$method->invoke($object);
1209
	}
1210
1211
	public function testValidateModelDefinitionsFailsWithIntValue() {
1212
		$object = new DataObjectTest_Team;
1213
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1214
1215
		Config::inst()->update('DataObjectTest_Team', 'many_many', array('Players' => 12));
1216
		$this->setExpectedException('LogicException');
1217
1218
		$method->invoke($object);
1219
	}
1220
1221
	/**
1222
	 * many_many_extraFields is allowed to have an array value, so shouldn't throw an exception
1223
	 */
1224
	public function testValidateModelDefinitionsPassesWithExtraFields() {
1225
		$object = new DataObjectTest_Team;
1226
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1227
1228
		Config::inst()->update('DataObjectTest_Team', 'many_many_extraFields',
1229
			array('Relations' => array('Price' => 'Int')));
1230
1231
		try {
1232
			$method->invoke($object);
1233
		} catch(Exception $e) {
1234
			$this->fail('Exception should not be thrown');
1235
			throw $e;
1236
		}
1237
	}
1238
1239
	public function testNewClassInstance() {
1240
		$dataObject = $this->objFromFixture('DataObjectTest_Team', 'team1');
1241
		$changedDO = $dataObject->newClassInstance('DataObjectTest_SubTeam');
1242
		$changedFields = $changedDO->getChangedFields();
1243
1244
		// Don't write the record, it will reset changed fields
1245
		$this->assertInstanceOf('DataObjectTest_SubTeam', $changedDO);
1246
		$this->assertEquals($changedDO->ClassName, 'DataObjectTest_SubTeam');
1247
		$this->assertContains('ClassName', array_keys($changedFields));
1248
		$this->assertEquals($changedFields['ClassName']['before'], 'DataObjectTest_Team');
1249
		$this->assertEquals($changedFields['ClassName']['after'], 'DataObjectTest_SubTeam');
1250
1251
		$changedDO->write();
1252
1253
		$this->assertInstanceOf('DataObjectTest_SubTeam', $changedDO);
1254
		$this->assertEquals($changedDO->ClassName, 'DataObjectTest_SubTeam');
1255
	}
1256
1257
	public function testMultipleManyManyWithSameClass() {
1258
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
1259
		$sponsors = $team->Sponsors();
1260
		$equipmentSuppliers = $team->EquipmentSuppliers();
1261
1262
		// Check that DataObject::many_many() works as expected
1263
		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...
1264
		$this->assertEquals('DataObjectTest_Team', $class,
1265
			'DataObject::many_many() didn\'t find the correct base class');
1266
		$this->assertEquals('DataObjectTest_EquipmentCompany', $targetClass,
1267
			'DataObject::many_many() didn\'t find the correct target class for the relation');
1268
		$this->assertEquals('DataObjectTest_EquipmentCompany_SponsoredTeams', $joinTable,
1269
			'DataObject::many_many() didn\'t find the correct relation table');
1270
1271
		// Check that ManyManyList still works
1272
		$this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation');
1273
		$this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation');
1274
1275
		// Check everything works when no relation is present
1276
		$teamWithoutSponsor = $this->objFromFixture('DataObjectTest_Team', 'team3');
1277
		$this->assertInstanceOf('ManyManyList', $teamWithoutSponsor->Sponsors());
1278
		$this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count());
1279
1280
		// Check many_many_extraFields still works
1281
		$equipmentCompany = $this->objFromFixture('DataObjectTest_EquipmentCompany', 'equipmentcompany1');
1282
		$equipmentCompany->SponsoredTeams()->add($teamWithoutSponsor, array('SponsorFee' => 1000));
1283
		$sponsoredTeams = $equipmentCompany->SponsoredTeams();
1284
		$this->assertEquals(1000, $sponsoredTeams->byID($teamWithoutSponsor->ID)->SponsorFee,
1285
			'Data from many_many_extraFields was not stored/extracted correctly');
1286
1287
		// Check subclasses correctly inherit multiple many_manys
1288
		$subTeam = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1289
		$this->assertEquals(2, $subTeam->Sponsors()->count(),
1290
			'Child class did not inherit multiple many_manys');
1291
		$this->assertEquals(1, $subTeam->EquipmentSuppliers()->count(),
1292
			'Child class did not inherit multiple many_manys');
1293
		// Team 2 has one EquipmentCompany sponsor and one SubEquipmentCompany
1294
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
1295
		$this->assertEquals(2, $team2->Sponsors()->count(),
1296
			'Child class did not inherit multiple belongs_many_manys');
1297
1298
		// Check many_many_extraFields also works from the belongs_many_many side
1299
		$sponsors = $team2->Sponsors();
1300
		$sponsors->add($equipmentCompany, array('SponsorFee' => 750));
1301
		$this->assertEquals(750, $sponsors->byID($equipmentCompany->ID)->SponsorFee,
1302
			'Data from many_many_extraFields was not stored/extracted correctly');
1303
1304
		$subEquipmentCompany = $this->objFromFixture('DataObjectTest_SubEquipmentCompany', 'subequipmentcompany1');
1305
		$subTeam->Sponsors()->add($subEquipmentCompany, array('SponsorFee' => 1200));
1306
		$this->assertEquals(1200, $subTeam->Sponsors()->byID($subEquipmentCompany->ID)->SponsorFee,
1307
			'Data from inherited many_many_extraFields was not stored/extracted correctly');
1308
	}
1309
1310
	public function testManyManyExtraFields() {
1311
		$player = $this->objFromFixture('DataObjectTest_Player', 'player1');
1312
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
1313
1314
		// Get all extra fields
1315
		$teamExtraFields = $team->manyManyExtraFields();
1316
		$this->assertEquals(array(
1317
			'Players' => array('Position' => 'Varchar(100)')
1318
		), $teamExtraFields);
1319
1320
		// Ensure fields from parent classes are included
1321
		$subTeam = singleton('DataObjectTest_SubTeam');
1322
		$teamExtraFields = $subTeam->manyManyExtraFields();
1323
		$this->assertEquals(array(
1324
			'Players' => array('Position' => 'Varchar(100)'),
1325
			'FormerPlayers' => array('Position' => 'Varchar(100)')
1326
		), $teamExtraFields);
1327
1328
		// Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
1329
		$teamExtraFields = $team->manyManyExtraFieldsForComponent('Players');
1330
		$this->assertEquals($teamExtraFields, array(
1331
			'Position' => 'Varchar(100)'
1332
		));
1333
1334
		// We'll have to go through the relation to get the extra fields on Player
1335
		$playerExtraFields = $player->manyManyExtraFieldsForComponent('Teams');
1336
		$this->assertEquals($playerExtraFields, array(
1337
			'Position' => 'Varchar(100)'
1338
		));
1339
1340
		// Iterate through a many-many relationship and confirm that extra fields are included
1341
		$newTeam = new DataObjectTest_Team();
1342
		$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...
1343
		$newTeam->write();
1344
		$newTeamID = $newTeam->ID;
1345
1346
		$newPlayer = new DataObjectTest_Player();
1347
		$newPlayer->FirstName = "Sam";
1348
		$newPlayer->Surname = "Minnee";
1349
		$newPlayer->write();
1350
1351
		// The idea of Sam as a prop is essentially humourous.
1352
		$newTeam->Players()->add($newPlayer, array("Position" => "Prop"));
1353
1354
		// Requery and uncache everything
1355
		$newTeam->flushCache();
1356
		$newTeam = DataObject::get_by_id('DataObjectTest_Team', $newTeamID);
1357
1358
		// Check that the Position many_many_extraField is extracted.
1359
		$player = $newTeam->Players()->First();
1360
		$this->assertEquals('Sam', $player->FirstName);
1361
		$this->assertEquals("Prop", $player->Position);
1362
1363
		// Check that ordering a many-many relation by an aggregate column doesn't fail
1364
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1365
		$player->Teams()->sort("count(DISTINCT \"DataObjectTest_Team_Players\".\"DataObjectTest_PlayerID\") DESC");
1366
	}
1367
1368
	/**
1369
	 * Check that the queries generated for many-many relation queries can have unlimitedRowCount
1370
	 * called on them.
1371
	 */
1372
	public function testManyManyUnlimitedRowCount() {
1373
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1374
		// TODO: What's going on here?
1375
		$this->assertEquals(2, $player->Teams()->dataQuery()->query()->unlimitedRowCount());
1376
	}
1377
1378
	/**
1379
	 * Tests that singular_name() generates sensible defaults.
1380
	 */
1381
	public function testSingularName() {
1382
		$assertions = array(
1383
			'DataObjectTest_Player'       => 'Data Object Test Player',
1384
			'DataObjectTest_Team'         => 'Data Object Test Team',
1385
			'DataObjectTest_Fixture'      => 'Data Object Test Fixture'
1386
		);
1387
1388
		foreach($assertions as $class => $expectedSingularName) {
1389
			$this->assertEquals(
1390
				$expectedSingularName,
1391
				singleton($class)->singular_name(),
1392
				"Assert that the singular_name for '$class' is correct."
1393
			);
1394
		}
1395
	}
1396
1397
	/**
1398
	 * Tests that plural_name() generates sensible defaults.
1399
	 */
1400
	public function testPluralName() {
1401
		$assertions = array(
1402
			'DataObjectTest_Player'       => 'Data Object Test Players',
1403
			'DataObjectTest_Team'         => 'Data Object Test Teams',
1404
			'DataObjectTest_Fixture'      => 'Data Object Test Fixtures',
1405
			'DataObjectTest_Play'         => 'Data Object Test Plays',
1406
			'DataObjectTest_Bogey'        => 'Data Object Test Bogeys',
1407
			'DataObjectTest_Ploy'         => 'Data Object Test Ploys',
1408
		);
1409
1410
		foreach($assertions as $class => $expectedPluralName) {
1411
			$this->assertEquals(
1412
				$expectedPluralName,
1413
				singleton($class)->plural_name(),
1414
				"Assert that the plural_name for '$class' is correct."
1415
			);
1416
		}
1417
	}
1418
1419
	public function testHasDatabaseField() {
1420
		$team = singleton('DataObjectTest_Team');
1421
		$subteam = singleton('DataObjectTest_SubTeam');
1422
1423
		$this->assertTrue(
1424
			$team->hasDatabaseField('Title'),
1425
			"hasOwnDatabaseField() works with \$db fields"
1426
		);
1427
		$this->assertTrue(
1428
			$team->hasDatabaseField('CaptainID'),
1429
			"hasOwnDatabaseField() works with \$has_one fields"
1430
		);
1431
		$this->assertFalse(
1432
			$team->hasDatabaseField('NonExistentField'),
1433
			"hasOwnDatabaseField() doesn't detect non-existend fields"
1434
		);
1435
		$this->assertTrue(
1436
			$team->hasDatabaseField('ExtendedDatabaseField'),
1437
			"hasOwnDatabaseField() works with extended fields"
1438
		);
1439
		$this->assertFalse(
1440
			$team->hasDatabaseField('SubclassDatabaseField'),
1441
			"hasOwnDatabaseField() doesn't pick up fields in subclasses on parent class"
1442
		);
1443
1444
		$this->assertTrue(
1445
			$subteam->hasDatabaseField('SubclassDatabaseField'),
1446
			"hasOwnDatabaseField() picks up fields in subclasses"
1447
		);
1448
1449
	}
1450
1451
	public function testFieldTypes() {
1452
		$obj = new DataObjectTest_Fixture();
1453
		$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...
1454
		$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...
1455
		$obj->write();
1456
		$obj->flushCache();
1457
1458
		$obj = DataObject::get_by_id('DataObjectTest_Fixture', $obj->ID);
1459
		$this->assertEquals('1988-01-02', $obj->DateField);
1460
		$this->assertEquals('1988-03-04 06:30:00', $obj->DatetimeField);
1461
	}
1462
1463
	public function testTwoSubclassesWithTheSameFieldNameWork() {
1464
		// Create two objects of different subclasses, setting the values of fields that are
1465
		// defined separately in each subclass
1466
		$obj1 = new DataObjectTest_SubTeam();
1467
		$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...
1468
		$obj2 = new OtherSubclassWithSameField();
1469
		$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...
1470
1471
		// Write them to the database
1472
		$obj1->write();
1473
		$obj2->write();
1474
1475
		// Check that the values of those fields are properly read from the database
1476
		$values = DataObject::get("DataObjectTest_Team", "\"DataObjectTest_Team\".\"ID\" IN
1477
			($obj1->ID, $obj2->ID)")->column("SubclassDatabaseField");
1478
		$this->assertEquals(array_intersect($values, array('obj1', 'obj2')), $values);
1479
	}
1480
1481
	public function testClassNameSetForNewObjects() {
1482
		$d = new DataObjectTest_Player();
1483
		$this->assertEquals('DataObjectTest_Player', $d->ClassName);
1484
	}
1485
1486
	public function testHasValue() {
1487
		$team = new DataObjectTest_Team();
1488
		$this->assertFalse($team->hasValue('Title', null, false));
1489
		$this->assertFalse($team->hasValue('DatabaseField', null, false));
1490
1491
		$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...
1492
		$this->assertTrue($team->hasValue('Title', null, false));
1493
		$this->assertFalse($team->hasValue('DatabaseField', null, false));
1494
1495
		$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...
1496
		$this->assertTrue($team->hasValue('Title', null, false));
1497
		$this->assertFalse (
1498
			$team->hasValue('DatabaseField', null, false),
1499
			'Test that a blank paragraph on a HTML field is not a valid value.'
1500
		);
1501
1502
		$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...
1503
		$this->assertTrue (
1504
			$team->hasValue('Title', null, false),
1505
			'Test that an empty paragraph is a value for non-HTML fields.'
1506
		);
1507
1508
		$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...
1509
		$this->assertTrue($team->hasValue('Title', null, false));
1510
		$this->assertTrue($team->hasValue('DatabaseField', null, false));
1511
	}
1512
1513
	public function testHasMany() {
1514
		$company = new DataObjectTest_Company();
1515
1516
		$this->assertEquals (
1517
			array (
1518
				'CurrentStaff'     => 'DataObjectTest_Staff',
1519
				'PreviousStaff'    => 'DataObjectTest_Staff'
1520
			),
1521
			$company->hasMany(),
1522
			'has_many strips field name data by default.'
1523
		);
1524
1525
		$this->assertEquals (
1526
			'DataObjectTest_Staff',
1527
			$company->hasManyComponent('CurrentStaff'),
1528
			'has_many strips field name data by default on single relationships.'
1529
		);
1530
1531
		$this->assertEquals (
1532
			array (
1533
				'CurrentStaff'     => 'DataObjectTest_Staff.CurrentCompany',
1534
				'PreviousStaff'    => 'DataObjectTest_Staff.PreviousCompany'
1535
			),
1536
			$company->hasMany(null, false),
1537
			'has_many returns field name data when $classOnly is false.'
1538
		);
1539
1540
		$this->assertEquals (
1541
			'DataObjectTest_Staff.CurrentCompany',
1542
			$company->hasManyComponent('CurrentStaff', false),
1543
			'has_many returns field name data on single records when $classOnly is false.'
1544
		);
1545
	}
1546
1547
	public function testGetRemoteJoinField() {
1548
		$company = new DataObjectTest_Company();
1549
1550
		$staffJoinField = $company->getRemoteJoinField('CurrentStaff', 'has_many', $polymorphic);
1551
		$this->assertEquals('CurrentCompanyID', $staffJoinField);
1552
		$this->assertFalse($polymorphic, 'DataObjectTest_Company->CurrentStaff is not polymorphic');
1553
		$previousStaffJoinField = $company->getRemoteJoinField('PreviousStaff', 'has_many', $polymorphic);
1554
		$this->assertEquals('PreviousCompanyID', $previousStaffJoinField);
1555
		$this->assertFalse($polymorphic, 'DataObjectTest_Company->PreviousStaff is not polymorphic');
1556
1557
		$ceo = new DataObjectTest_CEO();
1558
1559
		$this->assertEquals('CEOID', $ceo->getRemoteJoinField('Company', 'belongs_to', $polymorphic));
1560
		$this->assertFalse($polymorphic, 'DataObjectTest_CEO->Company is not polymorphic');
1561
		$this->assertEquals('PreviousCEOID', $ceo->getRemoteJoinField('PreviousCompany', 'belongs_to', $polymorphic));
1562
		$this->assertFalse($polymorphic, 'DataObjectTest_CEO->PreviousCompany is not polymorphic');
1563
1564
		$team = new DataObjectTest_Team();
1565
1566
		$this->assertEquals('Favourite', $team->getRemoteJoinField('Fans', 'has_many', $polymorphic));
1567
		$this->assertTrue($polymorphic, 'DataObjectTest_Team->Fans is polymorphic');
1568
		$this->assertEquals('TeamID', $team->getRemoteJoinField('Comments', 'has_many', $polymorphic));
1569
		$this->assertFalse($polymorphic, 'DataObjectTest_Team->Comments is not polymorphic');
1570
	}
1571
1572
	public function testBelongsTo() {
1573
		$company = new DataObjectTest_Company();
1574
		$ceo     = new DataObjectTest_CEO();
1575
1576
		$company->write();
1577
		$ceo->write();
1578
1579
		// Test belongs_to assignment
1580
		$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...
1581
		$company->write();
1582
1583
		$this->assertEquals($company->ID, $ceo->Company()->ID, 'belongs_to returns the right results.');
1584
1585
		// Test automatic creation of class where no assigment exists
1586
		$ceo = new DataObjectTest_CEO();
1587
		$ceo->write();
1588
1589
		$this->assertTrue (
1590
			$ceo->Company() instanceof DataObjectTest_Company,
1591
			'DataObjects across belongs_to relations are automatically created.'
1592
		);
1593
		$this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
1594
1595
		// Write object with components
1596
		$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...
1597
		$ceo->write(false, false, false, true);
1598
		$this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
1599
1600
		$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
1601
		$this->assertEquals (
1602
			$ceo->Company()->ID, $newCEO->Company()->ID, 'belongs_to can be retrieved from the database.'
1603
		);
1604
	}
1605
1606
	public function testBelongsToPolymorphic() {
1607
		$company = new DataObjectTest_Company();
1608
		$ceo     = new DataObjectTest_CEO();
1609
1610
		$company->write();
1611
		$ceo->write();
1612
1613
		// Test belongs_to assignment
1614
		$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...
1615
		$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...
1616
		$company->write();
1617
1618
		$this->assertEquals($company->ID, $ceo->CompanyOwned()->ID, 'belongs_to returns the right results.');
1619
		$this->assertEquals($company->class, $ceo->CompanyOwned()->class, 'belongs_to returns the right results.');
1620
1621
		// Test automatic creation of class where no assigment exists
1622
		$ceo = new DataObjectTest_CEO();
1623
		$ceo->write();
1624
1625
		$this->assertTrue (
1626
			$ceo->CompanyOwned() instanceof DataObjectTest_Company,
1627
			'DataObjects across polymorphic belongs_to relations are automatically created.'
1628
		);
1629
		$this->assertEquals($ceo->ID, $ceo->CompanyOwned()->OwnerID, 'Remote IDs are automatically set.');
1630
		$this->assertInstanceOf($ceo->CompanyOwned()->OwnerClass, $ceo, 'Remote class is automatically  set');
1631
1632
		// Write object with components
1633
		$ceo->write(false, false, false, true);
1634
		$this->assertTrue($ceo->CompanyOwned()->isInDB(), 'write() writes belongs_to components to the database.');
1635
1636
		$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
1637
		$this->assertEquals (
1638
			$ceo->CompanyOwned()->ID,
1639
			$newCEO->CompanyOwned()->ID,
1640
			'polymorphic belongs_to can be retrieved from the database.'
1641
		);
1642
	}
1643
1644
	/**
1645
	 * @expectedException LogicException
1646
	 */
1647
	public function testInvalidate() {
1648
		$do = new DataObjectTest_Fixture();
1649
		$do->write();
1650
1651
		$do->delete();
1652
1653
		$do->delete(); // Prohibit invalid object manipulation
1654
		$do->write();
1655
		$do->duplicate();
1656
	}
1657
1658
	public function testToMap() {
1659
		$obj = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1660
1661
		$map = $obj->toMap();
1662
1663
		$this->assertArrayHasKey('ID', $map, 'Contains base fields');
1664
		$this->assertArrayHasKey('Title', $map, 'Contains fields from parent class');
1665
		$this->assertArrayHasKey('SubclassDatabaseField', $map, 'Contains fields from concrete class');
1666
1667
		$this->assertEquals($obj->ID, $map['ID'],
1668
			'Contains values from base fields');
1669
		$this->assertEquals($obj->Title, $map['Title'],
1670
			'Contains values from parent class fields');
1671
		$this->assertEquals($obj->SubclassDatabaseField, $map['SubclassDatabaseField'],
1672
			'Contains values from concrete class fields');
1673
1674
		$newObj = new DataObjectTest_SubTeam();
1675
		$this->assertArrayHasKey('Title', $map, 'Contains null fields');
1676
	}
1677
1678
	public function testIsEmpty() {
1679
		$objEmpty = new DataObjectTest_Team();
1680
		$this->assertTrue($objEmpty->isEmpty(), 'New instance without populated defaults is empty');
1681
1682
		$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...
1683
		$this->assertFalse($objEmpty->isEmpty(), 'Zero value in attribute considered non-empty');
1684
	}
1685
1686
	public function testRelField() {
1687
		$captain = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1688
		// Test traversal of a single has_one
1689
		$this->assertEquals("Team 1", $captain->relField('FavouriteTeam.Title'));
1690
		// Test direct field access
1691
		$this->assertEquals("Captain", $captain->relField('FirstName'));
1692
1693
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1694
		// Test that we can traverse more than once, and that arbitrary methods are okay
1695
		$this->assertEquals("Team 1", $player->relField('Teams.First.Title'));
1696
1697
		$newPlayer = new DataObjectTest_Player();
1698
		$this->assertNull($newPlayer->relField('Teams.First.Title'));
1699
1700
		// Test that relField works on db field manipulations
1701
		$comment = $this->objFromFixture('DataObjectTest_TeamComment', 'comment3');
1702
		$this->assertEquals("PHIL IS A UNIQUE GUY, AND COMMENTS ON TEAM2" , $comment->relField('Comment.UpperCase'));
1703
	}
1704
1705
	public function testRelObject() {
1706
		$captain = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1707
1708
		// Test traversal of a single has_one
1709
		$this->assertInstanceOf("Varchar", $captain->relObject('FavouriteTeam.Title'));
1710
		$this->assertEquals("Team 1", $captain->relObject('FavouriteTeam.Title')->getValue());
1711
1712
		// Test direct field access
1713
		$this->assertInstanceOf("Boolean", $captain->relObject('IsRetired'));
1714
		$this->assertEquals(1, $captain->relObject('IsRetired')->getValue());
1715
1716
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1717
		// Test that we can traverse more than once, and that arbitrary methods are okay
1718
		$this->assertInstanceOf("Varchar", $player->relObject('Teams.First.Title'));
1719
		$this->assertEquals("Team 1", $player->relObject('Teams.First.Title')->getValue());
1720
	}
1721
1722
	public function testLateStaticBindingStyle() {
1723
		// Confirm that DataObjectTest_Player::get() operates as excepted
1724
		$this->assertEquals(4, DataObjectTest_Player::get()->Count());
1725
		$this->assertInstanceOf('DataObjectTest_Player', DataObjectTest_Player::get()->First());
1726
1727
		// You can't pass arguments to LSB syntax - use the DataList methods instead.
1728
		$this->setExpectedException('InvalidArgumentException');
1729
		DataObjectTest_Player::get(null, "\"ID\" = 1");
1730
1731
	}
1732
1733
	public function testBrokenLateStaticBindingStyle() {
1734
		// If you call DataObject::get() you have to pass a first argument
1735
		$this->setExpectedException('InvalidArgumentException');
1736
		DataObject::get();
1737
1738
	}
1739
1740
	public function testBigIntField() {
1741
		$staff = new DataObjectTest_Staff();
1742
		$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...
1743
		$staff->write();
1744
		$this->assertEquals(PHP_INT_MAX, DataObjectTest_Staff::get()->byID($staff->ID)->Salary);
1745
	}
1746
1747
}
1748
1749
class DataObjectTest_Sortable extends DataObject implements TestOnly {
1750
	private static $db = array(
1751
		'Sort' => 'Int',
1752
		'Name' => 'Varchar',
1753
	);
1754
}
1755
1756
class DataObjectTest_Player extends Member implements TestOnly {
1757
	private static $db = array(
1758
		'IsRetired' => 'Boolean',
1759
		'ShirtNumber' => 'Varchar',
1760
	);
1761
1762
	private static $has_one = array(
1763
		'FavouriteTeam' => 'DataObjectTest_Team',
1764
	);
1765
1766
	private static $belongs_many_many = array(
1767
		'Teams' => 'DataObjectTest_Team'
1768
	);
1769
1770
	private static $has_many = array(
1771
		'Fans' => 'DataObjectTest_Fan.Favourite' // Polymorphic - Player fans
1772
	);
1773
1774
	private static $belongs_to = array (
1775
		'CompanyOwned'    => 'DataObjectTest_Company.Owner'
1776
	);
1777
1778
	private static $searchable_fields = array(
1779
		'IsRetired',
1780
		'ShirtNumber'
1781
	);
1782
}
1783
1784
class DataObjectTest_Team extends DataObject implements TestOnly {
1785
1786
	private static $db = array(
1787
		'Title' => 'Varchar',
1788
		'DatabaseField' => 'HTMLVarchar'
1789
	);
1790
1791
	private static $has_one = array(
1792
		"Captain" => 'DataObjectTest_Player',
1793
		'HasOneRelationship' => 'DataObjectTest_Player',
1794
	);
1795
1796
	private static $has_many = array(
1797
		'SubTeams' => 'DataObjectTest_SubTeam',
1798
		'Comments' => 'DataObjectTest_TeamComment',
1799
		'Fans' => 'DataObjectTest_Fan.Favourite' // Polymorphic - Team fans
1800
	);
1801
1802
	private static $many_many = array(
1803
		'Players' => 'DataObjectTest_Player'
1804
	);
1805
1806
	private static $many_many_extraFields = array(
1807
		'Players' => array(
1808
			'Position' => 'Varchar(100)'
1809
		)
1810
	);
1811
1812
	private static $belongs_many_many = array(
1813
		'Sponsors' => 'DataObjectTest_EquipmentCompany.SponsoredTeams',
1814
		'EquipmentSuppliers' => 'DataObjectTest_EquipmentCompany.EquipmentCustomers'
1815
	);
1816
1817
	private static $summary_fields = array(
1818
		'Title' => 'Custom Title',
1819
		'Title.UpperCase' => 'Title',
1820
		'Captain.ShirtNumber' => 'Captain\'s shirt number',
1821
		'Captain.FavouriteTeam.Title' => 'Captain\'s favourite team'
1822
	);
1823
1824
	private static $default_sort = '"Title"';
1825
1826
	public function MyTitle() {
1827
		return 'Team ' . $this->Title;
1828
	}
1829
1830
	public function getDynamicField() {
1831
		return 'dynamicfield';
1832
	}
1833
1834
}
1835
1836
class DataObjectTest_Fixture extends DataObject implements TestOnly {
1837
	private static $db = array(
1838
		// Funny field names
1839
		'Data' => 'Varchar',
1840
		'Duplicate' => 'Varchar',
1841
		'DbObject' => 'Varchar',
1842
1843
		// Field types
1844
		'DateField' => 'Date',
1845
		'DatetimeField' => 'Datetime',
1846
1847
		'MyFieldWithDefault' => 'Varchar',
1848
		'MyFieldWithAltDefault' => 'Varchar'
1849
	);
1850
1851
	private static $defaults = array(
1852
		'MyFieldWithDefault' => 'Default Value',
1853
	);
1854
1855
	private static $summary_fields = array(
1856
		'Data' => 'Data',
1857
		'DateField.Nice' => 'Date'
1858
	);
1859
1860
	private static $searchable_fields = array();
1861
1862
	public function populateDefaults() {
1863
		parent::populateDefaults();
1864
1865
		$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...
1866
	}
1867
1868
}
1869
1870
class DataObjectTest_SubTeam extends DataObjectTest_Team implements TestOnly {
1871
	private static $db = array(
1872
		'SubclassDatabaseField' => 'Varchar'
1873
	);
1874
1875
	private static $has_one = array(
1876
		"ParentTeam" => 'DataObjectTest_Team',
1877
	);
1878
1879
	private static $many_many = array(
1880
		'FormerPlayers' => 'DataObjectTest_Player'
1881
	);
1882
1883
	private static $many_many_extraFields = array(
1884
		'FormerPlayers' => array(
1885
			'Position' => 'Varchar(100)'
1886
		)
1887
	);
1888
}
1889
class OtherSubclassWithSameField extends DataObjectTest_Team implements TestOnly {
1890
	private static $db = array(
1891
		'SubclassDatabaseField' => 'Varchar',
1892
	);
1893
}
1894
1895
1896
class DataObjectTest_FieldlessTable extends DataObject implements TestOnly {
1897
}
1898
1899
class DataObjectTest_FieldlessSubTable extends DataObjectTest_Team implements TestOnly {
1900
}
1901
1902
1903
class DataObjectTest_Team_Extension extends DataExtension implements TestOnly {
1904
1905
	private static $db = array(
1906
		'ExtendedDatabaseField' => 'Varchar'
1907
	);
1908
1909
	private static $has_one = array(
1910
		'ExtendedHasOneRelationship' => 'DataObjectTest_Player'
1911
	);
1912
1913
	public function getExtendedDynamicField() {
1914
		return "extended dynamic field";
1915
	}
1916
1917
}
1918
1919
class DataObjectTest_ValidatedObject extends DataObject implements TestOnly {
1920
1921
	private static $db = array(
1922
		'Name' => 'Varchar(50)'
1923
	);
1924
1925
	protected function validate() {
1926
		if(!empty($this->Name)) {
1927
			return new ValidationResult();
1928
		} else {
1929
			return new ValidationResult(false, "This object needs a name. Otherwise it will have an identity crisis!");
1930
		}
1931
	}
1932
}
1933
1934
class DataObjectTest_Company extends DataObject implements TestOnly {
1935
1936
	private static $db = array(
1937
		'Name' => 'Varchar'
1938
	);
1939
1940
	private static $has_one = array (
1941
		'CEO'         => 'DataObjectTest_CEO',
1942
		'PreviousCEO' => 'DataObjectTest_CEO',
1943
		'Owner'       => 'DataObject' // polymorphic
1944
	);
1945
1946
	private static $has_many = array (
1947
		'CurrentStaff'     => 'DataObjectTest_Staff.CurrentCompany',
1948
		'PreviousStaff'    => 'DataObjectTest_Staff.PreviousCompany'
1949
	);
1950
}
1951
1952
class DataObjectTest_EquipmentCompany extends DataObjectTest_Company implements TestOnly {
1953
	private static $many_many = array(
1954
		'SponsoredTeams' => 'DataObjectTest_Team',
1955
		'EquipmentCustomers' => 'DataObjectTest_Team'
1956
	);
1957
1958
	private static $many_many_extraFields = array(
1959
		'SponsoredTeams' => array(
1960
			'SponsorFee' => 'Int'
1961
		)
1962
	);
1963
}
1964
1965
class DataObjectTest_SubEquipmentCompany extends DataObjectTest_EquipmentCompany implements TestOnly {
1966
	private static $db = array(
1967
		'SubclassDatabaseField' => 'Varchar',
1968
	);
1969
}
1970
1971
class DataObjectTest_Staff extends DataObject implements TestOnly {
1972
	private static $db = array(
1973
		'Salary' => 'BigInt',
1974
	);
1975
	private static $has_one = array (
1976
		'CurrentCompany'  => 'DataObjectTest_Company',
1977
		'PreviousCompany' => 'DataObjectTest_Company'
1978
	);
1979
}
1980
1981
class DataObjectTest_CEO extends DataObjectTest_Staff {
1982
	private static $belongs_to = array (
1983
		'Company'         => 'DataObjectTest_Company.CEO',
1984
		'PreviousCompany' => 'DataObjectTest_Company.PreviousCEO',
1985
		'CompanyOwned'    => 'DataObjectTest_Company.Owner'
1986
	);
1987
}
1988
1989
class DataObjectTest_TeamComment extends DataObject implements TestOnly {
1990
	private static $db = array(
1991
		'Name' => 'Varchar',
1992
		'Comment' => 'Text'
1993
	);
1994
1995
	private static $has_one = array(
1996
		'Team' => 'DataObjectTest_Team'
1997
	);
1998
1999
	private static $default_sort = '"Name" ASC';
2000
}
2001
2002
class DataObjectTest_Fan extends DataObject implements TestOnly {
2003
2004
	private static $db = array(
2005
		'Name' => 'Varchar(255)'
2006
	);
2007
2008
	private static $has_one = array(
2009
		'Favourite' => 'DataObject', // Polymorphic relation
2010
		'SecondFavourite' => 'DataObject'
2011
	);
2012
}
2013
2014
class DataObjectTest_ExtendedTeamComment extends DataObjectTest_TeamComment {
2015
	private static $db = array(
2016
		'Comment' => 'HTMLText'
2017
	);
2018
}
2019
2020
class DataObjectTest_Play extends DataObject implements TestOnly {}
2021
class DataObjectTest_Ploy extends DataObject implements TestOnly {}
2022
class DataObjectTest_Bogey extends DataObject implements TestOnly {}
2023
2024
DataObjectTest_Team::add_extension('DataObjectTest_Team_Extension');
2025
2026