Completed
Push — hash-nonce ( 07e2e8 )
by Sam
08:52
created

testWritingInvalidDataObjectThrowsException()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
nc 1
nop 0
dl 0
loc 6
rs 9.4285
c 0
b 0
f 0
1
<?php
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
	public function testDb() {
37
		$obj = new DataObjectTest_TeamComment();
38
		$dbFields = $obj->db();
39
40
		// Assert fields are included
41
		$this->assertArrayHasKey('Name', $dbFields);
42
43
		// Assert the base fields are excluded
44
		$this->assertArrayNotHasKey('Created', $dbFields);
45
		$this->assertArrayNotHasKey('LastEdited', $dbFields);
46
		$this->assertArrayNotHasKey('ClassName', $dbFields);
47
		$this->assertArrayNotHasKey('ID', $dbFields);
48
49
		// Assert that the correct field type is returned when passing a field
50
		$this->assertEquals('Varchar', $obj->db('Name'));
51
		$this->assertEquals('Text', $obj->db('Comment'));
52
53
		$obj = new DataObjectTest_ExtendedTeamComment();
54
		$dbFields = $obj->db();
55
56
		// Assert overloaded fields have correct data type
57
		$this->assertEquals('HTMLText', $obj->db('Comment'));
58
		$this->assertEquals('HTMLText', $dbFields['Comment'],
59
			'Calls to DataObject::db without a field specified return correct data types');
60
61
		// assertEquals doesn't verify the order of array elements, so access keys manually to check order:
62
		// expected: array('Name' => 'Varchar', 'Comment' => 'HTMLText')
63
		reset($dbFields);
64
		$this->assertEquals('Name', key($dbFields), 'DataObject::db returns fields in correct order');
65
		next($dbFields);
66
		$this->assertEquals('Comment', key($dbFields), 'DataObject::db returns fields in correct order');
67
	}
68
69
	public function testConstructAcceptsValues() {
70
		// Values can be an array...
71
		$player = new DataObjectTest_Player(array(
72
			'FirstName' => 'James',
73
			'Surname' => 'Smith'
74
		));
75
76
		$this->assertEquals('James', $player->FirstName);
77
		$this->assertEquals('Smith', $player->Surname);
78
79
		// ... or a stdClass inst
80
		$data = new stdClass();
81
		$data->FirstName = 'John';
82
		$data->Surname = 'Doe';
83
		$player = new DataObjectTest_Player($data);
0 ignored issues
show
Documentation introduced by
$data is of type object<stdClass>, but the function expects a array|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
84
85
		$this->assertEquals('John', $player->FirstName);
86
		$this->assertEquals('Doe', $player->Surname);
87
88
		// IDs should be stored as integers, not strings
89
		$player = new DataObjectTest_Player(array('ID' => '5'));
90
		$this->assertSame(5, $player->ID);
91
	}
92
93
	public function testValidObjectsForBaseFields() {
94
		$obj = new DataObjectTest_ValidatedObject();
95
96
		foreach (array('Created', 'LastEdited', 'ClassName', 'ID') as $field) {
97
			$helper = $obj->dbObject($field);
98
			$this->assertTrue(
99
				($helper instanceof DBField),
100
				"for {$field} expected helper to be DBField, but was " .
101
				(is_object($helper) ? get_class($helper) : "null")
102
			);
103
		}
104
	}
105
106
	public function testDataIntegrityWhenTwoSubclassesHaveSameField() {
107
		// Save data into DataObjectTest_SubTeam.SubclassDatabaseField
108
		$obj = new DataObjectTest_SubTeam();
109
		$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...
110
		$obj->write();
111
112
		// Change the class
113
		$obj->ClassName = 'OtherSubclassWithSameField';
114
		$obj->write();
115
		$obj->flushCache();
116
117
		// Re-fetch from the database and confirm that the data is sourced from
118
		// OtherSubclassWithSameField.SubclassDatabaseField
119
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
120
		$this->assertNull($obj->SubclassDatabaseField);
121
122
		// Confirm that save the object in the other direction.
123
		$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...
124
		$obj->write();
125
126
		$obj->ClassName = 'DataObjectTest_SubTeam';
127
		$obj->write();
128
		$obj->flushCache();
129
130
		// If we restore the class, the old value has been lying dormant and will be available again.
131
		// NOTE: This behaviour is volatile; we may change this in the future to clear fields that
132
		// are no longer relevant when changing ClassName
133
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
134
		$this->assertEquals('obj-SubTeam', $obj->SubclassDatabaseField);
135
	}
136
137
	/**
138
	 * Test deletion of DataObjects
139
	 *   - Deleting using delete() on the DataObject
140
	 *   - Deleting using DataObject::delete_by_id()
141
	 */
142
	public function testDelete() {
143
		// Test deleting using delete() on the DataObject
144
		// Get the first page
145
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
146
		$objID = $obj->ID;
147
		// Check the page exists before deleting
148
		$this->assertTrue(is_object($obj) && $obj->exists());
149
		// Delete the page
150
		$obj->delete();
151
		// Check that page does not exist after deleting
152
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
153
		$this->assertTrue(!$obj || !$obj->exists());
154
155
156
		// Test deleting using DataObject::delete_by_id()
157
		// Get the second page
158
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
159
		$objID = $obj->ID;
160
		// Check the page exists before deleting
161
		$this->assertTrue(is_object($obj) && $obj->exists());
162
		// Delete the page
163
		DataObject::delete_by_id('DataObjectTest_Player', $obj->ID);
164
		// Check that page does not exist after deleting
165
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
166
		$this->assertTrue(!$obj || !$obj->exists());
167
	}
168
169
	/**
170
	 * Test methods that get DataObjects
171
	 *   - DataObject::get()
172
	 *       - All records of a DataObject
173
	 *       - Filtering
174
	 *       - Sorting
175
	 *       - Joins
176
	 *       - Limit
177
	 *       - Container class
178
	 *   - DataObject::get_by_id()
179
	 *   - DataObject::get_one()
180
	 *        - With and without caching
181
	 *        - With and without ordering
182
	 */
183
	public function testGet() {
184
		// Test getting all records of a DataObject
185
		$comments = DataObject::get('DataObjectTest_TeamComment');
186
		$this->assertEquals(3, $comments->Count());
187
188
		// Test WHERE clause
189
		$comments = DataObject::get('DataObjectTest_TeamComment', "\"Name\"='Bob'");
190
		$this->assertEquals(1, $comments->Count());
191
		foreach($comments as $comment) {
192
			$this->assertEquals('Bob', $comment->Name);
193
		}
194
195
		// Test sorting
196
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC");
197
		$this->assertEquals(3, $comments->Count());
198
		$this->assertEquals('Bob', $comments->First()->Name);
199
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" DESC");
200
		$this->assertEquals(3, $comments->Count());
201
		$this->assertEquals('Phil', $comments->First()->Name);
202
203
		// Test limit
204
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC", '', '1,2');
205
		$this->assertEquals(2, $comments->Count());
206
		$this->assertEquals('Joe', $comments->First()->Name);
207
		$this->assertEquals('Phil', $comments->Last()->Name);
208
209
		// Test get_by_id()
210
		$captain1ID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
211
		$captain1 = DataObject::get_by_id('DataObjectTest_Player', $captain1ID);
212
		$this->assertEquals('Captain', $captain1->FirstName);
213
214
		// Test get_one() without caching
215
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
216
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
217
		), false);
218
		$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...
219
220
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
221
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
222
		), false);
223
		$this->assertNotEquals($comment1->Comment, $comment2->Comment);
224
225
		// Test get_one() with caching
226
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
227
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
228
		), true);
229
		$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...
230
231
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
232
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
233
		), true);
234
		$this->assertEquals((string)$comment1->Comment, (string)$comment2->Comment);
235
236
		// Test get_one() with order by without caching
237
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" ASC");
238
		$this->assertEquals('Bob', $comment->Name);
239
240
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" DESC");
241
		$this->assertEquals('Phil', $comment->Name);
242
243
		// Test get_one() with order by with caching
244
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" ASC');
245
		$this->assertEquals('Bob', $comment->Name);
246
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" DESC');
247
		$this->assertEquals('Phil', $comment->Name);
248
	}
249
250 View Code Duplication
	public function testGetCaseInsensitive() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
251
		// Test get_one() with bad case on the classname
252
		// Note: This will succeed only if the underlying DB server supports case-insensitive
253
		// table names (e.g. such as MySQL, but not SQLite3)
254
		if(!(DB::get_conn() instanceof MySQLDatabase)) {
255
			$this->markTestSkipped('MySQL only');
256
		}
257
258
		$subteam1 = DataObject::get_one('dataobjecttest_subteam', array(
259
			'"DataObjectTest_Team"."Title"' => 'Subteam 1'
260
		), true);
261
		$this->assertNotEmpty($subteam1);
262
		$this->assertEquals($subteam1->Title, "Subteam 1");
263
	}
264
265
	public function testGetSubclassFields() {
266
		/* Test that fields / has_one relations from the parent table and the subclass tables are extracted */
267
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
268
		// Base field
269
		$this->assertEquals('Captain', $captain1->FirstName);
270
		// Subclass field
271
		$this->assertEquals('007', $captain1->ShirtNumber);
272
		// Subclass has_one relation
273
		$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $captain1->FavouriteTeamID);
274
	}
275
276
	public function testGetRelationClass() {
277
		$obj = new DataObjectTest_Player();
0 ignored issues
show
Unused Code introduced by
$obj is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
278
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('FavouriteTeam'),
279
			'DataObjectTest_Team', 'has_one is properly inspected');
280
		$this->assertEquals(singleton('DataObjectTest_Company')->getRelationClass('CurrentStaff'),
281
			'DataObjectTest_Staff', 'has_many is properly inspected');
282
		$this->assertEquals(singleton('DataObjectTest_Team')->getRelationClass('Players'), 'DataObjectTest_Player',
283
			'many_many is properly inspected');
284
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('Teams'), 'DataObjectTest_Team',
285
			'belongs_many_many is properly inspected');
286
		$this->assertEquals(singleton('DataObjectTest_CEO')->getRelationClass('Company'), 'DataObjectTest_Company',
287
			'belongs_to is properly inspected');
288
		$this->assertEquals(singleton('DataObjectTest_Fan')->getRelationClass('Favourite'), 'DataObject',
289
			'polymorphic has_one is properly inspected');
290
	}
291
292
	/**
293
	 * Test that has_one relations can be retrieved
294
	 */
295
	public function testGetHasOneRelations() {
296
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
297
		$team1ID = $this->idFromFixture('DataObjectTest_Team', 'team1');
298
299
		// There will be a field called (relname)ID that contains the ID of the
300
		// object linked to via the has_one relation
301
		$this->assertEquals($team1ID, $captain1->FavouriteTeamID);
302
303
		// There will be a method called $obj->relname() that returns the object itself
304
		$this->assertEquals($team1ID, $captain1->FavouriteTeam()->ID);
305
306
		// Check entity with polymorphic has-one
307
		$fan1 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
308
		$this->assertTrue((bool)$fan1->hasValue('Favourite'));
309
310
		// There will be fields named (relname)ID and (relname)Class for polymorphic
311
		// entities
312
		$this->assertEquals($team1ID, $fan1->FavouriteID);
313
		$this->assertEquals('DataObjectTest_Team', $fan1->FavouriteClass);
314
315
		// There will be a method called $obj->relname() that returns the object itself
316
		$favourite = $fan1->Favourite();
317
		$this->assertEquals($team1ID, $favourite->ID);
318
		$this->assertInstanceOf('DataObjectTest_Team', $favourite);
319
320
		// check behaviour of dbObject with polymorphic relations
321
		$favouriteDBObject = $fan1->dbObject('Favourite');
322
		$favouriteValue = $favouriteDBObject->getValue();
323
		$this->assertInstanceOf('PolymorphicForeignKey', $favouriteDBObject);
324
		$this->assertEquals($favourite->ID, $favouriteValue->ID);
325
		$this->assertEquals($favourite->ClassName, $favouriteValue->ClassName);
326
	}
327
328
	/**
329
	 * Simple test to ensure that namespaced classes and polymorphic relations work together
330
	 */
331
	public function testPolymorphicNamespacedRelations() {
332
		$parent = new \DataObjectTest\NamespacedClass();
333
		$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...
334
		$parent->write();
335
336
		$child = new \DataObjectTest\RelationClass();
337
		$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...
338
		$child->write();
339
		$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...
340
341
		$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...
342
		$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...
343
		$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...
344
	}
345
346
	public function testLimitAndCount() {
347
		$players = DataObject::get("DataObjectTest_Player");
348
349
		// There's 4 records in total
350
		$this->assertEquals(4, $players->count());
351
352
		// Testing "##, ##" syntax
353
		$this->assertEquals(4, $players->limit(20)->count());
354
		$this->assertEquals(4, $players->limit(20, 0)->count());
355
		$this->assertEquals(0, $players->limit(20, 20)->count());
356
		$this->assertEquals(2, $players->limit(2, 0)->count());
357
		$this->assertEquals(1, $players->limit(5, 3)->count());
358
	}
359
360
	/**
361
	 * Test writing of database columns which don't correlate to a DBField,
362
	 * e.g. all relation fields on has_one/has_many like "ParentID".
363
	 *
364
	 */
365
	public function testWritePropertyWithoutDBField() {
366
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
367
		$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...
368
		$obj->write();
369
370
		// reload the page from the database
371
		$savedObj = DataObject::get_by_id('DataObjectTest_Player', $obj->ID);
372
		$this->assertTrue($savedObj->FavouriteTeamID == 99);
373
374
		// Test with porymorphic relation
375
		$obj2 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
376
		$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...
377
		$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...
378
		$obj2->write();
379
380
		$savedObj2 = DataObject::get_by_id('DataObjectTest_Fan', $obj2->ID);
381
		$this->assertTrue($savedObj2->FavouriteID == 99);
382
		$this->assertTrue($savedObj2->FavouriteClass == 'DataObjectTest_Player');
383
	}
384
385
	/**
386
	 * Test has many relationships
387
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
388
	 *   - Test the IDs on the DataObjects are set correctly
389
	 */
390
	public function testHasManyRelationships() {
391
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
392
393
		// Test getComponents() gets the ComponentSet of the other side of the relation
394
		$this->assertTrue($team1->Comments()->Count() == 2);
395
396
		// Test the IDs on the DataObjects are set correctly
397
		foreach($team1->Comments() as $comment) {
398
			$this->assertEquals($team1->ID, $comment->TeamID);
399
		}
400
401
		// Test that we can add and remove items that already exist in the database
402
		$newComment = new DataObjectTest_TeamComment();
403
		$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...
404
		$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...
405
		$newComment->write();
406
		$team1->Comments()->add($newComment);
407
		$this->assertEquals($team1->ID, $newComment->TeamID);
408
409
		$comment1 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment1');
410
		$comment2 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment2');
411
		$team1->Comments()->remove($comment2);
412
413
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
414
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
415
416
		// Test that removing an item from a list doesn't remove it from the same
417
		// relation belonging to a different object
418
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
419
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
420
		$team2->Comments()->remove($comment1);
421
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
422
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
423
	}
424
425
426
	/**
427
	 * Test has many relationships against polymorphic has_one fields
428
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
429
	 *   - Test the IDs on the DataObjects are set correctly
430
	 */
431
	public function testHasManyPolymorphicRelationships() {
432
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
433
434
		// Test getComponents() gets the ComponentSet of the other side of the relation
435
		$this->assertTrue($team1->Fans()->Count() == 2);
436
437
		// Test the IDs/Classes on the DataObjects are set correctly
438
		foreach($team1->Fans() as $fan) {
439
			$this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID');
440
			$this->assertEquals('DataObjectTest_Team', $fan->FavouriteClass, 'Fan has the correct FavouriteClass');
441
		}
442
443
		// Test that we can add and remove items that already exist in the database
444
		$newFan = new DataObjectTest_Fan();
445
		$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...
446
		$newFan->write();
447
		$team1->Fans()->add($newFan);
448
		$this->assertEquals($team1->ID, $newFan->FavouriteID, 'Newly created fan has the correct FavouriteID');
449
		$this->assertEquals(
450
			'DataObjectTest_Team',
451
			$newFan->FavouriteClass,
452
			'Newly created fan has the correct FavouriteClass'
453
		);
454
455
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
456
		$fan3 = $this->objFromFixture('DataObjectTest_Fan', 'fan3');
457
		$team1->Fans()->remove($fan3);
458
459
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
460
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
461
462
		// Test that removing an item from a list doesn't remove it from the same
463
		// relation belonging to a different object
464
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
465
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
466
		$player1->Fans()->remove($fan1);
467
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
468
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
469
	}
470
471
472
	public function testHasOneRelationship() {
473
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
474
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
475
		$player2 = $this->objFromFixture('DataObjectTest_Player', 'player2');
476
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
477
478
		// Test relation probing
479
		$this->assertFalse((bool)$team1->hasValue('Captain', null, false));
480
		$this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
481
482
		// Add a captain to team 1
483
		$team1->setField('CaptainID', $player1->ID);
484
		$team1->write();
485
486
		$this->assertTrue((bool)$team1->hasValue('Captain', null, false));
487
		$this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
488
489
		$this->assertEquals($player1->ID, $team1->Captain()->ID,
490
			'The captain exists for team 1');
491
		$this->assertEquals($player1->ID, $team1->getComponent('Captain')->ID,
492
			'The captain exists through the component getter');
493
494
		$this->assertEquals($team1->Captain()->FirstName, 'Player 1',
495
			'Player 1 is the captain');
496
		$this->assertEquals($team1->getComponent('Captain')->FirstName, 'Player 1',
497
			'Player 1 is the captain');
498
499
		$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...
500
		$team1->write();
501
502
		$this->assertEquals($player2->ID, $team1->Captain()->ID);
503
		$this->assertEquals($player2->ID, $team1->getComponent('Captain')->ID);
504
		$this->assertEquals('Player 2', $team1->Captain()->FirstName);
505
		$this->assertEquals('Player 2', $team1->getComponent('Captain')->FirstName);
506
507
508
		// Set the favourite team for fan1
509
		$fan1->setField('FavouriteID', $team1->ID);
510
		$fan1->setField('FavouriteClass', $team1->class);
511
512
		$this->assertEquals($team1->ID, $fan1->Favourite()->ID, 'The team is assigned to fan 1');
513
		$this->assertInstanceOf($team1->class, $fan1->Favourite(), 'The team is assigned to fan 1');
514
		$this->assertEquals($team1->ID, $fan1->getComponent('Favourite')->ID,
515
			'The team exists through the component getter'
516
		);
517
		$this->assertInstanceOf($team1->class, $fan1->getComponent('Favourite'),
518
			'The team exists through the component getter'
519
		);
520
521
		$this->assertEquals($fan1->Favourite()->Title, 'Team 1',
522
			'Team 1 is the favourite');
523
		$this->assertEquals($fan1->getComponent('Favourite')->Title, 'Team 1',
524
			'Team 1 is the favourite');
525
	}
526
527
	/**
528
	 * @todo Extend type change tests (e.g. '0'==NULL)
529
	 */
530
	public function testChangedFields() {
531
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
532
		$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...
533
		$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...
534
535
		$this->assertEquals(
536
			$obj->getChangedFields(true, DataObject::CHANGE_STRICT),
537
			array(
538
				'FirstName' => array(
539
					'before' => 'Captain',
540
					'after' => 'Captain-changed',
541
					'level' => DataObject::CHANGE_VALUE
542
				),
543
				'IsRetired' => array(
544
					'before' => 1,
545
					'after' => true,
546
					'level' => DataObject::CHANGE_STRICT
547
				)
548
			),
549
			'Changed fields are correctly detected with strict type changes (level=1)'
550
		);
551
552
		$this->assertEquals(
553
			$obj->getChangedFields(true, DataObject::CHANGE_VALUE),
554
			array(
555
				'FirstName' => array(
556
					'before'=>'Captain',
557
					'after'=>'Captain-changed',
558
					'level' => DataObject::CHANGE_VALUE
559
				)
560
			),
561
			'Changed fields are correctly detected while ignoring type changes (level=2)'
562
		);
563
564
		$newObj = new DataObjectTest_Player();
565
		$newObj->FirstName = "New Player";
566
		$this->assertEquals(
567
			array(
568
				'FirstName' => array(
569
					'before' => null,
570
					'after' => 'New Player',
571
					'level' => DataObject::CHANGE_VALUE
572
				)
573
			),
574
			$newObj->getChangedFields(true, DataObject::CHANGE_VALUE),
575
			'Initialised fields are correctly detected as full changes'
576
		);
577
	}
578
579
	public function testIsChanged() {
580
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
581
		$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...
582
		$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...
583
		$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...
584
585
		// Now that DB fields are changed, isChanged is true
586
		$this->assertTrue($obj->isChanged('NonDBField'));
587
		$this->assertFalse($obj->isChanged('NonField'));
588
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
589
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
590
		$this->assertTrue($obj->isChanged('IsRetired', DataObject::CHANGE_STRICT));
591
		$this->assertFalse($obj->isChanged('IsRetired', DataObject::CHANGE_VALUE));
592
		$this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
593
		$this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
594
595
		$newObj = new DataObjectTest_Player();
596
		$newObj->FirstName = "New Player";
597
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
598
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
599
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
600
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
601
602
		$newObj->write();
603
		$this->assertFalse($newObj->ischanged());
604
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
605
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
606
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
607
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
608
609
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
610
		$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...
611
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
612
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
613
614
		/* Test when there's not field provided */
615
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
616
		$this->assertFalse($obj->isChanged());
617
		$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...
618
		$this->assertFalse($obj->isChanged());
619
		$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...
620
		$this->assertTrue($obj->isChanged());
621
622
		$obj->write();
623
		$this->assertFalse($obj->isChanged());
624
	}
625
626
	public function testRandomSort() {
627
		/* If we perform the same regularly sorted query twice, it should return the same results */
628
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", "ID");
629
		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...
630
631
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", "ID");
632
		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...
633
634
		/* Test when there's not field provided */
635
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
636
		$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...
637
		$this->assertTrue($obj->isChanged());
638
639
		$obj->write();
640
		$this->assertFalse($obj->isChanged());
641
642
		/* If we perform the same random query twice, it shouldn't return the same results */
643
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
644
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
645
		$itemsC = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
646
		$itemsD = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
647
		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...
648
		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...
649
		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...
650
		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...
651
652
		// These shouldn't all be the same (run it 4 times to minimise chance of an accidental collision)
653
		// There's about a 1 in a billion chance of an accidental collision
654
		$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...
655
	}
656
657
	public function testWriteSavesToHasOneRelations() {
658
		/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
659
		$team = new DataObjectTest_Team();
660
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'player1');
661
		$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...
662
		$team->write();
663
		$this->assertEquals($captainID,
664
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
665
666
		/* After giving it a value, you should also be able to set it back to null */
667
		$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...
668
		$team->write();
669
		$this->assertEquals(0,
670
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
671
672
		/* You should also be able to save a blank to it when it's first created */
673
		$team = new DataObjectTest_Team();
674
		$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...
675
		$team->write();
676
		$this->assertEquals(0,
677
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
678
679
		/* Ditto for existing records without a value */
680
		$existingTeam = $this->objFromFixture('DataObjectTest_Team', 'team1');
681
		$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...
682
		$existingTeam->write();
683
		$this->assertEquals(0,
684
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value());
685
	}
686
687
	public function testCanAccessHasOneObjectsAsMethods() {
688
		/* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the
689
		 * object itself should be accessible as $obj->Captain() */
690
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
691
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
692
693
		$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...
694
		$this->assertNotNull($team->Captain());
695
		$this->assertEquals($captainID, $team->Captain()->ID);
696
697
		// Test for polymorphic has_one relations
698
		$fan = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
699
		$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...
700
		$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...
701
		$this->assertNotNull($fan->Favourite());
702
		$this->assertEquals($team->ID, $fan->Favourite()->ID);
703
		$this->assertInstanceOf($team->class, $fan->Favourite());
704
	}
705
706
	public function testFieldNamesThatMatchMethodNamesWork() {
707
		/* Check that a field name that corresponds to a method on DataObject will still work */
708
		$obj = new DataObjectTest_Fixture();
709
		$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...
710
		$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...
711
		$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...
712
		$obj->write();
713
714
		$this->assertNotNull($obj->ID);
715
		$this->assertEquals('value1',
716
			DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
717
		$this->assertEquals('value2',
718
			DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
719
		$this->assertEquals('value3',
720
			DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
721
	}
722
723
	/**
724
	 * @todo Re-enable all test cases for field existence after behaviour has been fixed
725
	 */
726
	public function testFieldExistence() {
727
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
728
		$teamSingleton = singleton('DataObjectTest_Team');
729
730
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
731
		$subteamSingleton = singleton('DataObjectTest_SubTeam');
0 ignored issues
show
Unused Code introduced by
$subteamSingleton is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
732
733
		/* hasField() singleton checks */
734
		$this->assertTrue($teamSingleton->hasField('ID'),
735
			'hasField() finds built-in fields in singletons');
736
		$this->assertTrue($teamSingleton->hasField('Title'),
737
			'hasField() finds custom fields in singletons');
738
739
		/* hasField() instance checks */
740
		$this->assertFalse($teamInstance->hasField('NonExistingField'),
741
			'hasField() doesnt find non-existing fields in instances');
742
		$this->assertTrue($teamInstance->hasField('ID'),
743
			'hasField() finds built-in fields in instances');
744
		$this->assertTrue($teamInstance->hasField('Created'),
745
			'hasField() finds built-in fields in instances');
746
		$this->assertTrue($teamInstance->hasField('DatabaseField'),
747
			'hasField() finds custom fields in instances');
748
		//$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'),
749
		//'hasField() doesnt find subclass fields in parentclass instances');
750
		$this->assertTrue($teamInstance->hasField('DynamicField'),
751
			'hasField() finds dynamic getters in instances');
752
		$this->assertTrue($teamInstance->hasField('HasOneRelationshipID'),
753
			'hasField() finds foreign keys in instances');
754
		$this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'),
755
			'hasField() finds extended fields in instances');
756
		$this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'),
757
			'hasField() finds extended foreign keys in instances');
758
		//$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'),
759
		//'hasField() includes extended dynamic getters in instances');
760
761
		/* hasField() subclass checks */
762
		$this->assertTrue($subteamInstance->hasField('ID'),
763
			'hasField() finds built-in fields in subclass instances');
764
		$this->assertTrue($subteamInstance->hasField('Created'),
765
			'hasField() finds built-in fields in subclass instances');
766
		$this->assertTrue($subteamInstance->hasField('DatabaseField'),
767
			'hasField() finds custom fields in subclass instances');
768
		$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'),
769
			'hasField() finds custom fields in subclass instances');
770
		$this->assertTrue($subteamInstance->hasField('DynamicField'),
771
			'hasField() finds dynamic getters in subclass instances');
772
		$this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'),
773
			'hasField() finds foreign keys in subclass instances');
774
		$this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'),
775
			'hasField() finds extended fields in subclass instances');
776
		$this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'),
777
			'hasField() finds extended foreign keys in subclass instances');
778
779
		/* hasDatabaseField() singleton checks */
780
		//$this->assertTrue($teamSingleton->hasDatabaseField('ID'),
781
		//'hasDatabaseField() finds built-in fields in singletons');
782
		$this->assertTrue($teamSingleton->hasDatabaseField('Title'),
783
			'hasDatabaseField() finds custom fields in singletons');
784
785
		/* hasDatabaseField() instance checks */
786
		$this->assertFalse($teamInstance->hasDatabaseField('NonExistingField'),
787
			'hasDatabaseField() doesnt find non-existing fields in instances');
788
		//$this->assertTrue($teamInstance->hasDatabaseField('ID'),
789
		//'hasDatabaseField() finds built-in fields in instances');
790
		$this->assertTrue($teamInstance->hasDatabaseField('Created'),
791
			'hasDatabaseField() finds built-in fields in instances');
792
		$this->assertTrue($teamInstance->hasDatabaseField('DatabaseField'),
793
			'hasDatabaseField() finds custom fields in instances');
794
		$this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'),
795
			'hasDatabaseField() doesnt find subclass fields in parentclass instances');
796
		//$this->assertFalse($teamInstance->hasDatabaseField('DynamicField'),
797
		//'hasDatabaseField() doesnt dynamic getters in instances');
798
		$this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'),
799
			'hasDatabaseField() finds foreign keys in instances');
800
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'),
801
			'hasDatabaseField() finds extended fields in instances');
802
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'),
803
			'hasDatabaseField() finds extended foreign keys in instances');
804
		$this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'),
805
			'hasDatabaseField() doesnt include extended dynamic getters in instances');
806
807
		/* hasDatabaseField() subclass checks */
808
		$this->assertTrue($subteamInstance->hasDatabaseField('DatabaseField'),
809
			'hasField() finds custom fields in subclass instances');
810
		$this->assertTrue($subteamInstance->hasDatabaseField('SubclassDatabaseField'),
811
			'hasField() finds custom fields in subclass instances');
812
813
	}
814
815
	/**
816
	 * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed
817
	 */
818
	public function testFieldInheritance() {
819
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
820
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
821
822
		$this->assertEquals(
823
			array_keys($teamInstance->inheritedDatabaseFields()),
824
			array(
825
				//'ID',
826
				//'ClassName',
827
				//'Created',
828
				//'LastEdited',
829
				'Title',
830
				'DatabaseField',
831
				'ExtendedDatabaseField',
832
				'CaptainID',
833
				'HasOneRelationshipID',
834
				'ExtendedHasOneRelationshipID'
835
			),
836
			'inheritedDatabaseFields() contains all fields defined on instance: base, extended and foreign keys'
837
		);
838
839
		$this->assertEquals(
840
			array_keys(DataObject::database_fields('DataObjectTest_Team', false)),
841
			array(
842
				//'ID',
843
				'ClassName',
844
				'LastEdited',
845
				'Created',
846
				'Title',
847
				'DatabaseField',
848
				'ExtendedDatabaseField',
849
				'CaptainID',
850
				'HasOneRelationshipID',
851
				'ExtendedHasOneRelationshipID'
852
			),
853
			'databaseFields() contains only fields defined on instance, including base, extended and foreign keys'
854
		);
855
856
		$this->assertEquals(
857
			array_keys($subteamInstance->inheritedDatabaseFields()),
858
			array(
859
				//'ID',
860
				//'ClassName',
861
				//'Created',
862
				//'LastEdited',
863
				'SubclassDatabaseField',
864
				'ParentTeamID',
865
				'Title',
866
				'DatabaseField',
867
				'ExtendedDatabaseField',
868
				'CaptainID',
869
				'HasOneRelationshipID',
870
				'ExtendedHasOneRelationshipID',
871
			),
872
			'inheritedDatabaseFields() on subclass contains all fields, including base, extended  and foreign keys'
873
		);
874
875
		$this->assertEquals(
876
			array_keys(DataObject::database_fields('DataObjectTest_SubTeam', false)),
877
			array(
878
				'SubclassDatabaseField',
879
				'ParentTeamID',
880
			),
881
			'databaseFields() on subclass contains only fields defined on instance'
882
		);
883
	}
884
885
	public function testSearchableFields() {
886
		$player = $this->objFromFixture('DataObjectTest_Player', 'captain1');
887
		$fields = $player->searchableFields();
888
		$this->assertArrayHasKey(
889
			'IsRetired',
890
			$fields,
891
			'Fields defined by $searchable_fields static are correctly detected'
892
		);
893
		$this->assertArrayHasKey(
894
			'ShirtNumber',
895
			$fields,
896
			'Fields defined by $searchable_fields static are correctly detected'
897
		);
898
899
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
900
		$fields = $team->searchableFields();
901
		$this->assertArrayHasKey(
902
			'Title',
903
			$fields,
904
			'Fields can be inherited from the $summary_fields static, including methods called on fields'
905
		);
906
		$this->assertArrayHasKey(
907
			'Captain.ShirtNumber',
908
			$fields,
909
			'Fields on related objects can be inherited from the $summary_fields static'
910
		);
911
		$this->assertArrayHasKey(
912
			'Captain.FavouriteTeam.Title',
913
			$fields,
914
			'Fields on related objects can be inherited from the $summary_fields static'
915
		);
916
917
		$testObj = new DataObjectTest_Fixture();
918
		$fields = $testObj->searchableFields();
919
		$this->assertEmpty($fields);
920
	}
921
922 View Code Duplication
	public function testCastingHelper() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
923
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
924
925
		$this->assertEquals('Varchar', $team->castingHelper('Title'), 'db field wasn\'t casted correctly');
926
		$this->assertEquals('HTMLVarchar', $team->castingHelper('DatabaseField'), 'db field wasn\'t casted correctly');
927
928
		$sponsor = $team->Sponsors()->first();
929
		$this->assertEquals('Int', $sponsor->castingHelper('SponsorFee'), 'many_many_extraFields not casted correctly');
930
	}
931
932
	public function testSummaryFieldsCustomLabels() {
933
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
934
		$summaryFields = $team->summaryFields();
935
936
		$this->assertEquals(
937
			'Custom Title',
938
			$summaryFields['Title'],
939
			'Custom title is preserved'
940
		);
941
942
		$this->assertEquals(
943
			'Captain\'s shirt number',
944
			$summaryFields['Captain.ShirtNumber'],
945
			'Custom title on relation is preserved'
946
		);
947
	}
948
949
	public function testDataObjectUpdate() {
950
		/* update() calls can use the dot syntax to reference has_one relations and other methods that return
951
		 * objects */
952
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
953
		$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...
954
955
		$team1->update(array(
956
			'DatabaseField' => 'Something',
957
			'Captain.FirstName' => 'Jim',
958
			'Captain.Email' => '[email protected]',
959
			'Captain.FavouriteTeam.Title' => 'New and improved team 1',
960
		));
961
962
		/* Test the simple case of updating fields on the object itself */
963
		$this->assertEquals('Something', $team1->DatabaseField);
964
965
		/* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in
966
		 * the database.  Although update() doesn't usually write, it does write related records automatically. */
967
		$captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1');
968
		$this->assertEquals('Jim', $captain1->FirstName);
969
		$this->assertEquals('[email protected]', $captain1->Email);
970
971
		/* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.
972
		 * FavouriteTeam.Title made */
973
		$reloadedTeam1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
974
		$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
975
	}
976
977
	public function testDataObjectUpdateNew() {
978
		/* update() calls can use the dot syntax to reference has_one relations and other methods that return
979
		 * objects */
980
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
981
		$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...
982
983
		$team1->update(array(
984
			'Captain.FirstName' => 'Jim',
985
			'Captain.FavouriteTeam.Title' => 'New and improved team 1',
986
		));
987
		/* Test that the captain ID has been updated */
988
		$this->assertGreaterThan(0, $team1->CaptainID);
989
990
		/* Fetch the newly created captain */
991
		$captain1 = DataObjectTest_Player::get()->byID($team1->CaptainID);
992
		$this->assertEquals('Jim', $captain1->FirstName);
993
994
		/* Grab the favourite team and make sure it has the correct values */
995
		$reloadedTeam1 = $captain1->FavouriteTeam();
996
		$this->assertEquals($reloadedTeam1->ID, $captain1->FavouriteTeamID);
997
		$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
998
	}
999
1000
	public function testWritingInvalidDataObjectThrowsException() {
1001
		$validatedObject = new DataObjectTest_ValidatedObject();
1002
1003
		$this->setExpectedException('ValidationException');
1004
		$validatedObject->write();
1005
	}
1006
1007
	public function testWritingValidDataObjectDoesntThrowException() {
1008
		$validatedObject = new DataObjectTest_ValidatedObject();
1009
		$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...
1010
1011
		$validatedObject->write();
1012
		$this->assertTrue($validatedObject->isInDB(), "Validated object was not saved to database");
1013
	}
1014
1015
	public function testSubclassCreation() {
1016
		/* Creating a new object of a subclass should set the ClassName field correctly */
1017
		$obj = new DataObjectTest_SubTeam();
1018
		$obj->write();
1019
		$this->assertEquals("DataObjectTest_SubTeam",
1020
			DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
1021
	}
1022
1023
	public function testForceInsert() {
1024
		/* If you set an ID on an object and pass forceInsert = true, then the object should be correctly created */
1025
		$conn = DB::get_conn();
1026
		if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', true);
1027
		$obj = new DataObjectTest_SubTeam();
1028
		$obj->ID = 1001;
1029
		$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...
1030
		$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...
1031
		$obj->write(false, true);
1032
		if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', false);
1033
1034
		$this->assertEquals("DataObjectTest_SubTeam",
1035
			DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
1036
1037
		/* Check that it actually saves to the database with the correct ID */
1038
		$this->assertEquals("1001", DB::query(
1039
			"SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'")->value());
1040
		$this->assertEquals("1001",
1041
			DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value());
1042
	}
1043
1044
	public function TestHasOwnTable() {
1045
		/* Test DataObject::has_own_table() returns true if the object has $has_one or $db values */
1046
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Player"));
1047
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Team"));
1048
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Fixture"));
1049
1050
		/* Root DataObject that always have a table, even if they lack both $db and $has_one */
1051
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_FieldlessTable"));
1052
1053
		/* Subclasses without $db or $has_one don't have a table */
1054
		$this->assertFalse(DataObject::has_own_table("DataObjectTest_FieldlessSubTable"));
1055
1056
		/* Return false if you don't pass it a subclass of DataObject */
1057
		$this->assertFalse(DataObject::has_own_table("DataObject"));
1058
		$this->assertFalse(DataObject::has_own_table("ViewableData"));
1059
		$this->assertFalse(DataObject::has_own_table("ThisIsntADataObject"));
1060
	}
1061
1062
	public function testMerge() {
1063
		// test right merge of subclasses
1064
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1065
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1066
		$leftOrigID = $left->ID;
1067
		$left->merge($right, 'right', false, false);
1068
		$this->assertEquals(
1069
			$left->Title,
1070
			'Subteam 2',
1071
			'merge() with "right" priority overwrites fields with existing values on subclasses'
1072
		);
1073
		$this->assertEquals(
1074
			$left->ID,
1075
			$leftOrigID,
1076
			'merge() with "right" priority doesnt overwrite database ID'
1077
		);
1078
1079
		// test overwriteWithEmpty flag on existing left values
1080
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1081
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam3_with_empty_fields');
1082
		$left->merge($right, 'right', false, true);
1083
		$this->assertEquals(
1084
			$left->Title,
1085
			'Subteam 3',
1086
			'merge() with $overwriteWithEmpty overwrites non-empty fields on left object'
1087
		);
1088
1089
		// test overwriteWithEmpty flag on empty left values
1090
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1091
		// $SubclassDatabaseField is empty on here
1092
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1093
		$left->merge($right, 'right', false, true);
1094
		$this->assertEquals(
1095
			$left->SubclassDatabaseField,
1096
			NULL,
1097
			'merge() with $overwriteWithEmpty overwrites empty fields on left object'
1098
		);
1099
1100
		// @todo test "left" priority flag
1101
		// @todo test includeRelations flag
1102
		// @todo test includeRelations in combination with overwriteWithEmpty
1103
		// @todo test has_one relations
1104
		// @todo test has_many and many_many relations
1105
	}
1106
1107
	public function testPopulateDefaults() {
1108
		$obj = new DataObjectTest_Fixture();
1109
		$this->assertEquals(
1110
			$obj->MyFieldWithDefault,
1111
			'Default Value',
1112
			'Defaults are populated for in-memory object from $defaults array'
1113
		);
1114
1115
		$this->assertEquals(
1116
			$obj->MyFieldWithAltDefault,
1117
			'Default Value',
1118
			'Defaults are populated from overloaded populateDefaults() method'
1119
		);
1120
	}
1121
1122
	protected function makeAccessible($object, $method) {
1123
		$reflectionMethod = new ReflectionMethod($object, $method);
1124
		$reflectionMethod->setAccessible(true);
1125
		return $reflectionMethod;
1126
	}
1127
1128 View Code Duplication
	public function testValidateModelDefinitionsFailsWithArray() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1129
		Config::nest();
1130
1131
		$object = new DataObjectTest_Team;
1132
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1133
1134
		Config::inst()->update('DataObjectTest_Team', 'has_one', array('NotValid' => array('NoArraysAllowed')));
1135
		$this->setExpectedException('LogicException');
1136
1137
		try {
1138
			$method->invoke($object);
1139
		} catch(Exception $e) {
1140
			Config::unnest(); // Catch the exception so we can unnest config before failing the test
1141
			throw $e;
1142
		}
1143
	}
1144
1145 View Code Duplication
	public function testValidateModelDefinitionsFailsWithIntKey() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1146
		Config::nest();
1147
1148
		$object = new DataObjectTest_Team;
1149
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1150
1151
		Config::inst()->update('DataObjectTest_Team', 'has_many', array(12 => 'DataObjectTest_Player'));
1152
		$this->setExpectedException('LogicException');
1153
1154
		try {
1155
			$method->invoke($object);
1156
		} catch(Exception $e) {
1157
			Config::unnest(); // Catch the exception so we can unnest config before failing the test
1158
			throw $e;
1159
		}
1160
	}
1161
1162 View Code Duplication
	public function testValidateModelDefinitionsFailsWithIntValue() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1163
		Config::nest();
1164
1165
		$object = new DataObjectTest_Team;
1166
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1167
1168
		Config::inst()->update('DataObjectTest_Team', 'many_many', array('Players' => 12));
1169
		$this->setExpectedException('LogicException');
1170
1171
		try {
1172
			$method->invoke($object);
1173
		} catch(Exception $e) {
1174
			Config::unnest(); // Catch the exception so we can unnest config before failing the test
1175
			throw $e;
1176
		}
1177
	}
1178
1179
	/**
1180
	 * many_many_extraFields is allowed to have an array value, so shouldn't throw an exception
1181
	 */
1182 View Code Duplication
	public function testValidateModelDefinitionsPassesWithExtraFields() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1183
		Config::nest();
1184
1185
		$object = new DataObjectTest_Team;
1186
		$method = $this->makeAccessible($object, 'validateModelDefinitions');
1187
1188
		Config::inst()->update('DataObjectTest_Team', 'many_many_extraFields',
1189
			array('Relations' => array('Price' => 'Int')));
1190
1191
		try {
1192
			$method->invoke($object);
1193
		} catch(Exception $e) {
1194
			Config::unnest();
1195
			$this->fail('Exception should not be thrown');
1196
			throw $e;
1197
		}
1198
1199
		Config::unnest();
1200
	}
1201
1202
	public function testNewClassInstance() {
1203
		$dataObject = $this->objFromFixture('DataObjectTest_Team', 'team1');
1204
		$changedDO = $dataObject->newClassInstance('DataObjectTest_SubTeam');
1205
		$changedFields = $changedDO->getChangedFields();
1206
1207
		// Don't write the record, it will reset changed fields
1208
		$this->assertInstanceOf('DataObjectTest_SubTeam', $changedDO);
1209
		$this->assertEquals($changedDO->ClassName, 'DataObjectTest_SubTeam');
1210
		$this->assertContains('ClassName', array_keys($changedFields));
1211
		$this->assertEquals($changedFields['ClassName']['before'], 'DataObjectTest_Team');
1212
		$this->assertEquals($changedFields['ClassName']['after'], 'DataObjectTest_SubTeam');
1213
1214
		$changedDO->write();
1215
1216
		$this->assertInstanceOf('DataObjectTest_SubTeam', $changedDO);
1217
		$this->assertEquals($changedDO->ClassName, 'DataObjectTest_SubTeam');
1218
	}
1219
1220
	public function testMultipleManyManyWithSameClass() {
1221
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
1222
		$sponsors = $team->Sponsors();
1223
		$equipmentSuppliers = $team->EquipmentSuppliers();
1224
1225
		// Check that DataObject::many_many() works as expected
1226
		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...
1227
		$this->assertEquals('DataObjectTest_Team', $class,
1228
			'DataObject::many_many() didn\'t find the correct base class');
1229
		$this->assertEquals('DataObjectTest_EquipmentCompany', $targetClass,
1230
			'DataObject::many_many() didn\'t find the correct target class for the relation');
1231
		$this->assertEquals('DataObjectTest_EquipmentCompany_SponsoredTeams', $joinTable,
1232
			'DataObject::many_many() didn\'t find the correct relation table');
1233
1234
		// Check that ManyManyList still works
1235
		$this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation');
1236
		$this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation');
1237
1238
		// Check everything works when no relation is present
1239
		$teamWithoutSponsor = $this->objFromFixture('DataObjectTest_Team', 'team3');
1240
		$this->assertInstanceOf('ManyManyList', $teamWithoutSponsor->Sponsors());
1241
		$this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count());
1242
1243
		// Check many_many_extraFields still works
1244
		$equipmentCompany = $this->objFromFixture('DataObjectTest_EquipmentCompany', 'equipmentcompany1');
1245
		$equipmentCompany->SponsoredTeams()->add($teamWithoutSponsor, array('SponsorFee' => 1000));
1246
		$sponsoredTeams = $equipmentCompany->SponsoredTeams();
1247
		$this->assertEquals(1000, $sponsoredTeams->byID($teamWithoutSponsor->ID)->SponsorFee,
1248
			'Data from many_many_extraFields was not stored/extracted correctly');
1249
1250
		// Check subclasses correctly inherit multiple many_manys
1251
		$subTeam = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1252
		$this->assertEquals(2, $subTeam->Sponsors()->count(),
1253
			'Child class did not inherit multiple many_manys');
1254
		$this->assertEquals(1, $subTeam->EquipmentSuppliers()->count(),
1255
			'Child class did not inherit multiple many_manys');
1256
		// Team 2 has one EquipmentCompany sponsor and one SubEquipmentCompany
1257
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
1258
		$this->assertEquals(2, $team2->Sponsors()->count(),
1259
			'Child class did not inherit multiple belongs_many_manys');
1260
1261
		// Check many_many_extraFields also works from the belongs_many_many side
1262
		$sponsors = $team2->Sponsors();
1263
		$sponsors->add($equipmentCompany, array('SponsorFee' => 750));
1264
		$this->assertEquals(750, $sponsors->byID($equipmentCompany->ID)->SponsorFee,
1265
			'Data from many_many_extraFields was not stored/extracted correctly');
1266
1267
		$subEquipmentCompany = $this->objFromFixture('DataObjectTest_SubEquipmentCompany', 'subequipmentcompany1');
1268
		$subTeam->Sponsors()->add($subEquipmentCompany, array('SponsorFee' => 1200));
1269
		$this->assertEquals(1200, $subTeam->Sponsors()->byID($subEquipmentCompany->ID)->SponsorFee,
1270
			'Data from inherited many_many_extraFields was not stored/extracted correctly');
1271
	}
1272
1273
	public function testManyManyExtraFields() {
1274
		$player = $this->objFromFixture('DataObjectTest_Player', 'player1');
1275
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
1276
1277
		// Get all extra fields
1278
		$teamExtraFields = $team->manyManyExtraFields();
1279
		$this->assertEquals(array(
1280
			'Players' => array('Position' => 'Varchar(100)')
1281
		), $teamExtraFields);
1282
1283
		// Ensure fields from parent classes are included
1284
		$subTeam = singleton('DataObjectTest_SubTeam');
1285
		$teamExtraFields = $subTeam->manyManyExtraFields();
1286
		$this->assertEquals(array(
1287
			'Players' => array('Position' => 'Varchar(100)'),
1288
			'FormerPlayers' => array('Position' => 'Varchar(100)')
1289
		), $teamExtraFields);
1290
1291
		// Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
1292
		$teamExtraFields = $team->manyManyExtraFieldsForComponent('Players');
1293
		$this->assertEquals($teamExtraFields, array(
1294
			'Position' => 'Varchar(100)'
1295
		));
1296
1297
		// We'll have to go through the relation to get the extra fields on Player
1298
		$playerExtraFields = $player->manyManyExtraFieldsForComponent('Teams');
1299
		$this->assertEquals($playerExtraFields, array(
1300
			'Position' => 'Varchar(100)'
1301
		));
1302
1303
		// Iterate through a many-many relationship and confirm that extra fields are included
1304
		$newTeam = new DataObjectTest_Team();
1305
		$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...
1306
		$newTeam->write();
1307
		$newTeamID = $newTeam->ID;
1308
1309
		$newPlayer = new DataObjectTest_Player();
1310
		$newPlayer->FirstName = "Sam";
1311
		$newPlayer->Surname = "Minnee";
1312
		$newPlayer->write();
1313
1314
		// The idea of Sam as a prop is essentially humourous.
1315
		$newTeam->Players()->add($newPlayer, array("Position" => "Prop"));
1316
1317
		// Requery and uncache everything
1318
		$newTeam->flushCache();
1319
		$newTeam = DataObject::get_by_id('DataObjectTest_Team', $newTeamID);
1320
1321
		// Check that the Position many_many_extraField is extracted.
1322
		$player = $newTeam->Players()->First();
1323
		$this->assertEquals('Sam', $player->FirstName);
1324
		$this->assertEquals("Prop", $player->Position);
1325
1326
		// Check that ordering a many-many relation by an aggregate column doesn't fail
1327
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1328
		$player->Teams()->sort("count(DISTINCT \"DataObjectTest_Team_Players\".\"DataObjectTest_PlayerID\") DESC");
1329
	}
1330
1331
	/**
1332
	 * Check that the queries generated for many-many relation queries can have unlimitedRowCount
1333
	 * called on them.
1334
	 */
1335
	public function testManyManyUnlimitedRowCount() {
1336
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1337
		// TODO: What's going on here?
1338
		$this->assertEquals(2, $player->Teams()->dataQuery()->query()->unlimitedRowCount());
1339
	}
1340
1341
	/**
1342
	 * Tests that singular_name() generates sensible defaults.
1343
	 */
1344
	public function testSingularName() {
1345
		$assertions = array(
1346
			'DataObjectTest_Player'       => 'Data Object Test Player',
1347
			'DataObjectTest_Team'         => 'Data Object Test Team',
1348
			'DataObjectTest_Fixture'      => 'Data Object Test Fixture'
1349
		);
1350
1351
		foreach($assertions as $class => $expectedSingularName) {
1352
			$this->assertEquals(
1353
				$expectedSingularName,
1354
				singleton($class)->singular_name(),
1355
				"Assert that the singular_name for '$class' is correct."
1356
			);
1357
		}
1358
	}
1359
1360
	/**
1361
	 * Tests that plural_name() generates sensible defaults.
1362
	 */
1363
	public function testPluralName() {
1364
		$assertions = array(
1365
			'DataObjectTest_Player'       => 'Data Object Test Players',
1366
			'DataObjectTest_Team'         => 'Data Object Test Teams',
1367
			'DataObjectTest_Fixture'      => 'Data Object Test Fixtures',
1368
			'DataObjectTest_Play'         => 'Data Object Test Plays',
1369
			'DataObjectTest_Bogey'        => 'Data Object Test Bogeys',
1370
			'DataObjectTest_Ploy'         => 'Data Object Test Ploys',
1371
		);
1372
1373
		foreach($assertions as $class => $expectedPluralName) {
1374
			$this->assertEquals(
1375
				$expectedPluralName,
1376
				singleton($class)->plural_name(),
1377
				"Assert that the plural_name for '$class' is correct."
1378
			);
1379
		}
1380
	}
1381
1382
	public function testHasDatabaseField() {
1383
		$team = singleton('DataObjectTest_Team');
1384
		$subteam = singleton('DataObjectTest_SubTeam');
1385
1386
		$this->assertTrue(
1387
			$team->hasDatabaseField('Title'),
1388
			"hasOwnDatabaseField() works with \$db fields"
1389
		);
1390
		$this->assertTrue(
1391
			$team->hasDatabaseField('CaptainID'),
1392
			"hasOwnDatabaseField() works with \$has_one fields"
1393
		);
1394
		$this->assertFalse(
1395
			$team->hasDatabaseField('NonExistentField'),
1396
			"hasOwnDatabaseField() doesn't detect non-existend fields"
1397
		);
1398
		$this->assertTrue(
1399
			$team->hasDatabaseField('ExtendedDatabaseField'),
1400
			"hasOwnDatabaseField() works with extended fields"
1401
		);
1402
		$this->assertFalse(
1403
			$team->hasDatabaseField('SubclassDatabaseField'),
1404
			"hasOwnDatabaseField() doesn't pick up fields in subclasses on parent class"
1405
		);
1406
1407
		$this->assertTrue(
1408
			$subteam->hasDatabaseField('SubclassDatabaseField'),
1409
			"hasOwnDatabaseField() picks up fields in subclasses"
1410
		);
1411
1412
	}
1413
1414
	public function testFieldTypes() {
1415
		$obj = new DataObjectTest_Fixture();
1416
		$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...
1417
		$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...
1418
		$obj->write();
1419
		$obj->flushCache();
1420
1421
		$obj = DataObject::get_by_id('DataObjectTest_Fixture', $obj->ID);
1422
		$this->assertEquals('1988-01-02', $obj->DateField);
1423
		$this->assertEquals('1988-03-04 06:30:00', $obj->DatetimeField);
1424
	}
1425
1426
	public function testTwoSubclassesWithTheSameFieldNameWork() {
1427
		// Create two objects of different subclasses, setting the values of fields that are
1428
		// defined separately in each subclass
1429
		$obj1 = new DataObjectTest_SubTeam();
1430
		$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...
1431
		$obj2 = new OtherSubclassWithSameField();
1432
		$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...
1433
1434
		// Write them to the database
1435
		$obj1->write();
1436
		$obj2->write();
1437
1438
		// Check that the values of those fields are properly read from the database
1439
		$values = DataObject::get("DataObjectTest_Team", "\"DataObjectTest_Team\".\"ID\" IN
1440
			($obj1->ID, $obj2->ID)")->column("SubclassDatabaseField");
1441
		$this->assertEquals(array_intersect($values, array('obj1', 'obj2')), $values);
1442
	}
1443
1444
	public function testClassNameSetForNewObjects() {
1445
		$d = new DataObjectTest_Player();
1446
		$this->assertEquals('DataObjectTest_Player', $d->ClassName);
1447
	}
1448
1449
	public function testHasValue() {
1450
		$team = new DataObjectTest_Team();
1451
		$this->assertFalse($team->hasValue('Title', null, false));
1452
		$this->assertFalse($team->hasValue('DatabaseField', null, false));
1453
1454
		$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...
1455
		$this->assertTrue($team->hasValue('Title', null, false));
1456
		$this->assertFalse($team->hasValue('DatabaseField', null, false));
1457
1458
		$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...
1459
		$this->assertTrue($team->hasValue('Title', null, false));
1460
		$this->assertFalse (
1461
			$team->hasValue('DatabaseField', null, false),
1462
			'Test that a blank paragraph on a HTML field is not a valid value.'
1463
		);
1464
1465
		$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...
1466
		$this->assertTrue (
1467
			$team->hasValue('Title', null, false),
1468
			'Test that an empty paragraph is a value for non-HTML fields.'
1469
		);
1470
1471
		$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...
1472
		$this->assertTrue($team->hasValue('Title', null, false));
1473
		$this->assertTrue($team->hasValue('DatabaseField', null, false));
1474
	}
1475
1476
	public function testHasMany() {
1477
		$company = new DataObjectTest_Company();
1478
1479
		$this->assertEquals (
1480
			array (
1481
				'CurrentStaff'     => 'DataObjectTest_Staff',
1482
				'PreviousStaff'    => 'DataObjectTest_Staff'
1483
			),
1484
			$company->hasMany(),
1485
			'has_many strips field name data by default.'
1486
		);
1487
1488
		$this->assertEquals (
1489
			'DataObjectTest_Staff',
1490
			$company->hasManyComponent('CurrentStaff'),
1491
			'has_many strips field name data by default on single relationships.'
1492
		);
1493
1494
		$this->assertEquals (
1495
			array (
1496
				'CurrentStaff'     => 'DataObjectTest_Staff.CurrentCompany',
1497
				'PreviousStaff'    => 'DataObjectTest_Staff.PreviousCompany'
1498
			),
1499
			$company->hasMany(null, false),
1500
			'has_many returns field name data when $classOnly is false.'
1501
		);
1502
1503
		$this->assertEquals (
1504
			'DataObjectTest_Staff.CurrentCompany',
1505
			$company->hasManyComponent('CurrentStaff', false),
1506
			'has_many returns field name data on single records when $classOnly is false.'
1507
		);
1508
	}
1509
1510
	public function testGetRemoteJoinField() {
1511
		$company = new DataObjectTest_Company();
1512
1513
		$staffJoinField = $company->getRemoteJoinField('CurrentStaff', 'has_many', $polymorphic);
1514
		$this->assertEquals('CurrentCompanyID', $staffJoinField);
1515
		$this->assertFalse($polymorphic, 'DataObjectTest_Company->CurrentStaff is not polymorphic');
1516
		$previousStaffJoinField = $company->getRemoteJoinField('PreviousStaff', 'has_many', $polymorphic);
1517
		$this->assertEquals('PreviousCompanyID', $previousStaffJoinField);
1518
		$this->assertFalse($polymorphic, 'DataObjectTest_Company->PreviousStaff is not polymorphic');
1519
1520
		$ceo = new DataObjectTest_CEO();
1521
1522
		$this->assertEquals('CEOID', $ceo->getRemoteJoinField('Company', 'belongs_to', $polymorphic));
1523
		$this->assertFalse($polymorphic, 'DataObjectTest_CEO->Company is not polymorphic');
1524
		$this->assertEquals('PreviousCEOID', $ceo->getRemoteJoinField('PreviousCompany', 'belongs_to', $polymorphic));
1525
		$this->assertFalse($polymorphic, 'DataObjectTest_CEO->PreviousCompany is not polymorphic');
1526
1527
		$team = new DataObjectTest_Team();
1528
1529
		$this->assertEquals('Favourite', $team->getRemoteJoinField('Fans', 'has_many', $polymorphic));
1530
		$this->assertTrue($polymorphic, 'DataObjectTest_Team->Fans is polymorphic');
1531
		$this->assertEquals('TeamID', $team->getRemoteJoinField('Comments', 'has_many', $polymorphic));
1532
		$this->assertFalse($polymorphic, 'DataObjectTest_Team->Comments is not polymorphic');
1533
	}
1534
1535
	public function testBelongsTo() {
1536
		$company = new DataObjectTest_Company();
1537
		$ceo     = new DataObjectTest_CEO();
1538
1539
		$company->write();
1540
		$ceo->write();
1541
1542
		// Test belongs_to assignment
1543
		$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...
1544
		$company->write();
1545
1546
		$this->assertEquals($company->ID, $ceo->Company()->ID, 'belongs_to returns the right results.');
1547
1548
		// Test automatic creation of class where no assigment exists
1549
		$ceo = new DataObjectTest_CEO();
1550
		$ceo->write();
1551
1552
		$this->assertTrue (
1553
			$ceo->Company() instanceof DataObjectTest_Company,
1554
			'DataObjects across belongs_to relations are automatically created.'
1555
		);
1556
		$this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
1557
1558
		// Write object with components
1559
		$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...
1560
		$ceo->write(false, false, false, true);
1561
		$this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
1562
1563
		$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
1564
		$this->assertEquals (
1565
			$ceo->Company()->ID, $newCEO->Company()->ID, 'belongs_to can be retrieved from the database.'
1566
		);
1567
	}
1568
1569
	public function testBelongsToPolymorphic() {
1570
		$company = new DataObjectTest_Company();
1571
		$ceo     = new DataObjectTest_CEO();
1572
1573
		$company->write();
1574
		$ceo->write();
1575
1576
		// Test belongs_to assignment
1577
		$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...
1578
		$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...
1579
		$company->write();
1580
1581
		$this->assertEquals($company->ID, $ceo->CompanyOwned()->ID, 'belongs_to returns the right results.');
1582
		$this->assertEquals($company->class, $ceo->CompanyOwned()->class, 'belongs_to returns the right results.');
1583
1584
		// Test automatic creation of class where no assigment exists
1585
		$ceo = new DataObjectTest_CEO();
1586
		$ceo->write();
1587
1588
		$this->assertTrue (
1589
			$ceo->CompanyOwned() instanceof DataObjectTest_Company,
1590
			'DataObjects across polymorphic belongs_to relations are automatically created.'
1591
		);
1592
		$this->assertEquals($ceo->ID, $ceo->CompanyOwned()->OwnerID, 'Remote IDs are automatically set.');
1593
		$this->assertInstanceOf($ceo->CompanyOwned()->OwnerClass, $ceo, 'Remote class is automatically  set');
1594
1595
		// Write object with components
1596
		$ceo->write(false, false, false, true);
1597
		$this->assertTrue($ceo->CompanyOwned()->isInDB(), 'write() writes belongs_to components to the database.');
1598
1599
		$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
1600
		$this->assertEquals (
1601
			$ceo->CompanyOwned()->ID,
1602
			$newCEO->CompanyOwned()->ID,
1603
			'polymorphic belongs_to can be retrieved from the database.'
1604
		);
1605
	}
1606
1607
	/**
1608
	 * @expectedException LogicException
1609
	 */
1610
	public function testInvalidate() {
1611
		$do = new DataObjectTest_Fixture();
1612
		$do->write();
1613
1614
		$do->delete();
1615
1616
		$do->delete(); // Prohibit invalid object manipulation
1617
		$do->write();
1618
		$do->duplicate();
1619
	}
1620
1621
	public function testToMap() {
1622
		$obj = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1623
1624
		$map = $obj->toMap();
1625
1626
		$this->assertArrayHasKey('ID', $map, 'Contains base fields');
1627
		$this->assertArrayHasKey('Title', $map, 'Contains fields from parent class');
1628
		$this->assertArrayHasKey('SubclassDatabaseField', $map, 'Contains fields from concrete class');
1629
1630
		$this->assertEquals($obj->ID, $map['ID'],
1631
			'Contains values from base fields');
1632
		$this->assertEquals($obj->Title, $map['Title'],
1633
			'Contains values from parent class fields');
1634
		$this->assertEquals($obj->SubclassDatabaseField, $map['SubclassDatabaseField'],
1635
			'Contains values from concrete class fields');
1636
1637
		$newObj = new DataObjectTest_SubTeam();
0 ignored issues
show
Unused Code introduced by
$newObj is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
1638
		$this->assertArrayHasKey('Title', $map, 'Contains null fields');
1639
	}
1640
1641
	public function testIsEmpty() {
1642
		$objEmpty = new DataObjectTest_Team();
1643
		$this->assertTrue($objEmpty->isEmpty(), 'New instance without populated defaults is empty');
1644
1645
		$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...
1646
		$this->assertFalse($objEmpty->isEmpty(), 'Zero value in attribute considered non-empty');
1647
	}
1648
1649
	public function testRelField() {
1650
		$captain = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1651
		// Test traversal of a single has_one
1652
		$this->assertEquals("Team 1", $captain->relField('FavouriteTeam.Title'));
1653
		// Test direct field access
1654
		$this->assertEquals("Captain", $captain->relField('FirstName'));
1655
1656
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1657
		// Test that we can traverse more than once, and that arbitrary methods are okay
1658
		$this->assertEquals("Team 1", $player->relField('Teams.First.Title'));
1659
1660
		$newPlayer = new DataObjectTest_Player();
1661
		$this->assertNull($newPlayer->relField('Teams.First.Title'));
1662
1663
		// Test that relField works on db field manipulations
1664
		$comment = $this->objFromFixture('DataObjectTest_TeamComment', 'comment3');
1665
		$this->assertEquals("PHIL IS A UNIQUE GUY, AND COMMENTS ON TEAM2" , $comment->relField('Comment.UpperCase'));
1666
	}
1667
1668
	public function testRelObject() {
1669
		$captain = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1670
1671
		// Test traversal of a single has_one
1672
		$this->assertInstanceOf("Varchar", $captain->relObject('FavouriteTeam.Title'));
1673
		$this->assertEquals("Team 1", $captain->relObject('FavouriteTeam.Title')->getValue());
1674
1675
		// Test direct field access
1676
		$this->assertInstanceOf("Boolean", $captain->relObject('IsRetired'));
1677
		$this->assertEquals(1, $captain->relObject('IsRetired')->getValue());
1678
1679
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1680
		// Test that we can traverse more than once, and that arbitrary methods are okay
1681
		$this->assertInstanceOf("Varchar", $player->relObject('Teams.First.Title'));
1682
		$this->assertEquals("Team 1", $player->relObject('Teams.First.Title')->getValue());
1683
	}
1684
1685
	public function testLateStaticBindingStyle() {
1686
		// Confirm that DataObjectTest_Player::get() operates as excepted
1687
		$this->assertEquals(4, DataObjectTest_Player::get()->Count());
1688
		$this->assertInstanceOf('DataObjectTest_Player', DataObjectTest_Player::get()->First());
1689
1690
		// You can't pass arguments to LSB syntax - use the DataList methods instead.
1691
		$this->setExpectedException('InvalidArgumentException');
1692
		DataObjectTest_Player::get(null, "\"ID\" = 1");
1693
1694
	}
1695
1696
	public function testBrokenLateStaticBindingStyle() {
1697
		// If you call DataObject::get() you have to pass a first argument
1698
		$this->setExpectedException('InvalidArgumentException');
1699
		DataObject::get();
1700
1701
	}
1702
1703
}
1704
1705
class DataObjectTest_Player extends Member implements TestOnly {
1706
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1707
		'IsRetired' => 'Boolean',
1708
		'ShirtNumber' => 'Varchar',
1709
	);
1710
1711
	private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1712
		'FavouriteTeam' => 'DataObjectTest_Team',
1713
	);
1714
1715
	private static $belongs_many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1716
		'Teams' => 'DataObjectTest_Team'
1717
	);
1718
1719
	private static $has_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1720
		'Fans' => 'DataObjectTest_Fan.Favourite' // Polymorphic - Player fans
1721
	);
1722
1723
	private static $belongs_to = array (
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1724
		'CompanyOwned'    => 'DataObjectTest_Company.Owner'
1725
	);
1726
1727
	private static $searchable_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1728
		'IsRetired',
1729
		'ShirtNumber'
1730
	);
1731
}
1732
1733
class DataObjectTest_Team extends DataObject implements TestOnly {
1734
1735
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1736
		'Title' => 'Varchar',
1737
		'DatabaseField' => 'HTMLVarchar'
1738
	);
1739
1740
	private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1741
		"Captain" => 'DataObjectTest_Player',
1742
		'HasOneRelationship' => 'DataObjectTest_Player',
1743
	);
1744
1745
	private static $has_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1746
		'SubTeams' => 'DataObjectTest_SubTeam',
1747
		'Comments' => 'DataObjectTest_TeamComment',
1748
		'Fans' => 'DataObjectTest_Fan.Favourite' // Polymorphic - Team fans
1749
	);
1750
1751
	private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1752
		'Players' => 'DataObjectTest_Player'
1753
	);
1754
1755
	private static $many_many_extraFields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1756
		'Players' => array(
1757
			'Position' => 'Varchar(100)'
1758
		)
1759
	);
1760
1761
	private static $belongs_many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1762
		'Sponsors' => 'DataObjectTest_EquipmentCompany.SponsoredTeams',
1763
		'EquipmentSuppliers' => 'DataObjectTest_EquipmentCompany.EquipmentCustomers'
1764
	);
1765
1766
	private static $summary_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1767
		'Title' => 'Custom Title',
1768
		'Title.UpperCase' => 'Title',
1769
		'Captain.ShirtNumber' => 'Captain\'s shirt number',
1770
		'Captain.FavouriteTeam.Title' => 'Captain\'s favourite team'
1771
	);
1772
1773
	private static $default_sort = '"Title"';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1774
1775
	public function MyTitle() {
1776
		return 'Team ' . $this->Title;
1777
	}
1778
1779
	public function getDynamicField() {
1780
		return 'dynamicfield';
1781
	}
1782
1783
}
1784
1785
class DataObjectTest_Fixture extends DataObject implements TestOnly {
1786
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1787
		// Funny field names
1788
		'Data' => 'Varchar',
1789
		'Duplicate' => 'Varchar',
1790
		'DbObject' => 'Varchar',
1791
1792
		// Field types
1793
		'DateField' => 'Date',
1794
		'DatetimeField' => 'Datetime',
1795
1796
		'MyFieldWithDefault' => 'Varchar',
1797
		'MyFieldWithAltDefault' => 'Varchar'
1798
	);
1799
1800
	private static $defaults = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1801
		'MyFieldWithDefault' => 'Default Value',
1802
	);
1803
1804
	private static $summary_fields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1805
		'Data' => 'Data',
1806
		'DateField.Nice' => 'Date'
1807
	);
1808
1809
	private static $searchable_fields = array();
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1810
1811
	public function populateDefaults() {
1812
		parent::populateDefaults();
1813
1814
		$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...
1815
	}
1816
1817
}
1818
1819
class DataObjectTest_SubTeam extends DataObjectTest_Team implements TestOnly {
1820
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1821
		'SubclassDatabaseField' => 'Varchar'
1822
	);
1823
1824
	private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1825
		"ParentTeam" => 'DataObjectTest_Team',
1826
	);
1827
1828
	private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1829
		'FormerPlayers' => 'DataObjectTest_Player'
1830
	);
1831
1832
	private static $many_many_extraFields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1833
		'FormerPlayers' => array(
1834
			'Position' => 'Varchar(100)'
1835
		)
1836
	);
1837
}
1838
class OtherSubclassWithSameField extends DataObjectTest_Team implements TestOnly {
1839
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1840
		'SubclassDatabaseField' => 'Varchar',
1841
	);
1842
}
1843
1844
1845
class DataObjectTest_FieldlessTable extends DataObject implements TestOnly {
1846
}
1847
1848
class DataObjectTest_FieldlessSubTable extends DataObjectTest_Team implements TestOnly {
1849
}
1850
1851
1852
class DataObjectTest_Team_Extension extends DataExtension implements TestOnly {
1853
1854
	private static $db = array(
1855
		'ExtendedDatabaseField' => 'Varchar'
1856
	);
1857
1858
	private static $has_one = array(
1859
		'ExtendedHasOneRelationship' => 'DataObjectTest_Player'
1860
	);
1861
1862
	public function getExtendedDynamicField() {
1863
		return "extended dynamic field";
1864
	}
1865
1866
}
1867
1868
class DataObjectTest_ValidatedObject extends DataObject implements TestOnly {
1869
1870
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1871
		'Name' => 'Varchar(50)'
1872
	);
1873
1874
	protected function validate() {
1875
		if(!empty($this->Name)) {
1876
			return new ValidationResult();
1877
		} else {
1878
			return new ValidationResult(false, "This object needs a name. Otherwise it will have an identity crisis!");
1879
		}
1880
	}
1881
}
1882
1883
class DataObjectTest_Company extends DataObject implements TestOnly {
1884
1885
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1886
		'Name' => 'Varchar'
1887
	);
1888
1889
	private static $has_one = array (
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1890
		'CEO'         => 'DataObjectTest_CEO',
1891
		'PreviousCEO' => 'DataObjectTest_CEO',
1892
		'Owner'       => 'DataObject' // polymorphic
1893
	);
1894
1895
	private static $has_many = array (
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1896
		'CurrentStaff'     => 'DataObjectTest_Staff.CurrentCompany',
1897
		'PreviousStaff'    => 'DataObjectTest_Staff.PreviousCompany'
1898
	);
1899
}
1900
1901
class DataObjectTest_EquipmentCompany extends DataObjectTest_Company implements TestOnly {
1902
	private static $many_many = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1903
		'SponsoredTeams' => 'DataObjectTest_Team',
1904
		'EquipmentCustomers' => 'DataObjectTest_Team'
1905
	);
1906
1907
	private static $many_many_extraFields = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1908
		'SponsoredTeams' => array(
1909
			'SponsorFee' => 'Int'
1910
		)
1911
	);
1912
}
1913
1914
class DataObjectTest_SubEquipmentCompany extends DataObjectTest_EquipmentCompany implements TestOnly {
1915
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1916
		'SubclassDatabaseField' => 'Varchar'
1917
	);
1918
}
1919
1920
class DataObjectTest_Staff extends DataObject implements TestOnly {
1921
	private static $has_one = array (
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1922
		'CurrentCompany'  => 'DataObjectTest_Company',
1923
		'PreviousCompany' => 'DataObjectTest_Company'
1924
	);
1925
}
1926
1927
class DataObjectTest_CEO extends DataObjectTest_Staff {
1928
	private static $belongs_to = array (
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1929
		'Company'         => 'DataObjectTest_Company.CEO',
1930
		'PreviousCompany' => 'DataObjectTest_Company.PreviousCEO',
1931
		'CompanyOwned'    => 'DataObjectTest_Company.Owner'
1932
	);
1933
}
1934
1935
class DataObjectTest_TeamComment extends DataObject implements TestOnly {
1936
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1937
		'Name' => 'Varchar',
1938
		'Comment' => 'Text'
1939
	);
1940
1941
	private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1942
		'Team' => 'DataObjectTest_Team'
1943
	);
1944
1945
	private static $default_sort = '"Name" ASC';
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1946
}
1947
1948
class DataObjectTest_Fan extends DataObject implements TestOnly {
1949
1950
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1951
		'Name' => 'Varchar(255)'
1952
	);
1953
1954
	private static $has_one = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1955
		'Favourite' => 'DataObject', // Polymorphic relation
1956
		'SecondFavourite' => 'DataObject'
1957
	);
1958
}
1959
1960
class DataObjectTest_ExtendedTeamComment extends DataObjectTest_TeamComment {
1961
	private static $db = array(
0 ignored issues
show
Comprehensibility introduced by
Consider using a different property name as you override a private property of the parent class.
Loading history...
1962
		'Comment' => 'HTMLText'
1963
	);
1964
}
1965
1966
class DataObjectTest_Play extends DataObject implements TestOnly {}
1967
class DataObjectTest_Ploy extends DataObject implements TestOnly {}
1968
class DataObjectTest_Bogey extends DataObject implements TestOnly {}
1969
1970
DataObjectTest_Team::add_extension('DataObjectTest_Team_Extension');
1971
1972