Passed
Push — 4.1 ( cb7f15...ac53f7 )
by Maxime
08:41
created

testWriteManipulationWithNonScalarValuesAllowed()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 14
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 9
nc 1
nop 0
dl 0
loc 14
rs 9.9666
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use InvalidArgumentException;
6
use LogicException;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\i18n\i18n;
10
use SilverStripe\ORM\Connect\MySQLDatabase;
11
use SilverStripe\ORM\DataObject;
12
use SilverStripe\ORM\DataObjectSchema;
13
use SilverStripe\ORM\DB;
14
use SilverStripe\ORM\FieldType\DBBoolean;
15
use SilverStripe\ORM\FieldType\DBDatetime;
16
use SilverStripe\ORM\FieldType\DBField;
17
use SilverStripe\ORM\FieldType\DBPolymorphicForeignKey;
18
use SilverStripe\ORM\FieldType\DBVarchar;
19
use SilverStripe\ORM\ManyManyList;
20
use SilverStripe\ORM\Tests\DataObjectTest\Company;
21
use SilverStripe\ORM\Tests\DataObjectTest\Player;
22
use SilverStripe\ORM\Tests\DataObjectTest\Team;
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
    public static function getExtraDataObjects()
65
    {
66
        return array_merge(
67
            DataObjectTest::$extra_data_objects,
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

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

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

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

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

Loading history...
68
            ManyManyListTest::$extra_data_objects
69
        );
70
    }
71
72
    /**
73
     * @dataProvider provideSingletons
74
     */
75
    public function testSingleton($inst, $defaultValue, $altDefaultValue)
76
    {
77
        $inst = $inst();
78
        // Test that populateDefaults() isn't called on singletons
79
        // which can lead to SQL errors during build, and endless loops
80
        if ($defaultValue) {
81
            $this->assertEquals($defaultValue, $inst->MyFieldWithDefault);
82
        } else {
83
            $this->assertEmpty($inst->MyFieldWithDefault);
84
        }
85
86
        if ($altDefaultValue) {
87
            $this->assertEquals($altDefaultValue, $inst->MyFieldWithAltDefault);
88
        } else {
89
            $this->assertEmpty($inst->MyFieldWithAltDefault);
90
        }
91
    }
92
93
    public function provideSingletons()
94
    {
95
        // because PHPUnit evalutes test providers *before* setUp methods
96
        // any extensions added in the setUp methods won't be available
97
        // we must return closures to generate the arguments at run time
98
        return array(
99
            'create() static method' => array(function () {
100
                return DataObjectTest\Fixture::create();
101
            }, 'Default Value', 'Default Value'),
102
            'New object creation' => array(function () {
103
                return new DataObjectTest\Fixture();
104
            }, 'Default Value', 'Default Value'),
105
            'singleton() function' => array(function () {
106
                return singleton(DataObjectTest\Fixture::class);
107
            }, null, null),
108
            'singleton() static method' => array(function () {
109
                return DataObjectTest\Fixture::singleton();
110
            }, null, null),
111
            'Manual constructor args' => array(function () {
112
                return new DataObjectTest\Fixture(null, true);
113
            }, null, null),
114
        );
115
    }
116
117
    public function testDb()
118
    {
119
        $schema = DataObject::getSchema();
120
        $dbFields = $schema->fieldSpecs(DataObjectTest\TeamComment::class);
121
122
        // Assert fields are included
123
        $this->assertArrayHasKey('Name', $dbFields);
124
125
        // Assert the base fields are included
126
        $this->assertArrayHasKey('Created', $dbFields);
127
        $this->assertArrayHasKey('LastEdited', $dbFields);
128
        $this->assertArrayHasKey('ClassName', $dbFields);
129
        $this->assertArrayHasKey('ID', $dbFields);
130
131
        // Assert that the correct field type is returned when passing a field
132
        $this->assertEquals('Varchar', $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Name'));
133
        $this->assertEquals('Text', $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Comment'));
134
135
        // Test with table required
136
        $this->assertEquals(
137
            DataObjectTest\TeamComment::class . '.Varchar',
138
            $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Name', DataObjectSchema::INCLUDE_CLASS)
139
        );
140
        $this->assertEquals(
141
            DataObjectTest\TeamComment::class . '.Text',
142
            $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Comment', DataObjectSchema::INCLUDE_CLASS)
143
        );
144
        $dbFields = $schema->fieldSpecs(DataObjectTest\ExtendedTeamComment::class);
145
146
        // fixed fields are still included in extended classes
147
        $this->assertArrayHasKey('Created', $dbFields);
148
        $this->assertArrayHasKey('LastEdited', $dbFields);
149
        $this->assertArrayHasKey('ClassName', $dbFields);
150
        $this->assertArrayHasKey('ID', $dbFields);
151
152
        // Assert overloaded fields have correct data type
153
        $this->assertEquals('HTMLText', $schema->fieldSpec(DataObjectTest\ExtendedTeamComment::class, 'Comment'));
154
        $this->assertEquals(
155
            'HTMLText',
156
            $dbFields['Comment'],
157
            'Calls to DataObject::db without a field specified return correct data types'
158
        );
159
160
        // assertEquals doesn't verify the order of array elements, so access keys manually to check order:
161
        // expected: array('Name' => 'Varchar', 'Comment' => 'HTMLText')
0 ignored issues
show
Unused Code Comprehensibility introduced by
56% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
162
        $this->assertEquals(
163
            array(
164
                'Name',
165
                'Comment'
166
            ),
167
            array_slice(array_keys($dbFields), 4, 2),
168
            'DataObject::db returns fields in correct order'
169
        );
170
    }
171
172
    public function testConstructAcceptsValues()
173
    {
174
        // Values can be an array...
175
        $player = new DataObjectTest\Player(
176
            array(
177
                'FirstName' => 'James',
178
                'Surname' => 'Smith'
179
            )
180
        );
181
182
        $this->assertEquals('James', $player->FirstName);
183
        $this->assertEquals('Smith', $player->Surname);
184
185
        // ... or a stdClass inst
186
        $data = new stdClass();
187
        $data->FirstName = 'John';
188
        $data->Surname = 'Doe';
189
        $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

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

455
        $this->assertEquals($team1ID, $captain1->/** @scrutinizer ignore-call */ FavouriteTeam()->ID);
Loading history...
456
457
        // Test that getNonReciprocalComponent can find has_one from the has_many end
458
        $this->assertEquals(
459
            $team1ID,
460
            $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...
461
        );
462
463
        // Check entity with polymorphic has-one
464
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, "fan1");
465
        $this->assertTrue((bool)$fan1->hasValue('Favourite'));
466
467
        // There will be fields named (relname)ID and (relname)Class for polymorphic
468
        // entities
469
        $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...
470
        $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...
471
472
        // There will be a method called $obj->relname() that returns the object itself
473
        $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

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

558
        $this->assertTrue($team1->/** @scrutinizer ignore-call */ Comments()->count() == 2);
Loading history...
559
560
        $team1Comments = [
561
            ['Comment' => 'This is a team comment by Joe'],
562
            ['Comment' => 'This is a team comment by Bob'],
563
        ];
564
565
        // Test the IDs on the DataObjects are set correctly
566
        $this->assertListEquals($team1Comments, $team1->Comments());
567
568
        // Test that has_many can be infered from the has_one via getNonReciprocalComponent
569
        $this->assertListEquals(
570
            $team1Comments,
571
            $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

571
            /** @scrutinizer ignore-type */ $team1->inferReciprocalComponent(DataObjectTest\TeamComment::class, 'Team')
Loading history...
572
        );
573
574
        // Test that we can add and remove items that already exist in the database
575
        $newComment = new DataObjectTest\TeamComment();
576
        $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...
577
        $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...
578
        $newComment->write();
579
        $team1->Comments()->add($newComment);
580
        $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...
581
582
        $comment1 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment1');
583
        $comment2 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment2');
584
        $team1->Comments()->remove($comment2);
585
586
        $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

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

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

666
            $team1->/** @scrutinizer ignore-call */ 
667
                    Captain()->ID,
Loading history...
667
            'The captain exists for team 1'
668
        );
669
        $this->assertEquals(
670
            $player1->ID,
671
            $team1->getComponent('Captain')->ID,
672
            'The captain exists through the component getter'
673
        );
674
675
        $this->assertEquals(
676
            $team1->Captain()->FirstName,
677
            'Player 1',
678
            'Player 1 is the captain'
679
        );
680
        $this->assertEquals(
681
            $team1->getComponent('Captain')->FirstName,
682
            'Player 1',
683
            'Player 1 is the captain'
684
        );
685
686
        $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...
687
        $team1->write();
688
689
        $this->assertEquals($player2->ID, $team1->Captain()->ID);
690
        $this->assertEquals($player2->ID, $team1->getComponent('Captain')->ID);
691
        $this->assertEquals('Player 2', $team1->Captain()->FirstName);
692
        $this->assertEquals('Player 2', $team1->getComponent('Captain')->FirstName);
693
694
695
        // Set the favourite team for fan1
696
        $fan1->setField('FavouriteID', $team1->ID);
697
        $fan1->setField('FavouriteClass', get_class($team1));
698
699
        $this->assertEquals($team1->ID, $fan1->Favourite()->ID, 'The team is assigned to fan 1');
700
        $this->assertInstanceOf(get_class($team1), $fan1->Favourite(), 'The team is assigned to fan 1');
701
        $this->assertEquals(
702
            $team1->ID,
703
            $fan1->getComponent('Favourite')->ID,
704
            'The team exists through the component getter'
705
        );
706
        $this->assertInstanceOf(
707
            get_class($team1),
708
            $fan1->getComponent('Favourite'),
709
            'The team exists through the component getter'
710
        );
711
712
        $this->assertEquals(
713
            $fan1->Favourite()->Title,
714
            'Team 1',
715
            'Team 1 is the favourite'
716
        );
717
        $this->assertEquals(
718
            $fan1->getComponent('Favourite')->Title,
719
            'Team 1',
720
            'Team 1 is the favourite'
721
        );
722
    }
723
724
    /**
725
     * Test has_one used as field getter/setter
726
     */
727
    public function testHasOneAsField()
728
    {
729
        /** @var DataObjectTest\Team $team1 */
730
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
731
        $captain1 = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
732
        $captain2 = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
733
734
        // Setter: By RelationID
735
        $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...
736
        $team1->write();
737
        $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...
738
739
        // Setter: New object
740
        $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...
741
        $team1->write();
742
        $this->assertEquals($captain2->ID, $team1->Captain->ID);
743
744
        // Setter: Custom data (required by DataDifferencer)
745
        $team1->Captain = DBField::create_field('HTMLFragment', '<p>No captain</p>');
746
        $this->assertEquals('<p>No captain</p>', $team1->Captain);
747
    }
748
749
    /**
750
     * @todo Extend type change tests (e.g. '0'==NULL)
751
     */
752
    public function testChangedFields()
753
    {
754
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
755
        $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...
756
        $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...
757
758
        $this->assertEquals(
759
            $obj->getChangedFields(true, DataObject::CHANGE_STRICT),
760
            array(
761
                'FirstName' => array(
762
                    'before' => 'Captain',
763
                    'after' => 'Captain-changed',
764
                    'level' => DataObject::CHANGE_VALUE
765
                ),
766
                'IsRetired' => array(
767
                    'before' => 1,
768
                    'after' => true,
769
                    'level' => DataObject::CHANGE_STRICT
770
                )
771
            ),
772
            'Changed fields are correctly detected with strict type changes (level=1)'
773
        );
774
775
        $this->assertEquals(
776
            $obj->getChangedFields(true, DataObject::CHANGE_VALUE),
777
            array(
778
                'FirstName' => array(
779
                    'before' => 'Captain',
780
                    'after' => 'Captain-changed',
781
                    'level' => DataObject::CHANGE_VALUE
782
                )
783
            ),
784
            'Changed fields are correctly detected while ignoring type changes (level=2)'
785
        );
786
787
        $newObj = new DataObjectTest\Player();
788
        $newObj->FirstName = "New Player";
789
        $this->assertEquals(
790
            array(
791
                'FirstName' => array(
792
                    'before' => null,
793
                    'after' => 'New Player',
794
                    'level' => DataObject::CHANGE_VALUE
795
                )
796
            ),
797
            $newObj->getChangedFields(true, DataObject::CHANGE_VALUE),
798
            'Initialised fields are correctly detected as full changes'
799
        );
800
    }
801
802
    /**
803
     * @skipUpgrade
804
     */
805
    public function testIsChanged()
806
    {
807
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
808
        $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...
809
        $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...
810
        $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...
811
812
        // Now that DB fields are changed, isChanged is true
813
        $this->assertTrue($obj->isChanged('NonDBField'));
814
        $this->assertFalse($obj->isChanged('NonField'));
815
        $this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
816
        $this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
817
        $this->assertTrue($obj->isChanged('IsRetired', DataObject::CHANGE_STRICT));
818
        $this->assertFalse($obj->isChanged('IsRetired', DataObject::CHANGE_VALUE));
819
        $this->assertFalse($obj->isChanged('Email', 1), 'Doesnt change mark unchanged property');
820
        $this->assertFalse($obj->isChanged('Email', 2), 'Doesnt change mark unchanged property');
821
822
        $newObj = new DataObjectTest\Player();
823
        $newObj->FirstName = "New Player";
824
        $this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
825
        $this->assertTrue($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
826
        $this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
827
        $this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
828
829
        $newObj->write();
830
        $this->assertFalse($newObj->ischanged());
831
        $this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_STRICT));
832
        $this->assertFalse($newObj->isChanged('FirstName', DataObject::CHANGE_VALUE));
833
        $this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_STRICT));
834
        $this->assertFalse($newObj->isChanged('Email', DataObject::CHANGE_VALUE));
835
836
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
837
        $obj->FirstName = null;
838
        $this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_STRICT));
839
        $this->assertTrue($obj->isChanged('FirstName', DataObject::CHANGE_VALUE));
840
841
        /* Test when there's not field provided */
842
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain2');
843
        $this->assertFalse($obj->isChanged());
844
        $obj->NonDBField = 'new value';
845
        $this->assertFalse($obj->isChanged());
846
        $obj->FirstName = "New Player";
847
        $this->assertTrue($obj->isChanged());
848
849
        $obj->write();
850
        $this->assertFalse($obj->isChanged());
851
    }
852
853
    public function testRandomSort()
854
    {
855
        /* If we perform the same regularly sorted query twice, it should return the same results */
856
        $itemsA = DataObject::get(DataObjectTest\TeamComment::class, "", "ID");
857
        foreach ($itemsA as $item) {
858
            $keysA[] = $item->ID;
859
        }
860
861
        $itemsB = DataObject::get(DataObjectTest\TeamComment::class, "", "ID");
862
        foreach ($itemsB as $item) {
863
            $keysB[] = $item->ID;
864
        }
865
866
        /* Test when there's not field provided */
867
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
868
        $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...
869
        $this->assertTrue($obj->isChanged());
870
871
        $obj->write();
872
        $this->assertFalse($obj->isChanged());
873
874
        /* If we perform the same random query twice, it shouldn't return the same results */
875
        $itemsA = DataObject::get(DataObjectTest\TeamComment::class, "", DB::get_conn()->random());
876
        $itemsB = DataObject::get(DataObjectTest\TeamComment::class, "", DB::get_conn()->random());
877
        $itemsC = DataObject::get(DataObjectTest\TeamComment::class, "", DB::get_conn()->random());
878
        $itemsD = DataObject::get(DataObjectTest\TeamComment::class, "", DB::get_conn()->random());
879
        foreach ($itemsA as $item) {
880
            $keysA[] = $item->ID;
881
        }
882
        foreach ($itemsB as $item) {
883
            $keysB[] = $item->ID;
884
        }
885
        foreach ($itemsC as $item) {
886
            $keysC[] = $item->ID;
887
        }
888
        foreach ($itemsD as $item) {
889
            $keysD[] = $item->ID;
890
        }
891
892
        // These shouldn't all be the same (run it 4 times to minimise chance of an accidental collision)
893
        // There's about a 1 in a billion chance of an accidental collision
894
        $this->assertTrue($keysA != $keysB || $keysB != $keysC || $keysC != $keysD);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $keysB seems to be defined by a foreach iteration on line 862. 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 857. 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 888. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
Comprehensibility Best Practice introduced by
The variable $keysC seems to be defined by a foreach iteration on line 885. Are you sure the iterator is never empty, otherwise this variable is not defined?
Loading history...
895
    }
896
897
    public function testWriteSavesToHasOneRelations()
898
    {
899
        /* DataObject::write() should save to a has_one relationship if you set a field called (relname)ID */
900
        $team = new DataObjectTest\Team();
901
        $captainID = $this->idFromFixture(DataObjectTest\Player::class, 'player1');
902
        $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...
903
        $team->write();
904
        $this->assertEquals(
905
            $captainID,
906
            DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()
907
        );
908
909
        /* After giving it a value, you should also be able to set it back to null */
910
        $team->CaptainID = '';
911
        $team->write();
912
        $this->assertEquals(
913
            0,
914
            DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()
915
        );
916
917
        /* You should also be able to save a blank to it when it's first created */
918
        $team = new DataObjectTest\Team();
919
        $team->CaptainID = '';
920
        $team->write();
921
        $this->assertEquals(
922
            0,
923
            DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $team->ID")->value()
924
        );
925
926
        /* Ditto for existing records without a value */
927
        $existingTeam = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
928
        $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...
929
        $existingTeam->write();
930
        $this->assertEquals(
931
            0,
932
            DB::query("SELECT \"CaptainID\" FROM \"DataObjectTest_Team\" WHERE \"ID\" = $existingTeam->ID")->value()
933
        );
934
    }
935
936
    public function testCanAccessHasOneObjectsAsMethods()
937
    {
938
        /* If you have a has_one relation 'Captain' on $obj, and you set the $obj->CaptainID = (ID), then the
939
        * object itself should be accessible as $obj->Captain() */
940
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
941
        $captainID = $this->idFromFixture(DataObjectTest\Player::class, 'captain1');
942
943
        $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...
944
        $this->assertNotNull($team->Captain());
945
        $this->assertEquals($captainID, $team->Captain()->ID);
946
947
        // Test for polymorphic has_one relations
948
        $fan = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
949
        $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...
950
        $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...
951
        $this->assertNotNull($fan->Favourite());
952
        $this->assertEquals($team->ID, $fan->Favourite()->ID);
953
        $this->assertInstanceOf(DataObjectTest\Team::class, $fan->Favourite());
954
    }
955
956
    public function testFieldNamesThatMatchMethodNamesWork()
957
    {
958
        /* Check that a field name that corresponds to a method on DataObject will still work */
959
        $obj = new DataObjectTest\Fixture();
960
        $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...
961
        $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...
962
        $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...
963
        $obj->write();
964
965
        $this->assertNotNull($obj->ID);
966
        $this->assertEquals(
967
            'value1',
968
            DB::query("SELECT \"Data\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()
969
        );
970
        $this->assertEquals(
971
            'value2',
972
            DB::query("SELECT \"DbObject\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()
973
        );
974
        $this->assertEquals(
975
            'value3',
976
            DB::query("SELECT \"Duplicate\" FROM \"DataObjectTest_Fixture\" WHERE \"ID\" = $obj->ID")->value()
977
        );
978
    }
979
980
    /**
981
     * @todo Re-enable all test cases for field existence after behaviour has been fixed
982
     */
983
    public function testFieldExistence()
984
    {
985
        $teamInstance = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
986
        $teamSingleton = singleton(DataObjectTest\Team::class);
987
988
        $subteamInstance = $this->objFromFixture(DataObjectTest\SubTeam::class, 'subteam1');
989
        $schema = DataObject::getSchema();
990
991
        /* hasField() singleton checks */
992
        $this->assertTrue(
993
            $teamSingleton->hasField('ID'),
994
            'hasField() finds built-in fields in singletons'
995
        );
996
        $this->assertTrue(
997
            $teamSingleton->hasField('Title'),
998
            'hasField() finds custom fields in singletons'
999
        );
1000
1001
        /* hasField() instance checks */
1002
        $this->assertFalse(
1003
            $teamInstance->hasField('NonExistingField'),
1004
            'hasField() doesnt find non-existing fields in instances'
1005
        );
1006
        $this->assertTrue(
1007
            $teamInstance->hasField('ID'),
1008
            'hasField() finds built-in fields in instances'
1009
        );
1010
        $this->assertTrue(
1011
            $teamInstance->hasField('Created'),
1012
            'hasField() finds built-in fields in instances'
1013
        );
1014
        $this->assertTrue(
1015
            $teamInstance->hasField('DatabaseField'),
1016
            'hasField() finds custom fields in instances'
1017
        );
1018
        //$this->assertFalse($teamInstance->hasField('SubclassDatabaseField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1019
        //'hasField() doesnt find subclass fields in parentclass instances');
1020
        $this->assertTrue(
1021
            $teamInstance->hasField('DynamicField'),
1022
            'hasField() finds dynamic getters in instances'
1023
        );
1024
        $this->assertTrue(
1025
            $teamInstance->hasField('HasOneRelationshipID'),
1026
            'hasField() finds foreign keys in instances'
1027
        );
1028
        $this->assertTrue(
1029
            $teamInstance->hasField('ExtendedDatabaseField'),
1030
            'hasField() finds extended fields in instances'
1031
        );
1032
        $this->assertTrue(
1033
            $teamInstance->hasField('ExtendedHasOneRelationshipID'),
1034
            'hasField() finds extended foreign keys in instances'
1035
        );
1036
        //$this->assertTrue($teamInstance->hasField('ExtendedDynamicField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1037
        //'hasField() includes extended dynamic getters in instances');
1038
1039
        /* hasField() subclass checks */
1040
        $this->assertTrue(
1041
            $subteamInstance->hasField('ID'),
1042
            'hasField() finds built-in fields in subclass instances'
1043
        );
1044
        $this->assertTrue(
1045
            $subteamInstance->hasField('Created'),
1046
            'hasField() finds built-in fields in subclass instances'
1047
        );
1048
        $this->assertTrue(
1049
            $subteamInstance->hasField('DatabaseField'),
1050
            'hasField() finds custom fields in subclass instances'
1051
        );
1052
        $this->assertTrue(
1053
            $subteamInstance->hasField('SubclassDatabaseField'),
1054
            'hasField() finds custom fields in subclass instances'
1055
        );
1056
        $this->assertTrue(
1057
            $subteamInstance->hasField('DynamicField'),
1058
            'hasField() finds dynamic getters in subclass instances'
1059
        );
1060
        $this->assertTrue(
1061
            $subteamInstance->hasField('HasOneRelationshipID'),
1062
            'hasField() finds foreign keys in subclass instances'
1063
        );
1064
        $this->assertTrue(
1065
            $subteamInstance->hasField('ExtendedDatabaseField'),
1066
            'hasField() finds extended fields in subclass instances'
1067
        );
1068
        $this->assertTrue(
1069
            $subteamInstance->hasField('ExtendedHasOneRelationshipID'),
1070
            'hasField() finds extended foreign keys in subclass instances'
1071
        );
1072
1073
        /* hasDatabaseField() singleton checks */
1074
        //$this->assertTrue($teamSingleton->hasDatabaseField('ID'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
82% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1075
        //'hasDatabaseField() finds built-in fields in singletons');
1076
        $this->assertNotEmpty(
1077
            $schema->fieldSpec(DataObjectTest\Team::class, 'Title'),
1078
            'hasDatabaseField() finds custom fields in singletons'
1079
        );
1080
1081
        /* hasDatabaseField() instance checks */
1082
        $this->assertNull(
1083
            $schema->fieldSpec(DataObjectTest\Team::class, 'NonExistingField'),
1084
            'hasDatabaseField() doesnt find non-existing fields in instances'
1085
        );
1086
        //$this->assertNotEmpty($schema->fieldSpec(DataObjectTest_Team::class, 'ID'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1087
        //'hasDatabaseField() finds built-in fields in instances');
1088
        $this->assertNotEmpty(
1089
            $schema->fieldSpec(DataObjectTest\Team::class, 'Created'),
1090
            'hasDatabaseField() finds built-in fields in instances'
1091
        );
1092
        $this->assertNotEmpty(
1093
            $schema->fieldSpec(DataObjectTest\Team::class, 'DatabaseField'),
1094
            'hasDatabaseField() finds custom fields in instances'
1095
        );
1096
        $this->assertNull(
1097
            $schema->fieldSpec(DataObjectTest\Team::class, 'SubclassDatabaseField'),
1098
            'hasDatabaseField() doesnt find subclass fields in parentclass instances'
1099
        );
1100
        //$this->assertNull($schema->fieldSpec(DataObjectTest_Team::class, 'DynamicField'),
0 ignored issues
show
Unused Code Comprehensibility introduced by
69% of this comment could be valid code. Did you maybe forget this after debugging?

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

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

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

Loading history...
1101
        //'hasDatabaseField() doesnt dynamic getters in instances');
1102
        $this->assertNotEmpty(
1103
            $schema->fieldSpec(DataObjectTest\Team::class, 'HasOneRelationshipID'),
1104
            'hasDatabaseField() finds foreign keys in instances'
1105
        );
1106
        $this->assertNotEmpty(
1107
            $schema->fieldSpec(DataObjectTest\Team::class, 'ExtendedDatabaseField'),
1108
            'hasDatabaseField() finds extended fields in instances'
1109
        );
1110
        $this->assertNotEmpty(
1111
            $schema->fieldSpec(DataObjectTest\Team::class, 'ExtendedHasOneRelationshipID'),
1112
            'hasDatabaseField() finds extended foreign keys in instances'
1113
        );
1114
        $this->assertNull(
1115
            $schema->fieldSpec(DataObjectTest\Team::class, 'ExtendedDynamicField'),
1116
            'hasDatabaseField() doesnt include extended dynamic getters in instances'
1117
        );
1118
1119
        /* hasDatabaseField() subclass checks */
1120
        $this->assertNotEmpty(
1121
            $schema->fieldSpec(DataObjectTest\SubTeam::class, 'DatabaseField'),
1122
            'hasField() finds custom fields in subclass instances'
1123
        );
1124
        $this->assertNotEmpty(
1125
            $schema->fieldSpec(DataObjectTest\SubTeam::class, 'SubclassDatabaseField'),
1126
            'hasField() finds custom fields in subclass instances'
1127
        );
1128
    }
1129
1130
    /**
1131
     * @todo Re-enable all test cases for field inheritance aggregation after behaviour has been fixed
1132
     */
1133
    public function testFieldInheritance()
1134
    {
1135
        $schema = DataObject::getSchema();
1136
1137
        // Test logical fields (including composite)
1138
        $teamSpecifications = $schema->fieldSpecs(DataObjectTest\Team::class);
1139
        $expected = array(
1140
            'ID',
1141
            'ClassName',
1142
            'LastEdited',
1143
            'Created',
1144
            'Title',
1145
            'DatabaseField',
1146
            'ExtendedDatabaseField',
1147
            'CaptainID',
1148
            'FounderID',
1149
            'HasOneRelationshipID',
1150
            'ExtendedHasOneRelationshipID'
1151
        );
1152
        $actual = array_keys($teamSpecifications);
1153
        sort($expected);
1154
        sort($actual);
1155
        $this->assertEquals(
1156
            $expected,
1157
            $actual,
1158
            'fieldSpecifications() contains all fields defined on instance: base, extended and foreign keys'
1159
        );
1160
1161
        $teamFields = $schema->databaseFields(DataObjectTest\Team::class, false);
1162
        $expected = array(
1163
            'ID',
1164
            'ClassName',
1165
            'LastEdited',
1166
            'Created',
1167
            'Title',
1168
            'DatabaseField',
1169
            'ExtendedDatabaseField',
1170
            'CaptainID',
1171
            'FounderID',
1172
            'HasOneRelationshipID',
1173
            'ExtendedHasOneRelationshipID'
1174
        );
1175
        $actual = array_keys($teamFields);
1176
        sort($expected);
1177
        sort($actual);
1178
        $this->assertEquals(
1179
            $expected,
1180
            $actual,
1181
            'databaseFields() contains only fields defined on instance, including base, extended and foreign keys'
1182
        );
1183
1184
        $subteamSpecifications = $schema->fieldSpecs(DataObjectTest\SubTeam::class);
1185
        $expected = array(
1186
            'ID',
1187
            'ClassName',
1188
            'LastEdited',
1189
            'Created',
1190
            'Title',
1191
            'DatabaseField',
1192
            'ExtendedDatabaseField',
1193
            'CaptainID',
1194
            'FounderID',
1195
            'HasOneRelationshipID',
1196
            'ExtendedHasOneRelationshipID',
1197
            'SubclassDatabaseField',
1198
            'ParentTeamID',
1199
        );
1200
        $actual = array_keys($subteamSpecifications);
1201
        sort($expected);
1202
        sort($actual);
1203
        $this->assertEquals(
1204
            $expected,
1205
            $actual,
1206
            'fieldSpecifications() on subclass contains all fields, including base, extended  and foreign keys'
1207
        );
1208
1209
        $subteamFields = $schema->databaseFields(DataObjectTest\SubTeam::class, false);
1210
        $expected = array(
1211
            'ID',
1212
            'SubclassDatabaseField',
1213
            'ParentTeamID',
1214
        );
1215
        $actual = array_keys($subteamFields);
1216
        sort($expected);
1217
        sort($actual);
1218
        $this->assertEquals(
1219
            $expected,
1220
            $actual,
1221
            'databaseFields() on subclass contains only fields defined on instance'
1222
        );
1223
    }
1224
1225
    public function testSearchableFields()
1226
    {
1227
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
1228
        $fields = $player->searchableFields();
1229
        $this->assertArrayHasKey(
1230
            'IsRetired',
1231
            $fields,
1232
            'Fields defined by $searchable_fields static are correctly detected'
1233
        );
1234
        $this->assertArrayHasKey(
1235
            'ShirtNumber',
1236
            $fields,
1237
            'Fields defined by $searchable_fields static are correctly detected'
1238
        );
1239
1240
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1241
        $fields = $team->searchableFields();
1242
        $this->assertArrayHasKey(
1243
            'Title',
1244
            $fields,
1245
            'Fields can be inherited from the $summary_fields static, including methods called on fields'
1246
        );
1247
        $this->assertArrayHasKey(
1248
            'Captain.ShirtNumber',
1249
            $fields,
1250
            'Fields on related objects can be inherited from the $summary_fields static'
1251
        );
1252
        $this->assertArrayHasKey(
1253
            'Captain.FavouriteTeam.Title',
1254
            $fields,
1255
            'Fields on related objects can be inherited from the $summary_fields static'
1256
        );
1257
1258
        $testObj = new DataObjectTest\Fixture();
1259
        $fields = $testObj->searchableFields();
1260
        $this->assertEmpty($fields);
1261
    }
1262
1263
    public function testCastingHelper()
1264
    {
1265
        $team = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
1266
1267
        $this->assertEquals('Varchar', $team->castingHelper('Title'), 'db field wasn\'t casted correctly');
1268
        $this->assertEquals('HTMLVarchar', $team->castingHelper('DatabaseField'), 'db field wasn\'t casted correctly');
1269
1270
        $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

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

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

1595
            /** @scrutinizer ignore-type */ $team->inferReciprocalComponent(DataObjectTest\EquipmentCompany::class, 'SponsoredTeams')
Loading history...
1596
        );
1597
1598
        // Test that many_many can be infered from getNonReciprocalComponent
1599
        $this->assertListEquals(
1600
            [
1601
                ['Title' => 'Team 1'],
1602
                ['Title' => 'Team 2'],
1603
                ['Title' => 'Subteam 1'],
1604
            ],
1605
            $company2->inferReciprocalComponent(DataObjectTest\Team::class, 'Sponsors')
1606
        );
1607
1608
        // Check many_many_extraFields still works
1609
        $equipmentCompany = $this->objFromFixture(DataObjectTest\EquipmentCompany::class, 'equipmentcompany1');
1610
        $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

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

1718
        $player = $newTeam->/** @scrutinizer ignore-call */ Players()->first();
Loading history...
1719
        $this->assertEquals('Sam', $player->FirstName);
1720
        $this->assertEquals("Prop", $player->Position);
1721
1722
        // Check that ordering a many-many relation by an aggregate column doesn't fail
1723
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
1724
        $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

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

1995
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ Company()->ID, 'belongs_to returns the right results.');
Loading history...
1996
1997
        // Test belongs_to can be infered via getNonReciprocalComponent
1998
        // Note: Will be returned as has_many since the belongs_to is ignored.
1999
        $this->assertListEquals(
2000
            [['Name' => 'New Company']],
2001
            $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

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

2028
            $newCEO->/** @scrutinizer ignore-call */ 
2029
                     Company()->ID,
Loading history...
2029
            'belongs_to can be retrieved from the database.'
2030
        );
2031
    }
2032
2033
    public function testBelongsToPolymorphic()
2034
    {
2035
        $company = new DataObjectTest\Company();
2036
        $ceo = new DataObjectTest\CEO();
2037
2038
        $company->write();
2039
        $ceo->write();
2040
2041
        // Test belongs_to assignment
2042
        $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...
2043
        $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...
2044
        $company->write();
2045
2046
        $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

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

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