Passed
Push — 4.2 ( 0758cd...79e44b )
by Robbie
31:45 queued 25:18
created

testSetFieldWithArrayOnScalarOnlyField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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