Passed
Push — 4 ( 45f866...a5d6b9 )
by Robbie
07:20
created

DataObjectTest::setUp()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 6
c 1
b 0
f 0
nc 2
nop 0
dl 0
loc 10
rs 10
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\Company;
21
use SilverStripe\ORM\Tests\DataObjectTest\Player;
22
use SilverStripe\Security\Member;
23
use SilverStripe\View\ViewableData;
24
use stdClass;
25
26
class DataObjectTest extends SapphireTest
27
{
28
29
    protected static $fixture_file = 'DataObjectTest.yml';
30
31
    /**
32
     * Standard set of dataobject test classes
33
     *
34
     * @var array
35
     */
36
    public static $extra_data_objects = array(
37
        DataObjectTest\Team::class,
38
        DataObjectTest\Fixture::class,
39
        DataObjectTest\SubTeam::class,
40
        DataObjectTest\OtherSubclassWithSameField::class,
41
        DataObjectTest\FieldlessTable::class,
42
        DataObjectTest\FieldlessSubTable::class,
43
        DataObjectTest\ValidatedObject::class,
44
        DataObjectTest\Player::class,
45
        DataObjectTest\TeamComment::class,
46
        DataObjectTest\EquipmentCompany::class,
47
        DataObjectTest\SubEquipmentCompany::class,
48
        DataObjectTest\ExtendedTeamComment::class,
49
        DataObjectTest\Company::class,
50
        DataObjectTest\Staff::class,
51
        DataObjectTest\CEO::class,
52
        DataObjectTest\Fan::class,
53
        DataObjectTest\Play::class,
54
        DataObjectTest\Ploy::class,
55
        DataObjectTest\Bogey::class,
56
        DataObjectTest\Sortable::class,
57
        DataObjectTest\Bracket::class,
58
        DataObjectTest\RelationParent::class,
59
        DataObjectTest\RelationChildFirst::class,
60
        DataObjectTest\RelationChildSecond::class,
61
        DataObjectTest\MockDynamicAssignmentDataObject::class
62
    );
63
64
    protected function setUp()
65
    {
66
        parent::setUp();
67
68
        $validator = Member::password_validator();
69
        if ($validator) {
0 ignored issues
show
introduced by
$validator is of type SilverStripe\Security\PasswordValidator, thus it always evaluated to true.
Loading history...
70
            // Set low default password strength requirements so tests are not interfered with by user code
71
            $validator
72
                ->setMinTestScore(0)
73
                ->setMinLength(6);
74
        }
75
    }
76
77
    public static function getExtraDataObjects()
78
    {
79
        return array_merge(
80
            DataObjectTest::$extra_data_objects,
81
            ManyManyListTest::$extra_data_objects
82
        );
83
    }
84
85
    /**
86
     * @dataProvider provideSingletons
87
     */
88
    public function testSingleton($inst, $defaultValue, $altDefaultValue)
89
    {
90
        $inst = $inst();
91
        // Test that populateDefaults() isn't called on singletons
92
        // which can lead to SQL errors during build, and endless loops
93
        if ($defaultValue) {
94
            $this->assertEquals($defaultValue, $inst->MyFieldWithDefault);
95
        } else {
96
            $this->assertEmpty($inst->MyFieldWithDefault);
97
        }
98
99
        if ($altDefaultValue) {
100
            $this->assertEquals($altDefaultValue, $inst->MyFieldWithAltDefault);
101
        } else {
102
            $this->assertEmpty($inst->MyFieldWithAltDefault);
103
        }
104
    }
105
106
    public function provideSingletons()
107
    {
108
        // because PHPUnit evalutes test providers *before* setUp methods
109
        // any extensions added in the setUp methods won't be available
110
        // we must return closures to generate the arguments at run time
111
        return array(
112
            'create() static method' => array(function () {
113
                return DataObjectTest\Fixture::create();
114
            }, 'Default Value', 'Default Value'),
115
            'New object creation' => array(function () {
116
                return new DataObjectTest\Fixture();
117
            }, 'Default Value', 'Default Value'),
118
            'singleton() function' => array(function () {
119
                return singleton(DataObjectTest\Fixture::class);
120
            }, null, null),
121
            'singleton() static method' => array(function () {
122
                return DataObjectTest\Fixture::singleton();
123
            }, null, null),
124
            'Manual constructor args' => array(function () {
125
                return new DataObjectTest\Fixture(null, true);
126
            }, null, null),
127
        );
128
    }
129
130
    public function testDb()
131
    {
132
        $schema = DataObject::getSchema();
133
        $dbFields = $schema->fieldSpecs(DataObjectTest\TeamComment::class);
134
135
        // Assert fields are included
136
        $this->assertArrayHasKey('Name', $dbFields);
137
138
        // Assert the base fields are included
139
        $this->assertArrayHasKey('Created', $dbFields);
140
        $this->assertArrayHasKey('LastEdited', $dbFields);
141
        $this->assertArrayHasKey('ClassName', $dbFields);
142
        $this->assertArrayHasKey('ID', $dbFields);
143
144
        // Assert that the correct field type is returned when passing a field
145
        $this->assertEquals('Varchar', $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Name'));
146
        $this->assertEquals('Text', $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Comment'));
147
148
        // Test with table required
149
        $this->assertEquals(
150
            DataObjectTest\TeamComment::class . '.Varchar',
151
            $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Name', DataObjectSchema::INCLUDE_CLASS)
152
        );
153
        $this->assertEquals(
154
            DataObjectTest\TeamComment::class . '.Text',
155
            $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Comment', DataObjectSchema::INCLUDE_CLASS)
156
        );
157
        $dbFields = $schema->fieldSpecs(DataObjectTest\ExtendedTeamComment::class);
158
159
        // fixed fields are still included in extended classes
160
        $this->assertArrayHasKey('Created', $dbFields);
161
        $this->assertArrayHasKey('LastEdited', $dbFields);
162
        $this->assertArrayHasKey('ClassName', $dbFields);
163
        $this->assertArrayHasKey('ID', $dbFields);
164
165
        // Assert overloaded fields have correct data type
166
        $this->assertEquals('HTMLText', $schema->fieldSpec(DataObjectTest\ExtendedTeamComment::class, 'Comment'));
167
        $this->assertEquals(
168
            'HTMLText',
169
            $dbFields['Comment'],
170
            'Calls to DataObject::db without a field specified return correct data types'
171
        );
172
173
        // assertEquals doesn't verify the order of array elements, so access keys manually to check order:
174
        // expected: array('Name' => 'Varchar', 'Comment' => 'HTMLText')
175
        $this->assertEquals(
176
            array(
177
                'Name',
178
                'Comment'
179
            ),
180
            array_slice(array_keys($dbFields), 4, 2),
181
            'DataObject::db returns fields in correct order'
182
        );
183
    }
184
185
    public function testConstructAcceptsValues()
186
    {
187
        // Values can be an array...
188
        $player = new DataObjectTest\Player(
189
            array(
190
                'FirstName' => 'James',
191
                'Surname' => 'Smith'
192
            )
193
        );
194
195
        $this->assertEquals('James', $player->FirstName);
196
        $this->assertEquals('Smith', $player->Surname);
197
198
        // ... or a stdClass inst
199
        $data = new stdClass();
200
        $data->FirstName = 'John';
201
        $data->Surname = 'Doe';
202
        $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

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

482
        $this->assertEquals($team1ID, $captain1->/** @scrutinizer ignore-call */ FavouriteTeam()->ID);
Loading history...
483
484
        // Test that getNonReciprocalComponent can find has_one from the has_many end
485
        $this->assertEquals(
486
            $team1ID,
487
            $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...
488
        );
489
490
        // Check entity with polymorphic has-one
491
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, "fan1");
492
        $this->assertTrue((bool)$fan1->hasValue('Favourite'));
493
494
        // There will be fields named (relname)ID and (relname)Class for polymorphic
495
        // entities
496
        $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...
497
        $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...
498
499
        // There will be a method called $obj->relname() that returns the object itself
500
        $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

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

585
        $this->assertTrue($team1->/** @scrutinizer ignore-call */ Comments()->count() == 2);
Loading history...
586
587
        $team1Comments = [
588
            ['Comment' => 'This is a team comment by Joe'],
589
            ['Comment' => 'This is a team comment by Bob'],
590
        ];
591
592
        // Test the IDs on the DataObjects are set correctly
593
        $this->assertListEquals($team1Comments, $team1->Comments());
594
595
        // Test that has_many can be infered from the has_one via getNonReciprocalComponent
596
        $this->assertListEquals(
597
            $team1Comments,
598
            $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

598
            /** @scrutinizer ignore-type */ $team1->inferReciprocalComponent(DataObjectTest\TeamComment::class, 'Team')
Loading history...
599
        );
600
601
        // Test that we can add and remove items that already exist in the database
602
        $newComment = new DataObjectTest\TeamComment();
603
        $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...
604
        $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...
605
        $newComment->write();
606
        $team1->Comments()->add($newComment);
607
        $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...
608
609
        $comment1 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment1');
610
        $comment2 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment2');
611
        $team1->Comments()->remove($comment2);
612
613
        $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

613
        $team1CommentIDs = $team1->Comments()->/** @scrutinizer ignore-call */ sort('ID')->column('ID');
Loading history...
614
        $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
615
616
        // Test that removing an item from a list doesn't remove it from the same
617
        // relation belonging to a different object
618
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
619
        $team2 = $this->objFromFixture(DataObjectTest\Team::class, 'team2');
620
        $team2->Comments()->remove($comment1);
621
        $team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
622
        $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
623
    }
624
625
626
    /**
627
     * Test has many relationships against polymorphic has_one fields
628
     *   - Test getComponents() gets the ComponentSet of the other side of the relation
629
     *   - Test the IDs on the DataObjects are set correctly
630
     */
631
    public function testHasManyPolymorphicRelationships()
632
    {
633
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
634
635
        // Test getComponents() gets the ComponentSet of the other side of the relation
636
        $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

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

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

1373
        $sponsor = $team->/** @scrutinizer ignore-call */ Sponsors()->first();
Loading history...
1374
        $this->assertEquals('Int', $sponsor->castingHelper('SponsorFee'), 'many_many_extraFields not casted correctly');
1375
    }
1376
1377
    public function testSummaryFieldsCustomLabels()
1378
    {
1379
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1380
        $summaryFields = $team->summaryFields();
1381
1382
        $this->assertEquals(
1383
            [
1384
                'Title' => 'Custom Title',
1385
                'Title.UpperCase' => 'Title',
1386
                'Captain.ShirtNumber' => 'Captain\'s shirt number',
1387
                'Captain.FavouriteTeam.Title' => 'Captain\'s favourite team',
1388
            ],
1389
            $summaryFields
1390
        );
1391
    }
1392
1393
    public function testDataObjectUpdate()
1394
    {
1395
        /* update() calls can use the dot syntax to reference has_one relations and other methods that return
1396
        * objects */
1397
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1398
        $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...
1399
1400
        $team1->update(
1401
            array(
1402
                'DatabaseField' => 'Something',
1403
                'Captain.FirstName' => 'Jim',
1404
                'Captain.Email' => '[email protected]',
1405
                'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1406
            )
1407
        );
1408
1409
        /* Test the simple case of updating fields on the object itself */
1410
        $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...
1411
1412
        /* Setting Captain.Email and Captain.FirstName will have updated DataObjectTest_Captain.captain1 in
1413
        * the database.  Although update() doesn't usually write, it does write related records automatically. */
1414
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
1415
        $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...
1416
        $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...
1417
1418
        /* Jim's favourite team is team 1; we need to reload the object to the the change that setting Captain.
1419
        * FavouriteTeam.Title made */
1420
        $reloadedTeam1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1421
        $this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1422
    }
1423
1424
    public function testDataObjectUpdateNew()
1425
    {
1426
        /* update() calls can use the dot syntax to reference has_one relations and other methods that return
1427
        * objects */
1428
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1429
        $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...
1430
1431
        $team1->update(
1432
            array(
1433
                'Captain.FirstName' => 'Jim',
1434
                'Captain.FavouriteTeam.Title' => 'New and improved team 1',
1435
            )
1436
        );
1437
        /* Test that the captain ID has been updated */
1438
        $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...
1439
1440
        /* Fetch the newly created captain */
1441
        $captain1 = DataObjectTest\Player::get()->byID($team1->CaptainID);
1442
        $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...
1443
1444
        /* Grab the favourite team and make sure it has the correct values */
1445
        $reloadedTeam1 = $captain1->FavouriteTeam();
1446
        $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...
1447
        $this->assertEquals('New and improved team 1', $reloadedTeam1->Title);
1448
    }
1449
1450
1451
    /**
1452
     * @expectedException \SilverStripe\ORM\ValidationException
1453
     */
1454
    public function testWritingInvalidDataObjectThrowsException()
1455
    {
1456
        $validatedObject = new DataObjectTest\ValidatedObject();
1457
        $validatedObject->write();
1458
    }
1459
1460
    public function testWritingValidDataObjectDoesntThrowException()
1461
    {
1462
        $validatedObject = new DataObjectTest\ValidatedObject();
1463
        $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...
1464
1465
        $validatedObject->write();
1466
        $this->assertTrue($validatedObject->isInDB(), "Validated object was not saved to database");
1467
    }
1468
1469
    public function testSubclassCreation()
1470
    {
1471
        /* Creating a new object of a subclass should set the ClassName field correctly */
1472
        $obj = new DataObjectTest\SubTeam();
1473
        $obj->write();
1474
        $this->assertEquals(
1475
            DataObjectTest\SubTeam::class,
1476
            DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value()
1477
        );
1478
    }
1479
1480
    public function testForceInsert()
1481
    {
1482
        /* If you set an ID on an object and pass forceInsert = true, then the object should be correctly created */
1483
        $conn = DB::get_conn();
1484
        if (method_exists($conn, 'allowPrimaryKeyEditing')) {
1485
            $conn->allowPrimaryKeyEditing(DataObjectTest\Team::class, true);
1486
        }
1487
        $obj = new DataObjectTest\SubTeam();
1488
        $obj->ID = 1001;
1489
        $obj->Title = 'asdfasdf';
1490
        $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...
1491
        $obj->write(false, true);
1492
        if (method_exists($conn, 'allowPrimaryKeyEditing')) {
1493
            $conn->allowPrimaryKeyEditing(DataObjectTest\Team::class, false);
1494
        }
1495
1496
        $this->assertEquals(
1497
            DataObjectTest\SubTeam::class,
1498
            DB::query("SELECT \"ClassName\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $obj->ID")->value()
1499
        );
1500
1501
        /* Check that it actually saves to the database with the correct ID */
1502
        $this->assertEquals(
1503
            "1001",
1504
            DB::query(
1505
                "SELECT \"ID\" FROM \"DataObjectTest_SubTeam\" WHERE \"SubclassDatabaseField\" = 'asdfasdf'"
1506
            )->value()
1507
        );
1508
        $this->assertEquals(
1509
            "1001",
1510
            DB::query("SELECT \"ID\" FROM \"DataObjectTest_Team\" WHERE \"Title\" = 'asdfasdf'")->value()
1511
        );
1512
    }
1513
1514
    public function testHasOwnTable()
1515
    {
1516
        $schema = DataObject::getSchema();
1517
        /* Test DataObject::has_own_table() returns true if the object has $has_one or $db values */
1518
        $this->assertTrue($schema->classHasTable(DataObjectTest\Player::class));
1519
        $this->assertTrue($schema->classHasTable(DataObjectTest\Team::class));
1520
        $this->assertTrue($schema->classHasTable(DataObjectTest\Fixture::class));
1521
1522
        /* Root DataObject that always have a table, even if they lack both $db and $has_one */
1523
        $this->assertTrue($schema->classHasTable(DataObjectTest\FieldlessTable::class));
1524
1525
        /* Subclasses without $db or $has_one don't have a table */
1526
        $this->assertFalse($schema->classHasTable(DataObjectTest\FieldlessSubTable::class));
1527
1528
        /* Return false if you don't pass it a subclass of DataObject */
1529
        $this->assertFalse($schema->classHasTable(DataObject::class));
1530
        $this->assertFalse($schema->classHasTable(ViewableData::class));
1531
1532
        /* Invalid class name */
1533
        $this->assertFalse($schema->classHasTable("ThisIsntADataObject"));
1534
    }
1535
1536
    public function testMerge()
1537
    {
1538
        // test right merge of subclasses
1539
        $left = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
1540
        $right = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam2_with_player_relation');
1541
        $leftOrigID = $left->ID;
1542
        $left->merge($right, 'right', false, false);
1543
        $this->assertEquals(
1544
            $left->Title,
1545
            'Subteam 2',
1546
            'merge() with "right" priority overwrites fields with existing values on subclasses'
1547
        );
1548
        $this->assertEquals(
1549
            $left->ID,
1550
            $leftOrigID,
1551
            'merge() with "right" priority doesnt overwrite database ID'
1552
        );
1553
1554
        // test overwriteWithEmpty flag on existing left values
1555
        $left = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam2_with_player_relation');
1556
        $right = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam3_with_empty_fields');
1557
        $left->merge($right, 'right', false, true);
1558
        $this->assertEquals(
1559
            $left->Title,
1560
            'Subteam 3',
1561
            'merge() with $overwriteWithEmpty overwrites non-empty fields on left object'
1562
        );
1563
1564
        // test overwriteWithEmpty flag on empty left values
1565
        $left = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
1566
        // $SubclassDatabaseField is empty on here
1567
        $right = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam2_with_player_relation');
1568
        $left->merge($right, 'right', false, true);
1569
        $this->assertEquals(
1570
            $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...
1571
            null,
1572
            'merge() with $overwriteWithEmpty overwrites empty fields on left object'
1573
        );
1574
1575
        // @todo test "left" priority flag
1576
        // @todo test includeRelations flag
1577
        // @todo test includeRelations in combination with overwriteWithEmpty
1578
        // @todo test has_one relations
1579
        // @todo test has_many and many_many relations
1580
    }
1581
1582
    public function testPopulateDefaults()
1583
    {
1584
        $obj = new DataObjectTest\Fixture();
1585
        $this->assertEquals(
1586
            $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...
1587
            'Default Value',
1588
            'Defaults are populated for in-memory object from $defaults array'
1589
        );
1590
1591
        $this->assertEquals(
1592
            $obj->MyFieldWithAltDefault,
1593
            'Default Value',
1594
            'Defaults are populated from overloaded populateDefaults() method'
1595
        );
1596
1597
        // Test populate defaults on subclasses
1598
        $staffObj = new DataObjectTest\Staff();
1599
        $this->assertEquals('Staff', $staffObj->EmploymentType);
0 ignored issues
show
Bug Best Practice introduced by
The property EmploymentType does not exist on SilverStripe\ORM\Tests\DataObjectTest\Staff. Since you implemented __get, consider adding a @property annotation.
Loading history...
1600
1601
        $ceoObj = new DataObjectTest\CEO();
1602
        $this->assertEquals('Staff', $ceoObj->EmploymentType);
0 ignored issues
show
Bug Best Practice introduced by
The property EmploymentType does not exist on SilverStripe\ORM\Tests\DataObjectTest\CEO. Since you implemented __get, consider adding a @property annotation.
Loading history...
1603
    }
1604
1605
    /**
1606
     * @expectedException \InvalidArgumentException
1607
     */
1608
    public function testValidateModelDefinitionsFailsWithArray()
1609
    {
1610
        Config::modify()->merge(DataObjectTest\Team::class, 'has_one', array('NotValid' => array('NoArraysAllowed')));
1611
        DataObject::getSchema()->hasOneComponent(DataObjectTest\Team::class, 'NotValid');
1612
    }
1613
1614
    /**
1615
     * @expectedException \InvalidArgumentException
1616
     */
1617
    public function testValidateModelDefinitionsFailsWithIntKey()
1618
    {
1619
        Config::modify()->set(DataObjectTest\Team::class, 'has_many', array(0 => DataObjectTest\Player::class));
1620
        DataObject::getSchema()->hasManyComponent(DataObjectTest\Team::class, 0);
1621
    }
1622
1623
    /**
1624
     * @expectedException \InvalidArgumentException
1625
     */
1626
    public function testValidateModelDefinitionsFailsWithIntValue()
1627
    {
1628
        Config::modify()->merge(DataObjectTest\Team::class, 'many_many', array('Players' => 12));
1629
        DataObject::getSchema()->manyManyComponent(DataObjectTest\Team::class, 'Players');
1630
    }
1631
1632
    public function testNewClassInstance()
1633
    {
1634
        $dataObject = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1635
        $changedDO = $dataObject->newClassInstance(DataObjectTest\SubTeam::class);
1636
        $changedFields = $changedDO->getChangedFields();
1637
1638
        // Don't write the record, it will reset changed fields
1639
        $this->assertInstanceOf(DataObjectTest\SubTeam::class, $changedDO);
1640
        $this->assertEquals($changedDO->ClassName, DataObjectTest\SubTeam::class);
1641
        $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...
1642
        $this->assertContains('ClassName', array_keys($changedFields));
1643
        $this->assertEquals($changedFields['ClassName']['before'], DataObjectTest\Team::class);
1644
        $this->assertEquals($changedFields['ClassName']['after'], DataObjectTest\SubTeam::class);
1645
        $this->assertEquals($changedFields['RecordClassName']['before'], DataObjectTest\Team::class);
1646
        $this->assertEquals($changedFields['RecordClassName']['after'], DataObjectTest\SubTeam::class);
1647
1648
        $changedDO->write();
1649
1650
        $this->assertInstanceOf(DataObjectTest\SubTeam::class, $changedDO);
1651
        $this->assertEquals($changedDO->ClassName, DataObjectTest\SubTeam::class);
1652
1653
        // Test invalid classes fail
1654
        $this->expectException(InvalidArgumentException::class);
1655
        $this->expectExceptionMessage('Controller is not a valid subclass of DataObject');
1656
        /**
1657
         * @skipUpgrade
1658
         */
1659
        $dataObject->newClassInstance('Controller');
1660
    }
1661
1662
    public function testMultipleManyManyWithSameClass()
1663
    {
1664
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1665
        $company2 = $this->objFromFixture(DataObjectTest\EquipmentCompany::class, 'equipmentcompany2');
1666
        $sponsors = $team->Sponsors();
1667
        $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

1667
        /** @scrutinizer ignore-call */ 
1668
        $equipmentSuppliers = $team->EquipmentSuppliers();
Loading history...
1668
1669
        // Check that DataObject::many_many() works as expected
1670
        $manyManyComponent = DataObject::getSchema()->manyManyComponent(DataObjectTest\Team::class, 'Sponsors');
1671
        $this->assertEquals(ManyManyList::class, $manyManyComponent['relationClass']);
1672
        $this->assertEquals(
1673
            DataObjectTest\Team::class,
1674
            $manyManyComponent['parentClass'],
1675
            'DataObject::many_many() didn\'t find the correct base class'
1676
        );
1677
        $this->assertEquals(
1678
            DataObjectTest\EquipmentCompany::class,
1679
            $manyManyComponent['childClass'],
1680
            'DataObject::many_many() didn\'t find the correct target class for the relation'
1681
        );
1682
        $this->assertEquals(
1683
            'DataObjectTest_EquipmentCompany_SponsoredTeams',
1684
            $manyManyComponent['join'],
1685
            'DataObject::many_many() didn\'t find the correct relation table'
1686
        );
1687
        $this->assertEquals('DataObjectTest_TeamID', $manyManyComponent['parentField']);
1688
        $this->assertEquals('DataObjectTest_EquipmentCompanyID', $manyManyComponent['childField']);
1689
1690
        // Check that ManyManyList still works
1691
        $this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation');
1692
        $this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation');
1693
1694
        // Check everything works when no relation is present
1695
        $teamWithoutSponsor = $this->objFromFixture(DataObjectTest\Team::class, 'team3');
1696
        $this->assertInstanceOf(ManyManyList::class, $teamWithoutSponsor->Sponsors());
1697
        $this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count());
1698
1699
        // Test that belongs_many_many can be infered from with getNonReciprocalComponent
1700
        $this->assertListEquals(
1701
            [
1702
                ['Name' => 'Company corp'],
1703
                ['Name' => 'Team co.'],
1704
            ],
1705
            $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

1705
            /** @scrutinizer ignore-type */ $team->inferReciprocalComponent(DataObjectTest\EquipmentCompany::class, 'SponsoredTeams')
Loading history...
1706
        );
1707
1708
        // Test that many_many can be infered from getNonReciprocalComponent
1709
        $this->assertListEquals(
1710
            [
1711
                ['Title' => 'Team 1'],
1712
                ['Title' => 'Team 2'],
1713
                ['Title' => 'Subteam 1'],
1714
            ],
1715
            $company2->inferReciprocalComponent(DataObjectTest\Team::class, 'Sponsors')
1716
        );
1717
1718
        // Check many_many_extraFields still works
1719
        $equipmentCompany = $this->objFromFixture(DataObjectTest\EquipmentCompany::class, 'equipmentcompany1');
1720
        $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

1720
        $equipmentCompany->/** @scrutinizer ignore-call */ 
1721
                           SponsoredTeams()->add($teamWithoutSponsor, array('SponsorFee' => 1000));
Loading history...
1721
        $sponsoredTeams = $equipmentCompany->SponsoredTeams();
1722
        $this->assertEquals(
1723
            1000,
1724
            $sponsoredTeams->byID($teamWithoutSponsor->ID)->SponsorFee,
1725
            'Data from many_many_extraFields was not stored/extracted correctly'
1726
        );
1727
1728
        // Check subclasses correctly inherit multiple many_manys
1729
        $subTeam = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
1730
        $this->assertEquals(
1731
            2,
1732
            $subTeam->Sponsors()->count(),
1733
            'Child class did not inherit multiple many_manys'
1734
        );
1735
        $this->assertEquals(
1736
            1,
1737
            $subTeam->EquipmentSuppliers()->count(),
1738
            'Child class did not inherit multiple many_manys'
1739
        );
1740
        // Team 2 has one EquipmentCompany sponsor and one SubEquipmentCompany
1741
        $team2 = $this->objFromFixture(DataObjectTest\Team::class, 'team2');
1742
        $this->assertEquals(
1743
            2,
1744
            $team2->Sponsors()->count(),
1745
            'Child class did not inherit multiple belongs_many_manys'
1746
        );
1747
1748
        // Check many_many_extraFields also works from the belongs_many_many side
1749
        $sponsors = $team2->Sponsors();
1750
        $sponsors->add($equipmentCompany, array('SponsorFee' => 750));
1751
        $this->assertEquals(
1752
            750,
1753
            $sponsors->byID($equipmentCompany->ID)->SponsorFee,
1754
            'Data from many_many_extraFields was not stored/extracted correctly'
1755
        );
1756
1757
        $subEquipmentCompany = $this->objFromFixture(DataObjectTest\SubEquipmentCompany::class, 'subequipmentcompany1');
1758
        $subTeam->Sponsors()->add($subEquipmentCompany, array('SponsorFee' => 1200));
1759
        $this->assertEquals(
1760
            1200,
1761
            $subTeam->Sponsors()->byID($subEquipmentCompany->ID)->SponsorFee,
1762
            'Data from inherited many_many_extraFields was not stored/extracted correctly'
1763
        );
1764
    }
1765
1766
    public function testManyManyExtraFields()
1767
    {
1768
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1769
        $schema = DataObject::getSchema();
1770
1771
        // Get all extra fields
1772
        $teamExtraFields = $team->manyManyExtraFields();
1773
        $this->assertEquals(
1774
            array(
1775
                'Players' => array('Position' => 'Varchar(100)')
1776
            ),
1777
            $teamExtraFields
1778
        );
1779
1780
        // Ensure fields from parent classes are included
1781
        $subTeam = singleton(DataObjectTest\SubTeam::class);
1782
        $teamExtraFields = $subTeam->manyManyExtraFields();
1783
        $this->assertEquals(
1784
            array(
1785
                'Players' => array('Position' => 'Varchar(100)'),
1786
                'FormerPlayers' => array('Position' => 'Varchar(100)')
1787
            ),
1788
            $teamExtraFields
1789
        );
1790
1791
        // Extra fields are immediately available on the Team class (defined in $many_many_extraFields)
1792
        $teamExtraFields = $schema->manyManyExtraFieldsForComponent(DataObjectTest\Team::class, 'Players');
1793
        $this->assertEquals(
1794
            $teamExtraFields,
1795
            array(
1796
                'Position' => 'Varchar(100)'
1797
            )
1798
        );
1799
1800
        // We'll have to go through the relation to get the extra fields on Player
1801
        $playerExtraFields = $schema->manyManyExtraFieldsForComponent(DataObjectTest\Player::class, 'Teams');
1802
        $this->assertEquals(
1803
            $playerExtraFields,
1804
            array(
1805
                'Position' => 'Varchar(100)'
1806
            )
1807
        );
1808
1809
        // Iterate through a many-many relationship and confirm that extra fields are included
1810
        $newTeam = new DataObjectTest\Team();
1811
        $newTeam->Title = "New team";
1812
        $newTeam->write();
1813
        $newTeamID = $newTeam->ID;
1814
1815
        $newPlayer = new DataObjectTest\Player();
1816
        $newPlayer->FirstName = "Sam";
1817
        $newPlayer->Surname = "Minnee";
1818
        $newPlayer->write();
1819
1820
        // The idea of Sam as a prop is essentially humourous.
1821
        $newTeam->Players()->add($newPlayer, array("Position" => "Prop"));
1822
1823
        // Requery and uncache everything
1824
        $newTeam->flushCache();
1825
        $newTeam = DataObject::get_by_id(DataObjectTest\Team::class, $newTeamID);
1826
1827
        // Check that the Position many_many_extraField is extracted.
1828
        $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

1828
        $player = $newTeam->/** @scrutinizer ignore-call */ Players()->first();
Loading history...
1829
        $this->assertEquals('Sam', $player->FirstName);
1830
        $this->assertEquals("Prop", $player->Position);
1831
1832
        // Check that ordering a many-many relation by an aggregate column doesn't fail
1833
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
1834
        $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

1834
        $player->/** @scrutinizer ignore-call */ 
1835
                 Teams()->sort("count(DISTINCT \"DataObjectTest_Team_Players\".\"DataObjectTest_PlayerID\") DESC");
Loading history...
1835
    }
1836
1837
    /**
1838
     * Check that the queries generated for many-many relation queries can have unlimitedRowCount
1839
     * called on them.
1840
     */
1841
    public function testManyManyUnlimitedRowCount()
1842
    {
1843
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
1844
        // TODO: What's going on here?
1845
        $this->assertEquals(2, $player->Teams()->dataQuery()->query()->unlimitedRowCount());
1846
    }
1847
1848
    /**
1849
     * Tests that singular_name() generates sensible defaults.
1850
     */
1851
    public function testSingularName()
1852
    {
1853
        $assertions = array(
1854
            DataObjectTest\Player::class => 'Player',
1855
            DataObjectTest\Team::class => 'Team',
1856
            DataObjectTest\Fixture::class => 'Fixture',
1857
        );
1858
1859
        foreach ($assertions as $class => $expectedSingularName) {
1860
            $this->assertEquals(
1861
                $expectedSingularName,
1862
                singleton($class)->singular_name(),
1863
                "Assert that the singular_name for '$class' is correct."
1864
            );
1865
        }
1866
    }
1867
1868
    /**
1869
     * Tests that plural_name() generates sensible defaults.
1870
     */
1871
    public function testPluralName()
1872
    {
1873
        $assertions = array(
1874
            DataObjectTest\Player::class => 'Players',
1875
            DataObjectTest\Team::class => 'Teams',
1876
            DataObjectTest\Fixture::class => 'Fixtures',
1877
            DataObjectTest\Play::class => 'Plays',
1878
            DataObjectTest\Bogey::class => 'Bogeys',
1879
            DataObjectTest\Ploy::class => 'Ploys',
1880
        );
1881
        i18n::set_locale('en_NZ');
1882
        foreach ($assertions as $class => $expectedPluralName) {
1883
            $this->assertEquals(
1884
                $expectedPluralName,
1885
                DataObject::singleton($class)->plural_name(),
1886
                "Assert that the plural_name for '$class' is correct."
1887
            );
1888
            $this->assertEquals(
1889
                $expectedPluralName,
1890
                DataObject::singleton($class)->i18n_plural_name(),
1891
                "Assert that the i18n_plural_name for '$class' is correct."
1892
            );
1893
        }
1894
    }
1895
1896
    public function testHasDatabaseField()
1897
    {
1898
        $team = singleton(DataObjectTest\Team::class);
1899
        $subteam = singleton(DataObjectTest\SubTeam::class);
1900
1901
        $this->assertTrue(
1902
            $team->hasDatabaseField('Title'),
1903
            "hasOwnDatabaseField() works with \$db fields"
1904
        );
1905
        $this->assertTrue(
1906
            $team->hasDatabaseField('CaptainID'),
1907
            "hasOwnDatabaseField() works with \$has_one fields"
1908
        );
1909
        $this->assertFalse(
1910
            $team->hasDatabaseField('NonExistentField'),
1911
            "hasOwnDatabaseField() doesn't detect non-existend fields"
1912
        );
1913
        $this->assertTrue(
1914
            $team->hasDatabaseField('ExtendedDatabaseField'),
1915
            "hasOwnDatabaseField() works with extended fields"
1916
        );
1917
        $this->assertFalse(
1918
            $team->hasDatabaseField('SubclassDatabaseField'),
1919
            "hasOwnDatabaseField() doesn't pick up fields in subclasses on parent class"
1920
        );
1921
1922
        $this->assertTrue(
1923
            $subteam->hasDatabaseField('SubclassDatabaseField'),
1924
            "hasOwnDatabaseField() picks up fields in subclasses"
1925
        );
1926
    }
1927
1928
    public function testFieldTypes()
1929
    {
1930
        $obj = new DataObjectTest\Fixture();
1931
        $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...
1932
        $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...
1933
        $obj->write();
1934
        $obj->flushCache();
1935
1936
        $obj = DataObject::get_by_id(DataObjectTest\Fixture::class, $obj->ID);
1937
        $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...
1938
        $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...
1939
    }
1940
1941
    /**
1942
     * Tests that the autogenerated ID is returned as int
1943
     */
1944
    public function testIDFieldTypeAfterInsert()
1945
    {
1946
        $obj = new DataObjectTest\Fixture();
1947
        $obj->write();
1948
1949
        $this->assertInternalType("int", $obj->ID);
1950
    }
1951
1952
    /**
1953
     * Tests that zero values are returned with the correct types
1954
     */
1955
    public function testZeroIsFalse()
1956
    {
1957
        $obj = new DataObjectTest\Fixture();
1958
        $obj->MyInt = 0;
0 ignored issues
show
Bug Best Practice introduced by
The property MyInt does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
1959
        $obj->MyDecimal = 0.00;
0 ignored issues
show
Bug Best Practice introduced by
The property MyDecimal does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
1960
        $obj->MyCurrency = 0.00;
0 ignored issues
show
Bug Best Practice introduced by
The property MyCurrency does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __set, consider adding a @property annotation.
Loading history...
1961
        $obj->write();
1962
1963
        $this->assertEquals(0, $obj->MyInt, 'DBInt fields should be integer on first assignment');
0 ignored issues
show
Bug Best Practice introduced by
The property MyInt does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __get, consider adding a @property annotation.
Loading history...
1964
        $this->assertEquals(0.00, $obj->MyDecimal, 'DBDecimal fields should be float on first assignment');
0 ignored issues
show
Bug Best Practice introduced by
The property MyDecimal does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __get, consider adding a @property annotation.
Loading history...
1965
        $this->assertEquals(0.00, $obj->MyCurrency, 'DBCurrency fields should be float on first assignment');
0 ignored issues
show
Bug Best Practice introduced by
The property MyCurrency does not exist on SilverStripe\ORM\Tests\DataObjectTest\Fixture. Since you implemented __get, consider adding a @property annotation.
Loading history...
1966
1967
        $obj2 = DataObjectTest\Fixture::get()->byId($obj->ID);
1968
1969
        $this->assertEquals(0, $obj2->MyInt, 'DBInt fields should be integer');
0 ignored issues
show
Bug Best Practice introduced by
The property MyInt does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1970
        $this->assertEquals(0.00, $obj2->MyDecimal, 'DBDecimal fields should be float');
0 ignored issues
show
Bug Best Practice introduced by
The property MyDecimal does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1971
        $this->assertEquals(0.00, $obj2->MyCurrency, 'DBCurrency fields should be float');
0 ignored issues
show
Bug Best Practice introduced by
The property MyCurrency does not exist on SilverStripe\ORM\DataObject. Since you implemented __get, consider adding a @property annotation.
Loading history...
1972
1973
        $this->assertFalse((bool)$obj2->MyInt, 'DBInt zero fields should be falsey on fetch from DB');
1974
        $this->assertFalse((bool)$obj2->MyDecimal, 'DBDecimal zero fields should be falsey on fetch from DB');
1975
        $this->assertFalse((bool)$obj2->MyCurrency, 'DBCurrency zero fields should be falsey on fetch from DB');
1976
    }
1977
1978
    public function testTwoSubclassesWithTheSameFieldNameWork()
1979
    {
1980
        // Create two objects of different subclasses, setting the values of fields that are
1981
        // defined separately in each subclass
1982
        $obj1 = new DataObjectTest\SubTeam();
1983
        $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...
1984
        $obj2 = new DataObjectTest\OtherSubclassWithSameField();
1985
        $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...
1986
1987
        // Write them to the database
1988
        $obj1->write();
1989
        $obj2->write();
1990
1991
        // Check that the values of those fields are properly read from the database
1992
        $values = DataObject::get(
1993
            DataObjectTest\Team::class,
1994
            "\"DataObjectTest_Team\".\"ID\" IN
1995
			($obj1->ID, $obj2->ID)"
1996
        )->column("SubclassDatabaseField");
1997
        $this->assertEquals(array_intersect($values, array('obj1', 'obj2')), $values);
1998
    }
1999
2000
    public function testClassNameSetForNewObjects()
2001
    {
2002
        $d = new DataObjectTest\Player();
2003
        $this->assertEquals(DataObjectTest\Player::class, $d->ClassName);
2004
    }
2005
2006
    public function testHasValue()
2007
    {
2008
        $team = new DataObjectTest\Team();
2009
        $this->assertFalse($team->hasValue('Title', null, false));
2010
        $this->assertFalse($team->hasValue('DatabaseField', null, false));
2011
2012
        $team->Title = 'hasValue';
2013
        $this->assertTrue($team->hasValue('Title', null, false));
2014
        $this->assertFalse($team->hasValue('DatabaseField', null, false));
2015
2016
        $team->Title = '<p></p>';
2017
        $this->assertTrue(
2018
            $team->hasValue('Title', null, false),
2019
            'Test that an empty paragraph is a value for non-HTML fields.'
2020
        );
2021
2022
        $team->DatabaseField = 'hasValue';
2023
        $this->assertTrue($team->hasValue('Title', null, false));
2024
        $this->assertTrue($team->hasValue('DatabaseField', null, false));
2025
    }
2026
2027
    public function testHasMany()
2028
    {
2029
        $company = new DataObjectTest\Company();
2030
2031
        $this->assertEquals(
2032
            array(
2033
                'CurrentStaff' => DataObjectTest\Staff::class,
2034
                'PreviousStaff' => DataObjectTest\Staff::class
2035
            ),
2036
            $company->hasMany(),
2037
            'has_many strips field name data by default.'
2038
        );
2039
2040
        $this->assertEquals(
2041
            DataObjectTest\Staff::class,
2042
            DataObject::getSchema()->hasManyComponent(DataObjectTest\Company::class, 'CurrentStaff'),
2043
            'has_many strips field name data by default on single relationships.'
2044
        );
2045
2046
        $this->assertEquals(
2047
            array(
2048
                'CurrentStaff' => DataObjectTest\Staff::class . '.CurrentCompany',
2049
                'PreviousStaff' => DataObjectTest\Staff::class . '.PreviousCompany'
2050
            ),
2051
            $company->hasMany(false),
2052
            'has_many returns field name data when $classOnly is false.'
2053
        );
2054
2055
        $this->assertEquals(
2056
            DataObjectTest\Staff::class . '.CurrentCompany',
2057
            DataObject::getSchema()->hasManyComponent(DataObjectTest\Company::class, 'CurrentStaff', false),
2058
            'has_many returns field name data on single records when $classOnly is false.'
2059
        );
2060
    }
2061
2062
    public function testGetRemoteJoinField()
2063
    {
2064
        $schema = DataObject::getSchema();
2065
2066
        // Company schema
2067
        $staffJoinField = $schema->getRemoteJoinField(
2068
            DataObjectTest\Company::class,
2069
            'CurrentStaff',
2070
            'has_many',
2071
            $polymorphic
2072
        );
2073
        $this->assertEquals('CurrentCompanyID', $staffJoinField);
2074
        $this->assertFalse($polymorphic, 'DataObjectTest_Company->CurrentStaff is not polymorphic');
2075
        $previousStaffJoinField = $schema->getRemoteJoinField(
2076
            DataObjectTest\Company::class,
2077
            'PreviousStaff',
2078
            'has_many',
2079
            $polymorphic
2080
        );
2081
        $this->assertEquals('PreviousCompanyID', $previousStaffJoinField);
2082
        $this->assertFalse($polymorphic, 'DataObjectTest_Company->PreviousStaff is not polymorphic');
2083
2084
        // CEO Schema
2085
        $this->assertEquals(
2086
            'CEOID',
2087
            $schema->getRemoteJoinField(
2088
                DataObjectTest\CEO::class,
2089
                'Company',
2090
                'belongs_to',
2091
                $polymorphic
2092
            )
2093
        );
2094
        $this->assertFalse($polymorphic, 'DataObjectTest_CEO->Company is not polymorphic');
2095
        $this->assertEquals(
2096
            'PreviousCEOID',
2097
            $schema->getRemoteJoinField(
2098
                DataObjectTest\CEO::class,
2099
                'PreviousCompany',
2100
                'belongs_to',
2101
                $polymorphic
2102
            )
2103
        );
2104
        $this->assertFalse($polymorphic, 'DataObjectTest_CEO->PreviousCompany is not polymorphic');
2105
2106
        // Team schema
2107
        $this->assertEquals(
2108
            'Favourite',
2109
            $schema->getRemoteJoinField(
2110
                DataObjectTest\Team::class,
2111
                'Fans',
2112
                'has_many',
2113
                $polymorphic
2114
            )
2115
        );
2116
        $this->assertTrue($polymorphic, 'DataObjectTest_Team->Fans is polymorphic');
2117
        $this->assertEquals(
2118
            'TeamID',
2119
            $schema->getRemoteJoinField(
2120
                DataObjectTest\Team::class,
2121
                'Comments',
2122
                'has_many',
2123
                $polymorphic
2124
            )
2125
        );
2126
        $this->assertFalse($polymorphic, 'DataObjectTest_Team->Comments is not polymorphic');
2127
    }
2128
2129
    public function testBelongsTo()
2130
    {
2131
        $company = new DataObjectTest\Company();
2132
        $ceo = new DataObjectTest\CEO();
2133
2134
        $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...
2135
        $company->write();
2136
        $ceo->write();
2137
2138
        // Test belongs_to assignment
2139
        $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...
2140
        $company->write();
2141
2142
        $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

2142
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ Company()->ID, 'belongs_to returns the right results.');
Loading history...
2143
2144
        // Test belongs_to can be infered via getNonReciprocalComponent
2145
        // Note: Will be returned as has_many since the belongs_to is ignored.
2146
        $this->assertListEquals(
2147
            [['Name' => 'New Company']],
2148
            $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

2148
            /** @scrutinizer ignore-type */ $ceo->inferReciprocalComponent(DataObjectTest\Company::class, 'CEO')
Loading history...
2149
        );
2150
2151
        // Test has_one to a belongs_to can be infered via getNonReciprocalComponent
2152
        $this->assertEquals(
2153
            $ceo->ID,
2154
            $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...
2155
        );
2156
2157
        // Test automatic creation of class where no assigment exists
2158
        $ceo = new DataObjectTest\CEO();
2159
        $ceo->write();
2160
2161
        $this->assertTrue(
2162
            $ceo->Company() instanceof DataObjectTest\Company,
2163
            'DataObjects across belongs_to relations are automatically created.'
2164
        );
2165
        $this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
2166
2167
        // Write object with components
2168
        $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...
2169
        $ceo->write(false, false, false, true);
2170
        $this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
2171
2172
        $newCEO = DataObject::get_by_id(DataObjectTest\CEO::class, $ceo->ID);
2173
        $this->assertEquals(
2174
            $ceo->Company()->ID,
2175
            $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

2175
            $newCEO->/** @scrutinizer ignore-call */ 
2176
                     Company()->ID,
Loading history...
2176
            'belongs_to can be retrieved from the database.'
2177
        );
2178
    }
2179
2180
    public function testBelongsToPolymorphic()
2181
    {
2182
        $company = new DataObjectTest\Company();
2183
        $ceo = new DataObjectTest\CEO();
2184
2185
        $company->write();
2186
        $ceo->write();
2187
2188
        // Test belongs_to assignment
2189
        $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...
2190
        $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...
2191
        $company->write();
2192
2193
        $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

2193
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ CompanyOwned()->ID, 'belongs_to returns the right results.');
Loading history...
2194
        $this->assertInstanceOf(
2195
            DataObjectTest\Company::class,
2196
            $ceo->CompanyOwned(),
2197
            'belongs_to returns the right results.'
2198
        );
2199
2200
        // Test automatic creation of class where no assigment exists
2201
        $ceo = new DataObjectTest\CEO();
2202
        $ceo->write();
2203
2204
        $this->assertTrue(
2205
            $ceo->CompanyOwned() instanceof DataObjectTest\Company,
2206
            'DataObjects across polymorphic belongs_to relations are automatically created.'
2207
        );
2208
        $this->assertEquals($ceo->ID, $ceo->CompanyOwned()->OwnerID, 'Remote IDs are automatically set.');
2209
        $this->assertInstanceOf($ceo->CompanyOwned()->OwnerClass, $ceo, 'Remote class is automatically  set');
2210
2211
        // Write object with components
2212
        $ceo->write(false, false, false, true);
2213
        $this->assertTrue($ceo->CompanyOwned()->isInDB(), 'write() writes belongs_to components to the database.');
2214
2215
        $newCEO = DataObject::get_by_id(DataObjectTest\CEO::class, $ceo->ID);
2216
        $this->assertEquals(
2217
            $ceo->CompanyOwned()->ID,
2218
            $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

2218
            $newCEO->/** @scrutinizer ignore-call */ 
2219
                     CompanyOwned()->ID,
Loading history...
2219
            'polymorphic belongs_to can be retrieved from the database.'
2220
        );
2221
    }
2222
2223
    /**
2224
     * @expectedException LogicException
2225
     */
2226
    public function testInvalidate()
2227
    {
2228
        $do = new DataObjectTest\Fixture();
2229
        $do->write();
2230
2231
        $do->delete();
2232
2233
        $do->delete(); // Prohibit invalid object manipulation
2234
        $do->write();
2235
        $do->duplicate();
2236
    }
2237
2238
    public function testToMap()
2239
    {
2240
        $obj = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
2241
2242
        $map = $obj->toMap();
2243
2244
        $this->assertArrayHasKey('ID', $map, 'Contains base fields');
2245
        $this->assertArrayHasKey('Title', $map, 'Contains fields from parent class');
2246
        $this->assertArrayHasKey('SubclassDatabaseField', $map, 'Contains fields from concrete class');
2247
2248
        $this->assertEquals(
2249
            $obj->ID,
2250
            $map['ID'],
2251
            'Contains values from base fields'
2252
        );
2253
        $this->assertEquals(
2254
            $obj->Title,
2255
            $map['Title'],
2256
            'Contains values from parent class fields'
2257
        );
2258
        $this->assertEquals(
2259
            $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...
2260
            $map['SubclassDatabaseField'],
2261
            'Contains values from concrete class fields'
2262
        );
2263
2264
        $newObj = new DataObjectTest\SubTeam();
0 ignored issues
show
Unused Code introduced by
The assignment to $newObj is dead and can be removed.
Loading history...
2265
        $this->assertArrayHasKey('Title', $map, 'Contains null fields');
2266
    }
2267
2268
    public function testIsEmpty()
2269
    {
2270
        $objEmpty = new DataObjectTest\Team();
2271
        $this->assertTrue($objEmpty->isEmpty(), 'New instance without populated defaults is empty');
2272
2273
        $objEmpty->Title = '0'; //
2274
        $this->assertFalse($objEmpty->isEmpty(), 'Zero value in attribute considered non-empty');
2275
    }
2276
2277
    public function testRelField()
2278
    {
2279
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
2280
        // Test traversal of a single has_one
2281
        $this->assertEquals("Team 1", $captain1->relField('FavouriteTeam.Title'));
2282
        // Test direct field access
2283
        $this->assertEquals("Captain", $captain1->relField('FirstName'));
2284
2285
        // Test empty link
2286
        $captain2 = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
2287
        $this->assertEmpty($captain2->relField('FavouriteTeam.Title'));
2288
        $this->assertNull($captain2->relField('FavouriteTeam.ReturnsNull'));
2289
        $this->assertNull($captain2->relField('FavouriteTeam.ReturnsNull.Title'));
2290
2291
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
2292
        // Test that we can traverse more than once, and that arbitrary methods are okay
2293
        $this->assertEquals("Team 1", $player->relField('Teams.First.Title'));
2294
2295
        $newPlayer = new DataObjectTest\Player();
2296
        $this->assertNull($newPlayer->relField('Teams.First.Title'));
2297
2298
        // Test that relField works on db field manipulations
2299
        $comment = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment3');
2300
        $this->assertEquals("PHIL IS A UNIQUE GUY, AND COMMENTS ON TEAM2", $comment->relField('Comment.UpperCase'));
2301
2302
        // relField throws exception on invalid properties
2303
        $this->expectException(LogicException::class);
2304
        $this->expectExceptionMessage("Not is not a relation/field on " . DataObjectTest\TeamComment::class);
2305
        $comment->relField('Not.A.Field');
2306
    }
2307
2308
    public function testRelObject()
2309
    {
2310
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
2311
2312
        // Test traversal of a single has_one
2313
        $this->assertInstanceOf(DBVarchar::class, $captain1->relObject('FavouriteTeam.Title'));
2314
        $this->assertEquals("Team 1", $captain1->relObject('FavouriteTeam.Title')->getValue());
2315
2316
        // Test empty link
2317
        $captain2 = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
2318
        $this->assertEmpty($captain2->relObject('FavouriteTeam.Title')->getValue());
2319
        $this->assertNull($captain2->relObject('FavouriteTeam.ReturnsNull.Title'));
2320
2321
        // Test direct field access
2322
        $this->assertInstanceOf(DBBoolean::class, $captain1->relObject('IsRetired'));
2323
        $this->assertEquals(1, $captain1->relObject('IsRetired')->getValue());
2324
2325
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
2326
        // Test that we can traverse more than once, and that arbitrary methods are okay
2327
        $this->assertInstanceOf(DBVarchar::class, $player->relObject('Teams.First.Title'));
2328
        $this->assertEquals("Team 1", $player->relObject('Teams.First.Title')->getValue());
2329
2330
        // relObject throws exception on invalid properties
2331
        $this->expectException(LogicException::class);
2332
        $this->expectExceptionMessage("Not is not a relation/field on " . DataObjectTest\Player::class);
2333
        $player->relObject('Not.A.Field');
2334
    }
2335
2336
    public function testLateStaticBindingStyle()
2337
    {
2338
        // Confirm that DataObjectTest_Player::get() operates as excepted
2339
        $this->assertEquals(4, DataObjectTest\Player::get()->count());
2340
        $this->assertInstanceOf(DataObjectTest\Player::class, DataObjectTest\Player::get()->first());
2341
2342
        // You can't pass arguments to LSB syntax - use the DataList methods instead.
2343
        $this->expectException(InvalidArgumentException::class);
2344
2345
        DataObjectTest\Player::get(null, "\"ID\" = 1");
2346
    }
2347
2348
    /**
2349
     * @expectedException \InvalidArgumentException
2350
     */
2351
    public function testBrokenLateStaticBindingStyle()
2352
    {
2353
        // If you call DataObject::get() you have to pass a first argument
2354
        DataObject::get();
2355
    }
2356
2357
    public function testBigIntField()
2358
    {
2359
        $staff = new DataObjectTest\Staff();
2360
        $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...
2361
        $staff->write();
2362
        $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...
2363
    }
2364
2365
    public function testGetOneMissingValueReturnsNull()
2366
    {
2367
2368
        // Test that missing values return null
2369
        $this->assertEquals(null, DataObject::get_one(
2370
            DataObjectTest\TeamComment::class,
2371
            ['"DataObjectTest_TeamComment"."Name"' => 'does not exists']
2372
        ));
2373
    }
2374
2375
    public function testSetFieldWithArrayOnScalarOnlyField()
2376
    {
2377
        $this->expectException(InvalidArgumentException::class);
2378
        $do = Company::singleton();
2379
        $do->FoundationYear = '1984';
2380
        $do->FoundationYear = array('Amount' => 123, 'Currency' => 'CAD');
2381
        $this->assertEmpty($do->FoundationYear);
2382
    }
2383
2384
    public function testSetFieldWithArrayOnCompositeField()
2385
    {
2386
        $do = Company::singleton();
2387
        $do->SalaryCap = array('Amount' => 123456, 'Currency' => 'CAD');
2388
        $this->assertNotEmpty($do->SalaryCap);
2389
    }
2390
2391
    public function testWriteManipulationWithNonScalarValuesAllowed()
2392
    {
2393
        $do = DataObjectTest\MockDynamicAssignmentDataObject::create();
2394
        $do->write();
2395
2396
        $do->StaticScalarOnlyField = true;
2397
        $do->DynamicScalarOnlyField = false;
2398
        $do->DynamicField = true;
2399
2400
        $do->write();
2401
2402
        $this->assertTrue($do->StaticScalarOnlyField);
2403
        $this->assertFalse($do->DynamicScalarOnlyField);
2404
        $this->assertTrue($do->DynamicField);
2405
    }
2406
2407
    public function testWriteManipulationWithNonScalarValuesDisallowed()
2408
    {
2409
        $this->expectException(InvalidArgumentException::class);
2410
2411
        $do = DataObjectTest\MockDynamicAssignmentDataObject::create();
2412
        $do->write();
2413
2414
        $do->StaticScalarOnlyField = false;
2415
        $do->DynamicScalarOnlyField = true;
2416
        $do->DynamicField = false;
2417
2418
        $do->write();
2419
    }
2420
}
2421