Passed
Pull Request — 4 (#8453)
by Sam
09:08 queued 28s
created

DataObjectTest::getExtraDataObjects()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 0
dl 0
loc 5
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\Player;
21
use SilverStripe\View\ViewableData;
22
use stdClass;
23
24
class DataObjectTest extends SapphireTest
25
{
26
27
    protected static $fixture_file = 'DataObjectTest.yml';
28
29
    /**
30
     * Standard set of dataobject test classes
31
     *
32
     * @var array
33
     */
34
    public static $extra_data_objects = array(
35
        DataObjectTest\Team::class,
36
        DataObjectTest\Fixture::class,
37
        DataObjectTest\SubTeam::class,
38
        DataObjectTest\OtherSubclassWithSameField::class,
39
        DataObjectTest\FieldlessTable::class,
40
        DataObjectTest\FieldlessSubTable::class,
41
        DataObjectTest\ValidatedObject::class,
42
        DataObjectTest\Player::class,
43
        DataObjectTest\TeamComment::class,
44
        DataObjectTest\EquipmentCompany::class,
45
        DataObjectTest\SubEquipmentCompany::class,
46
        DataObjectTest\ExtendedTeamComment::class,
47
        DataObjectTest\Company::class,
48
        DataObjectTest\Staff::class,
49
        DataObjectTest\CEO::class,
50
        DataObjectTest\Fan::class,
51
        DataObjectTest\Play::class,
52
        DataObjectTest\Ploy::class,
53
        DataObjectTest\Bogey::class,
54
        DataObjectTest\Sortable::class,
55
        DataObjectTest\Bracket::class,
56
        DataObjectTest\RelationParent::class,
57
        DataObjectTest\RelationChildFirst::class,
58
        DataObjectTest\RelationChildSecond::class,
59
    );
60
61
    public static function getExtraDataObjects()
62
    {
63
        return array_merge(
64
            DataObjectTest::$extra_data_objects,
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

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

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

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

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

Loading history...
65
            ManyManyListTest::$extra_data_objects
66
        );
67
    }
68
69
    public function testDb()
70
    {
71
        $schema = DataObject::getSchema();
72
        $dbFields = $schema->fieldSpecs(DataObjectTest\TeamComment::class);
73
74
        // Assert fields are included
75
        $this->assertArrayHasKey('Name', $dbFields);
76
77
        // Assert the base fields are included
78
        $this->assertArrayHasKey('Created', $dbFields);
79
        $this->assertArrayHasKey('LastEdited', $dbFields);
80
        $this->assertArrayHasKey('ClassName', $dbFields);
81
        $this->assertArrayHasKey('ID', $dbFields);
82
83
        // Assert that the correct field type is returned when passing a field
84
        $this->assertEquals('Varchar', $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Name'));
85
        $this->assertEquals('Text', $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Comment'));
86
87
        // Test with table required
88
        $this->assertEquals(
89
            DataObjectTest\TeamComment::class . '.Varchar',
90
            $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Name', DataObjectSchema::INCLUDE_CLASS)
91
        );
92
        $this->assertEquals(
93
            DataObjectTest\TeamComment::class . '.Text',
94
            $schema->fieldSpec(DataObjectTest\TeamComment::class, 'Comment', DataObjectSchema::INCLUDE_CLASS)
95
        );
96
        $dbFields = $schema->fieldSpecs(DataObjectTest\ExtendedTeamComment::class);
97
98
        // fixed fields are still included in extended classes
99
        $this->assertArrayHasKey('Created', $dbFields);
100
        $this->assertArrayHasKey('LastEdited', $dbFields);
101
        $this->assertArrayHasKey('ClassName', $dbFields);
102
        $this->assertArrayHasKey('ID', $dbFields);
103
104
        // Assert overloaded fields have correct data type
105
        $this->assertEquals('HTMLText', $schema->fieldSpec(DataObjectTest\ExtendedTeamComment::class, 'Comment'));
106
        $this->assertEquals(
107
            'HTMLText',
108
            $dbFields['Comment'],
109
            'Calls to DataObject::db without a field specified return correct data types'
110
        );
111
112
        // assertEquals doesn't verify the order of array elements, so access keys manually to check order:
113
        // 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...
114
        $this->assertEquals(
115
            array(
116
                'Name',
117
                'Comment'
118
            ),
119
            array_slice(array_keys($dbFields), 4, 2),
120
            'DataObject::db returns fields in correct order'
121
        );
122
    }
123
124
    public function testConstructAcceptsValues()
125
    {
126
        // Values can be an array...
127
        $player = new DataObjectTest\Player(
128
            array(
129
                'FirstName' => 'James',
130
                'Surname' => 'Smith'
131
            )
132
        );
133
134
        $this->assertEquals('James', $player->FirstName);
135
        $this->assertEquals('Smith', $player->Surname);
136
137
        // ... or a stdClass inst
138
        $data = new stdClass();
139
        $data->FirstName = 'John';
140
        $data->Surname = 'Doe';
141
        $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

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

421
        $this->assertEquals($team1ID, $captain1->/** @scrutinizer ignore-call */ FavouriteTeam()->ID);
Loading history...
422
423
        // Test that getNonReciprocalComponent can find has_one from the has_many end
424
        $this->assertEquals(
425
            $team1ID,
426
            $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...
427
        );
428
429
        // Check entity with polymorphic has-one
430
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, "fan1");
431
        $this->assertTrue((bool)$fan1->hasValue('Favourite'));
432
433
        // There will be fields named (relname)ID and (relname)Class for polymorphic
434
        // entities
435
        $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...
436
        $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...
437
438
        // There will be a method called $obj->relname() that returns the object itself
439
        $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

439
        /** @scrutinizer ignore-call */ 
440
        $favourite = $fan1->Favourite();
Loading history...
440
        $this->assertEquals($team1ID, $favourite->ID);
441
        $this->assertInstanceOf(DataObjectTest\Team::class, $favourite);
442
443
        // check behaviour of dbObject with polymorphic relations
444
        $favouriteDBObject = $fan1->dbObject('Favourite');
445
        $favouriteValue = $favouriteDBObject->getValue();
446
        $this->assertInstanceOf(DBPolymorphicForeignKey::class, $favouriteDBObject);
447
        $this->assertEquals($favourite->ID, $favouriteValue->ID);
448
        $this->assertEquals($favourite->ClassName, $favouriteValue->ClassName);
449
    }
450
451
    public function testLimitAndCount()
452
    {
453
        $players = DataObject::get(DataObjectTest\Player::class);
454
455
        // There's 4 records in total
456
        $this->assertEquals(4, $players->count());
457
458
        // Testing "##, ##" syntax
459
        $this->assertEquals(4, $players->limit(20)->count());
460
        $this->assertEquals(4, $players->limit(20, 0)->count());
461
        $this->assertEquals(0, $players->limit(20, 20)->count());
462
        $this->assertEquals(2, $players->limit(2, 0)->count());
463
        $this->assertEquals(1, $players->limit(5, 3)->count());
464
    }
465
466
    public function testWriteNoChangesDoesntUpdateLastEdited()
467
    {
468
        // set mock now so we can be certain of LastEdited time for our test
469
        DBDatetime::set_mock_now('2017-01-01 00:00:00');
470
        $obj = new Player();
471
        $obj->FirstName = 'Test';
472
        $obj->Surname = 'Plater';
473
        $obj->Email = '[email protected]';
474
        $obj->write();
475
        $this->assertEquals('2017-01-01 00:00:00', $obj->LastEdited);
476
        $writtenObj = Player::get()->byID($obj->ID);
477
        $this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
478
479
        // set mock now so we get a new LastEdited if, for some reason, it's updated
480
        DBDatetime::set_mock_now('2017-02-01 00:00:00');
481
        $writtenObj->write();
482
        $this->assertEquals('2017-01-01 00:00:00', $writtenObj->LastEdited);
483
        $this->assertEquals($obj->ID, $writtenObj->ID);
484
485
        $reWrittenObj = Player::get()->byID($writtenObj->ID);
486
        $this->assertEquals('2017-01-01 00:00:00', $reWrittenObj->LastEdited);
487
    }
488
489
    /**
490
     * Test writing of database columns which don't correlate to a DBField,
491
     * e.g. all relation fields on has_one/has_many like "ParentID".
492
     */
493
    public function testWritePropertyWithoutDBField()
494
    {
495
        $obj = $this->objFromFixture(DataObjectTest\Player::class, 'captain1');
496
        $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...
497
        $obj->write();
498
499
        // reload the page from the database
500
        $savedObj = DataObject::get_by_id(DataObjectTest\Player::class, $obj->ID);
501
        $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...
502
503
        // Test with porymorphic relation
504
        $obj2 = $this->objFromFixture(DataObjectTest\Fan::class, "fan1");
505
        $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...
506
        $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...
507
        $obj2->write();
508
509
        $savedObj2 = DataObject::get_by_id(DataObjectTest\Fan::class, $obj2->ID);
510
        $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...
511
        $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...
512
    }
513
514
    /**
515
     * Test has many relationships
516
     *   - Test getComponents() gets the ComponentSet of the other side of the relation
517
     *   - Test the IDs on the DataObjects are set correctly
518
     */
519
    public function testHasManyRelationships()
520
    {
521
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
522
523
        // Test getComponents() gets the ComponentSet of the other side of the relation
524
        $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

524
        $this->assertTrue($team1->/** @scrutinizer ignore-call */ Comments()->count() == 2);
Loading history...
525
526
        $team1Comments = [
527
            ['Comment' => 'This is a team comment by Joe'],
528
            ['Comment' => 'This is a team comment by Bob'],
529
        ];
530
531
        // Test the IDs on the DataObjects are set correctly
532
        $this->assertListEquals($team1Comments, $team1->Comments());
533
534
        // Test that has_many can be infered from the has_one via getNonReciprocalComponent
535
        $this->assertListEquals(
536
            $team1Comments,
537
            $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

537
            /** @scrutinizer ignore-type */ $team1->inferReciprocalComponent(DataObjectTest\TeamComment::class, 'Team')
Loading history...
538
        );
539
540
        // Test that we can add and remove items that already exist in the database
541
        $newComment = new DataObjectTest\TeamComment();
542
        $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...
543
        $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...
544
        $newComment->write();
545
        $team1->Comments()->add($newComment);
546
        $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...
547
548
        $comment1 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment1');
549
        $comment2 = $this->objFromFixture(DataObjectTest\TeamComment::class, 'comment2');
550
        $team1->Comments()->remove($comment2);
551
552
        $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

552
        $team1CommentIDs = $team1->Comments()->/** @scrutinizer ignore-call */ sort('ID')->column('ID');
Loading history...
553
        $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
554
555
        // Test that removing an item from a list doesn't remove it from the same
556
        // relation belonging to a different object
557
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
558
        $team2 = $this->objFromFixture(DataObjectTest\Team::class, 'team2');
559
        $team2->Comments()->remove($comment1);
560
        $team1CommentIDs = $team1->Comments()->sort('ID')->column('ID');
561
        $this->assertEquals(array($comment1->ID, $newComment->ID), $team1CommentIDs);
562
    }
563
564
565
    /**
566
     * Test has many relationships against polymorphic has_one fields
567
     *   - Test getComponents() gets the ComponentSet of the other side of the relation
568
     *   - Test the IDs on the DataObjects are set correctly
569
     */
570
    public function testHasManyPolymorphicRelationships()
571
    {
572
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
573
574
        // Test getComponents() gets the ComponentSet of the other side of the relation
575
        $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

575
        $this->assertTrue($team1->/** @scrutinizer ignore-call */ Fans()->count() == 2);
Loading history...
576
577
        // Test the IDs/Classes on the DataObjects are set correctly
578
        foreach ($team1->Fans() as $fan) {
579
            $this->assertEquals($team1->ID, $fan->FavouriteID, 'Fan has the correct FavouriteID');
580
            $this->assertEquals(DataObjectTest\Team::class, $fan->FavouriteClass, 'Fan has the correct FavouriteClass');
581
        }
582
583
        // Test that we can add and remove items that already exist in the database
584
        $newFan = new DataObjectTest\Fan();
585
        $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...
586
        $newFan->write();
587
        $team1->Fans()->add($newFan);
588
        $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...
589
        $this->assertEquals(
590
            DataObjectTest\Team::class,
591
            $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...
592
            'Newly created fan has the correct FavouriteClass'
593
        );
594
595
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
596
        $fan3 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan3');
597
        $team1->Fans()->remove($fan3);
598
599
        $team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
600
        $this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
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
        $player1 = $this->objFromFixture(DataObjectTest\Player::class, 'player1');
606
        $player1->Fans()->remove($fan1);
607
        $team1FanIDs = $team1->Fans()->sort('ID')->column('ID');
608
        $this->assertEquals(array($fan1->ID, $newFan->ID), $team1FanIDs);
609
    }
610
611
612
    public function testHasOneRelationship()
613
    {
614
        $team1 = $this->objFromFixture(DataObjectTest\Team::class, 'team1');
615
        $player1 = $this->objFromFixture(DataObjectTest\Player::class, 'player1');
616
        $player2 = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
617
        $fan1 = $this->objFromFixture(DataObjectTest\Fan::class, 'fan1');
618
619
        // Test relation probing
620
        $this->assertFalse((bool)$team1->hasValue('Captain', null, false));
621
        $this->assertFalse((bool)$team1->hasValue('CaptainID', null, false));
622
623
        // Add a captain to team 1
624
        $team1->setField('CaptainID', $player1->ID);
625
        $team1->write();
626
627
        $this->assertTrue((bool)$team1->hasValue('Captain', null, false));
628
        $this->assertTrue((bool)$team1->hasValue('CaptainID', null, false));
629
630
        $this->assertEquals(
631
            $player1->ID,
632
            $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

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

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

1599
        /** @scrutinizer ignore-call */ 
1600
        $equipmentSuppliers = $team->EquipmentSuppliers();
Loading history...
1600
1601
        // Check that DataObject::many_many() works as expected
1602
        $manyManyComponent = DataObject::getSchema()->manyManyComponent(DataObjectTest\Team::class, 'Sponsors');
1603
        $this->assertEquals(ManyManyList::class, $manyManyComponent['relationClass']);
1604
        $this->assertEquals(
1605
            DataObjectTest\Team::class,
1606
            $manyManyComponent['parentClass'],
1607
            'DataObject::many_many() didn\'t find the correct base class'
1608
        );
1609
        $this->assertEquals(
1610
            DataObjectTest\EquipmentCompany::class,
1611
            $manyManyComponent['childClass'],
1612
            'DataObject::many_many() didn\'t find the correct target class for the relation'
1613
        );
1614
        $this->assertEquals(
1615
            'DataObjectTest_EquipmentCompany_SponsoredTeams',
1616
            $manyManyComponent['join'],
1617
            'DataObject::many_many() didn\'t find the correct relation table'
1618
        );
1619
        $this->assertEquals('DataObjectTest_TeamID', $manyManyComponent['parentField']);
1620
        $this->assertEquals('DataObjectTest_EquipmentCompanyID', $manyManyComponent['childField']);
1621
1622
        // Check that ManyManyList still works
1623
        $this->assertEquals(2, $sponsors->count(), 'Rows are missing from relation');
1624
        $this->assertEquals(1, $equipmentSuppliers->count(), 'Rows are missing from relation');
1625
1626
        // Check everything works when no relation is present
1627
        $teamWithoutSponsor = $this->objFromFixture(DataObjectTest\Team::class, 'team3');
1628
        $this->assertInstanceOf(ManyManyList::class, $teamWithoutSponsor->Sponsors());
1629
        $this->assertEquals(0, $teamWithoutSponsor->Sponsors()->count());
1630
1631
        // Test that belongs_many_many can be infered from with getNonReciprocalComponent
1632
        $this->assertListEquals(
1633
            [
1634
                ['Name' => 'Company corp'],
1635
                ['Name' => 'Team co.'],
1636
            ],
1637
            $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

1637
            /** @scrutinizer ignore-type */ $team->inferReciprocalComponent(DataObjectTest\EquipmentCompany::class, 'SponsoredTeams')
Loading history...
1638
        );
1639
1640
        // Test that many_many can be infered from getNonReciprocalComponent
1641
        $this->assertListEquals(
1642
            [
1643
                ['Title' => 'Team 1'],
1644
                ['Title' => 'Team 2'],
1645
                ['Title' => 'Subteam 1'],
1646
            ],
1647
            $company2->inferReciprocalComponent(DataObjectTest\Team::class, 'Sponsors')
1648
        );
1649
1650
        // Check many_many_extraFields still works
1651
        $equipmentCompany = $this->objFromFixture(DataObjectTest\EquipmentCompany::class, 'equipmentcompany1');
1652
        $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

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

1760
        $player = $newTeam->/** @scrutinizer ignore-call */ Players()->first();
Loading history...
1761
        $this->assertEquals('Sam', $player->FirstName);
1762
        $this->assertEquals("Prop", $player->Position);
1763
1764
        // Check that ordering a many-many relation by an aggregate column doesn't fail
1765
        $player = $this->objFromFixture(DataObjectTest\Player::class, 'player2');
1766
        $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

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

2037
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ Company()->ID, 'belongs_to returns the right results.');
Loading history...
2038
2039
        // Test belongs_to can be infered via getNonReciprocalComponent
2040
        // Note: Will be returned as has_many since the belongs_to is ignored.
2041
        $this->assertListEquals(
2042
            [['Name' => 'New Company']],
2043
            $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

2043
            /** @scrutinizer ignore-type */ $ceo->inferReciprocalComponent(DataObjectTest\Company::class, 'CEO')
Loading history...
2044
        );
2045
2046
        // Test has_one to a belongs_to can be infered via getNonReciprocalComponent
2047
        $this->assertEquals(
2048
            $ceo->ID,
2049
            $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...
2050
        );
2051
2052
        // Test automatic creation of class where no assigment exists
2053
        $ceo = new DataObjectTest\CEO();
2054
        $ceo->write();
2055
2056
        $this->assertTrue(
2057
            $ceo->Company() instanceof DataObjectTest\Company,
2058
            'DataObjects across belongs_to relations are automatically created.'
2059
        );
2060
        $this->assertEquals($ceo->ID, $ceo->Company()->CEOID, 'Remote IDs are automatically set.');
2061
2062
        // Write object with components
2063
        $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...
2064
        $ceo->write(false, false, false, true);
2065
        $this->assertTrue($ceo->Company()->isInDB(), 'write() writes belongs_to components to the database.');
2066
2067
        $newCEO = DataObject::get_by_id(DataObjectTest\CEO::class, $ceo->ID);
2068
        $this->assertEquals(
2069
            $ceo->Company()->ID,
2070
            $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

2070
            $newCEO->/** @scrutinizer ignore-call */ 
2071
                     Company()->ID,
Loading history...
2071
            'belongs_to can be retrieved from the database.'
2072
        );
2073
    }
2074
2075
    public function testBelongsToPolymorphic()
2076
    {
2077
        $company = new DataObjectTest\Company();
2078
        $ceo = new DataObjectTest\CEO();
2079
2080
        $company->write();
2081
        $ceo->write();
2082
2083
        // Test belongs_to assignment
2084
        $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...
2085
        $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...
2086
        $company->write();
2087
2088
        $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

2088
        $this->assertEquals($company->ID, $ceo->/** @scrutinizer ignore-call */ CompanyOwned()->ID, 'belongs_to returns the right results.');
Loading history...
2089
        $this->assertInstanceOf(
2090
            DataObjectTest\Company::class,
2091
            $ceo->CompanyOwned(),
2092
            'belongs_to returns the right results.'
2093
        );
2094
2095
        // Test automatic creation of class where no assigment exists
2096
        $ceo = new DataObjectTest\CEO();
2097
        $ceo->write();
2098
2099
        $this->assertTrue(
2100
            $ceo->CompanyOwned() instanceof DataObjectTest\Company,
2101
            'DataObjects across polymorphic belongs_to relations are automatically created.'
2102
        );
2103
        $this->assertEquals($ceo->ID, $ceo->CompanyOwned()->OwnerID, 'Remote IDs are automatically set.');
2104
        $this->assertInstanceOf($ceo->CompanyOwned()->OwnerClass, $ceo, 'Remote class is automatically  set');
2105
2106
        // Write object with components
2107
        $ceo->write(false, false, false, true);
2108
        $this->assertTrue($ceo->CompanyOwned()->isInDB(), 'write() writes belongs_to components to the database.');
2109
2110
        $newCEO = DataObject::get_by_id(DataObjectTest\CEO::class, $ceo->ID);
2111
        $this->assertEquals(
2112
            $ceo->CompanyOwned()->ID,
2113
            $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

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