Passed
Push — 4.1 ( e36c06...e72fc9 )
by
unknown
07:45
created

DataObjectTest::provideSingletons()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 21
Code Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 16
nc 1
nop 0
dl 0
loc 21
rs 9.7333
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use InvalidArgumentException;
6
use LogicException;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\i18n\i18n;
10
use SilverStripe\ORM\Connect\MySQLDatabase;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\ORM\DataObjectSchema;
13
use SilverStripe\ORM\DB;
14
use SilverStripe\ORM\FieldType\DBBoolean;
15
use SilverStripe\ORM\FieldType\DBDatetime;
16
use SilverStripe\ORM\FieldType\DBField;
17
use SilverStripe\ORM\FieldType\DBPolymorphicForeignKey;
18
use SilverStripe\ORM\FieldType\DBVarchar;
19
use SilverStripe\ORM\ManyManyList;
20
use SilverStripe\ORM\Tests\DataObjectTest\Player;
21
use SilverStripe\View\ViewableData;
22
use stdClass;
23
24
class DataObjectTest extends SapphireTest
25
{
26
27
    protected static $fixture_file = 'DataObjectTest.yml';
28
29
    /**
30
     * Standard set of dataobject test classes
31
     *
32
     * @var array
33
     */
34
    public static $extra_data_objects = array(
35
        DataObjectTest\Team::class,
36
        DataObjectTest\Fixture::class,
37
        DataObjectTest\SubTeam::class,
38
        DataObjectTest\OtherSubclassWithSameField::class,
39
        DataObjectTest\FieldlessTable::class,
40
        DataObjectTest\FieldlessSubTable::class,
41
        DataObjectTest\ValidatedObject::class,
42
        DataObjectTest\Player::class,
43
        DataObjectTest\TeamComment::class,
44
        DataObjectTest\EquipmentCompany::class,
45
        DataObjectTest\SubEquipmentCompany::class,
46
        DataObjectTest\ExtendedTeamComment::class,
47
        DataObjectTest\Company::class,
48
        DataObjectTest\Staff::class,
49
        DataObjectTest\CEO::class,
50
        DataObjectTest\Fan::class,
51
        DataObjectTest\Play::class,
52
        DataObjectTest\Ploy::class,
53
        DataObjectTest\Bogey::class,
54
        DataObjectTest\Sortable::class,
55
        DataObjectTest\Bracket::class,
56
        DataObjectTest\RelationParent::class,
57
        DataObjectTest\RelationChildFirst::class,
58
        DataObjectTest\RelationChildSecond::class,
59
    );
60
61
    public static function getExtraDataObjects()
62
    {
63
        return array_merge(
64
            DataObjectTest::$extra_data_objects,
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
65
            ManyManyListTest::$extra_data_objects
66
        );
67
    }
68
69
    /**
70
     * @dataProvider provideSingletons
71
     */
72
    public function testSingleton($inst, $defaultValue, $altDefaultValue)
73
    {
74
        $inst = $inst();
75
        // Test that populateDefaults() isn't called on singletons
76
        // which can lead to SQL errors during build, and endless loops
77
        if ($defaultValue) {
78
            $this->assertEquals($defaultValue, $inst->MyFieldWithDefault);
79
        } else {
80
            $this->assertEmpty($inst->MyFieldWithDefault);
81
        }
82
83
        if ($altDefaultValue) {
84
            $this->assertEquals($altDefaultValue, $inst->MyFieldWithAltDefault);
85
        } else {
86
            $this->assertEmpty($inst->MyFieldWithAltDefault);
87
        }
88
    }
89
90
    public function provideSingletons()
91
    {
92
        // because PHPUnit evalutes test providers *before* setUp methods
93
        // any extensions added in the setUp methods won't be available
94
        // we must return closures to generate the arguments at run time
95
        return array(
96
            'create() static method' => array(function () {
97
                return DataObjectTest\Fixture::create();
98
            }, 'Default Value', 'Default Value'),
99
            'New object creation' => array(function () {
100
                return new DataObjectTest\Fixture();
101
            }, 'Default Value', 'Default Value'),
102
            'singleton() function' => array(function () {
103
                return singleton(DataObjectTest\Fixture::class);
104
            }, null, null),
105
            'singleton() static method' => array(function () {
106
                return DataObjectTest\Fixture::singleton();
107
            }, null, null),
108
            'Manual constructor args' => array(function () {
109
                return new DataObjectTest\Fixture(null, true);
110
            }, null, null),
111
        );
112
    }
113
114
    public function testDb()
115
    {
116
        $schema = DataObject::getSchema();
117
        $dbFields = $schema->fieldSpecs(DataObjectTest\TeamComment::class);
118
119
        // Assert fields are included
120
        $this->assertArrayHasKey('Name', $dbFields);
121
122
        // Assert the base fields are included
123
        $this->assertArrayHasKey('Created', $dbFields);
124
        $this->assertArrayHasKey('LastEdited', $dbFields);
125
        $this->assertArrayHasKey('ClassName', $dbFields);
126
        $this->assertArrayHasKey('ID', $dbFields);
127
128
        // Assert that the correct field type is returned when passing a field
129
        $this->assertEquals('Varchar', $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Name'));
130
        $this->assertEquals('Text', $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Comment'));
131
132
        // Test with table required
133
        $this->assertEquals(
134
            DataObjectTest\TeamComment::class . '.Varchar',
135
            $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Name', DataObjectSchema::INCLUDE_CLASS)
136
        );
137
        $this->assertEquals(
138
            DataObjectTest\TeamComment::class . '.Text',
139
            $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Comment', DataObjectSchema::INCLUDE_CLASS)
140
        );
141
        $dbFields = $schema->fieldSpecs(DataObjectTest\ExtendedTeamComment::class);
142
143
        // fixed fields are still included in extended classes
144
        $this->assertArrayHasKey('Created', $dbFields);
145
        $this->assertArrayHasKey('LastEdited', $dbFields);
146
        $this->assertArrayHasKey('ClassName', $dbFields);
147
        $this->assertArrayHasKey('ID', $dbFields);
148
149
        // Assert overloaded fields have correct data type
150
        $this->assertEquals('HTMLText', $schema->fieldSpec(DataObjectTest\ExtendedTeamComment::class, 'Comment'));
151
        $this->assertEquals(
152
            'HTMLText',
153
            $dbFields['Comment'],
154
            'Calls to DataObject::db without a field specified return correct data types'
155
        );
156
157
        // assertEquals doesn't verify the order of array elements, so access keys manually to check order:
158
        // 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...
159
        $this->assertEquals(
160
            array(
161
                'Name',
162
                'Comment'
163
            ),
164
            array_slice(array_keys($dbFields), 4, 2),
165
            'DataObject::db returns fields in correct order'
166
        );
167
    }
168
169
    public function testConstructAcceptsValues()
170
    {
171
        // Values can be an array...
172
        $player = new DataObjectTest\Player(
173
            array(
174
                'FirstName' => 'James',
175
                'Surname' => 'Smith'
176
            )
177
        );
178
179
        $this->assertEquals('James', $player->FirstName);
180
        $this->assertEquals('Smith', $player->Surname);
181
182
        // ... or a stdClass inst
183
        $data = new stdClass();
184
        $data->FirstName = 'John';
185
        $data->Surname = 'Doe';
186
        $player = new DataObjectTest\Player($data);
0 ignored issues
show
Bug introduced by
$data of type stdClass is incompatible with the type array|null expected by parameter $record of SilverStripe\ORM\Tests\D...t\Player::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

186
        $player = new DataObjectTest\Player(/** @scrutinizer ignore-type */ $data);
Loading history...
187
188
        $this->assertEquals('John', $player->FirstName);
189
        $this->assertEquals('Doe', $player->Surname);
190
191
        // IDs should be stored as integers, not strings
192
        $player = new DataObjectTest\Player(array('ID' => '5'));
193
        $this->assertSame(5, $player->ID);
194
    }
195
196
    public function testValidObjectsForBaseFields()
197
    {
198
        $obj = new DataObjectTest\ValidatedObject();
199
200
        foreach (array('Created', 'LastEdited', 'ClassName', 'ID') as $field) {
201
            $helper = $obj->dbObject($field);
202
            $this->assertTrue(
203
                ($helper instanceof DBField),
204
                "for {$field} expected helper to be DBField, but was " . (is_object($helper) ? get_class($helper) : "null")
205
            );
206
        }
207
    }
208
209
    public function testDataIntegrityWhenTwoSubclassesHaveSameField()
210
    {
211
        // Save data into DataObjectTest_SubTeam.SubclassDatabaseField
212
        $obj = new DataObjectTest\SubTeam();
213
        $obj->SubclassDatabaseField = "obj-SubTeam";
0 ignored issues
show
Bug Best Practice introduced by
The property SubclassDatabaseField does not exist on SilverStripe\ORM\Tests\DataObjectTest\SubTeam. Since you implemented __set, consider adding a @property annotation.
Loading history...
214
        $obj->write();
215
216
        // Change the class
217
        $obj->ClassName = DataObjectTest\OtherSubclassWithSameField::class;
218
        $obj->write();
219
        $obj->flushCache();
220
221
        // Re-fetch from the database and confirm that the data is sourced from
222
        // OtherSubclassWithSameField.SubclassDatabaseField
223
        $obj = DataObject::get_by_id(DataObjectTest\Team::class, $obj->ID);
224
        $this->assertNull($obj->SubclassDatabaseField);
0 ignored issues
show
Bug Best Practice introduced by
The property SubclassDatabaseField does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
225
226
        // Confirm that save the object in the other direction.
227
        $obj->SubclassDatabaseField = 'obj-Other';
0 ignored issues
show
Bug Best Practice introduced by
The property SubclassDatabaseField does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
228
        $obj->write();
229
230
        $obj->ClassName = DataObjectTest\SubTeam::class;
231
        $obj->write();
232
        $obj->flushCache();
233
234
        // If we restore the class, the old value has been lying dormant and will be available again.
235
        // NOTE: This behaviour is volatile; we may change this in the future to clear fields that
236
        // are no longer relevant when changing ClassName
237
        $obj = DataObject::get_by_id(DataObjectTest\Team::class, $obj->ID);
238
        $this->assertEquals('obj-SubTeam', $obj->SubclassDatabaseField);
239
    }
240
241
    /**
242
     * Test deletion of DataObjects
243
     *   - Deleting using delete() on the DataObject
244
     *   - Deleting using DataObject::delete_by_id()
245
     */
246
    public function testDelete()
247
    {
248
        // Test deleting using delete() on the DataObject
249
        // Get the first page
250
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
251
        $objID = $obj->ID;
252
        // Check the page exists before deleting
253
        $this->assertTrue(is_object($obj) && $obj->exists());
254
        // Delete the page
255
        $obj->delete();
256
        // Check that page does not exist after deleting
257
        $obj = DataObject::get_by_id(DataObjectTest\Player::class, $objID);
258
        $this->assertTrue(!$obj || !$obj->exists());
259
260
261
        // Test deleting using DataObject::delete_by_id()
262
        // Get the second page
263
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
264
        $objID = $obj->ID;
265
        // Check the page exists before deleting
266
        $this->assertTrue(is_object($obj) && $obj->exists());
267
        // Delete the page
268
        DataObject::delete_by_id(DataObjectTest\Player::class, $obj->ID);
269
        // Check that page does not exist after deleting
270
        $obj = DataObject::get_by_id(DataObjectTest\Player::class, $objID);
271
        $this->assertTrue(!$obj || !$obj->exists());
272
    }
273
274
    /**
275
     * Test methods that get DataObjects
276
     *   - DataObject::get()
277
     *       - All records of a DataObject
278
     *       - Filtering
279
     *       - Sorting
280
     *       - Joins
281
     *       - Limit
282
     *       - Container class
283
     *   - DataObject::get_by_id()
284
     *   - DataObject::get_one()
285
     *        - With and without caching
286
     *        - With and without ordering
287
     */
288
    public function testGet()
289
    {
290
        // Test getting all records of a DataObject
291
        $comments = DataObject::get(DataObjectTest\TeamComment::class);
292
        $this->assertEquals(3, $comments->count());
293
294
        // Test WHERE clause
295
        $comments = DataObject::get(DataObjectTest\TeamComment::class, "\"Name\"='Bob'");
296
        $this->assertEquals(1, $comments->count());
297
        foreach ($comments as $comment) {
298
            $this->assertEquals('Bob', $comment->Name);
299
        }
300
301
        // Test sorting
302
        $comments = DataObject::get(DataObjectTest\TeamComment::class, '', "\"Name\" ASC");
303
        $this->assertEquals(3, $comments->count());
304
        $this->assertEquals('Bob', $comments->first()->Name);
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
305
        $comments = DataObject::get(DataObjectTest\TeamComment::class, '', "\"Name\" DESC");
306
        $this->assertEquals(3, $comments->count());
307
        $this->assertEquals('Phil', $comments->first()->Name);
308
309
        // Test limit
310
        $comments = DataObject::get(DataObjectTest\TeamComment::class, '', "\"Name\" ASC", '', '1,2');
311
        $this->assertEquals(2, $comments->count());
312
        $this->assertEquals('Joe', $comments->first()->Name);
313
        $this->assertEquals('Phil', $comments->last()->Name);
314
315
        // Test get_by_id()
316
        $captain1ID = $this->idFromFixture(DataObjectTest\Player::class, 'captain1');
317
        $captain1 = DataObject::get_by_id(DataObjectTest\Player::class, $captain1ID);
318
        $this->assertEquals('Captain', $captain1->FirstName);
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
319
320
        // Test get_one() without caching
321
        $comment1 = DataObject::get_one(
322
            DataObjectTest\TeamComment::class,
323
            array(
324
                '"DataObjectTest_TeamComment"."Name"' => 'Joe'
325
            ),
326
            false
327
        );
328
        $comment1->Comment = "Something Else";
0 ignored issues
show
Bug Best Practice introduced by
The property Comment does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
329
330
        $comment2 = DataObject::get_one(
331
            DataObjectTest\TeamComment::class,
332
            array(
333
                '"DataObjectTest_TeamComment"."Name"' => 'Joe'
334
            ),
335
            false
336
        );
337
        $this->assertNotEquals($comment1->Comment, $comment2->Comment);
0 ignored issues
show
Bug Best Practice introduced by
The property Comment does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
338
339
        // Test get_one() with caching
340
        $comment1 = DataObject::get_one(
341
            DataObjectTest\TeamComment::class,
342
            array(
343
                '"DataObjectTest_TeamComment"."Name"' => 'Bob'
344
            ),
345
            true
346
        );
347
        $comment1->Comment = "Something Else";
348
349
        $comment2 = DataObject::get_one(
350
            DataObjectTest\TeamComment::class,
351
            array(
352
                '"DataObjectTest_TeamComment"."Name"' => 'Bob'
353
            ),
354
            true
355
        );
356
        $this->assertEquals((string)$comment1->Comment, (string)$comment2->Comment);
357
358
        // Test get_one() with order by without caching
359
        $comment = DataObject::get_one(DataObjectTest\TeamComment::class, '', false, "\"Name\" ASC");
360
        $this->assertEquals('Bob', $comment->Name);
361
362
        $comment = DataObject::get_one(DataObjectTest\TeamComment::class, '', false, "\"Name\" DESC");
363
        $this->assertEquals('Phil', $comment->Name);
364
365
        // Test get_one() with order by with caching
366
        $comment = DataObject::get_one(DataObjectTest\TeamComment::class, '', true, '"Name" ASC');
367
        $this->assertEquals('Bob', $comment->Name);
368
        $comment = DataObject::get_one(DataObjectTest\TeamComment::class, '', true, '"Name" DESC');
369
        $this->assertEquals('Phil', $comment->Name);
370
    }
371
372
    public function testGetCaseInsensitive()
373
    {
374
        // Test get_one() with bad case on the classname
375
        // Note: This will succeed only if the underlying DB server supports case-insensitive
376
        // table names (e.g. such as MySQL, but not SQLite3)
377
        if (!(DB::get_conn() instanceof MySQLDatabase)) {
378
            $this->markTestSkipped('MySQL only');
379
        }
380
381
        $subteam1 = DataObject::get_one(
382
            strtolower(DataObjectTest\SubTeam::class),
383
            array(
384
                '"DataObjectTest_Team"."Title"' => 'Subteam 1'
385
            ),
386
            true
387
        );
388
        $this->assertNotEmpty($subteam1);
389
        $this->assertEquals($subteam1->Title, "Subteam 1");
390
    }
391
392
    public function testGetSubclassFields()
393
    {
394
        /* Test that fields / has_one relations from the parent table and the subclass tables are extracted */
395
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, "captain1");
396
        // Base field
397
        $this->assertEquals('Captain', $captain1->FirstName);
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
398
        // Subclass field
399
        $this->assertEquals('007', $captain1->ShirtNumber);
0 ignored issues
show
Bug Best Practice introduced by
The property ShirtNumber does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
400
        // Subclass has_one relation
401
        $this->assertEquals($this->idFromFixture(DataObjectTest\Team::class, 'team1'), $captain1->FavouriteTeamID);
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteTeamID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
402
    }
403
404
    public function testGetRelationClass()
405
    {
406
        $obj = new DataObjectTest\Player();
0 ignored issues
show
Unused Code introduced by
The assignment to $obj is dead and can be removed.
Loading history...
407
        $this->assertEquals(
408
            singleton(DataObjectTest\Player::class)->getRelationClass('FavouriteTeam'),
409
            DataObjectTest\Team::class,
410
            'has_one is properly inspected'
411
        );
412
        $this->assertEquals(
413
            singleton(DataObjectTest\Company::class)->getRelationClass('CurrentStaff'),
414
            DataObjectTest\Staff::class,
415
            'has_many is properly inspected'
416
        );
417
        $this->assertEquals(
418
            singleton(DataObjectTest\Team::class)->getRelationClass('Players'),
419
            DataObjectTest\Player::class,
420
            'many_many is properly inspected'
421
        );
422
        $this->assertEquals(
423
            singleton(DataObjectTest\Player::class)->getRelationClass('Teams'),
424
            DataObjectTest\Team::class,
425
            'belongs_many_many is properly inspected'
426
        );
427
        $this->assertEquals(
428
            singleton(DataObjectTest\CEO::class)->getRelationClass('Company'),
429
            DataObjectTest\Company::class,
430
            'belongs_to is properly inspected'
431
        );
432
        $this->assertEquals(
433
            singleton(DataObjectTest\Fan::class)->getRelationClass('Favourite'),
434
            DataObject::class,
435
            'polymorphic has_one is properly inspected'
436
        );
437
    }
438
439
    /**
440
     * Test that has_one relations can be retrieved
441
     */
442
    public function testGetHasOneRelations()
443
    {
444
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, "captain1");
445
        $team1ID = $this->idFromFixture(DataObjectTest\Team::class, 'team1');
446
447
        // There will be a field called (relname)ID that contains the ID of the
448
        // object linked to via the has_one relation
449
        $this->assertEquals($team1ID, $captain1->FavouriteTeamID);
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteTeamID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
450
451
        // There will be a method called $obj->relname() that returns the object itself
452
        $this->assertEquals($team1ID, $captain1->FavouriteTeam()->ID);
0 ignored issues
show
Bug introduced by
The method FavouriteTeam() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

452
        $this->assertEquals($team1ID, $captain1->/** @scrutinizer ignore-call */ FavouriteTeam()->ID);
Loading history...
453
454
        // Test that getNonReciprocalComponent can find has_one from the has_many end
455
        $this->assertEquals(
456
            $team1ID,
457
            $captain1->inferReciprocalComponent(DataObjectTest\Team::class, 'PlayerFans')->ID
0 ignored issues
show
Bug Best Practice introduced by
The property ID does not exist on SilverStripe\ORM\DataList. Since you implemented __get, consider adding a @property annotation.
Loading history...
458
        );
459
460
        // Check entity with polymorphic has-one
461
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, "fan1");
462
        $this->assertTrue((bool)$fan1->hasValue('Favourite'));
463
464
        // There will be fields named (relname)ID and (relname)Class for polymorphic
465
        // entities
466
        $this->assertEquals($team1ID, $fan1->FavouriteID);
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
467
        $this->assertEquals(DataObjectTest\Team::class, $fan1->FavouriteClass);
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteClass does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
468
469
        // There will be a method called $obj->relname() that returns the object itself
470
        $favourite = $fan1->Favourite();
0 ignored issues
show
Bug introduced by
The method Favourite() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

470
        /** @scrutinizer ignore-call */ 
471
        $favourite = $fan1->Favourite();
Loading history...
471
        $this->assertEquals($team1ID, $favourite->ID);
472
        $this->assertInstanceOf(DataObjectTest\Team::class, $favourite);
473
474
        // check behaviour of dbObject with polymorphic relations
475
        $favouriteDBObject = $fan1->dbObject('Favourite');
476
        $favouriteValue = $favouriteDBObject->getValue();
477
        $this->assertInstanceOf(DBPolymorphicForeignKey::class, $favouriteDBObject);
478
        $this->assertEquals($favourite->ID, $favouriteValue->ID);
479
        $this->assertEquals($favourite->ClassName, $favouriteValue->ClassName);
480
    }
481
482
    public function testLimitAndCount()
483
    {
484
        $players = DataObject::get(DataObjectTest\Player::class);
485
486
        // There's 4 records in total
487
        $this->assertEquals(4, $players->count());
488
489
        // Testing "##, ##" syntax
490
        $this->assertEquals(4, $players->limit(20)->count());
491
        $this->assertEquals(4, $players->limit(20, 0)->count());
492
        $this->assertEquals(0, $players->limit(20, 20)->count());
493
        $this->assertEquals(2, $players->limit(2, 0)->count());
494
        $this->assertEquals(1, $players->limit(5, 3)->count());
495
    }
496
497
    public function testWriteNoChangesDoesntUpdateLastEdited()
498
    {
499
        // set mock now so we can be certain of LastEdited time for our test
500
        DBDatetime::set_mock_now('2017-01-01 00:00:00');
501
        $obj = new Player();
502
        $obj->FirstName = 'Test';
503
        $obj->Surname = 'Plater';
504
        $obj->Email = '[email protected]';
505
        $obj->write();
506
        $this->assertEquals('2017-01-01 00:00:00', $obj->LastEdited);
507
        $writtenObj = Player::get()->byID($obj->ID);
508
        $this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
509
510
        // set mock now so we get a new LastEdited if, for some reason, it's updated
511
        DBDatetime::set_mock_now('2017-02-01 00:00:00');
512
        $writtenObj->write();
513
        $this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
514
        $this->assertEquals($obj->ID, $writtenObj->ID);
515
516
        $reWrittenObj = Player::get()->byID($writtenObj->ID);
517
        $this->assertEquals('2017-01-01 00:00:00', $reWrittenObj->LastEdited);
518
    }
519
520
    /**
521
     * Test writing of database columns which don't correlate to a DBField,
522
     * e.g. all relation fields on has_one/has_many like "ParentID".
523
     */
524
    public function testWritePropertyWithoutDBField()
525
    {
526
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
527
        $obj->FavouriteTeamID = 99;
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteTeamID does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
528
        $obj->write();
529
530
        // reload the page from the database
531
        $savedObj = DataObject::get_by_id(DataObjectTest\Player::class, $obj->ID);
532
        $this->assertTrue($savedObj->FavouriteTeamID == 99);
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteTeamID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
533
534
        // Test with porymorphic relation
535
        $obj2 = $this->objFromFixture(DataObjectTest\Fan::class, "fan1");
536
        $obj2->FavouriteID = 99;
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteID does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
537
        $obj2->FavouriteClass = DataObjectTest\Player::class;
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteClass does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
538
        $obj2->write();
539
540
        $savedObj2 = DataObject::get_by_id(DataObjectTest\Fan::class, $obj2->ID);
541
        $this->assertTrue($savedObj2->FavouriteID == 99);
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
542
        $this->assertTrue($savedObj2->FavouriteClass == DataObjectTest\Player::class);
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteClass does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
543
    }
544
545
    /**
546
     * Test has many relationships
547
     *   - Test getComponents() gets the ComponentSet of the other side of the relation
548
     *   - Test the IDs on the DataObjects are set correctly
549
     */
550
    public function testHasManyRelationships()
551
    {
552
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
553
554
        // Test getComponents() gets the ComponentSet of the other side of the relation
555
        $this->assertTrue($team1->Comments()->count() == 2);
0 ignored issues
show
Bug introduced by
The method Comments() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

555
        $this->assertTrue($team1->/** @scrutinizer ignore-call */ Comments()->count() == 2);
Loading history...
556
557
        $team1Comments = [
558
            ['Comment' => 'This is a team comment by Joe'],
559
            ['Comment' => 'This is a team comment by Bob'],
560
        ];
561
562
        // Test the IDs on the DataObjects are set correctly
563
        $this->assertListEquals($team1Comments, $team1->Comments());
564
565
        // Test that has_many can be infered from the has_one via getNonReciprocalComponent
566
        $this->assertListEquals(
567
            $team1Comments,
568
            $team1->inferReciprocalComponent(DataObjectTest\TeamComment::class, 'Team')
0 ignored issues
show
Bug introduced by
It seems like $team1->inferReciprocalC...Comment::class, 'Team') can also be of type SilverStripe\ORM\DataObject; however, parameter $list of SilverStripe\Dev\SapphireTest::assertListEquals() does only seem to accept SilverStripe\ORM\SS_List, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

568
            /** @scrutinizer ignore-type */ $team1->inferReciprocalComponent(DataObjectTest\TeamComment::class, 'Team')
Loading history...
569
        );
570
571
        // Test that we can add and remove items that already exist in the database
572
        $newComment = new DataObjectTest\TeamComment();
573
        $newComment->Name = "Automated commenter";
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\Tests\DataObjectTest\TeamComment. Since you implemented __set, consider adding a @property annotation.
Loading history...
574
        $newComment->Comment = "This is a new comment";
0 ignored issues
show
Bug Best Practice introduced by
The property Comment does not exist on SilverStripe\ORM\Tests\DataObjectTest\TeamComment. Since you implemented __set, consider adding a @property annotation.
Loading history...
575
        $newComment->write();
576
        $team1->Comments()->add($newComment);
577
        $this->assertEquals($team1->ID, $newComment->TeamID);
0 ignored issues
show
Bug Best Practice introduced by
The property TeamID does not exist on SilverStripe\ORM\Tests\DataObjectTest\TeamComment. Since you implemented __get, consider adding a @property annotation.
Loading history...
578
579
        $comment1 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment1');
580
        $comment2 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment2');
581
        $team1->Comments()->remove($comment2);
582
583
        $team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
0 ignored issues
show
Bug introduced by
The method sort() does not exist on SilverStripe\ORM\SS_List. It seems like you code against a sub-type of said class. However, the method does not exist in SilverStripe\ORM\Filterable or SilverStripe\ORM\Limitable. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

583
        $team1CommentIDs = $team1->Comments()->/** @scrutinizer ignore-call */ sort('ID')->column('ID');
Loading history...
584
        $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
585
586
        // Test that removing an item from a list doesn't remove it from the same
587
        // relation belonging to a different object
588
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
589
        $team2 = $this->objFromFixture(DataObjectTest\Team::class, 'team2');
590
        $team2->Comments()->remove($comment1);
591
        $team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
592
        $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
593
    }
594
595
596
    /**
597
     * Test has many relationships against polymorphic has_one fields
598
     *   - Test getComponents() gets the ComponentSet of the other side of the relation
599
     *   - Test the IDs on the DataObjects are set correctly
600
     */
601
    public function testHasManyPolymorphicRelationships()
602
    {
603
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
604
605
        // Test getComponents() gets the ComponentSet of the other side of the relation
606
        $this->assertTrue($team1->Fans()->count() == 2);
0 ignored issues
show
Bug introduced by
The method Fans() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

606
        $this->assertTrue($team1->/** @scrutinizer ignore-call */ Fans()->count() == 2);
Loading history...
607
608
        // Test the IDs/Classes on the DataObjects are set correctly
609
        foreach ($team1->Fans() as $fan) {
610
            $this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID');
611
            $this->assertEquals(DataObjectTest\Team::class, $fan->FavouriteClass, 'Fan has the correct FavouriteClass');
612
        }
613
614
        // Test that we can add and remove items that already exist in the database
615
        $newFan = new DataObjectTest\Fan();
616
        $newFan->Name = "New fan";
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fan. Since you implemented __set, consider adding a @property annotation.
Loading history...
617
        $newFan->write();
618
        $team1->Fans()->add($newFan);
619
        $this->assertEquals($team1->ID, $newFan->FavouriteID, 'Newly created fan has the correct FavouriteID');
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteID does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fan. Since you implemented __get, consider adding a @property annotation.
Loading history...
620
        $this->assertEquals(
621
            DataObjectTest\Team::class,
622
            $newFan->FavouriteClass,
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteClass does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fan. Since you implemented __get, consider adding a @property annotation.
Loading history...
623
            'Newly created fan has the correct FavouriteClass'
624
        );
625
626
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
627
        $fan3 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan3');
628
        $team1->Fans()->remove($fan3);
629
630
        $team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
631
        $this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
632
633
        // Test that removing an item from a list doesn't remove it from the same
634
        // relation belonging to a different object
635
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
636
        $player1 = $this->objFromFixture(DataObjectTest\Player::class, 'player1');
637
        $player1->Fans()->remove($fan1);
638
        $team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
639
        $this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
640
    }
641
642
643
    public function testHasOneRelationship()
644
    {
645
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
646
        $player1 = $this->objFromFixture(DataObjectTest\Player::class, 'player1');
647
        $player2 = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
648
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
649
650
        // Test relation probing
651
        $this->assertFalse((bool)$team1->hasValue('Captain', null, false));
652
        $this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
653
654
        // Add a captain to team 1
655
        $team1->setField('CaptainID', $player1->ID);
656
        $team1->write();
657
658
        $this->assertTrue((bool)$team1->hasValue('Captain', null, false));
659
        $this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
660
661
        $this->assertEquals(
662
            $player1->ID,
663
            $team1->Captain()->ID,
0 ignored issues
show
Bug introduced by
The method Captain() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

663
            $team1->/** @scrutinizer ignore-call */ 
664
                    Captain()->ID,
Loading history...
664
            'The captain exists for team 1'
665
        );
666
        $this->assertEquals(
667
            $player1->ID,
668
            $team1->getComponent('Captain')->ID,
669
            'The captain exists through the component getter'
670
        );
671
672
        $this->assertEquals(
673
            $team1->Captain()->FirstName,
674
            'Player 1',
675
            'Player 1 is the captain'
676
        );
677
        $this->assertEquals(
678
            $team1->getComponent('Captain')->FirstName,
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
679
            'Player 1',
680
            'Player 1 is the captain'
681
        );
682
683
        $team1->CaptainID = $player2->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property CaptainID does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
684
        $team1->write();
685
686
        $this->assertEquals($player2->ID, $team1->Captain()->ID);
687
        $this->assertEquals($player2->ID, $team1->getComponent('Captain')->ID);
688
        $this->assertEquals('Player 2', $team1->Captain()->FirstName);
689
        $this->assertEquals('Player 2', $team1->getComponent('Captain')->FirstName);
690
691
692
        // Set the favourite team for fan1
693
        $fan1->setField('FavouriteID', $team1->ID);
694
        $fan1->setField('FavouriteClass', get_class($team1));
695
696
        $this->assertEquals($team1->ID, $fan1->Favourite()->ID, 'The team is assigned to fan 1');
697
        $this->assertInstanceOf(get_class($team1), $fan1->Favourite(), 'The team is assigned to fan 1');
698
        $this->assertEquals(
699
            $team1->ID,
700
            $fan1->getComponent('Favourite')->ID,
701
            'The team exists through the component getter'
702
        );
703
        $this->assertInstanceOf(
704
            get_class($team1),
705
            $fan1->getComponent('Favourite'),
706
            'The team exists through the component getter'
707
        );
708
709
        $this->assertEquals(
710
            $fan1->Favourite()->Title,
711
            'Team 1',
712
            'Team 1 is the favourite'
713
        );
714
        $this->assertEquals(
715
            $fan1->getComponent('Favourite')->Title,
716
            'Team 1',
717
            'Team 1 is the favourite'
718
        );
719
    }
720
721
    /**
722
     * Test has_one used as field getter/setter
723
     */
724
    public function testHasOneAsField()
725
    {
726
        /** @var DataObjectTest\Team $team1 */
727
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
728
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
729
        $captain2 = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
730
731
        // Setter: By RelationID
732
        $team1->CaptainID = $captain1->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property CaptainID does not exist on SilverStripe\ORM\Tests\DataObjectTest\Team. Since you implemented __set, consider adding a @property annotation.
Loading history...
733
        $team1->write();
734
        $this->assertEquals($captain1->ID, $team1->Captain->ID);
0 ignored issues
show
Bug Best Practice introduced by
The property Captain does not exist on SilverStripe\ORM\Tests\DataObjectTest\Team. Since you implemented __get, consider adding a @property annotation.
Loading history...
735
736
        // Setter: New object
737
        $team1->Captain = $captain2;
0 ignored issues
show
Bug Best Practice introduced by
The property Captain does not exist on SilverStripe\ORM\Tests\DataObjectTest\Team. Since you implemented __set, consider adding a @property annotation.
Loading history...
738
        $team1->write();
739
        $this->assertEquals($captain2->ID, $team1->Captain->ID);
740
741
        // Setter: Custom data (required by DataDifferencer)
742
        $team1->Captain = DBField::create_field('HTMLFragment', '<p>No captain</p>');
743
        $this->assertEquals('<p>No captain</p>', $team1->Captain);
744
    }
745
746
    /**
747
     * @todo Extend type change tests (e.g. '0'==NULL)
748
     */
749
    public function testChangedFields()
750
    {
751
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
752
        $obj->FirstName = 'Captain-changed';
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
753
        $obj->IsRetired = true;
0 ignored issues
show
Bug Best Practice introduced by
The property IsRetired does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
754
755
        $this->assertEquals(
756
            $obj->getChangedFields(true, DataObject::CHANGE_STRICT),
757
            array(
758
                'FirstName' => array(
759
                    'before' => 'Captain',
760
                    'after' => 'Captain-changed',
761
                    'level' => DataObject::CHANGE_VALUE
762
                ),
763
                'IsRetired' => array(
764
                    'before' => 1,
765
                    'after' => true,
766
                    'level' => DataObject::CHANGE_STRICT
767
                )
768
            ),
769
            'Changed fields are correctly detected with strict type changes (level=1)'
770
        );
771
772
        $this->assertEquals(
773
            $obj->getChangedFields(true, DataObject::CHANGE_VALUE),
774
            array(
775
                'FirstName' => array(
776
                    'before' => 'Captain',
777
                    'after' => 'Captain-changed',
778
                    'level' => DataObject::CHANGE_VALUE
779
                )
780
            ),
781
            'Changed fields are correctly detected while ignoring type changes (level=2)'
782
        );
783
784
        $newObj = new DataObjectTest\Player();
785
        $newObj->FirstName = "New Player";
786
        $this->assertEquals(
787
            array(
788
                'FirstName' => array(
789
                    'before' => null,
790
                    'after' => 'New Player',
791
                    'level' => DataObject::CHANGE_VALUE
792
                )
793
            ),
794
            $newObj->getChangedFields(true, DataObject::CHANGE_VALUE),
795
            'Initialised fields are correctly detected as full changes'
796
        );
797
    }
798
799
    /**
800
     * @skipUpgrade
801
     */
802
    public function testIsChanged()
803
    {
804
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
805
        $obj->NonDBField = 'bob';
0 ignored issues
show
Bug Best Practice introduced by
The property NonDBField does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
806
        $obj->FirstName = 'Captain-changed';
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
807
        $obj->IsRetired = true; // type change only, database stores "1"
0 ignored issues
show
Bug Best Practice introduced by
The property IsRetired does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
808
809
        // Now that DB fields are changed, isChanged is true
810
        $this->assertTrue($obj->isChanged('NonDBField'));
811
        $this->assertFalse($obj->isChanged('NonField'));
812
        $this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
813
        $this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
814
        $this->assertTrue($obj->isChanged('IsRetired', DataObject::CHANGE_STRICT));
815
        $this->assertFalse($obj->isChanged('IsRetired', DataObject::CHANGE_VALUE));
816
        $this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
817
        $this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
818
819
        $newObj = new DataObjectTest\Player();
820
        $newObj->FirstName = "New Player";
821
        $this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
822
        $this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
823
        $this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
824
        $this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
825
826
        $newObj->write();
827
        $this->assertFalse($newObj->ischanged());
828
        $this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
829
        $this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
830
        $this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
831
        $this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
832
833
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
834
        $obj->FirstName = null;
835
        $this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
836
        $this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
837
838
        /* Test when there's not field provided */
839
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
840
        $this->assertFalse($obj->isChanged());
841
        $obj->NonDBField = 'new value';
842
        $this->assertFalse($obj->isChanged());
843
        $obj->FirstName = "New Player";
844
        $this->assertTrue($obj->isChanged());
845
846
        $obj->write();
847
        $this->assertFalse($obj->isChanged());
848
    }
849
850
    public function testRandomSort()
851
    {
852
        /* If we perform the same regularly sorted query twice, it should return the same results */
853
        $itemsA = DataObject::get(DataObjectTest\TeamComment::class, "", "ID");
854
        foreach ($itemsA as $item) {
855
            $keysA[] = $item->ID;
856
        }
857
858
        $itemsB = DataObject::get(DataObjectTest\TeamComment::class, "", "ID");
859
        foreach ($itemsB as $item) {
860
            $keysB[] = $item->ID;
861
        }
862
863
        /* Test when there's not field provided */
864
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
865
        $obj->FirstName = "New Player";
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
866
        $this->assertTrue($obj->isChanged());
867
868
        $obj->write();
869
        $this->assertFalse($obj->isChanged());
870
871
        /* If we perform the same random query twice, it shouldn't return the same results */
872
        $itemsA = DataObject::get(DataObjectTest\TeamComment::class, "", DB::get_conn()->random());
873
        $itemsB = DataObject::get(DataObjectTest\TeamComment::class, "", DB::get_conn()->random());
874
        $itemsC = DataObject::get(DataObjectTest\TeamComment::class, "", DB::get_conn()->random());
875
        $itemsD = DataObject::get(DataObjectTest\TeamComment::class, "", DB::get_conn()->random());
876
        foreach ($itemsA as $item) {
877
            $keysA[] = $item->ID;
878
        }
879
        foreach ($itemsB as $item) {
880
            $keysB[] = $item->ID;
881
        }
882
        foreach ($itemsC as $item) {
883
            $keysC[] = $item->ID;
884
        }
885
        foreach ($itemsD as $item) {
886
            $keysD[] = $item->ID;
887
        }
888
889
        // These shouldn't all be the same (run it 4 times to minimise chance of an accidental collision)
890
        // There's about a 1 in a billion chance of an accidental collision
891
        $this->assertTrue($keysA != $keysB || $keysB != $keysC || $keysC != $keysD);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $keysB seems to be defined by a foreach iteration on line 859. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
Comprehensibility Best Practice introduced by
The variable $keysA seems to be defined by a foreach iteration on line 854. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
Comprehensibility Best Practice introduced by
The variable $keysC seems to be defined by a foreach iteration on line 882. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
Comprehensibility Best Practice introduced by
The variable $keysD seems to be defined by a foreach iteration on line 885. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
892
    }
893
894
    public function testWriteSavesToHasOneRelations()
895
    {
896
        /* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
897
        $team = new DataObjectTest\Team();
898
        $captainID = $this->idFromFixture(DataObjectTest\Player::class, 'player1');
899
        $team->CaptainID = $captainID;
0 ignored issues
show
Bug Best Practice introduced by
The property CaptainID does not exist on SilverStripe\ORM\Tests\DataObjectTest\Team. Since you implemented __set, consider adding a @property annotation.
Loading history...
900
        $team->write();
901
        $this->assertEquals(
902
            $captainID,
903
            DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()
904
        );
905
906
        /* After giving it a value, you should also be able to set it back to null */
907
        $team->CaptainID = '';
908
        $team->write();
909
        $this->assertEquals(
910
            0,
911
            DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()
912
        );
913
914
        /* You should also be able to save a blank to it when it's first created */
915
        $team = new DataObjectTest\Team();
916
        $team->CaptainID = '';
917
        $team->write();
918
        $this->assertEquals(
919
            0,
920
            DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()
921
        );
922
923
        /* Ditto for existing records without a value */
924
        $existingTeam = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
925
        $existingTeam->CaptainID = '';
0 ignored issues
show
Bug Best Practice introduced by
The property CaptainID does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
926
        $existingTeam->write();
927
        $this->assertEquals(
928
            0,
929
            DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value()
930
        );
931
    }
932
933
    public function testCanAccessHasOneObjectsAsMethods()
934
    {
935
        /* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the
936
        * object itself should be accessible as $obj->Captain() */
937
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
938
        $captainID = $this->idFromFixture(DataObjectTest\Player::class, 'captain1');
939
940
        $team->CaptainID = $captainID;
0 ignored issues
show
Bug Best Practice introduced by
The property CaptainID does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
941
        $this->assertNotNull($team->Captain());
942
        $this->assertEquals($captainID, $team->Captain()->ID);
943
944
        // Test for polymorphic has_one relations
945
        $fan = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
946
        $fan->FavouriteID = $team->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteID does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
947
        $fan->FavouriteClass = DataObjectTest\Team::class;
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteClass does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
948
        $this->assertNotNull($fan->Favourite());
949
        $this->assertEquals($team->ID, $fan->Favourite()->ID);
950
        $this->assertInstanceOf(DataObjectTest\Team::class, $fan->Favourite());
951
    }
952
953
    public function testFieldNamesThatMatchMethodNamesWork()
954
    {
955
        /* Check that a field name that corresponds to a method on DataObject will still work */
956
        $obj = new DataObjectTest\Fixture();
957
        $obj->Data = "value1";
0 ignored issues
show
Bug Best Practice introduced by
The property Data does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
958
        $obj->DbObject = "value2";
0 ignored issues
show
Bug Best Practice introduced by
The property DbObject does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
959
        $obj->Duplicate = "value3";
0 ignored issues
show
Bug Best Practice introduced by
The property Duplicate does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
960
        $obj->write();
961
962
        $this->assertNotNull($obj->ID);
963
        $this->assertEquals(
964
            'value1',
965
            DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()
966
        );
967
        $this->assertEquals(
968
            'value2',
969
            DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()
970
        );
971
        $this->assertEquals(
972
            'value3',
973
            DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()
974
        );
975
    }
976
977
    /**
978
     * @todo Re-enable all test cases for field existence after behaviour has been fixed
979
     */
980
    public function testFieldExistence()
981
    {
982
        $teamInstance = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
983
        $teamSingleton = singleton(DataObjectTest\Team::class);
984
985
        $subteamInstance = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
986
        $schema = DataObject::getSchema();
987
988
        /* hasField() singleton checks */
989
        $this->assertTrue(
990
            $teamSingleton->hasField('ID'),
991
            'hasField() finds built-in fields in singletons'
992
        );
993
        $this->assertTrue(
994
            $teamSingleton->hasField('Title'),
995
            'hasField() finds custom fields in singletons'
996
        );
997
998
        /* hasField() instance checks */
999
        $this->assertFalse(
1000
            $teamInstance->hasField('NonExistingField'),
1001
            'hasField() doesnt find non-existing fields in instances'
1002
        );
1003
        $this->assertTrue(
1004
            $teamInstance->hasField('ID'),
1005
            'hasField() finds built-in fields in instances'
1006
        );
1007
        $this->assertTrue(
1008
            $teamInstance->hasField('Created'),
1009
            'hasField() finds built-in fields in instances'
1010
        );
1011
        $this->assertTrue(
1012
            $teamInstance->hasField('DatabaseField'),
1013
            'hasField() finds custom fields in instances'
1014
        );
1015
        //$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...
1016
        //'hasField() doesnt find subclass fields in parentclass instances');
1017
        $this->assertTrue(
1018
            $teamInstance->hasField('DynamicField'),
1019
            'hasField() finds dynamic getters in instances'
1020
        );
1021
        $this->assertTrue(
1022
            $teamInstance->hasField('HasOneRelationshipID'),
1023
            'hasField() finds foreign keys in instances'
1024
        );
1025
        $this->assertTrue(
1026
            $teamInstance->hasField('ExtendedDatabaseField'),
1027
            'hasField() finds extended fields in instances'
1028
        );
1029
        $this->assertTrue(
1030
            $teamInstance->hasField('ExtendedHasOneRelationshipID'),
1031
            'hasField() finds extended foreign keys in instances'
1032
        );
1033
        //$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...
1034
        //'hasField() includes extended dynamic getters in instances');
1035
1036
        /* hasField() subclass checks */
1037
        $this->assertTrue(
1038
            $subteamInstance->hasField('ID'),
1039
            'hasField() finds built-in fields in subclass instances'
1040
        );
1041
        $this->assertTrue(
1042
            $subteamInstance->hasField('Created'),
1043
            'hasField() finds built-in fields in subclass instances'
1044
        );
1045
        $this->assertTrue(
1046
            $subteamInstance->hasField('DatabaseField'),
1047
            'hasField() finds custom fields in subclass instances'
1048
        );
1049
        $this->assertTrue(
1050
            $subteamInstance->hasField('SubclassDatabaseField'),
1051
            'hasField() finds custom fields in subclass instances'
1052
        );
1053
        $this->assertTrue(
1054
            $subteamInstance->hasField('DynamicField'),
1055
            'hasField() finds dynamic getters in subclass instances'
1056
        );
1057
        $this->assertTrue(
1058
            $subteamInstance->hasField('HasOneRelationshipID'),
1059
            'hasField() finds foreign keys in subclass instances'
1060
        );
1061
        $this->assertTrue(
1062
            $subteamInstance->hasField('ExtendedDatabaseField'),
1063
            'hasField() finds extended fields in subclass instances'
1064
        );
1065
        $this->assertTrue(
1066
            $subteamInstance->hasField('ExtendedHasOneRelationshipID'),
1067
            'hasField() finds extended foreign keys in subclass instances'
1068
        );
1069
1070
        /* hasDatabaseField() singleton checks */
1071
        //$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...
1072
        //'hasDatabaseField() finds built-in fields in singletons');
1073
        $this->assertNotEmpty(
1074
            $schema->fieldSpec(DataObjectTest\Team::class, 'Title'),
1075
            'hasDatabaseField() finds custom fields in singletons'
1076
        );
1077
1078
        /* hasDatabaseField() instance checks */
1079
        $this->assertNull(
1080
            $schema->fieldSpec(DataObjectTest\Team::class, 'NonExistingField'),
1081
            'hasDatabaseField() doesnt find non-existing fields in instances'
1082
        );
1083
        //$this->assertNotEmpty($schema->fieldSpec(DataObjectTest_Team::class, 'ID'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% 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...
1084
        //'hasDatabaseField() finds built-in fields in instances');
1085
        $this->assertNotEmpty(
1086
            $schema->fieldSpec(DataObjectTest\Team::class, 'Created'),
1087
            'hasDatabaseField() finds built-in fields in instances'
1088
        );
1089
        $this->assertNotEmpty(
1090
            $schema->fieldSpec(DataObjectTest\Team::class, 'DatabaseField'),
1091
            'hasDatabaseField() finds custom fields in instances'
1092
        );
1093
        $this->assertNull(
1094
            $schema->fieldSpec(DataObjectTest\Team::class, 'SubclassDatabaseField'),
1095
            'hasDatabaseField() doesnt find subclass fields in parentclass instances'
1096
        );
1097
        //$this->assertNull($schema->fieldSpec(DataObjectTest_Team::class, 'DynamicField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% 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...
1098
        //'hasDatabaseField() doesnt dynamic getters in instances');
1099
        $this->assertNotEmpty(
1100
            $schema->fieldSpec(DataObjectTest\Team::class, 'HasOneRelationshipID'),
1101
            'hasDatabaseField() finds foreign keys in instances'
1102
        );
1103
        $this->assertNotEmpty(
1104
            $schema->fieldSpec(DataObjectTest\Team::class, 'ExtendedDatabaseField'),
1105
            'hasDatabaseField() finds extended fields in instances'
1106
        );
1107
        $this->assertNotEmpty(
1108
            $schema->fieldSpec(DataObjectTest\Team::class, 'ExtendedHasOneRelationshipID'),
1109
            'hasDatabaseField() finds extended foreign keys in instances'
1110
        );
1111
        $this->assertNull(
1112
            $schema->fieldSpec(DataObjectTest\Team::class, 'ExtendedDynamicField'),
1113
            'hasDatabaseField() doesnt include extended dynamic getters in instances'
1114
        );
1115
1116
        /* hasDatabaseField() subclass checks */
1117
        $this->assertNotEmpty(
1118
            $schema->fieldSpec(DataObjectTest\SubTeam::class, 'DatabaseField'),
1119
            'hasField() finds custom fields in subclass instances'
1120
        );
1121
        $this->assertNotEmpty(
1122
            $schema->fieldSpec(DataObjectTest\SubTeam::class, 'SubclassDatabaseField'),
1123
            'hasField() finds custom fields in subclass instances'
1124
        );
1125
    }
1126
1127
    /**
1128
     * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed
1129
     */
1130
    public function testFieldInheritance()
1131
    {
1132
        $schema = DataObject::getSchema();
1133
1134
        // Test logical fields (including composite)
1135
        $teamSpecifications = $schema->fieldSpecs(DataObjectTest\Team::class);
1136
        $expected = array(
1137
            'ID',
1138
            'ClassName',
1139
            'LastEdited',
1140
            'Created',
1141
            'Title',
1142
            'DatabaseField',
1143
            'ExtendedDatabaseField',
1144
            'CaptainID',
1145
            'FounderID',
1146
            'HasOneRelationshipID',
1147
            'ExtendedHasOneRelationshipID'
1148
        );
1149
        $actual = array_keys($teamSpecifications);
1150
        sort($expected);
1151
        sort($actual);
1152
        $this->assertEquals(
1153
            $expected,
1154
            $actual,
1155
            'fieldSpecifications() contains all fields defined on instance: base, extended and foreign keys'
1156
        );
1157
1158
        $teamFields = $schema->databaseFields(DataObjectTest\Team::class, false);
1159
        $expected = array(
1160
            'ID',
1161
            'ClassName',
1162
            'LastEdited',
1163
            'Created',
1164
            'Title',
1165
            'DatabaseField',
1166
            'ExtendedDatabaseField',
1167
            'CaptainID',
1168
            'FounderID',
1169
            'HasOneRelationshipID',
1170
            'ExtendedHasOneRelationshipID'
1171
        );
1172
        $actual = array_keys($teamFields);
1173
        sort($expected);
1174
        sort($actual);
1175
        $this->assertEquals(
1176
            $expected,
1177
            $actual,
1178
            'databaseFields() contains only fields defined on instance, including base, extended and foreign keys'
1179
        );
1180
1181
        $subteamSpecifications = $schema->fieldSpecs(DataObjectTest\SubTeam::class);
1182
        $expected = array(
1183
            'ID',
1184
            'ClassName',
1185
            'LastEdited',
1186
            'Created',
1187
            'Title',
1188
            'DatabaseField',
1189
            'ExtendedDatabaseField',
1190
            'CaptainID',
1191
            'FounderID',
1192
            'HasOneRelationshipID',
1193
            'ExtendedHasOneRelationshipID',
1194
            'SubclassDatabaseField',
1195
            'ParentTeamID',
1196
        );
1197
        $actual = array_keys($subteamSpecifications);
1198
        sort($expected);
1199
        sort($actual);
1200
        $this->assertEquals(
1201
            $expected,
1202
            $actual,
1203
            'fieldSpecifications() on subclass contains all fields, including base, extended  and foreign keys'
1204
        );
1205
1206
        $subteamFields = $schema->databaseFields(DataObjectTest\SubTeam::class, false);
1207
        $expected = array(
1208
            'ID',
1209
            'SubclassDatabaseField',
1210
            'ParentTeamID',
1211
        );
1212
        $actual = array_keys($subteamFields);
1213
        sort($expected);
1214
        sort($actual);
1215
        $this->assertEquals(
1216
            $expected,
1217
            $actual,
1218
            'databaseFields() on subclass contains only fields defined on instance'
1219
        );
1220
    }
1221
1222
    public function testSearchableFields()
1223
    {
1224
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
1225
        $fields = $player->searchableFields();
1226
        $this->assertArrayHasKey(
1227
            'IsRetired',
1228
            $fields,
1229
            'Fields defined by $searchable_fields static are correctly detected'
1230
        );
1231
        $this->assertArrayHasKey(
1232
            'ShirtNumber',
1233
            $fields,
1234
            'Fields defined by $searchable_fields static are correctly detected'
1235
        );
1236
1237
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1238
        $fields = $team->searchableFields();
1239
        $this->assertArrayHasKey(
1240
            'Title',
1241
            $fields,
1242
            'Fields can be inherited from the $summary_fields static, including methods called on fields'
1243
        );
1244
        $this->assertArrayHasKey(
1245
            'Captain.ShirtNumber',
1246
            $fields,
1247
            'Fields on related objects can be inherited from the $summary_fields static'
1248
        );
1249
        $this->assertArrayHasKey(
1250
            'Captain.FavouriteTeam.Title',
1251
            $fields,
1252
            'Fields on related objects can be inherited from the $summary_fields static'
1253
        );
1254
1255
        $testObj = new DataObjectTest\Fixture();
1256
        $fields = $testObj->searchableFields();
1257
        $this->assertEmpty($fields);
1258
    }
1259
1260
    public function testCastingHelper()
1261
    {
1262
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1263
1264
        $this->assertEquals('Varchar', $team->castingHelper('Title'), 'db field wasn\'t casted correctly');
1265
        $this->assertEquals('HTMLVarchar', $team->castingHelper('DatabaseField'), 'db field wasn\'t casted correctly');
1266
1267
        $sponsor = $team->Sponsors()->first();
0 ignored issues
show
Bug introduced by
The method Sponsors() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1267
        $sponsor = $team->/** @scrutinizer ignore-call */ Sponsors()->first();
Loading history...
1268
        $this->assertEquals('Int', $sponsor->castingHelper('SponsorFee'), 'many_many_extraFields not casted correctly');
1269
    }
1270
1271
    public function testSummaryFieldsCustomLabels()
1272
    {
1273
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1274
        $summaryFields = $team->summaryFields();
1275
1276
        $this->assertEquals(
1277
            [
1278
                'Title' => 'Custom Title',
1279
                'Title.UpperCase' => 'Title',
1280
                'Captain.ShirtNumber' => 'Captain\'s shirt number',
1281
                'Captain.FavouriteTeam.Title' => 'Captain\'s favourite team',
1282
            ],
1283
            $summaryFields
1284
        );
1285
    }
1286
1287
    public function testDataObjectUpdate()
1288
    {
1289
        /* update() calls can use the dot syntax to reference has_one relations and other methods that return
1290
        * objects */
1291
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1292
        $team1->CaptainID = $this->idFromFixture(DataObjectTest\Player::class, 'captain1');
0 ignored issues
show
Bug Best Practice introduced by
The property CaptainID does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
1293
1294
        $team1->update(
1295
            array(
1296
                'DatabaseField' => 'Something',
1297
                'Captain.FirstName' => 'Jim',
1298
                'Captain.Email' => '[email protected]',
1299
                'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1300
            )
1301
        );
1302
1303
        /* Test the simple case of updating fields on the object itself */
1304
        $this->assertEquals('Something', $team1->DatabaseField);
0 ignored issues
show
Bug Best Practice introduced by
The property DatabaseField does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1305
1306
        /* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in
1307
        * the database.  Although update() doesn't usually write, it does write related records automatically. */
1308
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
1309
        $this->assertEquals('Jim', $captain1->FirstName);
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1310
        $this->assertEquals('[email protected]', $captain1->Email);
0 ignored issues
show
Bug Best Practice introduced by
The property Email does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1311
1312
        /* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.
1313
        * FavouriteTeam.Title made */
1314
        $reloadedTeam1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1315
        $this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1316
    }
1317
1318
    public function testDataObjectUpdateNew()
1319
    {
1320
        /* update() calls can use the dot syntax to reference has_one relations and other methods that return
1321
        * objects */
1322
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1323
        $team1->CaptainID = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property CaptainID does not exist on SilverStripe\ORM\DataObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
1324
1325
        $team1->update(
1326
            array(
1327
                'Captain.FirstName' => 'Jim',
1328
                'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1329
            )
1330
        );
1331
        /* Test that the captain ID has been updated */
1332
        $this->assertGreaterThan(0, $team1->CaptainID);
0 ignored issues
show
Bug Best Practice introduced by
The property CaptainID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1333
1334
        /* Fetch the newly created captain */
1335
        $captain1 = DataObjectTest\Player::get()->byID($team1->CaptainID);
1336
        $this->assertEquals('Jim', $captain1->FirstName);
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1337
1338
        /* Grab the favourite team and make sure it has the correct values */
1339
        $reloadedTeam1 = $captain1->FavouriteTeam();
1340
        $this->assertEquals($reloadedTeam1->ID, $captain1->FavouriteTeamID);
0 ignored issues
show
Bug Best Practice introduced by
The property FavouriteTeamID does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1341
        $this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1342
    }
1343
1344
1345
    /**
1346
     * @expectedException \SilverStripe\ORM\ValidationException
1347
     */
1348
    public function testWritingInvalidDataObjectThrowsException()
1349
    {
1350
        $validatedObject = new DataObjectTest\ValidatedObject();
1351
        $validatedObject->write();
1352
    }
1353
1354
    public function testWritingValidDataObjectDoesntThrowException()
1355
    {
1356
        $validatedObject = new DataObjectTest\ValidatedObject();
1357
        $validatedObject->Name = "Mr. Jones";
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\Tests\D...ectTest\ValidatedObject. Since you implemented __set, consider adding a @property annotation.
Loading history...
1358
1359
        $validatedObject->write();
1360
        $this->assertTrue($validatedObject->isInDB(), "Validated object was not saved to database");
1361
    }
1362
1363
    public function testSubclassCreation()
1364
    {
1365
        /* Creating a new object of a subclass should set the ClassName field correctly */
1366
        $obj = new DataObjectTest\SubTeam();
1367
        $obj->write();
1368
        $this->assertEquals(
1369
            DataObjectTest\SubTeam::class,
1370
            DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value()
1371
        );
1372
    }
1373
1374
    public function testForceInsert()
1375
    {
1376
        /* If you set an ID on an object and pass forceInsert = true, then the object should be correctly created */
1377
        $conn = DB::get_conn();
1378
        if (method_exists($conn, 'allowPrimaryKeyEditing')) {
1379
            $conn->allowPrimaryKeyEditing(DataObjectTest\Team::class, true);
1380
        }
1381
        $obj = new DataObjectTest\SubTeam();
1382
        $obj->ID = 1001;
1383
        $obj->Title = 'asdfasdf';
1384
        $obj->SubclassDatabaseField = 'asdfasdf';
0 ignored issues
show
Bug Best Practice introduced by
The property SubclassDatabaseField does not exist on SilverStripe\ORM\Tests\DataObjectTest\SubTeam. Since you implemented __set, consider adding a @property annotation.
Loading history...
1385
        $obj->write(false, true);
1386
        if (method_exists($conn, 'allowPrimaryKeyEditing')) {
1387
            $conn->allowPrimaryKeyEditing(DataObjectTest\Team::class, false);
1388
        }
1389
1390
        $this->assertEquals(
1391
            DataObjectTest\SubTeam::class,
1392
            DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value()
1393
        );
1394
1395
        /* Check that it actually saves to the database with the correct ID */
1396
        $this->assertEquals(
1397
            "1001",
1398
            DB::query(
1399
                "SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'"
1400
            )->value()
1401
        );
1402
        $this->assertEquals(
1403
            "1001",
1404
            DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value()
1405
        );
1406
    }
1407
1408
    public function testHasOwnTable()
1409
    {
1410
        $schema = DataObject::getSchema();
1411
        /* Test DataObject::has_own_table() returns true if the object has $has_one or $db values */
1412
        $this->assertTrue($schema->classHasTable(DataObjectTest\Player::class));
1413
        $this->assertTrue($schema->classHasTable(DataObjectTest\Team::class));
1414
        $this->assertTrue($schema->classHasTable(DataObjectTest\Fixture::class));
1415
1416
        /* Root DataObject that always have a table, even if they lack both $db and $has_one */
1417
        $this->assertTrue($schema->classHasTable(DataObjectTest\FieldlessTable::class));
1418
1419
        /* Subclasses without $db or $has_one don't have a table */
1420
        $this->assertFalse($schema->classHasTable(DataObjectTest\FieldlessSubTable::class));
1421
1422
        /* Return false if you don't pass it a subclass of DataObject */
1423
        $this->assertFalse($schema->classHasTable(DataObject::class));
1424
        $this->assertFalse($schema->classHasTable(ViewableData::class));
1425
1426
        /* Invalid class name */
1427
        $this->assertFalse($schema->classHasTable("ThisIsntADataObject"));
1428
    }
1429
1430
    public function testMerge()
1431
    {
1432
        // test right merge of subclasses
1433
        $left = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
1434
        $right = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam2_with_player_relation');
1435
        $leftOrigID = $left->ID;
1436
        $left->merge($right, 'right', false, false);
1437
        $this->assertEquals(
1438
            $left->Title,
1439
            'Subteam 2',
1440
            'merge() with "right" priority overwrites fields with existing values on subclasses'
1441
        );
1442
        $this->assertEquals(
1443
            $left->ID,
1444
            $leftOrigID,
1445
            'merge() with "right" priority doesnt overwrite database ID'
1446
        );
1447
1448
        // test overwriteWithEmpty flag on existing left values
1449
        $left = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam2_with_player_relation');
1450
        $right = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam3_with_empty_fields');
1451
        $left->merge($right, 'right', false, true);
1452
        $this->assertEquals(
1453
            $left->Title,
1454
            'Subteam 3',
1455
            'merge() with $overwriteWithEmpty overwrites non-empty fields on left object'
1456
        );
1457
1458
        // test overwriteWithEmpty flag on empty left values
1459
        $left = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
1460
        // $SubclassDatabaseField is empty on here
1461
        $right = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam2_with_player_relation');
1462
        $left->merge($right, 'right', false, true);
1463
        $this->assertEquals(
1464
            $left->SubclassDatabaseField,
0 ignored issues
show
Bug Best Practice introduced by
The property SubclassDatabaseField does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1465
            null,
1466
            'merge() with $overwriteWithEmpty overwrites empty fields on left object'
1467
        );
1468
1469
        // @todo test "left" priority flag
1470
        // @todo test includeRelations flag
1471
        // @todo test includeRelations in combination with overwriteWithEmpty
1472
        // @todo test has_one relations
1473
        // @todo test has_many and many_many relations
1474
    }
1475
1476
    public function testPopulateDefaults()
1477
    {
1478
        $obj = new DataObjectTest\Fixture();
1479
        $this->assertEquals(
1480
            $obj->MyFieldWithDefault,
0 ignored issues
show
Bug Best Practice introduced by
The property MyFieldWithDefault does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __get, consider adding a @property annotation.
Loading history...
1481
            'Default Value',
1482
            'Defaults are populated for in-memory object from $defaults array'
1483
        );
1484
1485
        $this->assertEquals(
1486
            $obj->MyFieldWithAltDefault,
1487
            'Default Value',
1488
            'Defaults are populated from overloaded populateDefaults() method'
1489
        );
1490
    }
1491
1492
    /**
1493
     * @expectedException \InvalidArgumentException
1494
     */
1495
    public function testValidateModelDefinitionsFailsWithArray()
1496
    {
1497
        Config::modify()->merge(DataObjectTest\Team::class, 'has_one', array('NotValid' => array('NoArraysAllowed')));
1498
        DataObject::getSchema()->hasOneComponent(DataObjectTest\Team::class, 'NotValid');
1499
    }
1500
1501
    /**
1502
     * @expectedException \InvalidArgumentException
1503
     */
1504
    public function testValidateModelDefinitionsFailsWithIntKey()
1505
    {
1506
        Config::modify()->set(DataObjectTest\Team::class, 'has_many', array(0 => DataObjectTest\Player::class));
1507
        DataObject::getSchema()->hasManyComponent(DataObjectTest\Team::class, 0);
1508
    }
1509
1510
    /**
1511
     * @expectedException \InvalidArgumentException
1512
     */
1513
    public function testValidateModelDefinitionsFailsWithIntValue()
1514
    {
1515
        Config::modify()->merge(DataObjectTest\Team::class, 'many_many', array('Players' => 12));
1516
        DataObject::getSchema()->manyManyComponent(DataObjectTest\Team::class, 'Players');
1517
    }
1518
1519
    public function testNewClassInstance()
1520
    {
1521
        $dataObject = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1522
        $changedDO = $dataObject->newClassInstance(DataObjectTest\SubTeam::class);
1523
        $changedFields = $changedDO->getChangedFields();
1524
1525
        // Don't write the record, it will reset changed fields
1526
        $this->assertInstanceOf(DataObjectTest\SubTeam::class, $changedDO);
1527
        $this->assertEquals($changedDO->ClassName, DataObjectTest\SubTeam::class);
1528
        $this->assertEquals($changedDO->RecordClassName, DataObjectTest\SubTeam::class);
0 ignored issues
show
Bug Best Practice introduced by
The property RecordClassName does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1529
        $this->assertContains('ClassName', array_keys($changedFields));
1530
        $this->assertEquals($changedFields['ClassName']['before'], DataObjectTest\Team::class);
1531
        $this->assertEquals($changedFields['ClassName']['after'], DataObjectTest\SubTeam::class);
1532
        $this->assertEquals($changedFields['RecordClassName']['before'], DataObjectTest\Team::class);
1533
        $this->assertEquals($changedFields['RecordClassName']['after'], DataObjectTest\SubTeam::class);
1534
1535
        $changedDO->write();
1536
1537
        $this->assertInstanceOf(DataObjectTest\SubTeam::class, $changedDO);
1538
        $this->assertEquals($changedDO->ClassName, DataObjectTest\SubTeam::class);
1539
1540
        // Test invalid classes fail
1541
        $this->expectException(InvalidArgumentException::class);
1542
        $this->expectExceptionMessage('Controller is not a valid subclass of DataObject');
1543
        /**
1544
         * @skipUpgrade
1545
         */
1546
        $dataObject->newClassInstance('Controller');
1547
    }
1548
1549
    public function testMultipleManyManyWithSameClass()
1550
    {
1551
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1552
        $company2 = $this->objFromFixture(DataObjectTest\EquipmentCompany::class, 'equipmentcompany2');
1553
        $sponsors = $team->Sponsors();
1554
        $equipmentSuppliers = $team->EquipmentSuppliers();
0 ignored issues
show
Bug introduced by
The method EquipmentSuppliers() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1554
        /** @scrutinizer ignore-call */ 
1555
        $equipmentSuppliers = $team->EquipmentSuppliers();
Loading history...
1555
1556
        // Check that DataObject::many_many() works as expected
1557
        $manyManyComponent = DataObject::getSchema()->manyManyComponent(DataObjectTest\Team::class, 'Sponsors');
1558
        $this->assertEquals(ManyManyList::class, $manyManyComponent['relationClass']);
1559
        $this->assertEquals(
1560
            DataObjectTest\Team::class,
1561
            $manyManyComponent['parentClass'],
1562
            'DataObject::many_many() didn\'t find the correct base class'
1563
        );
1564
        $this->assertEquals(
1565
            DataObjectTest\EquipmentCompany::class,
1566
            $manyManyComponent['childClass'],
1567
            'DataObject::many_many() didn\'t find the correct target class for the relation'
1568
        );
1569
        $this->assertEquals(
1570
            'DataObjectTest_EquipmentCompany_SponsoredTeams',
1571
            $manyManyComponent['join'],
1572
            'DataObject::many_many() didn\'t find the correct relation table'
1573
        );
1574
        $this->assertEquals('DataObjectTest_TeamID', $manyManyComponent['parentField']);
1575
        $this->assertEquals('DataObjectTest_EquipmentCompanyID', $manyManyComponent['childField']);
1576
1577
        // Check that ManyManyList still works
1578
        $this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation');
1579
        $this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation');
1580
1581
        // Check everything works when no relation is present
1582
        $teamWithoutSponsor = $this->objFromFixture(DataObjectTest\Team::class, 'team3');
1583
        $this->assertInstanceOf(ManyManyList::class, $teamWithoutSponsor->Sponsors());
1584
        $this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count());
1585
1586
        // Test that belongs_many_many can be infered from with getNonReciprocalComponent
1587
        $this->assertListEquals(
1588
            [
1589
                ['Name' => 'Company corp'],
1590
                ['Name' => 'Team co.'],
1591
            ],
1592
            $team->inferReciprocalComponent(DataObjectTest\EquipmentCompany::class, 'SponsoredTeams')
0 ignored issues
show
Bug introduced by
It seems like $team->inferReciprocalCo...lass, 'SponsoredTeams') can also be of type SilverStripe\ORM\DataObject; however, parameter $list of SilverStripe\Dev\SapphireTest::assertListEquals() does only seem to accept SilverStripe\ORM\SS_List, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1592
            /** @scrutinizer ignore-type */ $team->inferReciprocalComponent(DataObjectTest\EquipmentCompany::class, 'SponsoredTeams')
Loading history...
1593
        );
1594
1595
        // Test that many_many can be infered from getNonReciprocalComponent
1596
        $this->assertListEquals(
1597
            [
1598
                ['Title' => 'Team 1'],
1599
                ['Title' => 'Team 2'],
1600
                ['Title' => 'Subteam 1'],
1601
            ],
1602
            $company2->inferReciprocalComponent(DataObjectTest\Team::class, 'Sponsors')
1603
        );
1604
1605
        // Check many_many_extraFields still works
1606
        $equipmentCompany = $this->objFromFixture(DataObjectTest\EquipmentCompany::class, 'equipmentcompany1');
1607
        $equipmentCompany->SponsoredTeams()->add($teamWithoutSponsor, array('SponsorFee' => 1000));
0 ignored issues
show
Bug introduced by
The method SponsoredTeams() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1607
        $equipmentCompany->/** @scrutinizer ignore-call */ 
1608
                           SponsoredTeams()->add($teamWithoutSponsor, array('SponsorFee' => 1000));
Loading history...
1608
        $sponsoredTeams = $equipmentCompany->SponsoredTeams();
1609
        $this->assertEquals(
1610
            1000,
1611
            $sponsoredTeams->byID($teamWithoutSponsor->ID)->SponsorFee,
1612
            'Data from many_many_extraFields was not stored/extracted correctly'
1613
        );
1614
1615
        // Check subclasses correctly inherit multiple many_manys
1616
        $subTeam = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
1617
        $this->assertEquals(
1618
            2,
1619
            $subTeam->Sponsors()->count(),
1620
            'Child class did not inherit multiple many_manys'
1621
        );
1622
        $this->assertEquals(
1623
            1,
1624
            $subTeam->EquipmentSuppliers()->count(),
1625
            'Child class did not inherit multiple many_manys'
1626
        );
1627
        // Team 2 has one EquipmentCompany sponsor and one SubEquipmentCompany
1628
        $team2 = $this->objFromFixture(DataObjectTest\Team::class, 'team2');
1629
        $this->assertEquals(
1630
            2,
1631
            $team2->Sponsors()->count(),
1632
            'Child class did not inherit multiple belongs_many_manys'
1633
        );
1634
1635
        // Check many_many_extraFields also works from the belongs_many_many side
1636
        $sponsors = $team2->Sponsors();
1637
        $sponsors->add($equipmentCompany, array('SponsorFee' => 750));
1638
        $this->assertEquals(
1639
            750,
1640
            $sponsors->byID($equipmentCompany->ID)->SponsorFee,
1641
            'Data from many_many_extraFields was not stored/extracted correctly'
1642
        );
1643
1644
        $subEquipmentCompany = $this->objFromFixture(DataObjectTest\SubEquipmentCompany::class, 'subequipmentcompany1');
1645
        $subTeam->Sponsors()->add($subEquipmentCompany, array('SponsorFee' => 1200));
1646
        $this->assertEquals(
1647
            1200,
1648
            $subTeam->Sponsors()->byID($subEquipmentCompany->ID)->SponsorFee,
1649
            'Data from inherited many_many_extraFields was not stored/extracted correctly'
1650
        );
1651
    }
1652
1653
    public function testManyManyExtraFields()
1654
    {
1655
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1656
        $schema = DataObject::getSchema();
1657
1658
        // Get all extra fields
1659
        $teamExtraFields = $team->manyManyExtraFields();
1660
        $this->assertEquals(
1661
            array(
1662
                'Players' => array('Position' => 'Varchar(100)')
1663
            ),
1664
            $teamExtraFields
1665
        );
1666
1667
        // Ensure fields from parent classes are included
1668
        $subTeam = singleton(DataObjectTest\SubTeam::class);
1669
        $teamExtraFields = $subTeam->manyManyExtraFields();
1670
        $this->assertEquals(
1671
            array(
1672
                'Players' => array('Position' => 'Varchar(100)'),
1673
                'FormerPlayers' => array('Position' => 'Varchar(100)')
1674
            ),
1675
            $teamExtraFields
1676
        );
1677
1678
        // Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
1679
        $teamExtraFields = $schema->manyManyExtraFieldsForComponent(DataObjectTest\Team::class, 'Players');
1680
        $this->assertEquals(
1681
            $teamExtraFields,
1682
            array(
1683
                'Position' => 'Varchar(100)'
1684
            )
1685
        );
1686
1687
        // We'll have to go through the relation to get the extra fields on Player
1688
        $playerExtraFields = $schema->manyManyExtraFieldsForComponent(DataObjectTest\Player::class, 'Teams');
1689
        $this->assertEquals(
1690
            $playerExtraFields,
1691
            array(
1692
                'Position' => 'Varchar(100)'
1693
            )
1694
        );
1695
1696
        // Iterate through a many-many relationship and confirm that extra fields are included
1697
        $newTeam = new DataObjectTest\Team();
1698
        $newTeam->Title = "New team";
1699
        $newTeam->write();
1700
        $newTeamID = $newTeam->ID;
1701
1702
        $newPlayer = new DataObjectTest\Player();
1703
        $newPlayer->FirstName = "Sam";
1704
        $newPlayer->Surname = "Minnee";
1705
        $newPlayer->write();
1706
1707
        // The idea of Sam as a prop is essentially humourous.
1708
        $newTeam->Players()->add($newPlayer, array("Position" => "Prop"));
1709
1710
        // Requery and uncache everything
1711
        $newTeam->flushCache();
1712
        $newTeam = DataObject::get_by_id(DataObjectTest\Team::class, $newTeamID);
1713
1714
        // Check that the Position many_many_extraField is extracted.
1715
        $player = $newTeam->Players()->first();
0 ignored issues
show
Bug introduced by
The method Players() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1715
        $player = $newTeam->/** @scrutinizer ignore-call */ Players()->first();
Loading history...
1716
        $this->assertEquals('Sam', $player->FirstName);
1717
        $this->assertEquals("Prop", $player->Position);
1718
1719
        // Check that ordering a many-many relation by an aggregate column doesn't fail
1720
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
1721
        $player->Teams()->sort("count(DISTINCT \"DataObjectTest_Team_Players\".\"DataObjectTest_PlayerID\") DESC");
0 ignored issues
show
Bug introduced by
The method Teams() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1721
        $player->/** @scrutinizer ignore-call */ 
1722
                 Teams()->sort("count(DISTINCT \"DataObjectTest_Team_Players\".\"DataObjectTest_PlayerID\") DESC");
Loading history...
1722
    }
1723
1724
    /**
1725
     * Check that the queries generated for many-many relation queries can have unlimitedRowCount
1726
     * called on them.
1727
     */
1728
    public function testManyManyUnlimitedRowCount()
1729
    {
1730
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
1731
        // TODO: What's going on here?
1732
        $this->assertEquals(2, $player->Teams()->dataQuery()->query()->unlimitedRowCount());
1733
    }
1734
1735
    /**
1736
     * Tests that singular_name() generates sensible defaults.
1737
     */
1738
    public function testSingularName()
1739
    {
1740
        $assertions = array(
1741
            DataObjectTest\Player::class => 'Player',
1742
            DataObjectTest\Team::class => 'Team',
1743
            DataObjectTest\Fixture::class => 'Fixture',
1744
        );
1745
1746
        foreach ($assertions as $class => $expectedSingularName) {
1747
            $this->assertEquals(
1748
                $expectedSingularName,
1749
                singleton($class)->singular_name(),
1750
                "Assert that the singular_name for '$class' is correct."
1751
            );
1752
        }
1753
    }
1754
1755
    /**
1756
     * Tests that plural_name() generates sensible defaults.
1757
     */
1758
    public function testPluralName()
1759
    {
1760
        $assertions = array(
1761
            DataObjectTest\Player::class => 'Players',
1762
            DataObjectTest\Team::class => 'Teams',
1763
            DataObjectTest\Fixture::class => 'Fixtures',
1764
            DataObjectTest\Play::class => 'Plays',
1765
            DataObjectTest\Bogey::class => 'Bogeys',
1766
            DataObjectTest\Ploy::class => 'Ploys',
1767
        );
1768
        i18n::set_locale('en_NZ');
1769
        foreach ($assertions as $class => $expectedPluralName) {
1770
            $this->assertEquals(
1771
                $expectedPluralName,
1772
                DataObject::singleton($class)->plural_name(),
1773
                "Assert that the plural_name for '$class' is correct."
1774
            );
1775
            $this->assertEquals(
1776
                $expectedPluralName,
1777
                DataObject::singleton($class)->i18n_plural_name(),
1778
                "Assert that the i18n_plural_name for '$class' is correct."
1779
            );
1780
        }
1781
    }
1782
1783
    public function testHasDatabaseField()
1784
    {
1785
        $team = singleton(DataObjectTest\Team::class);
1786
        $subteam = singleton(DataObjectTest\SubTeam::class);
1787
1788
        $this->assertTrue(
1789
            $team->hasDatabaseField('Title'),
1790
            "hasOwnDatabaseField() works with \$db fields"
1791
        );
1792
        $this->assertTrue(
1793
            $team->hasDatabaseField('CaptainID'),
1794
            "hasOwnDatabaseField() works with \$has_one fields"
1795
        );
1796
        $this->assertFalse(
1797
            $team->hasDatabaseField('NonExistentField'),
1798
            "hasOwnDatabaseField() doesn't detect non-existend fields"
1799
        );
1800
        $this->assertTrue(
1801
            $team->hasDatabaseField('ExtendedDatabaseField'),
1802
            "hasOwnDatabaseField() works with extended fields"
1803
        );
1804
        $this->assertFalse(
1805
            $team->hasDatabaseField('SubclassDatabaseField'),
1806
            "hasOwnDatabaseField() doesn't pick up fields in subclasses on parent class"
1807
        );
1808
1809
        $this->assertTrue(
1810
            $subteam->hasDatabaseField('SubclassDatabaseField'),
1811
            "hasOwnDatabaseField() picks up fields in subclasses"
1812
        );
1813
    }
1814
1815
    public function testFieldTypes()
1816
    {
1817
        $obj = new DataObjectTest\Fixture();
1818
        $obj->DateField = '1988-01-02';
0 ignored issues
show
Bug Best Practice introduced by
The property DateField does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
1819
        $obj->DatetimeField = '1988-03-04 06:30';
0 ignored issues
show
Bug Best Practice introduced by
The property DatetimeField does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
1820
        $obj->write();
1821
        $obj->flushCache();
1822
1823
        $obj = DataObject::get_by_id(DataObjectTest\Fixture::class, $obj->ID);
1824
        $this->assertEquals('1988-01-02', $obj->DateField);
0 ignored issues
show
Bug Best Practice introduced by
The property DateField does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1825
        $this->assertEquals('1988-03-04 06:30:00', $obj->DatetimeField);
0 ignored issues
show
Bug Best Practice introduced by
The property DatetimeField does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1826
    }
1827
1828
    public function testTwoSubclassesWithTheSameFieldNameWork()
1829
    {
1830
        // Create two objects of different subclasses, setting the values of fields that are
1831
        // defined separately in each subclass
1832
        $obj1 = new DataObjectTest\SubTeam();
1833
        $obj1->SubclassDatabaseField = "obj1";
0 ignored issues
show
Bug Best Practice introduced by
The property SubclassDatabaseField does not exist on SilverStripe\ORM\Tests\DataObjectTest\SubTeam. Since you implemented __set, consider adding a @property annotation.
Loading history...
1834
        $obj2 = new DataObjectTest\OtherSubclassWithSameField();
1835
        $obj2->SubclassDatabaseField = "obj2";
0 ignored issues
show
Bug Best Practice introduced by
The property SubclassDatabaseField does not exist on SilverStripe\ORM\Tests\D...erSubclassWithSameField. Since you implemented __set, consider adding a @property annotation.
Loading history...
1836
1837
        // Write them to the database
1838
        $obj1->write();
1839
        $obj2->write();
1840
1841
        // Check that the values of those fields are properly read from the database
1842
        $values = DataObject::get(
1843
            DataObjectTest\Team::class,
1844
            "\"DataObjectTest_Team\".\"ID\" IN
1845
			($obj1->ID, $obj2->ID)"
1846
        )->column("SubclassDatabaseField");
1847
        $this->assertEquals(array_intersect($values, array('obj1', 'obj2')), $values);
1848
    }
1849
1850
    public function testClassNameSetForNewObjects()
1851
    {
1852
        $d = new DataObjectTest\Player();
1853
        $this->assertEquals(DataObjectTest\Player::class, $d->ClassName);
1854
    }
1855
1856
    public function testHasValue()
1857
    {
1858
        $team = new DataObjectTest\Team();
1859
        $this->assertFalse($team->hasValue('Title', null, false));
1860
        $this->assertFalse($team->hasValue('DatabaseField', null, false));
1861
1862
        $team->Title = 'hasValue';
1863
        $this->assertTrue($team->hasValue('Title', null, false));
1864
        $this->assertFalse($team->hasValue('DatabaseField', null, false));
1865
1866
        $team->Title = '<p></p>';
1867
        $this->assertTrue(
1868
            $team->hasValue('Title', null, false),
1869
            'Test that an empty paragraph is a value for non-HTML fields.'
1870
        );
1871
1872
        $team->DatabaseField = 'hasValue';
1873
        $this->assertTrue($team->hasValue('Title', null, false));
1874
        $this->assertTrue($team->hasValue('DatabaseField', null, false));
1875
    }
1876
1877
    public function testHasMany()
1878
    {
1879
        $company = new DataObjectTest\Company();
1880
1881
        $this->assertEquals(
1882
            array(
1883
                'CurrentStaff' => DataObjectTest\Staff::class,
1884
                'PreviousStaff' => DataObjectTest\Staff::class
1885
            ),
1886
            $company->hasMany(),
1887
            'has_many strips field name data by default.'
1888
        );
1889
1890
        $this->assertEquals(
1891
            DataObjectTest\Staff::class,
1892
            DataObject::getSchema()->hasManyComponent(DataObjectTest\Company::class, 'CurrentStaff'),
1893
            'has_many strips field name data by default on single relationships.'
1894
        );
1895
1896
        $this->assertEquals(
1897
            array(
1898
                'CurrentStaff' => DataObjectTest\Staff::class . '.CurrentCompany',
1899
                'PreviousStaff' => DataObjectTest\Staff::class . '.PreviousCompany'
1900
            ),
1901
            $company->hasMany(false),
1902
            'has_many returns field name data when $classOnly is false.'
1903
        );
1904
1905
        $this->assertEquals(
1906
            DataObjectTest\Staff::class . '.CurrentCompany',
1907
            DataObject::getSchema()->hasManyComponent(DataObjectTest\Company::class, 'CurrentStaff', false),
1908
            'has_many returns field name data on single records when $classOnly is false.'
1909
        );
1910
    }
1911
1912
    public function testGetRemoteJoinField()
1913
    {
1914
        $schema = DataObject::getSchema();
1915
1916
        // Company schema
1917
        $staffJoinField = $schema->getRemoteJoinField(
1918
            DataObjectTest\Company::class,
1919
            'CurrentStaff',
1920
            'has_many',
1921
            $polymorphic
1922
        );
1923
        $this->assertEquals('CurrentCompanyID', $staffJoinField);
1924
        $this->assertFalse($polymorphic, 'DataObjectTest_Company->CurrentStaff is not polymorphic');
1925
        $previousStaffJoinField = $schema->getRemoteJoinField(
1926
            DataObjectTest\Company::class,
1927
            'PreviousStaff',
1928
            'has_many',
1929
            $polymorphic
1930
        );
1931
        $this->assertEquals('PreviousCompanyID', $previousStaffJoinField);
1932
        $this->assertFalse($polymorphic, 'DataObjectTest_Company->PreviousStaff is not polymorphic');
1933
1934
        // CEO Schema
1935
        $this->assertEquals(
1936
            'CEOID',
1937
            $schema->getRemoteJoinField(
1938
                DataObjectTest\CEO::class,
1939
                'Company',
1940
                'belongs_to',
1941
                $polymorphic
1942
            )
1943
        );
1944
        $this->assertFalse($polymorphic, 'DataObjectTest_CEO->Company is not polymorphic');
1945
        $this->assertEquals(
1946
            'PreviousCEOID',
1947
            $schema->getRemoteJoinField(
1948
                DataObjectTest\CEO::class,
1949
                'PreviousCompany',
1950
                'belongs_to',
1951
                $polymorphic
1952
            )
1953
        );
1954
        $this->assertFalse($polymorphic, 'DataObjectTest_CEO->PreviousCompany is not polymorphic');
1955
1956
        // Team schema
1957
        $this->assertEquals(
1958
            'Favourite',
1959
            $schema->getRemoteJoinField(
1960
                DataObjectTest\Team::class,
1961
                'Fans',
1962
                'has_many',
1963
                $polymorphic
1964
            )
1965
        );
1966
        $this->assertTrue($polymorphic, 'DataObjectTest_Team->Fans is polymorphic');
1967
        $this->assertEquals(
1968
            'TeamID',
1969
            $schema->getRemoteJoinField(
1970
                DataObjectTest\Team::class,
1971
                'Comments',
1972
                'has_many',
1973
                $polymorphic
1974
            )
1975
        );
1976
        $this->assertFalse($polymorphic, 'DataObjectTest_Team->Comments is not polymorphic');
1977
    }
1978
1979
    public function testBelongsTo()
1980
    {
1981
        $company = new DataObjectTest\Company();
1982
        $ceo = new DataObjectTest\CEO();
1983
1984
        $company->Name = 'New Company';
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\Tests\DataObjectTest\Company. Since you implemented __set, consider adding a @property annotation.
Loading history...
1985
        $company->write();
1986
        $ceo->write();
1987
1988
        // Test belongs_to assignment
1989
        $company->CEOID = $ceo->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property CEOID does not exist on SilverStripe\ORM\Tests\DataObjectTest\Company. Since you implemented __set, consider adding a @property annotation.
Loading history...
1990
        $company->write();
1991
1992
        $this->assertEquals($company->ID, $ceo->Company()->ID, 'belongs_to returns the right results.');
0 ignored issues
show
Bug introduced by
The method Company() does not exist on SilverStripe\ORM\Tests\DataObjectTest\CEO. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1992
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ Company()->ID, 'belongs_to returns the right results.');
Loading history...
1993
1994
        // Test belongs_to can be infered via getNonReciprocalComponent
1995
        // Note: Will be returned as has_many since the belongs_to is ignored.
1996
        $this->assertListEquals(
1997
            [['Name' => 'New Company']],
1998
            $ceo->inferReciprocalComponent(DataObjectTest\Company::class, 'CEO')
0 ignored issues
show
Bug introduced by
It seems like $ceo->inferReciprocalCom...\Company::class, 'CEO') can also be of type SilverStripe\ORM\DataObject; however, parameter $list of SilverStripe\Dev\SapphireTest::assertListEquals() does only seem to accept SilverStripe\ORM\SS_List, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1998
            /** @scrutinizer ignore-type */ $ceo->inferReciprocalComponent(DataObjectTest\Company::class, 'CEO')
Loading history...
1999
        );
2000
2001
        // Test has_one to a belongs_to can be infered via getNonReciprocalComponent
2002
        $this->assertEquals(
2003
            $ceo->ID,
2004
            $company->inferReciprocalComponent(DataObjectTest\CEO::class, 'Company')->ID
0 ignored issues
show
Bug Best Practice introduced by
The property ID does not exist on SilverStripe\ORM\DataList. Since you implemented __get, consider adding a @property annotation.
Loading history...
2005
        );
2006
2007
        // Test automatic creation of class where no assigment exists
2008
        $ceo = new DataObjectTest\CEO();
2009
        $ceo->write();
2010
2011
        $this->assertTrue(
2012
            $ceo->Company() instanceof DataObjectTest\Company,
2013
            'DataObjects across belongs_to relations are automatically created.'
2014
        );
2015
        $this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
2016
2017
        // Write object with components
2018
        $ceo->Name = 'Edward Scissorhands';
0 ignored issues
show
Bug Best Practice introduced by
The property Name does not exist on SilverStripe\ORM\Tests\DataObjectTest\CEO. Since you implemented __set, consider adding a @property annotation.
Loading history...
2019
        $ceo->write(false, false, false, true);
2020
        $this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
2021
2022
        $newCEO = DataObject::get_by_id(DataObjectTest\CEO::class, $ceo->ID);
2023
        $this->assertEquals(
2024
            $ceo->Company()->ID,
2025
            $newCEO->Company()->ID,
0 ignored issues
show
Bug introduced by
The method Company() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2025
            $newCEO->/** @scrutinizer ignore-call */ 
2026
                     Company()->ID,
Loading history...
2026
            'belongs_to can be retrieved from the database.'
2027
        );
2028
    }
2029
2030
    public function testBelongsToPolymorphic()
2031
    {
2032
        $company = new DataObjectTest\Company();
2033
        $ceo = new DataObjectTest\CEO();
2034
2035
        $company->write();
2036
        $ceo->write();
2037
2038
        // Test belongs_to assignment
2039
        $company->OwnerID = $ceo->ID;
0 ignored issues
show
Bug Best Practice introduced by
The property OwnerID does not exist on SilverStripe\ORM\Tests\DataObjectTest\Company. Since you implemented __set, consider adding a @property annotation.
Loading history...
2040
        $company->OwnerClass = DataObjectTest\CEO::class;
0 ignored issues
show
Bug Best Practice introduced by
The property OwnerClass does not exist on SilverStripe\ORM\Tests\DataObjectTest\Company. Since you implemented __set, consider adding a @property annotation.
Loading history...
2041
        $company->write();
2042
2043
        $this->assertEquals($company->ID, $ceo->CompanyOwned()->ID, 'belongs_to returns the right results.');
0 ignored issues
show
Bug introduced by
The method CompanyOwned() does not exist on SilverStripe\ORM\Tests\DataObjectTest\CEO. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2043
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ CompanyOwned()->ID, 'belongs_to returns the right results.');
Loading history...
2044
        $this->assertInstanceOf(
2045
            DataObjectTest\Company::class,
2046
            $ceo->CompanyOwned(),
2047
            'belongs_to returns the right results.'
2048
        );
2049
2050
        // Test automatic creation of class where no assigment exists
2051
        $ceo = new DataObjectTest\CEO();
2052
        $ceo->write();
2053
2054
        $this->assertTrue(
2055
            $ceo->CompanyOwned() instanceof DataObjectTest\Company,
2056
            'DataObjects across polymorphic belongs_to relations are automatically created.'
2057
        );
2058
        $this->assertEquals($ceo->ID, $ceo->CompanyOwned()->OwnerID, 'Remote IDs are automatically set.');
2059
        $this->assertInstanceOf($ceo->CompanyOwned()->OwnerClass, $ceo, 'Remote class is automatically  set');
2060
2061
        // Write object with components
2062
        $ceo->write(false, false, false, true);
2063
        $this->assertTrue($ceo->CompanyOwned()->isInDB(), 'write() writes belongs_to components to the database.');
2064
2065
        $newCEO = DataObject::get_by_id(DataObjectTest\CEO::class, $ceo->ID);
2066
        $this->assertEquals(
2067
            $ceo->CompanyOwned()->ID,
2068
            $newCEO->CompanyOwned()->ID,
0 ignored issues
show
Bug introduced by
The method CompanyOwned() does not exist on SilverStripe\ORM\DataObject. Since you implemented __call, consider adding a @method annotation. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

2068
            $newCEO->/** @scrutinizer ignore-call */ 
2069
                     CompanyOwned()->ID,
Loading history...
2069
            'polymorphic belongs_to can be retrieved from the database.'
2070
        );
2071
    }
2072
2073
    /**
2074
     * @expectedException LogicException
2075
     */
2076
    public function testInvalidate()
2077
    {
2078
        $do = new DataObjectTest\Fixture();
2079
        $do->write();
2080
2081
        $do->delete();
2082
2083
        $do->delete(); // Prohibit invalid object manipulation
2084
        $do->write();
2085
        $do->duplicate();
2086
    }
2087
2088
    public function testToMap()
2089
    {
2090
        $obj = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
2091
2092
        $map = $obj->toMap();
2093
2094
        $this->assertArrayHasKey('ID', $map, 'Contains base fields');
2095
        $this->assertArrayHasKey('Title', $map, 'Contains fields from parent class');
2096
        $this->assertArrayHasKey('SubclassDatabaseField', $map, 'Contains fields from concrete class');
2097
2098
        $this->assertEquals(
2099
            $obj->ID,
2100
            $map['ID'],
2101
            'Contains values from base fields'
2102
        );
2103
        $this->assertEquals(
2104
            $obj->Title,
2105
            $map['Title'],
2106
            'Contains values from parent class fields'
2107
        );
2108
        $this->assertEquals(
2109
            $obj->SubclassDatabaseField,
0 ignored issues
show
Bug Best Practice introduced by
The property SubclassDatabaseField does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
2110
            $map['SubclassDatabaseField'],
2111
            'Contains values from concrete class fields'
2112
        );
2113
2114
        $newObj = new DataObjectTest\SubTeam();
0 ignored issues
show
Unused Code introduced by
The assignment to $newObj is dead and can be removed.
Loading history...
2115
        $this->assertArrayHasKey('Title', $map, 'Contains null fields');
2116
    }
2117
2118
    public function testIsEmpty()
2119
    {
2120
        $objEmpty = new DataObjectTest\Team();
2121
        $this->assertTrue($objEmpty->isEmpty(), 'New instance without populated defaults is empty');
2122
2123
        $objEmpty->Title = '0'; //
2124
        $this->assertFalse($objEmpty->isEmpty(), 'Zero value in attribute considered non-empty');
2125
    }
2126
2127
    public function testRelField()
2128
    {
2129
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
2130
        // Test traversal of a single has_one
2131
        $this->assertEquals("Team 1", $captain1->relField('FavouriteTeam.Title'));
2132
        // Test direct field access
2133
        $this->assertEquals("Captain", $captain1->relField('FirstName'));
2134
2135
        // Test empty link
2136
        $captain2 = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
2137
        $this->assertEmpty($captain2->relField('FavouriteTeam.Title'));
2138
        $this->assertNull($captain2->relField('FavouriteTeam.ReturnsNull'));
2139
        $this->assertNull($captain2->relField('FavouriteTeam.ReturnsNull.Title'));
2140
2141
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
2142
        // Test that we can traverse more than once, and that arbitrary methods are okay
2143
        $this->assertEquals("Team 1", $player->relField('Teams.First.Title'));
2144
2145
        $newPlayer = new DataObjectTest\Player();
2146
        $this->assertNull($newPlayer->relField('Teams.First.Title'));
2147
2148
        // Test that relField works on db field manipulations
2149
        $comment = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment3');
2150
        $this->assertEquals("PHIL IS A UNIQUE GUY, AND COMMENTS ON TEAM2", $comment->relField('Comment.UpperCase'));
2151
2152
        // relField throws exception on invalid properties
2153
        $this->expectException(LogicException::class);
2154
        $this->expectExceptionMessage("Not is not a relation/field on " . DataObjectTest\TeamComment::class);
2155
        $comment->relField('Not.A.Field');
2156
    }
2157
2158
    public function testRelObject()
2159
    {
2160
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
2161
2162
        // Test traversal of a single has_one
2163
        $this->assertInstanceOf(DBVarchar::class, $captain1->relObject('FavouriteTeam.Title'));
2164
        $this->assertEquals("Team 1", $captain1->relObject('FavouriteTeam.Title')->getValue());
2165
2166
        // Test empty link
2167
        $captain2 = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
2168
        $this->assertEmpty($captain2->relObject('FavouriteTeam.Title')->getValue());
2169
        $this->assertNull($captain2->relObject('FavouriteTeam.ReturnsNull.Title'));
2170
2171
        // Test direct field access
2172
        $this->assertInstanceOf(DBBoolean::class, $captain1->relObject('IsRetired'));
2173
        $this->assertEquals(1, $captain1->relObject('IsRetired')->getValue());
2174
2175
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
2176
        // Test that we can traverse more than once, and that arbitrary methods are okay
2177
        $this->assertInstanceOf(DBVarchar::class, $player->relObject('Teams.First.Title'));
2178
        $this->assertEquals("Team 1", $player->relObject('Teams.First.Title')->getValue());
2179
2180
        // relObject throws exception on invalid properties
2181
        $this->expectException(LogicException::class);
2182
        $this->expectExceptionMessage("Not is not a relation/field on " . DataObjectTest\Player::class);
2183
        $player->relObject('Not.A.Field');
2184
    }
2185
2186
    public function testLateStaticBindingStyle()
2187
    {
2188
        // Confirm that DataObjectTest_Player::get() operates as excepted
2189
        $this->assertEquals(4, DataObjectTest\Player::get()->count());
2190
        $this->assertInstanceOf(DataObjectTest\Player::class, DataObjectTest\Player::get()->first());
2191
2192
        // You can't pass arguments to LSB syntax - use the DataList methods instead.
2193
        $this->expectException(InvalidArgumentException::class);
2194
2195
        DataObjectTest\Player::get(null, "\"ID\" = 1");
2196
    }
2197
2198
    /**
2199
     * @expectedException \InvalidArgumentException
2200
     */
2201
    public function testBrokenLateStaticBindingStyle()
2202
    {
2203
        // If you call DataObject::get() you have to pass a first argument
2204
        DataObject::get();
2205
    }
2206
2207
    public function testBigIntField()
2208
    {
2209
        $staff = new DataObjectTest\Staff();
2210
        $staff->Salary = PHP_INT_MAX;
0 ignored issues
show
Bug Best Practice introduced by
The property Salary does not exist on SilverStripe\ORM\Tests\DataObjectTest\Staff. Since you implemented __set, consider adding a @property annotation.
Loading history...
2211
        $staff->write();
2212
        $this->assertEquals(PHP_INT_MAX, DataObjectTest\Staff::get()->byID($staff->ID)->Salary);
0 ignored issues
show
Bug Best Practice introduced by
The property Salary does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
2213
    }
2214
2215
    public function testGetOneMissingValueReturnsNull()
2216
    {
2217
2218
        // Test that missing values return null
2219
        $this->assertEquals(null, DataObject::get_one(
2220
            DataObjectTest\TeamComment::class,
2221
            ['"DataObjectTest_TeamComment"."Name"' => 'does not exists']
2222
        ));
2223
    }
2224
}
2225