Completed
Push — master ( 2fbd16...10f429 )
by Daniel
23s
created

DataObjectTest::testForceInsert()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 20
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

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

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
4
use SilverStripe\ORM\FieldType\DBField;
5
use SilverStripe\ORM\DataObject;
6
use SilverStripe\ORM\DB;
7
use SilverStripe\ORM\Connect\MySQLDatabase;
8
use SilverStripe\ORM\DataExtension;
9
use SilverStripe\ORM\ValidationResult;
10
use SilverStripe\Security\Member;
11
12
13
14
/**
15
 * @package framework
16
 * @subpackage tests
17
 */
18
class DataObjectTest extends SapphireTest {
19
20
	protected static $fixture_file = 'DataObjectTest.yml';
21
22
	/**
23
	 * Standard set of dataobject test classes
24
	 *
25
	 * @var array
26
	 */
27
	public static $extra_data_objects = array(
28
		'DataObjectTest_Team',
29
		'DataObjectTest_Fixture',
30
		'DataObjectTest_SubTeam',
31
		'OtherSubclassWithSameField',
32
		'DataObjectTest_FieldlessTable',
33
		'DataObjectTest_FieldlessSubTable',
34
		'DataObjectTest_ValidatedObject',
35
		'DataObjectTest_Player',
36
		'DataObjectTest_TeamComment',
37
		'DataObjectTest_EquipmentCompany',
38
		'DataObjectTest_SubEquipmentCompany',
39
		'DataObjectTest\NamespacedClass',
40
		'DataObjectTest\RelationClass',
41
		'DataObjectTest_ExtendedTeamComment',
42
		'DataObjectTest_Company',
43
		'DataObjectTest_Staff',
44
		'DataObjectTest_CEO',
45
		'DataObjectTest_Fan',
46
		'DataObjectTest_Play',
47
		'DataObjectTest_Ploy',
48
		'DataObjectTest_Bogey',
49
		// From ManyManyListTest
50
		'ManyManyListTest_ExtraFields',
51
		'ManyManyListTest_Product',
52
		'ManyManyListTest_Category',
53
	);
54
55
	public function setUpOnce() {
56
		$this->extraDataObjects = static::$extra_data_objects;
57
		parent::setUpOnce();
58
	}
59
60
	public function testDb() {
61
		$obj = new DataObjectTest_TeamComment();
62
		$dbFields = $obj->db();
63
64
		// Assert fields are included
65
		$this->assertArrayHasKey('Name', $dbFields);
66
67
		// Assert the base fields are included
68
		$this->assertArrayHasKey('Created', $dbFields);
69
		$this->assertArrayHasKey('LastEdited', $dbFields);
70
		$this->assertArrayHasKey('ClassName', $dbFields);
71
		$this->assertArrayHasKey('ID', $dbFields);
72
73
		// Assert that the correct field type is returned when passing a field
74
		$this->assertEquals('Varchar', $obj->db('Name'));
75
		$this->assertEquals('Text', $obj->db('Comment'));
76
77
		// Test with table required
78
		$this->assertEquals('DataObjectTest_TeamComment.Varchar', $obj->db('Name', true));
79
		$this->assertEquals('DataObjectTest_TeamComment.Text', $obj->db('Comment', true));
80
81
		$obj = new DataObjectTest_ExtendedTeamComment();
82
		$dbFields = $obj->db();
83
84
		// fixed fields are still included in extended classes
85
		$this->assertArrayHasKey('Created', $dbFields);
86
		$this->assertArrayHasKey('LastEdited', $dbFields);
87
		$this->assertArrayHasKey('ClassName', $dbFields);
88
		$this->assertArrayHasKey('ID', $dbFields);
89
90
		// Assert overloaded fields have correct data type
91
		$this->assertEquals('HTMLText', $obj->db('Comment'));
92
		$this->assertEquals('HTMLText', $dbFields['Comment'],
93
			'Calls to DataObject::db without a field specified return correct data types');
94
95
		// assertEquals doesn't verify the order of array elements, so access keys manually to check order:
96
		// 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...
97
		$this->assertEquals(
98
			array(
99
				'Name',
100
				'Comment'
101
			),
102
			array_slice(array_keys($dbFields), 4, 2),
103
			'DataObject::db returns fields in correct order'
104
		);
105
	}
106
107
	public function testConstructAcceptsValues() {
108
		// Values can be an array...
109
		$player = new DataObjectTest_Player(array(
110
			'FirstName' => 'James',
111
			'Surname' => 'Smith'
112
		));
113
114
		$this->assertEquals('James', $player->FirstName);
115
		$this->assertEquals('Smith', $player->Surname);
116
117
		// ... or a stdClass inst
118
		$data = new stdClass();
119
		$data->FirstName = 'John';
120
		$data->Surname = 'Doe';
121
		$player = new DataObjectTest_Player($data);
122
123
		$this->assertEquals('John', $player->FirstName);
124
		$this->assertEquals('Doe', $player->Surname);
125
126
		// IDs should be stored as integers, not strings
127
		$player = new DataObjectTest_Player(array('ID' => '5'));
128
		$this->assertSame(5, $player->ID);
129
	}
130
131
	public function testValidObjectsForBaseFields() {
132
		$obj = new DataObjectTest_ValidatedObject();
133
134
		foreach (array('Created', 'LastEdited', 'ClassName', 'ID') as $field) {
135
			$helper = $obj->dbObject($field);
136
			$this->assertTrue(
137
				($helper instanceof DBField),
138
				"for {$field} expected helper to be DBField, but was " .
139
				(is_object($helper) ? get_class($helper) : "null")
140
			);
141
		}
142
	}
143
144
	public function testDataIntegrityWhenTwoSubclassesHaveSameField() {
145
		// Save data into DataObjectTest_SubTeam.SubclassDatabaseField
146
		$obj = new DataObjectTest_SubTeam();
147
		$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...
148
		$obj->write();
149
150
		// Change the class
151
		$obj->ClassName = 'OtherSubclassWithSameField';
152
		$obj->write();
153
		$obj->flushCache();
154
155
		// Re-fetch from the database and confirm that the data is sourced from
156
		// OtherSubclassWithSameField.SubclassDatabaseField
157
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
158
		$this->assertNull($obj->SubclassDatabaseField);
159
160
		// Confirm that save the object in the other direction.
161
		$obj->SubclassDatabaseField = 'obj-Other';
0 ignored issues
show
Documentation introduced by
The property SubclassDatabaseField does not exist on object<SilverStripe\ORM\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...
162
		$obj->write();
163
164
		$obj->ClassName = 'DataObjectTest_SubTeam';
165
		$obj->write();
166
		$obj->flushCache();
167
168
		// If we restore the class, the old value has been lying dormant and will be available again.
169
		// NOTE: This behaviour is volatile; we may change this in the future to clear fields that
170
		// are no longer relevant when changing ClassName
171
		$obj = DataObject::get_by_id('DataObjectTest_Team', $obj->ID);
172
		$this->assertEquals('obj-SubTeam', $obj->SubclassDatabaseField);
173
	}
174
175
	/**
176
	 * Test deletion of DataObjects
177
	 *   - Deleting using delete() on the DataObject
178
	 *   - Deleting using DataObject::delete_by_id()
179
	 */
180
	public function testDelete() {
181
		// Test deleting using delete() on the DataObject
182
		// Get the first page
183
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
184
		$objID = $obj->ID;
185
		// Check the page exists before deleting
186
		$this->assertTrue(is_object($obj) && $obj->exists());
187
		// Delete the page
188
		$obj->delete();
189
		// Check that page does not exist after deleting
190
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
191
		$this->assertTrue(!$obj || !$obj->exists());
192
193
194
		// Test deleting using DataObject::delete_by_id()
195
		// Get the second page
196
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
197
		$objID = $obj->ID;
198
		// Check the page exists before deleting
199
		$this->assertTrue(is_object($obj) && $obj->exists());
200
		// Delete the page
201
		DataObject::delete_by_id('DataObjectTest_Player', $obj->ID);
202
		// Check that page does not exist after deleting
203
		$obj = DataObject::get_by_id('DataObjectTest_Player', $objID);
204
		$this->assertTrue(!$obj || !$obj->exists());
205
	}
206
207
	/**
208
	 * Test methods that get DataObjects
209
	 *   - DataObject::get()
210
	 *       - All records of a DataObject
211
	 *       - Filtering
212
	 *       - Sorting
213
	 *       - Joins
214
	 *       - Limit
215
	 *       - Container class
216
	 *   - DataObject::get_by_id()
217
	 *   - DataObject::get_one()
218
	 *        - With and without caching
219
	 *        - With and without ordering
220
	 */
221
	public function testGet() {
222
		// Test getting all records of a DataObject
223
		$comments = DataObject::get('DataObjectTest_TeamComment');
224
		$this->assertEquals(3, $comments->Count());
225
226
		// Test WHERE clause
227
		$comments = DataObject::get('DataObjectTest_TeamComment', "\"Name\"='Bob'");
228
		$this->assertEquals(1, $comments->Count());
229
		foreach($comments as $comment) {
230
			$this->assertEquals('Bob', $comment->Name);
231
		}
232
233
		// Test sorting
234
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC");
235
		$this->assertEquals(3, $comments->Count());
236
		$this->assertEquals('Bob', $comments->First()->Name);
237
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" DESC");
238
		$this->assertEquals(3, $comments->Count());
239
		$this->assertEquals('Phil', $comments->First()->Name);
240
241
		// Test limit
242
		$comments = DataObject::get('DataObjectTest_TeamComment', '', "\"Name\" ASC", '', '1,2');
243
		$this->assertEquals(2, $comments->Count());
244
		$this->assertEquals('Joe', $comments->First()->Name);
245
		$this->assertEquals('Phil', $comments->Last()->Name);
246
247
		// Test get_by_id()
248
		$captain1ID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
249
		$captain1 = DataObject::get_by_id('DataObjectTest_Player', $captain1ID);
250
		$this->assertEquals('Captain', $captain1->FirstName);
251
252
		// Test get_one() without caching
253
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
254
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
255
		), false);
256
		$comment1->Comment = "Something Else";
0 ignored issues
show
Documentation introduced by
The property Comment does not exist on object<SilverStripe\ORM\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...
257
258
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
259
			'"DataObjectTest_TeamComment"."Name"' => 'Joe'
260
		), false);
261
		$this->assertNotEquals($comment1->Comment, $comment2->Comment);
262
263
		// Test get_one() with caching
264
		$comment1 = DataObject::get_one('DataObjectTest_TeamComment', array(
265
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
266
		), true);
267
		$comment1->Comment = "Something Else";
0 ignored issues
show
Documentation introduced by
The property Comment does not exist on object<SilverStripe\ORM\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...
268
269
		$comment2 = DataObject::get_one('DataObjectTest_TeamComment', array(
270
			'"DataObjectTest_TeamComment"."Name"' => 'Bob'
271
		), true);
272
		$this->assertEquals((string)$comment1->Comment, (string)$comment2->Comment);
273
274
		// Test get_one() with order by without caching
275
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" ASC");
276
		$this->assertEquals('Bob', $comment->Name);
277
278
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', false, "\"Name\" DESC");
279
		$this->assertEquals('Phil', $comment->Name);
280
281
		// Test get_one() with order by with caching
282
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" ASC');
283
		$this->assertEquals('Bob', $comment->Name);
284
		$comment = DataObject::get_one('DataObjectTest_TeamComment', '', true, '"Name" DESC');
285
		$this->assertEquals('Phil', $comment->Name);
286
	}
287
288
	public function testGetCaseInsensitive() {
289
		// Test get_one() with bad case on the classname
290
		// Note: This will succeed only if the underlying DB server supports case-insensitive
291
		// table names (e.g. such as MySQL, but not SQLite3)
292
		if(!(DB::get_conn() instanceof MySQLDatabase)) {
293
			$this->markTestSkipped('MySQL only');
294
		}
295
296
		$subteam1 = DataObject::get_one('dataobjecttest_subteam', array(
297
			'"DataObjectTest_Team"."Title"' => 'Subteam 1'
298
		), true);
299
		$this->assertNotEmpty($subteam1);
300
		$this->assertEquals($subteam1->Title, "Subteam 1");
301
	}
302
303
	public function testGetSubclassFields() {
304
		/* Test that fields / has_one relations from the parent table and the subclass tables are extracted */
305
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
306
		// Base field
307
		$this->assertEquals('Captain', $captain1->FirstName);
308
		// Subclass field
309
		$this->assertEquals('007', $captain1->ShirtNumber);
310
		// Subclass has_one relation
311
		$this->assertEquals($this->idFromFixture('DataObjectTest_Team', 'team1'), $captain1->FavouriteTeamID);
312
	}
313
314
	public function testGetRelationClass() {
315
		$obj = new DataObjectTest_Player();
316
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('FavouriteTeam'),
317
			'DataObjectTest_Team', 'has_one is properly inspected');
318
		$this->assertEquals(singleton('DataObjectTest_Company')->getRelationClass('CurrentStaff'),
319
			'DataObjectTest_Staff', 'has_many is properly inspected');
320
		$this->assertEquals(singleton('DataObjectTest_Team')->getRelationClass('Players'), 'DataObjectTest_Player',
321
			'many_many is properly inspected');
322
		$this->assertEquals(singleton('DataObjectTest_Player')->getRelationClass('Teams'), 'DataObjectTest_Team',
323
			'belongs_many_many is properly inspected');
324
		$this->assertEquals(singleton('DataObjectTest_CEO')->getRelationClass('Company'), 'DataObjectTest_Company',
325
			'belongs_to is properly inspected');
326
		$this->assertEquals(singleton('DataObjectTest_Fan')->getRelationClass('Favourite'), 'SilverStripe\\ORM\\DataObject',
327
			'polymorphic has_one is properly inspected');
328
	}
329
330
	/**
331
	 * Test that has_one relations can be retrieved
332
	 */
333
	public function testGetHasOneRelations() {
334
		$captain1 = $this->objFromFixture("DataObjectTest_Player", "captain1");
335
		$team1ID = $this->idFromFixture('DataObjectTest_Team', 'team1');
336
337
		// There will be a field called (relname)ID that contains the ID of the
338
		// object linked to via the has_one relation
339
		$this->assertEquals($team1ID, $captain1->FavouriteTeamID);
340
341
		// There will be a method called $obj->relname() that returns the object itself
342
		$this->assertEquals($team1ID, $captain1->FavouriteTeam()->ID);
343
344
		// Test that getNonReciprocalComponent can find has_one from the has_many end
345
		$this->assertEquals(
346
			$team1ID,
347
			$captain1->inferReciprocalComponent('DataObjectTest_Team', 'PlayerFans')->ID
348
		);
349
350
		// Check entity with polymorphic has-one
351
		$fan1 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
352
		$this->assertTrue((bool)$fan1->hasValue('Favourite'));
353
354
		// There will be fields named (relname)ID and (relname)Class for polymorphic
355
		// entities
356
		$this->assertEquals($team1ID, $fan1->FavouriteID);
357
		$this->assertEquals('DataObjectTest_Team', $fan1->FavouriteClass);
358
359
		// There will be a method called $obj->relname() that returns the object itself
360
		$favourite = $fan1->Favourite();
361
		$this->assertEquals($team1ID, $favourite->ID);
362
		$this->assertInstanceOf('DataObjectTest_Team', $favourite);
363
364
		// check behaviour of dbObject with polymorphic relations
365
		$favouriteDBObject = $fan1->dbObject('Favourite');
366
		$favouriteValue = $favouriteDBObject->getValue();
367
		$this->assertInstanceOf('SilverStripe\\ORM\\FieldType\\DBPolymorphicForeignKey', $favouriteDBObject);
368
		$this->assertEquals($favourite->ID, $favouriteValue->ID);
369
		$this->assertEquals($favourite->ClassName, $favouriteValue->ClassName);
370
	}
371
372
	/**
373
	 * Simple test to ensure that namespaced classes and polymorphic relations work together
374
	 */
375
	public function testPolymorphicNamespacedRelations() {
376
		$parent = new \DataObjectTest\NamespacedClass();
377
		$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...
378
		$parent->write();
379
380
		$child = new \DataObjectTest\RelationClass();
381
		$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...
382
		$child->write();
383
		$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...
384
385
		$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...
386
		$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...
387
		$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...
388
	}
389
390
	public function testLimitAndCount() {
391
		$players = DataObject::get("DataObjectTest_Player");
392
393
		// There's 4 records in total
394
		$this->assertEquals(4, $players->count());
395
396
		// Testing "##, ##" syntax
397
		$this->assertEquals(4, $players->limit(20)->count());
398
		$this->assertEquals(4, $players->limit(20, 0)->count());
399
		$this->assertEquals(0, $players->limit(20, 20)->count());
400
		$this->assertEquals(2, $players->limit(2, 0)->count());
401
		$this->assertEquals(1, $players->limit(5, 3)->count());
402
	}
403
404
	/**
405
	 * Test writing of database columns which don't correlate to a DBField,
406
	 * e.g. all relation fields on has_one/has_many like "ParentID".
407
	 *
408
	 */
409
	public function testWritePropertyWithoutDBField() {
410
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
411
		$obj->FavouriteTeamID = 99;
0 ignored issues
show
Documentation introduced by
The property FavouriteTeamID does not exist on object<SilverStripe\ORM\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...
412
		$obj->write();
413
414
		// reload the page from the database
415
		$savedObj = DataObject::get_by_id('DataObjectTest_Player', $obj->ID);
416
		$this->assertTrue($savedObj->FavouriteTeamID == 99);
417
418
		// Test with porymorphic relation
419
		$obj2 = $this->objFromFixture("DataObjectTest_Fan", "fan1");
420
		$obj2->FavouriteID = 99;
0 ignored issues
show
Documentation introduced by
The property FavouriteID does not exist on object<SilverStripe\ORM\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...
421
		$obj2->FavouriteClass = 'DataObjectTest_Player';
0 ignored issues
show
Documentation introduced by
The property FavouriteClass does not exist on object<SilverStripe\ORM\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...
422
		$obj2->write();
423
424
		$savedObj2 = DataObject::get_by_id('DataObjectTest_Fan', $obj2->ID);
425
		$this->assertTrue($savedObj2->FavouriteID == 99);
426
		$this->assertTrue($savedObj2->FavouriteClass == 'DataObjectTest_Player');
427
	}
428
429
	/**
430
	 * Test has many relationships
431
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
432
	 *   - Test the IDs on the DataObjects are set correctly
433
	 */
434
	public function testHasManyRelationships() {
435
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
436
437
		// Test getComponents() gets the ComponentSet of the other side of the relation
438
		$this->assertTrue($team1->Comments()->Count() == 2);
439
440
		$team1Comments = [
441
			['Comment' => 'This is a team comment by Joe'],
442
			['Comment' => 'This is a team comment by Bob'],
443
		];
444
445
		// Test the IDs on the DataObjects are set correctly
446
		$this->assertDOSEquals($team1Comments, $team1->Comments());
447
448
		// Test that has_many can be infered from the has_one via getNonReciprocalComponent
449
		$this->assertDOSEquals(
450
			$team1Comments,
451
			$team1->inferReciprocalComponent('DataObjectTest_TeamComment', 'Team')
452
		);
453
454
		// Test that we can add and remove items that already exist in the database
455
		$newComment = new DataObjectTest_TeamComment();
456
		$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...
457
		$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...
458
		$newComment->write();
459
		$team1->Comments()->add($newComment);
460
		$this->assertEquals($team1->ID, $newComment->TeamID);
461
462
		$comment1 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment1');
463
		$comment2 = $this->objFromFixture('DataObjectTest_TeamComment', 'comment2');
464
		$team1->Comments()->remove($comment2);
465
466
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
467
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
468
469
		// Test that removing an item from a list doesn't remove it from the same
470
		// relation belonging to a different object
471
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
472
		$team2 = $this->objFromFixture('DataObjectTest_Team', 'team2');
473
		$team2->Comments()->remove($comment1);
474
		$team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
475
		$this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
476
	}
477
478
479
	/**
480
	 * Test has many relationships against polymorphic has_one fields
481
	 *   - Test getComponents() gets the ComponentSet of the other side of the relation
482
	 *   - Test the IDs on the DataObjects are set correctly
483
	 */
484
	public function testHasManyPolymorphicRelationships() {
485
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
486
487
		// Test getComponents() gets the ComponentSet of the other side of the relation
488
		$this->assertTrue($team1->Fans()->Count() == 2);
489
490
		// Test the IDs/Classes on the DataObjects are set correctly
491
		foreach($team1->Fans() as $fan) {
492
			$this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID');
493
			$this->assertEquals('DataObjectTest_Team', $fan->FavouriteClass, 'Fan has the correct FavouriteClass');
494
		}
495
496
		// Test that we can add and remove items that already exist in the database
497
		$newFan = new DataObjectTest_Fan();
498
		$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...
499
		$newFan->write();
500
		$team1->Fans()->add($newFan);
501
		$this->assertEquals($team1->ID, $newFan->FavouriteID, 'Newly created fan has the correct FavouriteID');
502
		$this->assertEquals(
503
			'DataObjectTest_Team',
504
			$newFan->FavouriteClass,
505
			'Newly created fan has the correct FavouriteClass'
506
		);
507
508
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
509
		$fan3 = $this->objFromFixture('DataObjectTest_Fan', 'fan3');
510
		$team1->Fans()->remove($fan3);
511
512
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
513
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
514
515
		// Test that removing an item from a list doesn't remove it from the same
516
		// relation belonging to a different object
517
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
518
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
519
		$player1->Fans()->remove($fan1);
520
		$team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
521
		$this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
522
	}
523
524
525
	public function testHasOneRelationship() {
526
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
527
		$player1 = $this->objFromFixture('DataObjectTest_Player', 'player1');
528
		$player2 = $this->objFromFixture('DataObjectTest_Player', 'player2');
529
		$fan1 = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
530
531
		// Test relation probing
532
		$this->assertFalse((bool)$team1->hasValue('Captain', null, false));
533
		$this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
534
535
		// Add a captain to team 1
536
		$team1->setField('CaptainID', $player1->ID);
537
		$team1->write();
538
539
		$this->assertTrue((bool)$team1->hasValue('Captain', null, false));
540
		$this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
541
542
		$this->assertEquals($player1->ID, $team1->Captain()->ID,
543
			'The captain exists for team 1');
544
		$this->assertEquals($player1->ID, $team1->getComponent('Captain')->ID,
545
			'The captain exists through the component getter');
546
547
		$this->assertEquals($team1->Captain()->FirstName, 'Player 1',
548
			'Player 1 is the captain');
549
		$this->assertEquals($team1->getComponent('Captain')->FirstName, 'Player 1',
550
			'Player 1 is the captain');
551
552
		$team1->CaptainID = $player2->ID;
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<SilverStripe\ORM\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...
553
		$team1->write();
554
555
		$this->assertEquals($player2->ID, $team1->Captain()->ID);
556
		$this->assertEquals($player2->ID, $team1->getComponent('Captain')->ID);
557
		$this->assertEquals('Player 2', $team1->Captain()->FirstName);
558
		$this->assertEquals('Player 2', $team1->getComponent('Captain')->FirstName);
559
560
561
		// Set the favourite team for fan1
562
		$fan1->setField('FavouriteID', $team1->ID);
563
		$fan1->setField('FavouriteClass', $team1->class);
564
565
		$this->assertEquals($team1->ID, $fan1->Favourite()->ID, 'The team is assigned to fan 1');
566
		$this->assertInstanceOf($team1->class, $fan1->Favourite(), 'The team is assigned to fan 1');
567
		$this->assertEquals($team1->ID, $fan1->getComponent('Favourite')->ID,
568
			'The team exists through the component getter'
569
		);
570
		$this->assertInstanceOf($team1->class, $fan1->getComponent('Favourite'),
571
			'The team exists through the component getter'
572
		);
573
574
		$this->assertEquals($fan1->Favourite()->Title, 'Team 1',
575
			'Team 1 is the favourite');
576
		$this->assertEquals($fan1->getComponent('Favourite')->Title, 'Team 1',
577
			'Team 1 is the favourite');
578
	}
579
580
	/**
581
	 * @todo Extend type change tests (e.g. '0'==NULL)
582
	 */
583
	public function testChangedFields() {
584
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
585
		$obj->FirstName = 'Captain-changed';
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<SilverStripe\ORM\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...
586
		$obj->IsRetired = true;
0 ignored issues
show
Documentation introduced by
The property IsRetired does not exist on object<SilverStripe\ORM\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...
587
588
		$this->assertEquals(
589
			$obj->getChangedFields(true, DataObject::CHANGE_STRICT),
590
			array(
591
				'FirstName' => array(
592
					'before' => 'Captain',
593
					'after' => 'Captain-changed',
594
					'level' => DataObject::CHANGE_VALUE
595
				),
596
				'IsRetired' => array(
597
					'before' => 1,
598
					'after' => true,
599
					'level' => DataObject::CHANGE_STRICT
600
				)
601
			),
602
			'Changed fields are correctly detected with strict type changes (level=1)'
603
		);
604
605
		$this->assertEquals(
606
			$obj->getChangedFields(true, DataObject::CHANGE_VALUE),
607
			array(
608
				'FirstName' => array(
609
					'before'=>'Captain',
610
					'after'=>'Captain-changed',
611
					'level' => DataObject::CHANGE_VALUE
612
				)
613
			),
614
			'Changed fields are correctly detected while ignoring type changes (level=2)'
615
		);
616
617
		$newObj = new DataObjectTest_Player();
618
		$newObj->FirstName = "New Player";
619
		$this->assertEquals(
620
			array(
621
				'FirstName' => array(
622
					'before' => null,
623
					'after' => 'New Player',
624
					'level' => DataObject::CHANGE_VALUE
625
				)
626
			),
627
			$newObj->getChangedFields(true, DataObject::CHANGE_VALUE),
628
			'Initialised fields are correctly detected as full changes'
629
		);
630
	}
631
632
	public function testIsChanged() {
633
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
634
		$obj->NonDBField = 'bob';
0 ignored issues
show
Documentation introduced by
The property NonDBField does not exist on object<SilverStripe\ORM\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...
635
		$obj->FirstName = 'Captain-changed';
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<SilverStripe\ORM\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...
636
		$obj->IsRetired = true; // type change only, database stores "1"
0 ignored issues
show
Documentation introduced by
The property IsRetired does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
637
638
		// Now that DB fields are changed, isChanged is true
639
		$this->assertTrue($obj->isChanged('NonDBField'));
640
		$this->assertFalse($obj->isChanged('NonField'));
641
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
642
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
643
		$this->assertTrue($obj->isChanged('IsRetired', DataObject::CHANGE_STRICT));
644
		$this->assertFalse($obj->isChanged('IsRetired', DataObject::CHANGE_VALUE));
645
		$this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
646
		$this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
647
648
		$newObj = new DataObjectTest_Player();
649
		$newObj->FirstName = "New Player";
650
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
651
		$this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
652
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
653
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
654
655
		$newObj->write();
656
		$this->assertFalse($newObj->ischanged());
657
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
658
		$this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
659
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
660
		$this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
661
662
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
663
		$obj->FirstName = null;
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<SilverStripe\ORM\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...
664
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
665
		$this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
666
667
		/* Test when there's not field provided */
668
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain2');
669
		$this->assertFalse($obj->isChanged());
670
		$obj->NonDBField = 'new value';
0 ignored issues
show
Documentation introduced by
The property NonDBField does not exist on object<SilverStripe\ORM\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...
671
		$this->assertFalse($obj->isChanged());
672
		$obj->FirstName = "New Player";
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
673
		$this->assertTrue($obj->isChanged());
674
675
		$obj->write();
676
		$this->assertFalse($obj->isChanged());
677
	}
678
679
	public function testRandomSort() {
680
		/* If we perform the same regularly sorted query twice, it should return the same results */
681
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", "ID");
682
		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...
683
684
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", "ID");
685
		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...
686
687
		/* Test when there's not field provided */
688
		$obj = $this->objFromFixture('DataObjectTest_Player', 'captain1');
689
		$obj->FirstName = "New Player";
0 ignored issues
show
Documentation introduced by
The property FirstName does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
690
		$this->assertTrue($obj->isChanged());
691
692
		$obj->write();
693
		$this->assertFalse($obj->isChanged());
694
695
		/* If we perform the same random query twice, it shouldn't return the same results */
696
		$itemsA = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
697
		$itemsB = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
698
		$itemsC = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
699
		$itemsD = DataObject::get("DataObjectTest_TeamComment", "", DB::get_conn()->random());
700
		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...
701
		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...
702
		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...
703
		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...
704
705
		// These shouldn't all be the same (run it 4 times to minimise chance of an accidental collision)
706
		// There's about a 1 in a billion chance of an accidental collision
707
		$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...
708
	}
709
710
	public function testWriteSavesToHasOneRelations() {
711
		/* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
712
		$team = new DataObjectTest_Team();
713
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'player1');
714
		$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...
715
		$team->write();
716
		$this->assertEquals($captainID,
717
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
718
719
		/* After giving it a value, you should also be able to set it back to null */
720
		$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...
721
		$team->write();
722
		$this->assertEquals(0,
723
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
724
725
		/* You should also be able to save a blank to it when it's first created */
726
		$team = new DataObjectTest_Team();
727
		$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...
728
		$team->write();
729
		$this->assertEquals(0,
730
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value());
731
732
		/* Ditto for existing records without a value */
733
		$existingTeam = $this->objFromFixture('DataObjectTest_Team', 'team1');
734
		$existingTeam->CaptainID = '';
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<SilverStripe\ORM\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...
735
		$existingTeam->write();
736
		$this->assertEquals(0,
737
			DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value());
738
	}
739
740
	public function testCanAccessHasOneObjectsAsMethods() {
741
		/* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the
742
		 * object itself should be accessible as $obj->Captain() */
743
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
744
		$captainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
745
746
		$team->CaptainID = $captainID;
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<SilverStripe\ORM\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...
747
		$this->assertNotNull($team->Captain());
748
		$this->assertEquals($captainID, $team->Captain()->ID);
749
750
		// Test for polymorphic has_one relations
751
		$fan = $this->objFromFixture('DataObjectTest_Fan', 'fan1');
752
		$fan->FavouriteID = $team->ID;
0 ignored issues
show
Documentation introduced by
The property FavouriteID does not exist on object<SilverStripe\ORM\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...
753
		$fan->FavouriteClass = $team->class;
0 ignored issues
show
Documentation introduced by
The property FavouriteClass does not exist on object<SilverStripe\ORM\DataObject>. Since you implemented __set, maybe consider adding a @property annotation.

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

<?php

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

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

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

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

}

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

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

See also the PhpDoc documentation for @property.

Loading history...
754
		$this->assertNotNull($fan->Favourite());
755
		$this->assertEquals($team->ID, $fan->Favourite()->ID);
756
		$this->assertInstanceOf($team->class, $fan->Favourite());
757
	}
758
759
	public function testFieldNamesThatMatchMethodNamesWork() {
760
		/* Check that a field name that corresponds to a method on DataObject will still work */
761
		$obj = new DataObjectTest_Fixture();
762
		$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...
763
		$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...
764
		$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...
765
		$obj->write();
766
767
		$this->assertNotNull($obj->ID);
768
		$this->assertEquals('value1',
769
			DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
770
		$this->assertEquals('value2',
771
			DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
772
		$this->assertEquals('value3',
773
			DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value());
774
	}
775
776
	/**
777
	 * @todo Re-enable all test cases for field existence after behaviour has been fixed
778
	 */
779
	public function testFieldExistence() {
780
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
781
		$teamSingleton = singleton('DataObjectTest_Team');
782
783
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
784
		$subteamSingleton = singleton('DataObjectTest_SubTeam');
785
786
		/* hasField() singleton checks */
787
		$this->assertTrue($teamSingleton->hasField('ID'),
788
			'hasField() finds built-in fields in singletons');
789
		$this->assertTrue($teamSingleton->hasField('Title'),
790
			'hasField() finds custom fields in singletons');
791
792
		/* hasField() instance checks */
793
		$this->assertFalse($teamInstance->hasField('NonExistingField'),
794
			'hasField() doesnt find non-existing fields in instances');
795
		$this->assertTrue($teamInstance->hasField('ID'),
796
			'hasField() finds built-in fields in instances');
797
		$this->assertTrue($teamInstance->hasField('Created'),
798
			'hasField() finds built-in fields in instances');
799
		$this->assertTrue($teamInstance->hasField('DatabaseField'),
800
			'hasField() finds custom fields in instances');
801
		//$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...
802
		//'hasField() doesnt find subclass fields in parentclass instances');
803
		$this->assertTrue($teamInstance->hasField('DynamicField'),
804
			'hasField() finds dynamic getters in instances');
805
		$this->assertTrue($teamInstance->hasField('HasOneRelationshipID'),
806
			'hasField() finds foreign keys in instances');
807
		$this->assertTrue($teamInstance->hasField('ExtendedDatabaseField'),
808
			'hasField() finds extended fields in instances');
809
		$this->assertTrue($teamInstance->hasField('ExtendedHasOneRelationshipID'),
810
			'hasField() finds extended foreign keys in instances');
811
		//$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...
812
		//'hasField() includes extended dynamic getters in instances');
813
814
		/* hasField() subclass checks */
815
		$this->assertTrue($subteamInstance->hasField('ID'),
816
			'hasField() finds built-in fields in subclass instances');
817
		$this->assertTrue($subteamInstance->hasField('Created'),
818
			'hasField() finds built-in fields in subclass instances');
819
		$this->assertTrue($subteamInstance->hasField('DatabaseField'),
820
			'hasField() finds custom fields in subclass instances');
821
		$this->assertTrue($subteamInstance->hasField('SubclassDatabaseField'),
822
			'hasField() finds custom fields in subclass instances');
823
		$this->assertTrue($subteamInstance->hasField('DynamicField'),
824
			'hasField() finds dynamic getters in subclass instances');
825
		$this->assertTrue($subteamInstance->hasField('HasOneRelationshipID'),
826
			'hasField() finds foreign keys in subclass instances');
827
		$this->assertTrue($subteamInstance->hasField('ExtendedDatabaseField'),
828
			'hasField() finds extended fields in subclass instances');
829
		$this->assertTrue($subteamInstance->hasField('ExtendedHasOneRelationshipID'),
830
			'hasField() finds extended foreign keys in subclass instances');
831
832
		/* hasDatabaseField() singleton checks */
833
		//$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...
834
		//'hasDatabaseField() finds built-in fields in singletons');
835
		$this->assertTrue($teamSingleton->hasDatabaseField('Title'),
836
			'hasDatabaseField() finds custom fields in singletons');
837
838
		/* hasDatabaseField() instance checks */
839
		$this->assertFalse($teamInstance->hasDatabaseField('NonExistingField'),
840
			'hasDatabaseField() doesnt find non-existing fields in instances');
841
		//$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...
842
		//'hasDatabaseField() finds built-in fields in instances');
843
		$this->assertTrue($teamInstance->hasDatabaseField('Created'),
844
			'hasDatabaseField() finds built-in fields in instances');
845
		$this->assertTrue($teamInstance->hasDatabaseField('DatabaseField'),
846
			'hasDatabaseField() finds custom fields in instances');
847
		$this->assertFalse($teamInstance->hasDatabaseField('SubclassDatabaseField'),
848
			'hasDatabaseField() doesnt find subclass fields in parentclass instances');
849
		//$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...
850
		//'hasDatabaseField() doesnt dynamic getters in instances');
851
		$this->assertTrue($teamInstance->hasDatabaseField('HasOneRelationshipID'),
852
			'hasDatabaseField() finds foreign keys in instances');
853
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedDatabaseField'),
854
			'hasDatabaseField() finds extended fields in instances');
855
		$this->assertTrue($teamInstance->hasDatabaseField('ExtendedHasOneRelationshipID'),
856
			'hasDatabaseField() finds extended foreign keys in instances');
857
		$this->assertFalse($teamInstance->hasDatabaseField('ExtendedDynamicField'),
858
			'hasDatabaseField() doesnt include extended dynamic getters in instances');
859
860
		/* hasDatabaseField() subclass checks */
861
		$this->assertTrue($subteamInstance->hasDatabaseField('DatabaseField'),
862
			'hasField() finds custom fields in subclass instances');
863
		$this->assertTrue($subteamInstance->hasDatabaseField('SubclassDatabaseField'),
864
			'hasField() finds custom fields in subclass instances');
865
866
	}
867
868
	/**
869
	 * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed
870
	 */
871
	public function testFieldInheritance() {
872
		$teamInstance = $this->objFromFixture('DataObjectTest_Team', 'team1');
873
		$subteamInstance = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
874
875
		$this->assertEquals(
876
			array(
877
				'ID',
878
				'ClassName',
879
				'LastEdited',
880
				'Created',
881
				'Title',
882
				'DatabaseField',
883
				'ExtendedDatabaseField',
884
				'CaptainID',
885
				'FounderID',
886
				'HasOneRelationshipID',
887
				'ExtendedHasOneRelationshipID'
888
			),
889
			array_keys($teamInstance->db()),
890
			'inheritedDatabaseFields() contains all fields defined on instance: base, extended and foreign keys'
891
		);
892
893
		$this->assertEquals(
894
			array(
895
				'ID',
896
				'ClassName',
897
				'LastEdited',
898
				'Created',
899
				'Title',
900
				'DatabaseField',
901
				'ExtendedDatabaseField',
902
				'CaptainID',
903
				'FounderID',
904
				'HasOneRelationshipID',
905
				'ExtendedHasOneRelationshipID'
906
			),
907
			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...
908
			'databaseFields() contains only fields defined on instance, including base, extended and foreign keys'
909
		);
910
911
		$this->assertEquals(
912
			array(
913
				'ID',
914
				'ClassName',
915
				'LastEdited',
916
				'Created',
917
				'Title',
918
				'DatabaseField',
919
				'ExtendedDatabaseField',
920
				'CaptainID',
921
				'FounderID',
922
				'HasOneRelationshipID',
923
				'ExtendedHasOneRelationshipID',
924
				'SubclassDatabaseField',
925
				'ParentTeamID',
926
			),
927
			array_keys($subteamInstance->db()),
928
			'inheritedDatabaseFields() on subclass contains all fields, including base, extended  and foreign keys'
929
		);
930
931
		$this->assertEquals(
932
			array(
933
				'ID',
934
				'SubclassDatabaseField',
935
				'ParentTeamID',
936
			),
937
			array_keys(DataObject::database_fields('DataObjectTest_SubTeam')),
938
			'databaseFields() on subclass contains only fields defined on instance'
939
		);
940
	}
941
942
	public function testSearchableFields() {
943
		$player = $this->objFromFixture('DataObjectTest_Player', 'captain1');
944
		$fields = $player->searchableFields();
945
		$this->assertArrayHasKey(
946
			'IsRetired',
947
			$fields,
948
			'Fields defined by $searchable_fields static are correctly detected'
949
		);
950
		$this->assertArrayHasKey(
951
			'ShirtNumber',
952
			$fields,
953
			'Fields defined by $searchable_fields static are correctly detected'
954
		);
955
956
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
957
		$fields = $team->searchableFields();
958
		$this->assertArrayHasKey(
959
			'Title',
960
			$fields,
961
			'Fields can be inherited from the $summary_fields static, including methods called on fields'
962
		);
963
		$this->assertArrayHasKey(
964
			'Captain.ShirtNumber',
965
			$fields,
966
			'Fields on related objects can be inherited from the $summary_fields static'
967
		);
968
		$this->assertArrayHasKey(
969
			'Captain.FavouriteTeam.Title',
970
			$fields,
971
			'Fields on related objects can be inherited from the $summary_fields static'
972
		);
973
974
		$testObj = new DataObjectTest_Fixture();
975
		$fields = $testObj->searchableFields();
976
		$this->assertEmpty($fields);
977
	}
978
979
	public function testCastingHelper() {
980
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
981
982
		$this->assertEquals('Varchar', $team->castingHelper('Title'), 'db field wasn\'t casted correctly');
983
		$this->assertEquals('HTMLVarchar', $team->castingHelper('DatabaseField'), 'db field wasn\'t casted correctly');
984
985
		$sponsor = $team->Sponsors()->first();
986
		$this->assertEquals('Int', $sponsor->castingHelper('SponsorFee'), 'many_many_extraFields not casted correctly');
987
	}
988
989
	public function testSummaryFieldsCustomLabels() {
990
		$team = $this->objFromFixture('DataObjectTest_Team', 'team1');
991
		$summaryFields = $team->summaryFields();
992
993
		$this->assertEquals(
994
			'Custom Title',
995
			$summaryFields['Title'],
996
			'Custom title is preserved'
997
		);
998
999
		$this->assertEquals(
1000
			'Captain\'s shirt number',
1001
			$summaryFields['Captain.ShirtNumber'],
1002
			'Custom title on relation is preserved'
1003
		);
1004
	}
1005
1006
	public function testDataObjectUpdate() {
1007
		/* update() calls can use the dot syntax to reference has_one relations and other methods that return
1008
		 * objects */
1009
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1010
		$team1->CaptainID = $this->idFromFixture('DataObjectTest_Player', 'captain1');
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<SilverStripe\ORM\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...
1011
1012
		$team1->update(array(
1013
			'DatabaseField' => 'Something',
1014
			'Captain.FirstName' => 'Jim',
1015
			'Captain.Email' => '[email protected]',
1016
			'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1017
		));
1018
1019
		/* Test the simple case of updating fields on the object itself */
1020
		$this->assertEquals('Something', $team1->DatabaseField);
1021
1022
		/* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in
1023
		 * the database.  Although update() doesn't usually write, it does write related records automatically. */
1024
		$captain1 = $this->objFromFixture('DataObjectTest_Player', 'captain1');
1025
		$this->assertEquals('Jim', $captain1->FirstName);
1026
		$this->assertEquals('[email protected]', $captain1->Email);
1027
1028
		/* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.
1029
		 * FavouriteTeam.Title made */
1030
		$reloadedTeam1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1031
		$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1032
	}
1033
1034
	public function testDataObjectUpdateNew() {
1035
		/* update() calls can use the dot syntax to reference has_one relations and other methods that return
1036
		 * objects */
1037
		$team1 = $this->objFromFixture('DataObjectTest_Team', 'team1');
1038
		$team1->CaptainID = 0;
0 ignored issues
show
Documentation introduced by
The property CaptainID does not exist on object<SilverStripe\ORM\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...
1039
1040
		$team1->update(array(
1041
			'Captain.FirstName' => 'Jim',
1042
			'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1043
		));
1044
		/* Test that the captain ID has been updated */
1045
		$this->assertGreaterThan(0, $team1->CaptainID);
1046
1047
		/* Fetch the newly created captain */
1048
		$captain1 = DataObjectTest_Player::get()->byID($team1->CaptainID);
1049
		$this->assertEquals('Jim', $captain1->FirstName);
1050
1051
		/* Grab the favourite team and make sure it has the correct values */
1052
		$reloadedTeam1 = $captain1->FavouriteTeam();
1053
		$this->assertEquals($reloadedTeam1->ID, $captain1->FavouriteTeamID);
1054
		$this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1055
	}
1056
1057
	public function testWritingInvalidDataObjectThrowsException() {
1058
		$validatedObject = new DataObjectTest_ValidatedObject();
1059
1060
		$this->setExpectedException('SilverStripe\\ORM\\ValidationException');
1061
		$validatedObject->write();
1062
	}
1063
1064
	public function testWritingValidDataObjectDoesntThrowException() {
1065
		$validatedObject = new DataObjectTest_ValidatedObject();
1066
		$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...
1067
1068
		$validatedObject->write();
1069
		$this->assertTrue($validatedObject->isInDB(), "Validated object was not saved to database");
1070
	}
1071
1072
	public function testSubclassCreation() {
1073
		/* Creating a new object of a subclass should set the ClassName field correctly */
1074
		$obj = new DataObjectTest_SubTeam();
1075
		$obj->write();
1076
		$this->assertEquals("DataObjectTest_SubTeam",
1077
			DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
1078
	}
1079
1080
	public function testForceInsert() {
1081
		/* If you set an ID on an object and pass forceInsert = true, then the object should be correctly created */
1082
		$conn = DB::get_conn();
1083
		if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', true);
1084
		$obj = new DataObjectTest_SubTeam();
1085
		$obj->ID = 1001;
1086
		$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...
1087
		$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...
1088
		$obj->write(false, true);
1089
		if(method_exists($conn, 'allowPrimaryKeyEditing')) $conn->allowPrimaryKeyEditing('DataObjectTest_Team', false);
1090
1091
		$this->assertEquals("DataObjectTest_SubTeam",
1092
			DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value());
1093
1094
		/* Check that it actually saves to the database with the correct ID */
1095
		$this->assertEquals("1001", DB::query(
1096
			"SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'")->value());
1097
		$this->assertEquals("1001",
1098
			DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value());
1099
	}
1100
1101
	public function TestHasOwnTable() {
1102
		/* Test DataObject::has_own_table() returns true if the object has $has_one or $db values */
1103
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Player"));
1104
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Team"));
1105
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_Fixture"));
1106
1107
		/* Root DataObject that always have a table, even if they lack both $db and $has_one */
1108
		$this->assertTrue(DataObject::has_own_table("DataObjectTest_FieldlessTable"));
1109
1110
		/* Subclasses without $db or $has_one don't have a table */
1111
		$this->assertFalse(DataObject::has_own_table("DataObjectTest_FieldlessSubTable"));
1112
1113
		/* Return false if you don't pass it a subclass of DataObject */
1114
		$this->assertFalse(DataObject::has_own_table("SilverStripe\\ORM\\DataObject"));
1115
		$this->assertFalse(DataObject::has_own_table("ViewableData"));
1116
		$this->assertFalse(DataObject::has_own_table("ThisIsntADataObject"));
1117
	}
1118
1119
	public function testMerge() {
1120
		// test right merge of subclasses
1121
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1122
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1123
		$leftOrigID = $left->ID;
1124
		$left->merge($right, 'right', false, false);
0 ignored issues
show
Bug introduced by
It seems like $right defined by $this->objFromFixture('D..._with_player_relation') on line 1122 can be null; however, SilverStripe\ORM\DataObject::merge() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1125
		$this->assertEquals(
1126
			$left->Title,
1127
			'Subteam 2',
1128
			'merge() with "right" priority overwrites fields with existing values on subclasses'
1129
		);
1130
		$this->assertEquals(
1131
			$left->ID,
1132
			$leftOrigID,
1133
			'merge() with "right" priority doesnt overwrite database ID'
1134
		);
1135
1136
		// test overwriteWithEmpty flag on existing left values
1137
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1138
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam3_with_empty_fields');
1139
		$left->merge($right, 'right', false, true);
0 ignored issues
show
Bug introduced by
It seems like $right defined by $this->objFromFixture('D...am3_with_empty_fields') on line 1138 can be null; however, SilverStripe\ORM\DataObject::merge() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

// Safe - Alternative 2
function withCheck2() {
    $x = mayReturnNull();
    if ($x instanceof stdClass) {
        doesNotAcceptNull($x);
    }
}
Loading history...
1140
		$this->assertEquals(
1141
			$left->Title,
1142
			'Subteam 3',
1143
			'merge() with $overwriteWithEmpty overwrites non-empty fields on left object'
1144
		);
1145
1146
		// test overwriteWithEmpty flag on empty left values
1147
		$left = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam1');
1148
		// $SubclassDatabaseField is empty on here
1149
		$right = $this->objFromFixture('DataObjectTest_SubTeam', 'subteam2_with_player_relation');
1150
		$left->merge($right, 'right', false, true);
0 ignored issues
show
Bug introduced by
It seems like $right defined by $this->objFromFixture('D..._with_player_relation') on line 1149 can be null; however, SilverStripe\ORM\DataObject::merge() does not accept null, maybe add an additional type check?

Unless you are absolutely sure that the expression can never be null because of other conditions, we strongly recommend to add an additional type check to your code:

/** @return stdClass|null */
function mayReturnNull() { }

function doesNotAcceptNull(stdClass $x) { }

// With potential error.
function withoutCheck() {
    $x = mayReturnNull();
    doesNotAcceptNull($x); // Potential error here.
}

// Safe - Alternative 1
function withCheck1() {
    $x = mayReturnNull();
    if ( ! $x instanceof stdClass) {
        throw new \LogicException('$x must be defined.');
    }
    doesNotAcceptNull($x);
}

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