Completed
Pull Request — master (#5574)
by Christopher
11:14
created

DataObjectTest::testCastingHelper()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 9
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

Loading history...
2
3
use SilverStripe\Model\FieldType\DBField;
4
5
/**
6
 * @package framework
7
 * @subpackage tests
8
 */
9
class DataObjectTest extends SapphireTest {
10
11
	protected static $fixture_file = 'DataObjectTest.yml';
12
13
	protected $extraDataObjects = array(
14
		'DataObjectTest_Team',
15
		'DataObjectTest_Fixture',
16
		'DataObjectTest_SubTeam',
17
		'OtherSubclassWithSameField',
18
		'DataObjectTest_FieldlessTable',
19
		'DataObjectTest_FieldlessSubTable',
20
		'DataObjectTest_ValidatedObject',
21
		'DataObjectTest_Player',
22
		'DataObjectTest_TeamComment',
23
		'DataObjectTest_EquipmentCompany',
24
		'DataObjectTest_SubEquipmentCompany',
25
		'DataObjectTest\NamespacedClass',
26
		'DataObjectTest\RelationClass',
27
		'DataObjectTest_ExtendedTeamComment',
28
		'DataObjectTest_Company',
29
		'DataObjectTest_Staff',
30
		'DataObjectTest_CEO',
31
		'DataObjectTest_Fan',
32
		'DataObjectTest_Play',
33
		'DataObjectTest_Ploy',
34
		'DataObjectTest_Bogey',
35
		'ManyManyListTest_Product',
36
		'ManyManyListTest_Category',
37
	);
38
39
	public function testDb() {
40
		$obj = new DataObjectTest_TeamComment();
41
		$dbFields = $obj->db();
42
43
		// Assert fields are included
44
		$this->assertArrayHasKey('Name', $dbFields);
45
46
		// Assert the base fields are included
47
		$this->assertArrayHasKey('Created', $dbFields);
48
		$this->assertArrayHasKey('LastEdited', $dbFields);
49
		$this->assertArrayHasKey('ClassName', $dbFields);
50
		$this->assertArrayHasKey('ID', $dbFields);
51
52
		// Assert that the correct field type is returned when passing a field
53
		$this->assertEquals('Varchar', $obj->db('Name'));
54
		$this->assertEquals('Text', $obj->db('Comment'));
55
56
		// Test with table required
57
		$this->assertEquals('DataObjectTest_TeamComment.Varchar', $obj->db('Name', true));
58
		$this->assertEquals('DataObjectTest_TeamComment.Text', $obj->db('Comment', true));
59
60
		$obj = new DataObjectTest_ExtendedTeamComment();
61
		$dbFields = $obj->db();
62
63
		// fixed fields are still included in extended classes
64
		$this->assertArrayHasKey('Created', $dbFields);
65
		$this->assertArrayHasKey('LastEdited', $dbFields);
66
		$this->assertArrayHasKey('ClassName', $dbFields);
67
		$this->assertArrayHasKey('ID', $dbFields);
68
69
		// Assert overloaded fields have correct data type
70
		$this->assertEquals('HTMLText', $obj->db('Comment'));
71
		$this->assertEquals('HTMLText', $dbFields['Comment'],
72
			'Calls to DataObject::db without a field specified return correct data types');
73
74
		// assertEquals doesn't verify the order of array elements, so access keys manually to check order:
75
		// expected: array('Name' => 'Varchar', 'Comment' => 'HTMLText')
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
76
		$this->assertEquals(
77
			array(
78
				'Name',
79
				'Comment'
80
			),
81
			array_slice(array_keys($dbFields), 4, 2),
82
			'DataObject::db returns fields in correct order'
83
		);
84
	}
85
86
	public function testConstructAcceptsValues() {
87
		// Values can be an array...
88
		$player = new DataObjectTest_Player(array(
89
			'FirstName' => 'James',
90
			'Surname' => 'Smith'
91
		));
92
93
		$this->assertEquals('James', $player->FirstName);
94
		$this->assertEquals('Smith', $player->Surname);
95
96
		// ... or a stdClass inst
97
		$data = new stdClass();
98
		$data->FirstName = 'John';
99
		$data->Surname = 'Doe';
100
		$player = new DataObjectTest_Player($data);
101
102
		$this->assertEquals('John', $player->FirstName);
103
		$this->assertEquals('Doe', $player->Surname);
104
105
		// IDs should be stored as integers, not strings
106
		$player = new DataObjectTest_Player(array('ID' => '5'));
107
		$this->assertSame(5, $player->ID);
108
	}
109
110
	public function testValidObjectsForBaseFields() {
111
		$obj = new DataObjectTest_ValidatedObject();
112
113
		foreach (array('Created', 'LastEdited', 'ClassName', 'ID') as $field) {
114
			$helper = $obj->dbObject($field);
115
			$this->assertTrue(
116
				($helper instanceof DBField),
117
				"for {$field} expected helper to be DBField, but was " .
118
				(is_object($helper) ? get_class($helper) : "null")
119
			);
120
		}
121
	}
122
123
	public function testDataIntegrityWhenTwoSubclassesHaveSameField() {
124
		// Save data into DataObjectTest_SubTeam.SubclassDatabaseField
125
		$obj = new DataObjectTest_SubTeam();
126
		$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...
127
		$obj->write();
128
129
		// Change the class
130
		$obj->ClassName = 'OtherSubclassWithSameField';
131
		$obj->write();
132
		$obj->flushCache();
133
134
		// Re-fetch from the database and confirm that the data is sourced from
135
		// OtherSubclassWithSameField.SubclassDatabaseField
136
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
137
		$this->assertNull($obj->SubclassDatabaseField);
138
139
		// Confirm that save the object in the other direction.
140
		$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...
141
		$obj->write();
142
143
		$obj->ClassName = 'DataObjectTest_SubTeam';
144
		$obj->write();
145
		$obj->flushCache();
146
147
		// If we restore the class, the old value has been lying dormant and will be available again.
148
		// NOTE: This behaviour is volatile; we may change this in the future to clear fields that
149
		// are no longer relevant when changing ClassName
150
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
151
		$this->assertEquals('obj-SubTeam', $obj->SubclassDatabaseField);
152
	}
153
154
	/**
155
	 * Test deletion of DataObjects
156
	 *   - Deleting using delete() on the DataObject
157
	 *   - Deleting using DataObject::delete_by_id()
158
	 */
159
	public function testDelete() {
160
		// Test deleting using delete() on the DataObject
161
		// Get the first page
162
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
163
		$objID = $obj->ID;
164
		// Check the page exists before deleting
165
		$this->assertTrue(is_object($obj) && $obj->exists());
166
		// Delete the page
167
		$obj->delete();
168
		// Check that page does not exist after deleting
169
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
170
		$this->assertTrue(!$obj || !$obj->exists());
171
172
173
		// Test deleting using DataObject::delete_by_id()
174
		// Get the second page
175
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
176
		$objID = $obj->ID;
177
		// Check the page exists before deleting
178
		$this->assertTrue(is_object($obj) && $obj->exists());
179
		// Delete the page
180
		DataObject::delete_by_id('DataObjectTest_Player', $obj->ID);
181
		// Check that page does not exist after deleting
182
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
183
		$this->assertTrue(!$obj || !$obj->exists());
184
	}
185
186
	/**
187
	 * Test methods that get DataObjects
188
	 *   - DataObject::get()
189
	 *       - All records of a DataObject
190
	 *       - Filtering
191
	 *       - Sorting
192
	 *       - Joins
193
	 *       - Limit
194
	 *       - Container class
195
	 *   - DataObject::get_by_id()
196
	 *   - DataObject::get_one()
197
	 *        - With and without caching
198
	 *        - With and without ordering
199
	 */
200
	public function testGet() {
201
		// Test getting all records of a DataObject
202
		$comments = DataObject::get('DataObjectTest_TeamComment');
203
		$this->assertEquals(3, $comments->Count());
204
205
		// Test WHERE clause
206
		$comments = DataObject::get('DataObjectTest_TeamComment', "\"Name\"='Bob'");
207
		$this->assertEquals(1, $comments->Count());
208
		foreach($comments as $comment) {
209
			$this->assertEquals('Bob', $comment->Name);
210
		}
211
212
		// Test sorting
213
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC");
214
		$this->assertEquals(3, $comments->Count());
215
		$this->assertEquals('Bob', $comments->First()->Name);
216
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" DESC");
217
		$this->assertEquals(3, $comments->Count());
218
		$this->assertEquals('Phil', $comments->First()->Name);
219
220
		// Test limit
221
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC", '', '1,2');
222
		$this->assertEquals(2, $comments->Count());
223
		$this->assertEquals('Joe', $comments->First()->Name);
224
		$this->assertEquals('Phil', $comments->Last()->Name);
225
226
		// Test get_by_id()
227
		$captain1ID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
228
		$captain1 = DataObject::get_by_id('DataObjectTest_Player', $captain1ID);
229
		$this->assertEquals('Captain', $captain1->FirstName);
230
231
		// Test get_one() without caching
232
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
233
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
234
		), false);
235
		$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...
236
237
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
238
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
239
		), false);
240
		$this->assertNotEquals($comment1->Comment, $comment2->Comment);
241
242
		// Test get_one() with caching
243
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
244
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
245
		), true);
246
		$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...
247
248
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
249
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
250
		), true);
251
		$this->assertEquals((string)$comment1->Comment, (string)$comment2->Comment);
252
253
		// Test get_one() with order by without caching
254
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" ASC");
255
		$this->assertEquals('Bob', $comment->Name);
256
257
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" DESC");
258
		$this->assertEquals('Phil', $comment->Name);
259
260
		// Test get_one() with order by with caching
261
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" ASC');
262
		$this->assertEquals('Bob', $comment->Name);
263
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" DESC');
264
		$this->assertEquals('Phil', $comment->Name);
265
	}
266
267
	public function testGetCaseInsensitive() {
268
		// Test get_one() with bad case on the classname
269
		// Note: This will succeed only if the underlying DB server supports case-insensitive
270
		// table names (e.g. such as MySQL, but not SQLite3)
271
		if(!(DB::get_conn() instanceof MySQLDatabase)) {
272
			$this->markTestSkipped('MySQL only');
273
		}
274
275
		$subteam1 = DataObject::get_one('dataobjecttest_subteam', array(
276
			'"DataObjectTest_Team"."Title"' => 'Subteam 1'
277
		), true);
278
		$this->assertNotEmpty($subteam1);
279
		$this->assertEquals($subteam1->Title, "Subteam 1");
280
	}
281
282
	public function testGetSubclassFields() {
283
		/* Test that fields / has_one relations from the parent table and the subclass tables are extracted */
284
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
285
		// Base field
286
		$this->assertEquals('Captain', $captain1->FirstName);
287
		// Subclass field
288
		$this->assertEquals('007', $captain1->ShirtNumber);
289
		// Subclass has_one relation
290
		$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $captain1->FavouriteTeamID);
291
	}
292
293
	public function testGetRelationClass() {
294
		$obj = new DataObjectTest_Player();
295
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('FavouriteTeam'),
296
			'DataObjectTest_Team', 'has_one is properly inspected');
297
		$this->assertEquals(singleton('DataObjectTest_Company')->getRelationClass('CurrentStaff'),
298
			'DataObjectTest_Staff', 'has_many is properly inspected');
299
		$this->assertEquals(singleton('DataObjectTest_Team')->getRelationClass('Players'), 'DataObjectTest_Player',
300
			'many_many is properly inspected');
301
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('Teams'), 'DataObjectTest_Team',
302
			'belongs_many_many is properly inspected');
303
		$this->assertEquals(singleton('DataObjectTest_CEO')->getRelationClass('Company'), 'DataObjectTest_Company',
304
			'belongs_to is properly inspected');
305
		$this->assertEquals(singleton('DataObjectTest_Fan')->getRelationClass('Favourite'), 'DataObject',
306
			'polymorphic has_one is properly inspected');
307
	}
308
309
	/**
310
	 * Test that has_one relations can be retrieved
311
	 */
312
	public function testGetHasOneRelations() {
313
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
314
		$team1ID = $this->idFromFixture('DataObjectTest_Team', 'team1');
315
316
		// There will be a field called (relname)ID that contains the ID of the
317
		// object linked to via the has_one relation
318
		$this->assertEquals($team1ID, $captain1->FavouriteTeamID);
319
320
		// There will be a method called $obj->relname() that returns the object itself
321
		$this->assertEquals($team1ID, $captain1->FavouriteTeam()->ID);
322
323
		// Test that getNonReciprocalComponent can find has_one from the has_many end
324
		$this->assertEquals(
325
			$team1ID,
326
			$captain1->inferReciprocalComponent('DataObjectTest_Team', 'PlayerFans')->ID
327
		);
328
329
		// Check entity with polymorphic has-one
330
		$fan1 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
331
		$this->assertTrue((bool)$fan1->hasValue('Favourite'));
332
333
		// There will be fields named (relname)ID and (relname)Class for polymorphic
334
		// entities
335
		$this->assertEquals($team1ID, $fan1->FavouriteID);
336
		$this->assertEquals('DataObjectTest_Team', $fan1->FavouriteClass);
337
338
		// There will be a method called $obj->relname() that returns the object itself
339
		$favourite = $fan1->Favourite();
340
		$this->assertEquals($team1ID, $favourite->ID);
341
		$this->assertInstanceOf('DataObjectTest_Team', $favourite);
342
343
		// check behaviour of dbObject with polymorphic relations
344
		$favouriteDBObject = $fan1->dbObject('Favourite');
345
		$favouriteValue = $favouriteDBObject->getValue();
346
		$this->assertInstanceOf('SilverStripe\Model\FieldType\DBPolymorphicForeignKey', $favouriteDBObject);
347
		$this->assertEquals($favourite->ID, $favouriteValue->ID);
348
		$this->assertEquals($favourite->ClassName, $favouriteValue->ClassName);
349
	}
350
351
	/**
352
	 * Simple test to ensure that namespaced classes and polymorphic relations work together
353
	 */
354
	public function testPolymorphicNamespacedRelations() {
355
		$parent = new \DataObjectTest\NamespacedClass();
356
		$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...
357
		$parent->write();
358
359
		$child = new \DataObjectTest\RelationClass();
360
		$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...
361
		$child->write();
362
		$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...
363
364
		$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...
365
		$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...
366
		$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...
367
	}
368
369
	public function testLimitAndCount() {
370
		$players = DataObject::get("DataObjectTest_Player");
371
372
		// There's 4 records in total
373
		$this->assertEquals(4, $players->count());
374
375
		// Testing "##, ##" syntax
376
		$this->assertEquals(4, $players->limit(20)->count());
377
		$this->assertEquals(4, $players->limit(20, 0)->count());
378
		$this->assertEquals(0, $players->limit(20, 20)->count());
379
		$this->assertEquals(2, $players->limit(2, 0)->count());
380
		$this->assertEquals(1, $players->limit(5, 3)->count());
381
	}
382
383
	/**
384
	 * Test writing of database columns which don't correlate to a DBField,
385
	 * e.g. all relation fields on has_one/has_many like "ParentID".
386
	 *
387
	 */
388
	public function testWritePropertyWithoutDBField() {
389
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
390
		$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...
391
		$obj->write();
392
393
		// reload the page from the database
394
		$savedObj = DataObject::get_by_id('DataObjectTest_Player', $obj->ID);
395
		$this->assertTrue($savedObj->FavouriteTeamID == 99);
396
397
		// Test with porymorphic relation
398
		$obj2 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
399
		$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...
400
		$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...
401
		$obj2->write();
402
403
		$savedObj2 = DataObject::get_by_id('DataObjectTest_Fan', $obj2->ID);
404
		$this->assertTrue($savedObj2->FavouriteID == 99);
405
		$this->assertTrue($savedObj2->FavouriteClass == 'DataObjectTest_Player');
406
	}
407
408
	/**
409
	 * Test has many relationships
410
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
411
	 *   - Test the IDs on the DataObjects are set correctly
412
	 */
413
	public function testHasManyRelationships() {
414
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
415
416
		// Test getComponents() gets the ComponentSet of the other side of the relation
417
		$this->assertTrue($team1->Comments()->Count() == 2);
418
419
		$team1Comments = [
420
			['Comment' => 'This is a team comment by Joe'],
421
			['Comment' => 'This is a team comment by Bob'],
422
		];
423
424
		// Test the IDs on the DataObjects are set correctly
425
		$this->assertDOSEquals($team1Comments, $team1->Comments());
426
427
		// Test that has_many can be infered from the has_one via getNonReciprocalComponent
428
		$this->assertDOSEquals(
429
			$team1Comments,
430
			$team1->inferReciprocalComponent('DataObjectTest_TeamComment', 'Team')
431
		);
432
433
		// Test that we can add and remove items that already exist in the database
434
		$newComment = new DataObjectTest_TeamComment();
435
		$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...
436
		$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...
437
		$newComment->write();
438
		$team1->Comments()->add($newComment);
439
		$this->assertEquals($team1->ID, $newComment->TeamID);
440
441
		$comment1 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment1');
442
		$comment2 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment2');
443
		$team1->Comments()->remove($comment2);
444
445
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
446
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
447
448
		// Test that removing an item from a list doesn't remove it from the same
449
		// relation belonging to a different object
450
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
451
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
452
		$team2->Comments()->remove($comment1);
453
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
454
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
455
	}
456
457
458
	/**
459
	 * Test has many relationships against polymorphic has_one fields
460
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
461
	 *   - Test the IDs on the DataObjects are set correctly
462
	 */
463
	public function testHasManyPolymorphicRelationships() {
464
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
465
466
		// Test getComponents() gets the ComponentSet of the other side of the relation
467
		$this->assertTrue($team1->Fans()->Count() == 2);
468
469
		// Test the IDs/Classes on the DataObjects are set correctly
470
		foreach($team1->Fans() as $fan) {
471
			$this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID');
472
			$this->assertEquals('DataObjectTest_Team', $fan->FavouriteClass, 'Fan has the correct FavouriteClass');
473
		}
474
475
		// Test that we can add and remove items that already exist in the database
476
		$newFan = new DataObjectTest_Fan();
477
		$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...
478
		$newFan->write();
479
		$team1->Fans()->add($newFan);
480
		$this->assertEquals($team1->ID, $newFan->FavouriteID, 'Newly created fan has the correct FavouriteID');
481
		$this->assertEquals(
482
			'DataObjectTest_Team',
483
			$newFan->FavouriteClass,
484
			'Newly created fan has the correct FavouriteClass'
485
		);
486
487
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
488
		$fan3 = $this->objFromFixture('DataObjectTest_Fan', 'fan3');
489
		$team1->Fans()->remove($fan3);
490
491
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
492
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
493
494
		// Test that removing an item from a list doesn't remove it from the same
495
		// relation belonging to a different object
496
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
497
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
498
		$player1->Fans()->remove($fan1);
499
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
500
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
501
	}
502
503
504
	public function testHasOneRelationship() {
505
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
506
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
507
		$player2 = $this->objFromFixture('DataObjectTest_Player', 'player2');
508
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
509
510
		// Test relation probing
511
		$this->assertFalse((bool)$team1->hasValue('Captain', null, false));
512
		$this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
513
514
		// Add a captain to team 1
515
		$team1->setField('CaptainID', $player1->ID);
516
		$team1->write();
517
518
		$this->assertTrue((bool)$team1->hasValue('Captain', null, false));
519
		$this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
520
521
		$this->assertEquals($player1->ID, $team1->Captain()->ID,
522
			'The captain exists for team 1');
523
		$this->assertEquals($player1->ID, $team1->getComponent('Captain')->ID,
524
			'The captain exists through the component getter');
525
526
		$this->assertEquals($team1->Captain()->FirstName, 'Player 1',
527
			'Player 1 is the captain');
528
		$this->assertEquals($team1->getComponent('Captain')->FirstName, 'Player 1',
529
			'Player 1 is the captain');
530
531
		$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...
532
		$team1->write();
533
534
		$this->assertEquals($player2->ID, $team1->Captain()->ID);
535
		$this->assertEquals($player2->ID, $team1->getComponent('Captain')->ID);
536
		$this->assertEquals('Player 2', $team1->Captain()->FirstName);
537
		$this->assertEquals('Player 2', $team1->getComponent('Captain')->FirstName);
538
539
540
		// Set the favourite team for fan1
541
		$fan1->setField('FavouriteID', $team1->ID);
542
		$fan1->setField('FavouriteClass', $team1->class);
543
544
		$this->assertEquals($team1->ID, $fan1->Favourite()->ID, 'The team is assigned to fan 1');
545
		$this->assertInstanceOf($team1->class, $fan1->Favourite(), 'The team is assigned to fan 1');
546
		$this->assertEquals($team1->ID, $fan1->getComponent('Favourite')->ID,
547
			'The team exists through the component getter'
548
		);
549
		$this->assertInstanceOf($team1->class, $fan1->getComponent('Favourite'),
550
			'The team exists through the component getter'
551
		);
552
553
		$this->assertEquals($fan1->Favourite()->Title, 'Team 1',
554
			'Team 1 is the favourite');
555
		$this->assertEquals($fan1->getComponent('Favourite')->Title, 'Team 1',
556
			'Team 1 is the favourite');
557
	}
558
559
	/**
560
	 * @todo Extend type change tests (e.g. '0'==NULL)
561
	 */
562
	public function testChangedFields() {
563
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
564
		$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...
565
		$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...
566
567
		$this->assertEquals(
568
			$obj->getChangedFields(true, DataObject::CHANGE_STRICT),
569
			array(
570
				'FirstName' => array(
571
					'before' => 'Captain',
572
					'after' => 'Captain-changed',
573
					'level' => DataObject::CHANGE_VALUE
574
				),
575
				'IsRetired' => array(
576
					'before' => 1,
577
					'after' => true,
578
					'level' => DataObject::CHANGE_STRICT
579
				)
580
			),
581
			'Changed fields are correctly detected with strict type changes (level=1)'
582
		);
583
584
		$this->assertEquals(
585
			$obj->getChangedFields(true, DataObject::CHANGE_VALUE),
586
			array(
587
				'FirstName' => array(
588
					'before'=>'Captain',
589
					'after'=>'Captain-changed',
590
					'level' => DataObject::CHANGE_VALUE
591
				)
592
			),
593
			'Changed fields are correctly detected while ignoring type changes (level=2)'
594
		);
595
596
		$newObj = new DataObjectTest_Player();
597
		$newObj->FirstName = "New Player";
598
		$this->assertEquals(
599
			array(
600
				'FirstName' => array(
601
					'before' => null,
602
					'after' => 'New Player',
603
					'level' => DataObject::CHANGE_VALUE
604
				)
605
			),
606
			$newObj->getChangedFields(true, DataObject::CHANGE_VALUE),
607
			'Initialised fields are correctly detected as full changes'
608
		);
609
	}
610
611
	public function testIsChanged() {
612
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
613
		$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...
614
		$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...
615
		$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...
616
617
		// Now that DB fields are changed, isChanged is true
618
		$this->assertTrue($obj->isChanged('NonDBField'));
619
		$this->assertFalse($obj->isChanged('NonField'));
620
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
621
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
622
		$this->assertTrue($obj->isChanged('IsRetired', DataObject::CHANGE_STRICT));
623
		$this->assertFalse($obj->isChanged('IsRetired', DataObject::CHANGE_VALUE));
624
		$this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
625
		$this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
626
627
		$newObj = new DataObjectTest_Player();
628
		$newObj->FirstName = "New Player";
629
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
630
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
631
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
632
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
633
634
		$newObj->write();
635
		$this->assertFalse($newObj->ischanged());
636
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
637
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
638
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
639
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
640
641
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
642
		$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...
643
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
644
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
645
646
		/* Test when there's not field provided */
647
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
648
		$this->assertFalse($obj->isChanged());
649
		$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...
650
		$this->assertFalse($obj->isChanged());
651
		$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...
652
		$this->assertTrue($obj->isChanged());
653
654
		$obj->write();
655
		$this->assertFalse($obj->isChanged());
656
	}
657
658
	public function testRandomSort() {
659
		/* If we perform the same regularly sorted query twice, it should return the same results */
660
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", "ID");
661
		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...
662
663
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", "ID");
664
		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...
665
666
		/* Test when there's not field provided */
667
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
668
		$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...
669
		$this->assertTrue($obj->isChanged());
670
671
		$obj->write();
672
		$this->assertFalse($obj->isChanged());
673
674
		/* If we perform the same random query twice, it shouldn't return the same results */
675
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
676
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
677
		$itemsC = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
678
		$itemsD = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
679
		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...
680
		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...
681
		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...
682
		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...
683
684
		// These shouldn't all be the same (run it 4 times to minimise chance of an accidental collision)
685
		// There's about a 1 in a billion chance of an accidental collision
686
		$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...
687
	}
688
689
	public function testWriteSavesToHasOneRelations() {
690
		/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
691
		$team = new DataObjectTest_Team();
692
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'player1');
693
		$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...
694
		$team->write();
695
		$this->assertEquals($captainID,
696
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
697
698
		/* After giving it a value, you should also be able to set it back to null */
699
		$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...
700
		$team->write();
701
		$this->assertEquals(0,
702
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
703
704
		/* You should also be able to save a blank to it when it's first created */
705
		$team = new DataObjectTest_Team();
706
		$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...
707
		$team->write();
708
		$this->assertEquals(0,
709
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
710
711
		/* Ditto for existing records without a value */
712
		$existingTeam = $this->objFromFixture('DataObjectTest_Team', 'team1');
713
		$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...
714
		$existingTeam->write();
715
		$this->assertEquals(0,
716
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value());
717
	}
718
719
	public function testCanAccessHasOneObjectsAsMethods() {
720
		/* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the
721
		 * object itself should be accessible as $obj->Captain() */
722
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
723
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
724
725
		$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...
726
		$this->assertNotNull($team->Captain());
727
		$this->assertEquals($captainID, $team->Captain()->ID);
728
729
		// Test for polymorphic has_one relations
730
		$fan = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
731
		$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...
732
		$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...
733
		$this->assertNotNull($fan->Favourite());
734
		$this->assertEquals($team->ID, $fan->Favourite()->ID);
735
		$this->assertInstanceOf($team->class, $fan->Favourite());
736
	}
737
738
	public function testFieldNamesThatMatchMethodNamesWork() {
739
		/* Check that a field name that corresponds to a method on DataObject will still work */
740
		$obj = new DataObjectTest_Fixture();
741
		$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...
742
		$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...
743
		$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...
744
		$obj->write();
745
746
		$this->assertNotNull($obj->ID);
747
		$this->assertEquals('value1',
748
			DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
749
		$this->assertEquals('value2',
750
			DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
751
		$this->assertEquals('value3',
752
			DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
753
	}
754
755
	/**
756
	 * @todo Re-enable all test cases for field existence after behaviour has been fixed
757
	 */
758
	public function testFieldExistence() {
759
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
760
		$teamSingleton = singleton('DataObjectTest_Team');
761
762
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
763
		$subteamSingleton = singleton('DataObjectTest_SubTeam');
764
765
		/* hasField() singleton checks */
766
		$this->assertTrue($teamSingleton->hasField('ID'),
767
			'hasField() finds built-in fields in singletons');
768
		$this->assertTrue($teamSingleton->hasField('Title'),
769
			'hasField() finds custom fields in singletons');
770
771
		/* hasField() instance checks */
772
		$this->assertFalse($teamInstance->hasField('NonExistingField'),
773
			'hasField() doesnt find non-existing fields in instances');
774
		$this->assertTrue($teamInstance->hasField('ID'),
775
			'hasField() finds built-in fields in instances');
776
		$this->assertTrue($teamInstance->hasField('Created'),
777
			'hasField() finds built-in fields in instances');
778
		$this->assertTrue($teamInstance->hasField('DatabaseField'),
779
			'hasField() finds custom fields in instances');
780
		//$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
781
		//'hasField() doesnt find subclass fields in parentclass instances');
782
		$this->assertTrue($teamInstance->hasField('DynamicField'),
783
			'hasField() finds dynamic getters in instances');
784
		$this->assertTrue($teamInstance->hasField('HasOneRelationshipID'),
785
			'hasField() finds foreign keys in instances');
786
		$this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'),
787
			'hasField() finds extended fields in instances');
788
		$this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'),
789
			'hasField() finds extended foreign keys in instances');
790
		//$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
791
		//'hasField() includes extended dynamic getters in instances');
792
793
		/* hasField() subclass checks */
794
		$this->assertTrue($subteamInstance->hasField('ID'),
795
			'hasField() finds built-in fields in subclass instances');
796
		$this->assertTrue($subteamInstance->hasField('Created'),
797
			'hasField() finds built-in fields in subclass instances');
798
		$this->assertTrue($subteamInstance->hasField('DatabaseField'),
799
			'hasField() finds custom fields in subclass instances');
800
		$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'),
801
			'hasField() finds custom fields in subclass instances');
802
		$this->assertTrue($subteamInstance->hasField('DynamicField'),
803
			'hasField() finds dynamic getters in subclass instances');
804
		$this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'),
805
			'hasField() finds foreign keys in subclass instances');
806
		$this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'),
807
			'hasField() finds extended fields in subclass instances');
808
		$this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'),
809
			'hasField() finds extended foreign keys in subclass instances');
810
811
		/* hasDatabaseField() singleton checks */
812
		//$this->assertTrue($teamSingleton->hasDatabaseField('ID'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
813
		//'hasDatabaseField() finds built-in fields in singletons');
814
		$this->assertTrue($teamSingleton->hasDatabaseField('Title'),
815
			'hasDatabaseField() finds custom fields in singletons');
816
817
		/* hasDatabaseField() instance checks */
818
		$this->assertFalse($teamInstance->hasDatabaseField('NonExistingField'),
819
			'hasDatabaseField() doesnt find non-existing fields in instances');
820
		//$this->assertTrue($teamInstance->hasDatabaseField('ID'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
821
		//'hasDatabaseField() finds built-in fields in instances');
822
		$this->assertTrue($teamInstance->hasDatabaseField('Created'),
823
			'hasDatabaseField() finds built-in fields in instances');
824
		$this->assertTrue($teamInstance->hasDatabaseField('DatabaseField'),
825
			'hasDatabaseField() finds custom fields in instances');
826
		$this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'),
827
			'hasDatabaseField() doesnt find subclass fields in parentclass instances');
828
		//$this->assertFalse($teamInstance->hasDatabaseField('DynamicField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
829
		//'hasDatabaseField() doesnt dynamic getters in instances');
830
		$this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'),
831
			'hasDatabaseField() finds foreign keys in instances');
832
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'),
833
			'hasDatabaseField() finds extended fields in instances');
834
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'),
835
			'hasDatabaseField() finds extended foreign keys in instances');
836
		$this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'),
837
			'hasDatabaseField() doesnt include extended dynamic getters in instances');
838
839
		/* hasDatabaseField() subclass checks */
840
		$this->assertTrue($subteamInstance->hasDatabaseField('DatabaseField'),
841
			'hasField() finds custom fields in subclass instances');
842
		$this->assertTrue($subteamInstance->hasDatabaseField('SubclassDatabaseField'),
843
			'hasField() finds custom fields in subclass instances');
844
845
	}
846
847
	/**
848
	 * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed
849
	 */
850
	public function testFieldInheritance() {
851
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
852
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
853
854
		$this->assertEquals(
855
			array(
856
				'ID',
857
				'ClassName',
858
				'LastEdited',
859
				'Created',
860
				'Title',
861
				'DatabaseField',
862
				'ExtendedDatabaseField',
863
				'CaptainID',
864
				'FounderID',
865
				'HasOneRelationshipID',
866
				'ExtendedHasOneRelationshipID'
867
			),
868
			array_keys($teamInstance->db()),
869
			'inheritedDatabaseFields() contains all fields defined on instance: base, extended and foreign keys'
870
		);
871
872
		$this->assertEquals(
873
			array(
874
				'ID',
875
				'ClassName',
876
				'LastEdited',
877
				'Created',
878
				'Title',
879
				'DatabaseField',
880
				'ExtendedDatabaseField',
881
				'CaptainID',
882
				'FounderID',
883
				'HasOneRelationshipID',
884
				'ExtendedHasOneRelationshipID'
885
			),
886
			array_keys(DataObject::database_fields('DataObjectTest_Team', false)),
0 ignored issues
show
Unused Code introduced by
The call to DataObject::database_fields() has too many arguments starting with false.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

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

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

Consider the following code example.

<?php

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

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

print $a . " - " . $c;

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

Instead, the list call could have been.

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

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

Consider the following code example.

<?php

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

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

print $a . " - " . $c;

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

Instead, the list call could have been.

list($a,, $c) = returnThreeValues();
Loading history...
1264
		$this->assertEquals('DataObjectTest_Team', $class,
1265
			'DataObject::many_many() didn\'t find the correct base class');
1266
		$this->assertEquals('DataObjectTest_EquipmentCompany', $targetClass,
1267
			'DataObject::many_many() didn\'t find the correct target class for the relation');
1268
		$this->assertEquals('DataObjectTest_EquipmentCompany_SponsoredTeams', $joinTable,
1269
			'DataObject::many_many() didn\'t find the correct relation table');
1270
1271
		// Check that ManyManyList still works
1272
		$this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation');
1273
		$this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation');
1274
1275
		// Check everything works when no relation is present
1276
		$teamWithoutSponsor = $this->objFromFixture('DataObjectTest_Team', 'team3');
1277
		$this->assertInstanceOf('ManyManyList', $teamWithoutSponsor->Sponsors());
1278
		$this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count());
1279
1280
		// Test that belongs_many_many can be infered from with getNonReciprocalComponent
1281
		$this->assertDOSEquals(
1282
			[
1283
				['Name' => 'Company corp'],
1284
				['Name' => 'Team co.'],
1285
			],
1286
			$team->inferReciprocalComponent('DataObjectTest_EquipmentCompany', 'SponsoredTeams')
1287
		);
1288
1289
		// Test that many_many can be infered from getNonReciprocalComponent
1290
		$this->assertDOSEquals(
1291
			[
1292
				['Title' => 'Team 1'],
1293
				['Title' => 'Team 2'],
1294
				['Title' => 'Subteam 1'],
1295
			],
1296
			$company2->inferReciprocalComponent('DataObjectTest_Team', 'Sponsors')
1297
		);
1298
1299
		// Check many_many_extraFields still works
1300
		$equipmentCompany = $this->objFromFixture('DataObjectTest_EquipmentCompany', 'equipmentcompany1');
1301
		$equipmentCompany->SponsoredTeams()->add($teamWithoutSponsor, array('SponsorFee' => 1000));
1302
		$sponsoredTeams = $equipmentCompany->SponsoredTeams();
1303
		$this->assertEquals(1000, $sponsoredTeams->byID($teamWithoutSponsor->ID)->SponsorFee,
1304
			'Data from many_many_extraFields was not stored/extracted correctly');
1305
1306
		// Check subclasses correctly inherit multiple many_manys
1307
		$subTeam = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1308
		$this->assertEquals(2, $subTeam->Sponsors()->count(),
1309
			'Child class did not inherit multiple many_manys');
1310
		$this->assertEquals(1, $subTeam->EquipmentSuppliers()->count(),
1311
			'Child class did not inherit multiple many_manys');
1312
		// Team 2 has one EquipmentCompany sponsor and one SubEquipmentCompany
1313
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
1314
		$this->assertEquals(2, $team2->Sponsors()->count(),
1315
			'Child class did not inherit multiple belongs_many_manys');
1316
1317
		// Check many_many_extraFields also works from the belongs_many_many side
1318
		$sponsors = $team2->Sponsors();
1319
		$sponsors->add($equipmentCompany, array('SponsorFee' => 750));
1320
		$this->assertEquals(750, $sponsors->byID($equipmentCompany->ID)->SponsorFee,
1321
			'Data from many_many_extraFields was not stored/extracted correctly');
1322
1323
		$subEquipmentCompany = $this->objFromFixture('DataObjectTest_SubEquipmentCompany', 'subequipmentcompany1');
1324
		$subTeam->Sponsors()->add($subEquipmentCompany, array('SponsorFee' => 1200));
1325
		$this->assertEquals(1200, $subTeam->Sponsors()->byID($subEquipmentCompany->ID)->SponsorFee,
1326
			'Data from inherited many_many_extraFields was not stored/extracted correctly');
1327
1328
	}
1329
1330
	public function testManyManyExtraFields() {
1331
		$player = $this->objFromFixture('DataObjectTest_Player', 'player1');
1332
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
1333
1334
		// Get all extra fields
1335
		$teamExtraFields = $team->manyManyExtraFields();
1336
		$this->assertEquals(array(
1337
			'Players' => array('Position' => 'Varchar(100)')
1338
		), $teamExtraFields);
1339
1340
		// Ensure fields from parent classes are included
1341
		$subTeam = singleton('DataObjectTest_SubTeam');
1342
		$teamExtraFields = $subTeam->manyManyExtraFields();
1343
		$this->assertEquals(array(
1344
			'Players' => array('Position' => 'Varchar(100)'),
1345
			'FormerPlayers' => array('Position' => 'Varchar(100)')
1346
		), $teamExtraFields);
1347
1348
		// Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
1349
		$teamExtraFields = $team->manyManyExtraFieldsForComponent('Players');
1350
		$this->assertEquals($teamExtraFields, array(
1351
			'Position' => 'Varchar(100)'
1352
		));
1353
1354
		// We'll have to go through the relation to get the extra fields on Player
1355
		$playerExtraFields = $player->manyManyExtraFieldsForComponent('Teams');
1356
		$this->assertEquals($playerExtraFields, array(
1357
			'Position' => 'Varchar(100)'
1358
		));
1359
1360
		// Iterate through a many-many relationship and confirm that extra fields are included
1361
		$newTeam = new DataObjectTest_Team();
1362
		$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...
1363
		$newTeam->write();
1364
		$newTeamID = $newTeam->ID;
1365
1366
		$newPlayer = new DataObjectTest_Player();
1367
		$newPlayer->FirstName = "Sam";
1368
		$newPlayer->Surname = "Minnee";
1369
		$newPlayer->write();
1370
1371
		// The idea of Sam as a prop is essentially humourous.
1372
		$newTeam->Players()->add($newPlayer, array("Position" => "Prop"));
1373
1374
		// Requery and uncache everything
1375
		$newTeam->flushCache();
1376
		$newTeam = DataObject::get_by_id('DataObjectTest_Team', $newTeamID);
1377
1378
		// Check that the Position many_many_extraField is extracted.
1379
		$player = $newTeam->Players()->First();
1380
		$this->assertEquals('Sam', $player->FirstName);
1381
		$this->assertEquals("Prop", $player->Position);
1382
1383
		// Check that ordering a many-many relation by an aggregate column doesn't fail
1384
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1385
		$player->Teams()->sort("count(DISTINCT \"DataObjectTest_Team_Players\".\"DataObjectTest_PlayerID\") DESC");
1386
	}
1387
1388
	/**
1389
	 * Check that the queries generated for many-many relation queries can have unlimitedRowCount
1390
	 * called on them.
1391
	 */
1392
	public function testManyManyUnlimitedRowCount() {
1393
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1394
		// TODO: What's going on here?
1395
		$this->assertEquals(2, $player->Teams()->dataQuery()->query()->unlimitedRowCount());
1396
	}
1397
1398
	/**
1399
	 * Tests that singular_name() generates sensible defaults.
1400
	 */
1401
	public function testSingularName() {
1402
		$assertions = array(
1403
			'DataObjectTest_Player'       => 'Data Object Test Player',
1404
			'DataObjectTest_Team'         => 'Data Object Test Team',
1405
			'DataObjectTest_Fixture'      => 'Data Object Test Fixture'
1406
		);
1407
1408
		foreach($assertions as $class => $expectedSingularName) {
1409
			$this->assertEquals(
1410
				$expectedSingularName,
1411
				singleton($class)->singular_name(),
1412
				"Assert that the singular_name for '$class' is correct."
1413
			);
1414
		}
1415
	}
1416
1417
	/**
1418
	 * Tests that plural_name() generates sensible defaults.
1419
	 */
1420
	public function testPluralName() {
1421
		$assertions = array(
1422
			'DataObjectTest_Player'       => 'Data Object Test Players',
1423
			'DataObjectTest_Team'         => 'Data Object Test Teams',
1424
			'DataObjectTest_Fixture'      => 'Data Object Test Fixtures',
1425
			'DataObjectTest_Play'         => 'Data Object Test Plays',
1426
			'DataObjectTest_Bogey'        => 'Data Object Test Bogeys',
1427
			'DataObjectTest_Ploy'         => 'Data Object Test Ploys',
1428
		);
1429
1430
		foreach($assertions as $class => $expectedPluralName) {
1431
			$this->assertEquals(
1432
				$expectedPluralName,
1433
				singleton($class)->plural_name(),
1434
				"Assert that the plural_name for '$class' is correct."
1435
			);
1436
		}
1437
	}
1438
1439
	public function testHasDatabaseField() {
1440
		$team = singleton('DataObjectTest_Team');
1441
		$subteam = singleton('DataObjectTest_SubTeam');
1442
1443
		$this->assertTrue(
1444
			$team->hasDatabaseField('Title'),
1445
			"hasOwnDatabaseField() works with \$db fields"
1446
		);
1447
		$this->assertTrue(
1448
			$team->hasDatabaseField('CaptainID'),
1449
			"hasOwnDatabaseField() works with \$has_one fields"
1450
		);
1451
		$this->assertFalse(
1452
			$team->hasDatabaseField('NonExistentField'),
1453
			"hasOwnDatabaseField() doesn't detect non-existend fields"
1454
		);
1455
		$this->assertTrue(
1456
			$team->hasDatabaseField('ExtendedDatabaseField'),
1457
			"hasOwnDatabaseField() works with extended fields"
1458
		);
1459
		$this->assertFalse(
1460
			$team->hasDatabaseField('SubclassDatabaseField'),
1461
			"hasOwnDatabaseField() doesn't pick up fields in subclasses on parent class"
1462
		);
1463
1464
		$this->assertTrue(
1465
			$subteam->hasDatabaseField('SubclassDatabaseField'),
1466
			"hasOwnDatabaseField() picks up fields in subclasses"
1467
		);
1468
1469
	}
1470
1471
	public function testFieldTypes() {
1472
		$obj = new DataObjectTest_Fixture();
1473
		$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...
1474
		$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...
1475
		$obj->write();
1476
		$obj->flushCache();
1477
1478
		$obj = DataObject::get_by_id('DataObjectTest_Fixture', $obj->ID);
1479
		$this->assertEquals('1988-01-02', $obj->DateField);
1480
		$this->assertEquals('1988-03-04 06:30:00', $obj->DatetimeField);
1481
	}
1482
1483
	public function testTwoSubclassesWithTheSameFieldNameWork() {
1484
		// Create two objects of different subclasses, setting the values of fields that are
1485
		// defined separately in each subclass
1486
		$obj1 = new DataObjectTest_SubTeam();
1487
		$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...
1488
		$obj2 = new OtherSubclassWithSameField();
1489
		$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...
1490
1491
		// Write them to the database
1492
		$obj1->write();
1493
		$obj2->write();
1494
1495
		// Check that the values of those fields are properly read from the database
1496
		$values = DataObject::get("DataObjectTest_Team", "\"DataObjectTest_Team\".\"ID\" IN
1497
			($obj1->ID, $obj2->ID)")->column("SubclassDatabaseField");
1498
		$this->assertEquals(array_intersect($values, array('obj1', 'obj2')), $values);
1499
	}
1500
1501
	public function testClassNameSetForNewObjects() {
1502
		$d = new DataObjectTest_Player();
1503
		$this->assertEquals('DataObjectTest_Player', $d->ClassName);
1504
	}
1505
1506
	public function testHasValue() {
1507
		$team = new DataObjectTest_Team();
1508
		$this->assertFalse($team->hasValue('Title', null, false));
1509
		$this->assertFalse($team->hasValue('DatabaseField', null, false));
1510
1511
		$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...
1512
		$this->assertTrue($team->hasValue('Title', null, false));
1513
		$this->assertFalse($team->hasValue('DatabaseField', null, false));
1514
1515
		$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...
1516
		$this->assertTrue($team->hasValue('Title', null, false));
1517
		$this->assertFalse (
1518
			$team->hasValue('DatabaseField', null, false),
1519
			'Test that a blank paragraph on a HTML field is not a valid value.'
1520
		);
1521
1522
		$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...
1523
		$this->assertTrue (
1524
			$team->hasValue('Title', null, false),
1525
			'Test that an empty paragraph is a value for non-HTML fields.'
1526
		);
1527
1528
		$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...
1529
		$this->assertTrue($team->hasValue('Title', null, false));
1530
		$this->assertTrue($team->hasValue('DatabaseField', null, false));
1531
	}
1532
1533
	public function testHasMany() {
1534
		$company = new DataObjectTest_Company();
1535
1536
		$this->assertEquals (
1537
			array (
1538
				'CurrentStaff'     => 'DataObjectTest_Staff',
1539
				'PreviousStaff'    => 'DataObjectTest_Staff'
1540
			),
1541
			$company->hasMany(),
1542
			'has_many strips field name data by default.'
1543
		);
1544
1545
		$this->assertEquals (
1546
			'DataObjectTest_Staff',
1547
			$company->hasManyComponent('CurrentStaff'),
1548
			'has_many strips field name data by default on single relationships.'
1549
		);
1550
1551
		$this->assertEquals (
1552
			array (
1553
				'CurrentStaff'     => 'DataObjectTest_Staff.CurrentCompany',
1554
				'PreviousStaff'    => 'DataObjectTest_Staff.PreviousCompany'
1555
			),
1556
			$company->hasMany(null, false),
1557
			'has_many returns field name data when $classOnly is false.'
1558
		);
1559
1560
		$this->assertEquals (
1561
			'DataObjectTest_Staff.CurrentCompany',
1562
			$company->hasManyComponent('CurrentStaff', false),
1563
			'has_many returns field name data on single records when $classOnly is false.'
1564
		);
1565
	}
1566
1567
	public function testGetRemoteJoinField() {
1568
		$company = new DataObjectTest_Company();
1569
1570
		$staffJoinField = $company->getRemoteJoinField('CurrentStaff', 'has_many', $polymorphic);
1571
		$this->assertEquals('CurrentCompanyID', $staffJoinField);
1572
		$this->assertFalse($polymorphic, 'DataObjectTest_Company->CurrentStaff is not polymorphic');
1573
		$previousStaffJoinField = $company->getRemoteJoinField('PreviousStaff', 'has_many', $polymorphic);
1574
		$this->assertEquals('PreviousCompanyID', $previousStaffJoinField);
1575
		$this->assertFalse($polymorphic, 'DataObjectTest_Company->PreviousStaff is not polymorphic');
1576
1577
		$ceo = new DataObjectTest_CEO();
1578
1579
		$this->assertEquals('CEOID', $ceo->getRemoteJoinField('Company', 'belongs_to', $polymorphic));
1580
		$this->assertFalse($polymorphic, 'DataObjectTest_CEO->Company is not polymorphic');
1581
		$this->assertEquals('PreviousCEOID', $ceo->getRemoteJoinField('PreviousCompany', 'belongs_to', $polymorphic));
1582
		$this->assertFalse($polymorphic, 'DataObjectTest_CEO->PreviousCompany is not polymorphic');
1583
1584
		$team = new DataObjectTest_Team();
1585
1586
		$this->assertEquals('Favourite', $team->getRemoteJoinField('Fans', 'has_many', $polymorphic));
1587
		$this->assertTrue($polymorphic, 'DataObjectTest_Team->Fans is polymorphic');
1588
		$this->assertEquals('TeamID', $team->getRemoteJoinField('Comments', 'has_many', $polymorphic));
1589
		$this->assertFalse($polymorphic, 'DataObjectTest_Team->Comments is not polymorphic');
1590
	}
1591
1592
	public function testBelongsTo() {
1593
		$company = new DataObjectTest_Company();
1594
		$ceo     = new DataObjectTest_CEO();
1595
1596
		$company->Name = 'New Company';
0 ignored issues
show
Documentation introduced by
The property Name 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...
1597
		$company->write();
1598
		$ceo->write();
1599
1600
		// Test belongs_to assignment
1601
		$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...
1602
		$company->write();
1603
1604
		$this->assertEquals($company->ID, $ceo->Company()->ID, 'belongs_to returns the right results.');
1605
1606
		// Test belongs_to can be infered via getNonReciprocalComponent
1607
		// Note: Will be returned as has_many since the belongs_to is ignored.
1608
		$this->assertDOSEquals(
1609
			[['Name' => 'New Company']],
1610
			$ceo->inferReciprocalComponent('DataObjectTest_Company', 'CEO')
1611
		);
1612
1613
		// Test has_one to a belongs_to can be infered via getNonReciprocalComponent
1614
		$this->assertEquals(
1615
			$ceo->ID,
1616
			$company->inferReciprocalComponent('DataObjectTest_CEO', 'Company')->ID
1617
		);
1618
1619
		// Test automatic creation of class where no assigment exists
1620
		$ceo = new DataObjectTest_CEO();
1621
		$ceo->write();
1622
1623
		$this->assertTrue (
1624
			$ceo->Company() instanceof DataObjectTest_Company,
1625
			'DataObjects across belongs_to relations are automatically created.'
1626
		);
1627
		$this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
1628
1629
		// Write object with components
1630
		$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...
1631
		$ceo->write(false, false, false, true);
1632
		$this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
1633
1634
		$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
1635
		$this->assertEquals (
1636
			$ceo->Company()->ID, $newCEO->Company()->ID, 'belongs_to can be retrieved from the database.'
1637
		);
1638
	}
1639
1640
	public function testBelongsToPolymorphic() {
1641
		$company = new DataObjectTest_Company();
1642
		$ceo     = new DataObjectTest_CEO();
1643
1644
		$company->write();
1645
		$ceo->write();
1646
1647
		// Test belongs_to assignment
1648
		$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...
1649
		$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...
1650
		$company->write();
1651
1652
		$this->assertEquals($company->ID, $ceo->CompanyOwned()->ID, 'belongs_to returns the right results.');
1653
		$this->assertEquals($company->class, $ceo->CompanyOwned()->class, 'belongs_to returns the right results.');
1654
1655
		// Test automatic creation of class where no assigment exists
1656
		$ceo = new DataObjectTest_CEO();
1657
		$ceo->write();
1658
1659
		$this->assertTrue (
1660
			$ceo->CompanyOwned() instanceof DataObjectTest_Company,
1661
			'DataObjects across polymorphic belongs_to relations are automatically created.'
1662
		);
1663
		$this->assertEquals($ceo->ID, $ceo->CompanyOwned()->OwnerID, 'Remote IDs are automatically set.');
1664
		$this->assertInstanceOf($ceo->CompanyOwned()->OwnerClass, $ceo, 'Remote class is automatically  set');
1665
1666
		// Write object with components
1667
		$ceo->write(false, false, false, true);
1668
		$this->assertTrue($ceo->CompanyOwned()->isInDB(), 'write() writes belongs_to components to the database.');
1669
1670
		$newCEO = DataObject::get_by_id('DataObjectTest_CEO', $ceo->ID);
1671
		$this->assertEquals (
1672
			$ceo->CompanyOwned()->ID,
1673
			$newCEO->CompanyOwned()->ID,
1674
			'polymorphic belongs_to can be retrieved from the database.'
1675
		);
1676
	}
1677
1678
	/**
1679
	 * @expectedException LogicException
1680
	 */
1681
	public function testInvalidate() {
1682
		$do = new DataObjectTest_Fixture();
1683
		$do->write();
1684
1685
		$do->delete();
1686
1687
		$do->delete(); // Prohibit invalid object manipulation
1688
		$do->write();
1689
		$do->duplicate();
1690
	}
1691
1692
	public function testToMap() {
1693
		$obj = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1694
1695
		$map = $obj->toMap();
1696
1697
		$this->assertArrayHasKey('ID', $map, 'Contains base fields');
1698
		$this->assertArrayHasKey('Title', $map, 'Contains fields from parent class');
1699
		$this->assertArrayHasKey('SubclassDatabaseField', $map, 'Contains fields from concrete class');
1700
1701
		$this->assertEquals($obj->ID, $map['ID'],
1702
			'Contains values from base fields');
1703
		$this->assertEquals($obj->Title, $map['Title'],
1704
			'Contains values from parent class fields');
1705
		$this->assertEquals($obj->SubclassDatabaseField, $map['SubclassDatabaseField'],
1706
			'Contains values from concrete class fields');
1707
1708
		$newObj = new DataObjectTest_SubTeam();
1709
		$this->assertArrayHasKey('Title', $map, 'Contains null fields');
1710
	}
1711
1712
	public function testIsEmpty() {
1713
		$objEmpty = new DataObjectTest_Team();
1714
		$this->assertTrue($objEmpty->isEmpty(), 'New instance without populated defaults is empty');
1715
1716
		$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...
1717
		$this->assertFalse($objEmpty->isEmpty(), 'Zero value in attribute considered non-empty');
1718
	}
1719
1720
	public function testRelField() {
1721
		$captain = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1722
		// Test traversal of a single has_one
1723
		$this->assertEquals("Team 1", $captain->relField('FavouriteTeam.Title'));
1724
		// Test direct field access
1725
		$this->assertEquals("Captain", $captain->relField('FirstName'));
1726
1727
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1728
		// Test that we can traverse more than once, and that arbitrary methods are okay
1729
		$this->assertEquals("Team 1", $player->relField('Teams.First.Title'));
1730
1731
		$newPlayer = new DataObjectTest_Player();
1732
		$this->assertNull($newPlayer->relField('Teams.First.Title'));
1733
1734
		// Test that relField works on db field manipulations
1735
		$comment = $this->objFromFixture('DataObjectTest_TeamComment', 'comment3');
1736
		$this->assertEquals("PHIL IS A UNIQUE GUY, AND COMMENTS ON TEAM2" , $comment->relField('Comment.UpperCase'));
1737
	}
1738
1739
	public function testRelObject() {
1740
		$captain = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1741
1742
		// Test traversal of a single has_one
1743
		$this->assertInstanceOf('SilverStripe\Model\FieldType\DBVarchar', $captain->relObject('FavouriteTeam.Title'));
1744
		$this->assertEquals("Team 1", $captain->relObject('FavouriteTeam.Title')->getValue());
1745
1746
		// Test direct field access
1747
		$this->assertInstanceOf('SilverStripe\Model\FieldType\DBBoolean', $captain->relObject('IsRetired'));
1748
		$this->assertEquals(1, $captain->relObject('IsRetired')->getValue());
1749
1750
		$player = $this->objFromFixture('DataObjectTest_Player', 'player2');
1751
		// Test that we can traverse more than once, and that arbitrary methods are okay
1752
		$this->assertInstanceOf('SilverStripe\Model\FieldType\DBVarchar', $player->relObject('Teams.First.Title'));
1753
		$this->assertEquals("Team 1", $player->relObject('Teams.First.Title')->getValue());
1754
	}
1755
1756
	public function testLateStaticBindingStyle() {
1757
		// Confirm that DataObjectTest_Player::get() operates as excepted
1758
		$this->assertEquals(4, DataObjectTest_Player::get()->Count());
1759
		$this->assertInstanceOf('DataObjectTest_Player', DataObjectTest_Player::get()->First());
1760
1761
		// You can't pass arguments to LSB syntax - use the DataList methods instead.
1762
		$this->setExpectedException('InvalidArgumentException');
1763
		DataObjectTest_Player::get(null, "\"ID\" = 1");
1764
1765
	}
1766
1767
	public function testBrokenLateStaticBindingStyle() {
1768
		// If you call DataObject::get() you have to pass a first argument
1769
		$this->setExpectedException('InvalidArgumentException');
1770
		DataObject::get();
1771
1772
	}
1773
1774
}
1775
1776
class DataObjectTest_Player extends Member implements TestOnly {
1777
	private static $db = array(
1778
		'IsRetired' => 'Boolean',
1779
		'ShirtNumber' => 'Varchar',
1780
	);
1781
1782
	private static $has_one = array(
1783
		'FavouriteTeam' => 'DataObjectTest_Team',
1784
	);
1785
1786
	private static $belongs_many_many = array(
1787
		'Teams' => 'DataObjectTest_Team'
1788
	);
1789
1790
	private static $has_many = array(
1791
		'Fans' => 'DataObjectTest_Fan.Favourite', // Polymorphic - Player fans
1792
		'CaptainTeams' => 'DataObjectTest_Team.Captain',
1793
		'FoundingTeams' => 'DataObjectTest_Team.Founder'
1794
	);
1795
1796
	private static $belongs_to = array (
1797
		'CompanyOwned'    => 'DataObjectTest_Company.Owner'
1798
	);
1799
1800
	private static $searchable_fields = array(
1801
		'IsRetired',
1802
		'ShirtNumber'
1803
	);
1804
}
1805
1806
class DataObjectTest_Team extends DataObject implements TestOnly {
1807
1808
	private static $db = array(
1809
		'Title' => 'Varchar',
1810
		'DatabaseField' => 'HTMLVarchar'
1811
	);
1812
1813
	private static $has_one = array(
1814
		"Captain" => 'DataObjectTest_Player',
1815
		"Founder" => 'DataObjectTest_Player',
1816
		'HasOneRelationship' => 'DataObjectTest_Player',
1817
	);
1818
1819
	private static $has_many = array(
1820
		'SubTeams' => 'DataObjectTest_SubTeam',
1821
		'Comments' => 'DataObjectTest_TeamComment',
1822
		'Fans' => 'DataObjectTest_Fan.Favourite', // Polymorphic - Team fans
1823
		'PlayerFans' => 'DataObjectTest_Player.FavouriteTeam'
1824
	);
1825
1826
	private static $many_many = array(
1827
		'Players' => 'DataObjectTest_Player'
1828
	);
1829
1830
	private static $many_many_extraFields = array(
1831
		'Players' => array(
1832
			'Position' => 'Varchar(100)'
1833
		)
1834
	);
1835
1836
	private static $belongs_many_many = array(
1837
		'Sponsors' => 'DataObjectTest_EquipmentCompany.SponsoredTeams',
1838
		'EquipmentSuppliers' => 'DataObjectTest_EquipmentCompany.EquipmentCustomers'
1839
	);
1840
1841
	private static $summary_fields = array(
1842
		'Title' => 'Custom Title',
1843
		'Title.UpperCase' => 'Title',
1844
		'Captain.ShirtNumber' => 'Captain\'s shirt number',
1845
		'Captain.FavouriteTeam.Title' => 'Captain\'s favourite team'
1846
	);
1847
1848
	private static $default_sort = '"Title"';
1849
1850
	public function MyTitle() {
1851
		return 'Team ' . $this->Title;
1852
	}
1853
1854
	public function getDynamicField() {
1855
		return 'dynamicfield';
1856
	}
1857
1858
}
1859
1860
class DataObjectTest_Fixture extends DataObject implements TestOnly {
1861
	private static $db = array(
1862
		// Funny field names
1863
		'Data' => 'Varchar',
1864
		'Duplicate' => 'Varchar',
1865
		'DbObject' => 'Varchar',
1866
1867
		// Field types
1868
		'DateField' => 'Date',
1869
		'DatetimeField' => 'SS_Datetime',
1870
1871
		'MyFieldWithDefault' => 'Varchar',
1872
		'MyFieldWithAltDefault' => 'Varchar'
1873
	);
1874
1875
	private static $defaults = array(
1876
		'MyFieldWithDefault' => 'Default Value',
1877
	);
1878
1879
	private static $summary_fields = array(
1880
		'Data' => 'Data',
1881
		'DateField.Nice' => 'Date'
1882
	);
1883
1884
	private static $searchable_fields = array();
1885
1886
	public function populateDefaults() {
1887
		parent::populateDefaults();
1888
1889
		$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...
1890
	}
1891
1892
}
1893
1894
class DataObjectTest_SubTeam extends DataObjectTest_Team implements TestOnly {
1895
	private static $db = array(
1896
		'SubclassDatabaseField' => 'Varchar'
1897
	);
1898
1899
	private static $has_one = array(
1900
		"ParentTeam" => 'DataObjectTest_Team',
1901
	);
1902
1903
	private static $many_many = array(
1904
		'FormerPlayers' => 'DataObjectTest_Player'
1905
	);
1906
1907
	private static $many_many_extraFields = array(
1908
		'FormerPlayers' => array(
1909
			'Position' => 'Varchar(100)'
1910
		)
1911
	);
1912
}
1913
class OtherSubclassWithSameField extends DataObjectTest_Team implements TestOnly {
1914
	private static $db = array(
1915
		'SubclassDatabaseField' => 'Varchar',
1916
	);
1917
}
1918
1919
1920
class DataObjectTest_FieldlessTable extends DataObject implements TestOnly {
1921
}
1922
1923
class DataObjectTest_FieldlessSubTable extends DataObjectTest_Team implements TestOnly {
1924
}
1925
1926
1927
class DataObjectTest_Team_Extension extends DataExtension implements TestOnly {
1928
1929
	private static $db = array(
1930
		'ExtendedDatabaseField' => 'Varchar'
1931
	);
1932
1933
	private static $has_one = array(
1934
		'ExtendedHasOneRelationship' => 'DataObjectTest_Player'
1935
	);
1936
1937
	public function getExtendedDynamicField() {
1938
		return "extended dynamic field";
1939
	}
1940
1941
}
1942
1943
class DataObjectTest_ValidatedObject extends DataObject implements TestOnly {
1944
1945
	private static $db = array(
1946
		'Name' => 'Varchar(50)'
1947
	);
1948
1949
	public function validate() {
1950
		if(!empty($this->Name)) {
1951
			return new ValidationResult();
1952
		} else {
1953
			return new ValidationResult(false, "This object needs a name. Otherwise it will have an identity crisis!");
1954
		}
1955
	}
1956
}
1957
1958
class DataObjectTest_Company extends DataObject implements TestOnly {
1959
1960
	private static $db = array(
1961
		'Name' => 'Varchar'
1962
	);
1963
1964
	private static $has_one = array (
1965
		'CEO'         => 'DataObjectTest_CEO',
1966
		'PreviousCEO' => 'DataObjectTest_CEO',
1967
		'Owner'       => 'DataObject' // polymorphic
1968
	);
1969
1970
	private static $has_many = array (
1971
		'CurrentStaff'     => 'DataObjectTest_Staff.CurrentCompany',
1972
		'PreviousStaff'    => 'DataObjectTest_Staff.PreviousCompany'
1973
	);
1974
}
1975
1976
class DataObjectTest_EquipmentCompany extends DataObjectTest_Company implements TestOnly {
1977
	private static $many_many = array(
1978
		'SponsoredTeams' => 'DataObjectTest_Team',
1979
		'EquipmentCustomers' => 'DataObjectTest_Team'
1980
	);
1981
1982
	private static $many_many_extraFields = array(
1983
		'SponsoredTeams' => array(
1984
			'SponsorFee' => 'Int'
1985
		)
1986
	);
1987
}
1988
1989
class DataObjectTest_SubEquipmentCompany extends DataObjectTest_EquipmentCompany implements TestOnly {
1990
	private static $db = array(
1991
		'SubclassDatabaseField' => 'Varchar'
1992
	);
1993
}
1994
1995
class DataObjectTest_Staff extends DataObject implements TestOnly {
1996
	private static $has_one = array (
1997
		'CurrentCompany'  => 'DataObjectTest_Company',
1998
		'PreviousCompany' => 'DataObjectTest_Company'
1999
	);
2000
}
2001
2002
class DataObjectTest_CEO extends DataObjectTest_Staff {
2003
	private static $belongs_to = array (
2004
		'Company'         => 'DataObjectTest_Company.CEO',
2005
		'PreviousCompany' => 'DataObjectTest_Company.PreviousCEO',
2006
		'CompanyOwned'    => 'DataObjectTest_Company.Owner'
2007
	);
2008
}
2009
2010
class DataObjectTest_TeamComment extends DataObject implements TestOnly {
2011
	private static $db = array(
2012
		'Name' => 'Varchar',
2013
		'Comment' => 'Text'
2014
	);
2015
2016
	private static $has_one = array(
2017
		'Team' => 'DataObjectTest_Team'
2018
	);
2019
2020
	private static $default_sort = '"Name" ASC';
2021
}
2022
2023
class DataObjectTest_Fan extends DataObject implements TestOnly {
2024
2025
	private static $db = array(
2026
		'Name' => 'Varchar(255)',
2027
		'Email' => 'Varchar',
2028
	);
2029
2030
	private static $has_one = array(
2031
		'Favourite' => 'DataObject', // Polymorphic relation
2032
		'SecondFavourite' => 'DataObject'
2033
	);
2034
}
2035
2036
class DataObjectTest_ExtendedTeamComment extends DataObjectTest_TeamComment {
2037
	private static $db = array(
2038
		'Comment' => 'HTMLText'
2039
	);
2040
}
2041
2042
class DataObjectTest_Play extends DataObject implements TestOnly {}
2043
class DataObjectTest_Ploy extends DataObject implements TestOnly {}
2044
class DataObjectTest_Bogey extends DataObject implements TestOnly {}
2045
2046
DataObjectTest_Team::add_extension('DataObjectTest_Team_Extension');
2047
2048