Passed
Push — 4.2 ( b8bb4f...e60cea )
by
unknown
18:31 queued 09:25
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 testGetByIDCallerClass()
373
    {
374
        $captain1ID = $this->idFromFixture(DataObjectTest\Player::class, 'captain1');
375
        $captain1 = DataObjectTest\Player::get_by_id($captain1ID);
376
        $this->assertInstanceOf(DataObjectTest\Player::class, $captain1);
377
        $this->assertEquals('Captain', $captain1->FirstName);
378
379
        $captain2ID = $this->idFromFixture(DataObjectTest\Player::class, 'captain2');
380
        // make sure we can call from any class but get the one passed as an argument
381
        $captain2 = DataObjectTest\TeamComment::get_by_id(DataObjectTest\Player::class, $captain2ID);
382
        $this->assertInstanceOf(DataObjectTest\Player::class, $captain2);
383
        $this->assertEquals('Captain 2', $captain2->FirstName);
0 ignored issues
show
Bug Best Practice introduced by
The property FirstName does not exist on SilverStripe\ORM\Tests\DataObjectTest\TeamComment. Since you implemented __get, consider adding a @property annotation.
Loading history...
384
    }
385
386
    public function testGetCaseInsensitive()
387
    {
388
        // Test get_one() with bad case on the classname
389
        // Note: This will succeed only if the underlying DB server supports case-insensitive
390
        // table names (e.g. such as MySQL, but not SQLite3)
391
        if (!(DB::get_conn() instanceof MySQLDatabase)) {
392
            $this->markTestSkipped('MySQL only');
393
        }
394
395
        $subteam1 = DataObject::get_one(
396
            strtolower(DataObjectTest\SubTeam::class),
397
            array(
398
                '"DataObjectTest_Team"."Title"' => 'Subteam 1'
399
            ),
400
            true
401
        );
402
        $this->assertNotEmpty($subteam1);
403
        $this->assertEquals($subteam1->Title, "Subteam 1");
404
    }
405
406
    public function testGetSubclassFields()
407
    {
408
        /* Test that fields / has_one relations from the parent table and the subclass tables are extracted */
409
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, "captain1");
410
        // Base field
411
        $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...
412
        // Subclass field
413
        $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...
414
        // Subclass has_one relation
415
        $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...
416
    }
417
418
    public function testGetRelationClass()
419
    {
420
        $obj = new DataObjectTest\Player();
0 ignored issues
show
Unused Code introduced by
The assignment to $obj is dead and can be removed.
Loading history...
421
        $this->assertEquals(
422
            singleton(DataObjectTest\Player::class)->getRelationClass('FavouriteTeam'),
423
            DataObjectTest\Team::class,
424
            'has_one is properly inspected'
425
        );
426
        $this->assertEquals(
427
            singleton(DataObjectTest\Company::class)->getRelationClass('CurrentStaff'),
428
            DataObjectTest\Staff::class,
429
            'has_many is properly inspected'
430
        );
431
        $this->assertEquals(
432
            singleton(DataObjectTest\Team::class)->getRelationClass('Players'),
433
            DataObjectTest\Player::class,
434
            'many_many is properly inspected'
435
        );
436
        $this->assertEquals(
437
            singleton(DataObjectTest\Player::class)->getRelationClass('Teams'),
438
            DataObjectTest\Team::class,
439
            'belongs_many_many is properly inspected'
440
        );
441
        $this->assertEquals(
442
            singleton(DataObjectTest\CEO::class)->getRelationClass('Company'),
443
            DataObjectTest\Company::class,
444
            'belongs_to is properly inspected'
445
        );
446
        $this->assertEquals(
447
            singleton(DataObjectTest\Fan::class)->getRelationClass('Favourite'),
448
            DataObject::class,
449
            'polymorphic has_one is properly inspected'
450
        );
451
    }
452
453
    /**
454
     * Test that has_one relations can be retrieved
455
     */
456
    public function testGetHasOneRelations()
457
    {
458
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, "captain1");
459
        $team1ID = $this->idFromFixture(DataObjectTest\Team::class, 'team1');
460
461
        // There will be a field called (relname)ID that contains the ID of the
462
        // object linked to via the has_one relation
463
        $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...
464
465
        // There will be a method called $obj->relname() that returns the object itself
466
        $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

466
        $this->assertEquals($team1ID, $captain1->/** @scrutinizer ignore-call */ FavouriteTeam()->ID);
Loading history...
467
468
        // Test that getNonReciprocalComponent can find has_one from the has_many end
469
        $this->assertEquals(
470
            $team1ID,
471
            $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...
472
        );
473
474
        // Check entity with polymorphic has-one
475
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, "fan1");
476
        $this->assertTrue((bool)$fan1->hasValue('Favourite'));
477
478
        // There will be fields named (relname)ID and (relname)Class for polymorphic
479
        // entities
480
        $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...
481
        $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...
482
483
        // There will be a method called $obj->relname() that returns the object itself
484
        $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

484
        /** @scrutinizer ignore-call */ 
485
        $favourite = $fan1->Favourite();
Loading history...
485
        $this->assertEquals($team1ID, $favourite->ID);
486
        $this->assertInstanceOf(DataObjectTest\Team::class, $favourite);
487
488
        // check behaviour of dbObject with polymorphic relations
489
        $favouriteDBObject = $fan1->dbObject('Favourite');
490
        $favouriteValue = $favouriteDBObject->getValue();
491
        $this->assertInstanceOf(DBPolymorphicForeignKey::class, $favouriteDBObject);
492
        $this->assertEquals($favourite->ID, $favouriteValue->ID);
493
        $this->assertEquals($favourite->ClassName, $favouriteValue->ClassName);
494
    }
495
496
    public function testLimitAndCount()
497
    {
498
        $players = DataObject::get(DataObjectTest\Player::class);
499
500
        // There's 4 records in total
501
        $this->assertEquals(4, $players->count());
502
503
        // Testing "##, ##" syntax
504
        $this->assertEquals(4, $players->limit(20)->count());
505
        $this->assertEquals(4, $players->limit(20, 0)->count());
506
        $this->assertEquals(0, $players->limit(20, 20)->count());
507
        $this->assertEquals(2, $players->limit(2, 0)->count());
508
        $this->assertEquals(1, $players->limit(5, 3)->count());
509
    }
510
511
    public function testWriteNoChangesDoesntUpdateLastEdited()
512
    {
513
        // set mock now so we can be certain of LastEdited time for our test
514
        DBDatetime::set_mock_now('2017-01-01 00:00:00');
515
        $obj = new Player();
516
        $obj->FirstName = 'Test';
517
        $obj->Surname = 'Plater';
518
        $obj->Email = '[email protected]';
519
        $obj->write();
520
        $this->assertEquals('2017-01-01 00:00:00', $obj->LastEdited);
521
        $writtenObj = Player::get()->byID($obj->ID);
522
        $this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
523
524
        // set mock now so we get a new LastEdited if, for some reason, it's updated
525
        DBDatetime::set_mock_now('2017-02-01 00:00:00');
526
        $writtenObj->write();
527
        $this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
528
        $this->assertEquals($obj->ID, $writtenObj->ID);
529
530
        $reWrittenObj = Player::get()->byID($writtenObj->ID);
531
        $this->assertEquals('2017-01-01 00:00:00', $reWrittenObj->LastEdited);
532
    }
533
534
    /**
535
     * Test writing of database columns which don't correlate to a DBField,
536
     * e.g. all relation fields on has_one/has_many like "ParentID".
537
     */
538
    public function testWritePropertyWithoutDBField()
539
    {
540
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
541
        $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...
542
        $obj->write();
543
544
        // reload the page from the database
545
        $savedObj = DataObject::get_by_id(DataObjectTest\Player::class, $obj->ID);
546
        $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...
547
548
        // Test with porymorphic relation
549
        $obj2 = $this->objFromFixture(DataObjectTest\Fan::class, "fan1");
550
        $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...
551
        $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...
552
        $obj2->write();
553
554
        $savedObj2 = DataObject::get_by_id(DataObjectTest\Fan::class, $obj2->ID);
555
        $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...
556
        $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...
557
    }
558
559
    /**
560
     * Test has many relationships
561
     *   - Test getComponents() gets the ComponentSet of the other side of the relation
562
     *   - Test the IDs on the DataObjects are set correctly
563
     */
564
    public function testHasManyRelationships()
565
    {
566
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
567
568
        // Test getComponents() gets the ComponentSet of the other side of the relation
569
        $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

569
        $this->assertTrue($team1->/** @scrutinizer ignore-call */ Comments()->count() == 2);
Loading history...
570
571
        $team1Comments = [
572
            ['Comment' => 'This is a team comment by Joe'],
573
            ['Comment' => 'This is a team comment by Bob'],
574
        ];
575
576
        // Test the IDs on the DataObjects are set correctly
577
        $this->assertListEquals($team1Comments, $team1->Comments());
578
579
        // Test that has_many can be infered from the has_one via getNonReciprocalComponent
580
        $this->assertListEquals(
581
            $team1Comments,
582
            $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

582
            /** @scrutinizer ignore-type */ $team1->inferReciprocalComponent(DataObjectTest\TeamComment::class, 'Team')
Loading history...
583
        );
584
585
        // Test that we can add and remove items that already exist in the database
586
        $newComment = new DataObjectTest\TeamComment();
587
        $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...
588
        $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...
589
        $newComment->write();
590
        $team1->Comments()->add($newComment);
591
        $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...
592
593
        $comment1 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment1');
594
        $comment2 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment2');
595
        $team1->Comments()->remove($comment2);
596
597
        $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

597
        $team1CommentIDs = $team1->Comments()->/** @scrutinizer ignore-call */ sort('ID')->column('ID');
Loading history...
598
        $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
599
600
        // Test that removing an item from a list doesn't remove it from the same
601
        // relation belonging to a different object
602
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
603
        $team2 = $this->objFromFixture(DataObjectTest\Team::class, 'team2');
604
        $team2->Comments()->remove($comment1);
605
        $team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
606
        $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
607
    }
608
609
610
    /**
611
     * Test has many relationships against polymorphic has_one fields
612
     *   - Test getComponents() gets the ComponentSet of the other side of the relation
613
     *   - Test the IDs on the DataObjects are set correctly
614
     */
615
    public function testHasManyPolymorphicRelationships()
616
    {
617
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
618
619
        // Test getComponents() gets the ComponentSet of the other side of the relation
620
        $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

620
        $this->assertTrue($team1->/** @scrutinizer ignore-call */ Fans()->count() == 2);
Loading history...
621
622
        // Test the IDs/Classes on the DataObjects are set correctly
623
        foreach ($team1->Fans() as $fan) {
624
            $this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID');
625
            $this->assertEquals(DataObjectTest\Team::class, $fan->FavouriteClass, 'Fan has the correct FavouriteClass');
626
        }
627
628
        // Test that we can add and remove items that already exist in the database
629
        $newFan = new DataObjectTest\Fan();
630
        $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...
631
        $newFan->write();
632
        $team1->Fans()->add($newFan);
633
        $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...
634
        $this->assertEquals(
635
            DataObjectTest\Team::class,
636
            $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...
637
            'Newly created fan has the correct FavouriteClass'
638
        );
639
640
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
641
        $fan3 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan3');
642
        $team1->Fans()->remove($fan3);
643
644
        $team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
645
        $this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
646
647
        // Test that removing an item from a list doesn't remove it from the same
648
        // relation belonging to a different object
649
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
650
        $player1 = $this->objFromFixture(DataObjectTest\Player::class, 'player1');
651
        $player1->Fans()->remove($fan1);
652
        $team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
653
        $this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
654
    }
655
656
657
    public function testHasOneRelationship()
658
    {
659
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
660
        $player1 = $this->objFromFixture(DataObjectTest\Player::class, 'player1');
661
        $player2 = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
662
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
663
664
        // Test relation probing
665
        $this->assertFalse((bool)$team1->hasValue('Captain', null, false));
666
        $this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
667
668
        // Add a captain to team 1
669
        $team1->setField('CaptainID', $player1->ID);
670
        $team1->write();
671
672
        $this->assertTrue((bool)$team1->hasValue('Captain', null, false));
673
        $this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
674
675
        $this->assertEquals(
676
            $player1->ID,
677
            $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

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

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

1568
        /** @scrutinizer ignore-call */ 
1569
        $equipmentSuppliers = $team->EquipmentSuppliers();
Loading history...
1569
1570
        // Check that DataObject::many_many() works as expected
1571
        $manyManyComponent = DataObject::getSchema()->manyManyComponent(DataObjectTest\Team::class, 'Sponsors');
1572
        $this->assertEquals(ManyManyList::class, $manyManyComponent['relationClass']);
1573
        $this->assertEquals(
1574
            DataObjectTest\Team::class,
1575
            $manyManyComponent['parentClass'],
1576
            'DataObject::many_many() didn\'t find the correct base class'
1577
        );
1578
        $this->assertEquals(
1579
            DataObjectTest\EquipmentCompany::class,
1580
            $manyManyComponent['childClass'],
1581
            'DataObject::many_many() didn\'t find the correct target class for the relation'
1582
        );
1583
        $this->assertEquals(
1584
            'DataObjectTest_EquipmentCompany_SponsoredTeams',
1585
            $manyManyComponent['join'],
1586
            'DataObject::many_many() didn\'t find the correct relation table'
1587
        );
1588
        $this->assertEquals('DataObjectTest_TeamID', $manyManyComponent['parentField']);
1589
        $this->assertEquals('DataObjectTest_EquipmentCompanyID', $manyManyComponent['childField']);
1590
1591
        // Check that ManyManyList still works
1592
        $this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation');
1593
        $this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation');
1594
1595
        // Check everything works when no relation is present
1596
        $teamWithoutSponsor = $this->objFromFixture(DataObjectTest\Team::class, 'team3');
1597
        $this->assertInstanceOf(ManyManyList::class, $teamWithoutSponsor->Sponsors());
1598
        $this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count());
1599
1600
        // Test that belongs_many_many can be infered from with getNonReciprocalComponent
1601
        $this->assertListEquals(
1602
            [
1603
                ['Name' => 'Company corp'],
1604
                ['Name' => 'Team co.'],
1605
            ],
1606
            $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

1606
            /** @scrutinizer ignore-type */ $team->inferReciprocalComponent(DataObjectTest\EquipmentCompany::class, 'SponsoredTeams')
Loading history...
1607
        );
1608
1609
        // Test that many_many can be infered from getNonReciprocalComponent
1610
        $this->assertListEquals(
1611
            [
1612
                ['Title' => 'Team 1'],
1613
                ['Title' => 'Team 2'],
1614
                ['Title' => 'Subteam 1'],
1615
            ],
1616
            $company2->inferReciprocalComponent(DataObjectTest\Team::class, 'Sponsors')
1617
        );
1618
1619
        // Check many_many_extraFields still works
1620
        $equipmentCompany = $this->objFromFixture(DataObjectTest\EquipmentCompany::class, 'equipmentcompany1');
1621
        $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

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

1729
        $player = $newTeam->/** @scrutinizer ignore-call */ Players()->first();
Loading history...
1730
        $this->assertEquals('Sam', $player->FirstName);
1731
        $this->assertEquals("Prop", $player->Position);
1732
1733
        // Check that ordering a many-many relation by an aggregate column doesn't fail
1734
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
1735
        $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

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

2006
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ Company()->ID, 'belongs_to returns the right results.');
Loading history...
2007
2008
        // Test belongs_to can be infered via getNonReciprocalComponent
2009
        // Note: Will be returned as has_many since the belongs_to is ignored.
2010
        $this->assertListEquals(
2011
            [['Name' => 'New Company']],
2012
            $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

2012
            /** @scrutinizer ignore-type */ $ceo->inferReciprocalComponent(DataObjectTest\Company::class, 'CEO')
Loading history...
2013
        );
2014
2015
        // Test has_one to a belongs_to can be infered via getNonReciprocalComponent
2016
        $this->assertEquals(
2017
            $ceo->ID,
2018
            $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...
2019
        );
2020
2021
        // Test automatic creation of class where no assigment exists
2022
        $ceo = new DataObjectTest\CEO();
2023
        $ceo->write();
2024
2025
        $this->assertTrue(
2026
            $ceo->Company() instanceof DataObjectTest\Company,
2027
            'DataObjects across belongs_to relations are automatically created.'
2028
        );
2029
        $this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
2030
2031
        // Write object with components
2032
        $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...
2033
        $ceo->write(false, false, false, true);
2034
        $this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
2035
2036
        $newCEO = DataObject::get_by_id(DataObjectTest\CEO::class, $ceo->ID);
2037
        $this->assertEquals(
2038
            $ceo->Company()->ID,
2039
            $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

2039
            $newCEO->/** @scrutinizer ignore-call */ 
2040
                     Company()->ID,
Loading history...
2040
            'belongs_to can be retrieved from the database.'
2041
        );
2042
    }
2043
2044
    public function testBelongsToPolymorphic()
2045
    {
2046
        $company = new DataObjectTest\Company();
2047
        $ceo = new DataObjectTest\CEO();
2048
2049
        $company->write();
2050
        $ceo->write();
2051
2052
        // Test belongs_to assignment
2053
        $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...
2054
        $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...
2055
        $company->write();
2056
2057
        $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

2057
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ CompanyOwned()->ID, 'belongs_to returns the right results.');
Loading history...
2058
        $this->assertInstanceOf(
2059
            DataObjectTest\Company::class,
2060
            $ceo->CompanyOwned(),
2061
            'belongs_to returns the right results.'
2062
        );
2063
2064
        // Test automatic creation of class where no assigment exists
2065
        $ceo = new DataObjectTest\CEO();
2066
        $ceo->write();
2067
2068
        $this->assertTrue(
2069
            $ceo->CompanyOwned() instanceof DataObjectTest\Company,
2070
            'DataObjects across polymorphic belongs_to relations are automatically created.'
2071
        );
2072
        $this->assertEquals($ceo->ID, $ceo->CompanyOwned()->OwnerID, 'Remote IDs are automatically set.');
2073
        $this->assertInstanceOf($ceo->CompanyOwned()->OwnerClass, $ceo, 'Remote class is automatically  set');
2074
2075
        // Write object with components
2076
        $ceo->write(false, false, false, true);
2077
        $this->assertTrue($ceo->CompanyOwned()->isInDB(), 'write() writes belongs_to components to the database.');
2078
2079
        $newCEO = DataObject::get_by_id(DataObjectTest\CEO::class, $ceo->ID);
2080
        $this->assertEquals(
2081
            $ceo->CompanyOwned()->ID,
2082
            $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

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