Passed
Pull Request — 4.2 (#8831)
by Damian
06:58
created

DataObjectSchemaTest::testWriteUniqueIndexes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 25
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 13
nc 1
nop 0
dl 0
loc 25
rs 9.8333
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\ORM\Tests;
4
5
use InvalidArgumentException;
6
use SilverStripe\Core\ClassInfo;
7
use SilverStripe\Core\Config\Config;
8
use SilverStripe\Dev\SapphireTest;
9
use SilverStripe\ORM\FieldType\DBMoney;
10
use SilverStripe\ORM\DataObject;
11
use SilverStripe\ORM\DataObjectSchema;
12
use SilverStripe\ORM\Tests\DataObjectSchemaTest\AllIndexes;
13
use SilverStripe\ORM\Tests\DataObjectSchemaTest\BaseClass;
14
use SilverStripe\ORM\Tests\DataObjectSchemaTest\BaseDataClass;
15
use SilverStripe\ORM\Tests\DataObjectSchemaTest\ChildClass;
16
use SilverStripe\ORM\Tests\DataObjectSchemaTest\DefaultTableName;
17
use SilverStripe\ORM\Tests\DataObjectSchemaTest\GrandChildClass;
18
use SilverStripe\ORM\Tests\DataObjectSchemaTest\HasComposites;
19
use SilverStripe\ORM\Tests\DataObjectSchemaTest\HasFields;
20
use SilverStripe\ORM\Tests\DataObjectSchemaTest\HasIndexesInFieldSpecs;
21
use SilverStripe\ORM\Tests\DataObjectSchemaTest\NoFields;
22
use SilverStripe\ORM\Tests\DataObjectSchemaTest\WithCustomTable;
23
use SilverStripe\ORM\Tests\DataObjectSchemaTest\WithRelation;
24
25
/**
26
 * Tests schema inspection of DataObjects
27
 *
28
 * @skipUpgrade
29
 */
30
class DataObjectSchemaTest extends SapphireTest
31
{
32
    protected static $extra_dataobjects = [
33
        // Classes in base namespace
34
        BaseClass::class,
35
        BaseDataClass::class,
36
        ChildClass::class,
37
        GrandChildClass::class,
38
        HasFields::Class,
39
        NoFields::class,
40
        WithCustomTable::class,
41
        WithRelation::class,
42
        DefaultTableName::class,
43
        AllIndexes::class,
44
    ];
45
46
    /**
47
     * Test table name generation
48
     */
49
    public function testTableName()
50
    {
51
        $schema = DataObject::getSchema();
52
53
        $this->assertEquals(
54
            'DataObjectSchemaTest_WithRelation',
55
            $schema->tableName(WithRelation::class)
56
        );
57
        $this->assertEquals(
58
            'DOSTWithCustomTable',
59
            $schema->tableName(WithCustomTable::class)
60
        );
61
        // Default table name is FQN
62
        $this->assertEquals(
63
            'SilverStripe_ORM_Tests_DataObjectSchemaTest_DefaultTableName',
64
            $schema->tableName(DefaultTableName::class)
65
        );
66
    }
67
68
    /**
69
     * Test that the class name is convertable from the table
70
     */
71
    public function testClassNameForTable()
72
    {
73
        $schema = DataObject::getSchema();
74
75
        // Tables that aren't classes
76
        $this->assertNull($schema->tableClass('NotARealTable'));
77
78
        // Non-namespaced tables
79
        $this->assertEquals(
80
            WithRelation::class,
81
            $schema->tableClass('DataObjectSchemaTest_WithRelation')
82
        );
83
        $this->assertEquals(
84
            WithCustomTable::class,
85
            $schema->tableClass('DOSTWithCustomTable')
86
        );
87
    }
88
89
    public function testTableForObjectField()
90
    {
91
        $schema = DataObject::getSchema();
92
        $this->assertEquals(
93
            'DataObjectSchemaTest_WithRelation',
94
            $schema->tableForField(WithRelation::class, 'RelationID')
95
        );
96
97
        $this->assertEquals(
98
            'DataObjectSchemaTest_WithRelation',
99
            $schema->tableForField(WithRelation::class, 'RelationID')
100
        );
101
102
        $this->assertEquals(
103
            'DataObjectSchemaTest_BaseDataClass',
104
            $schema->tableForField(BaseDataClass::class, 'Title')
105
        );
106
107
        $this->assertEquals(
108
            'DataObjectSchemaTest_BaseDataClass',
109
            $schema->tableForField(HasFields::class, 'Title')
110
        );
111
112
        $this->assertEquals(
113
            'DataObjectSchemaTest_BaseDataClass',
114
            $schema->tableForField(NoFields::class, 'Title')
115
        );
116
117
        $this->assertEquals(
118
            'DataObjectSchemaTest_BaseDataClass',
119
            $schema->tableForField(NoFields::class, 'Title')
120
        );
121
122
        $this->assertEquals(
123
            'DataObjectSchemaTest_HasFields',
124
            $schema->tableForField(HasFields::Class, 'Description')
0 ignored issues
show
Bug introduced by
The constant SilverStripe\ORM\Tests\D...maTest\HasFields::Class was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
125
        );
126
127
        // Class and table differ for this model
128
        $this->assertEquals(
129
            'DOSTWithCustomTable',
130
            $schema->tableForField(WithCustomTable::class, 'Description')
131
        );
132
        $this->assertEquals(
133
            WithCustomTable::class,
134
            $schema->classForField(WithCustomTable::class, 'Description')
135
        );
136
        $this->assertNull(
137
            $schema->tableForField(WithCustomTable::class, 'NotAField')
138
        );
139
        $this->assertNull(
140
            $schema->classForField(WithCustomTable::class, 'NotAField')
141
        );
142
143
        // Non-existant fields shouldn't match any table
144
        $this->assertNull(
145
            $schema->tableForField(BaseClass::class, 'Nonexist')
146
        );
147
148
        $this->assertNull(
149
            $schema->tableForField(ClassInfo::class, 'Title')
150
        );
151
152
        // Test fixed fields
153
        $this->assertEquals(
154
            'DataObjectSchemaTest_BaseDataClass',
155
            $schema->tableForField(HasFields::class, 'ID')
156
        );
157
        $this->assertEquals(
158
            'DataObjectSchemaTest_BaseDataClass',
159
            $schema->tableForField(NoFields::class, 'Created')
160
        );
161
    }
162
163
    public function testFieldSpec()
164
    {
165
        $schema = DataObject::getSchema();
166
        $this->assertEquals(
167
            [
168
                'ID' => 'PrimaryKey',
169
                'ClassName' => 'DBClassName',
170
                'LastEdited' => 'DBDatetime',
171
                'Created' => 'DBDatetime',
172
                'Title' => 'Varchar',
173
                'Description' => 'Varchar',
174
                'MoneyFieldCurrency' => 'Varchar(3)',
175
                'MoneyFieldAmount' => 'Decimal(19,4)',
176
                'MoneyField' => 'Money',
177
            ],
178
            $schema->fieldSpecs(HasFields::class)
179
        );
180
        $this->assertEquals(
181
            [
182
                'ID' => DataObjectSchemaTest\HasFields::class . '.PrimaryKey',
183
                'ClassName' => DataObjectSchemaTest\BaseDataClass::class . '.DBClassName',
184
                'LastEdited' => DataObjectSchemaTest\BaseDataClass::class . '.DBDatetime',
185
                'Created' => DataObjectSchemaTest\BaseDataClass::class . '.DBDatetime',
186
                'Title' => DataObjectSchemaTest\BaseDataClass::class . '.Varchar',
187
                'Description' => DataObjectSchemaTest\HasFields::class . '.Varchar',
188
                'MoneyFieldCurrency' => DataObjectSchemaTest\HasFields::class . '.Varchar(3)',
189
                'MoneyFieldAmount' => DataObjectSchemaTest\HasFields::class . '.Decimal(19,4)',
190
                'MoneyField' => DataObjectSchemaTest\HasFields::class . '.Money',
191
            ],
192
            $schema->fieldSpecs(HasFields::class, DataObjectSchema::INCLUDE_CLASS)
193
        );
194
        // DB_ONLY excludes composite field MoneyField
195
        $this->assertEquals(
196
            [
197
                'ID' => DataObjectSchemaTest\HasFields::class . '.PrimaryKey',
198
                'ClassName' => DataObjectSchemaTest\BaseDataClass::class . '.DBClassName',
199
                'LastEdited' => DataObjectSchemaTest\BaseDataClass::class . '.DBDatetime',
200
                'Created' => DataObjectSchemaTest\BaseDataClass::class . '.DBDatetime',
201
                'Title' => DataObjectSchemaTest\BaseDataClass::class . '.Varchar',
202
                'Description' => DataObjectSchemaTest\HasFields::class . '.Varchar',
203
                'MoneyFieldCurrency' => DataObjectSchemaTest\HasFields::class . '.Varchar(3)',
204
                'MoneyFieldAmount' => DataObjectSchemaTest\HasFields::class . '.Decimal(19,4)'
205
            ],
206
            $schema->fieldSpecs(
207
                HasFields::class,
208
                DataObjectSchema::INCLUDE_CLASS | DataObjectSchema::DB_ONLY
209
            )
210
        );
211
212
        // Use all options at once
213
        $this->assertEquals(
214
            [
215
                'ID' => DataObjectSchemaTest\HasFields::class . '.PrimaryKey',
216
                'Description' => DataObjectSchemaTest\HasFields::class . '.Varchar',
217
                'MoneyFieldCurrency' => DataObjectSchemaTest\HasFields::class . '.Varchar(3)',
218
                'MoneyFieldAmount' => DataObjectSchemaTest\HasFields::class . '.Decimal(19,4)',
219
            ],
220
            $schema->fieldSpecs(
221
                HasFields::class,
222
                DataObjectSchema::INCLUDE_CLASS | DataObjectSchema::DB_ONLY | DataObjectSchema::UNINHERITED
223
            )
224
        );
225
    }
226
227
    /**
228
     * @covers \SilverStripe\ORM\DataObjectSchema::baseDataClass()
229
     */
230
    public function testBaseDataClass()
231
    {
232
        $schema = DataObject::getSchema();
233
234
        $this->assertEquals(BaseClass::class, $schema->baseDataClass(BaseClass::class));
235
        $this->assertEquals(BaseClass::class, $schema->baseDataClass(strtolower(BaseClass::class)));
236
        $this->assertEquals(BaseClass::class, $schema->baseDataClass(ChildClass::class));
237
        $this->assertEquals(BaseClass::class, $schema->baseDataClass(strtoupper(ChildClass::class)));
238
        $this->assertEquals(BaseClass::class, $schema->baseDataClass(GrandChildClass::class));
239
        $this->assertEquals(BaseClass::class, $schema->baseDataClass(ucfirst(GrandChildClass::class)));
240
241
        $this->expectException(InvalidArgumentException::class);
242
243
        $schema->baseDataClass(DataObject::class);
244
    }
245
246
    public function testDatabaseIndexes()
247
    {
248
        $indexes = DataObject::getSchema()->databaseIndexes(AllIndexes::class);
249
        $this->assertCount(5, $indexes);
250
        $this->assertArrayHasKey('ClassName', $indexes);
251
        $this->assertEquals([
252
            'type' => 'index',
253
            'columns' => ['ClassName'],
254
        ], $indexes['ClassName']);
255
256
        $this->assertArrayHasKey('Content', $indexes);
257
        $this->assertEquals([
258
            'type' => 'index',
259
            'columns' => ['Content'],
260
        ], $indexes['Content']);
261
262
        $this->assertArrayHasKey('IndexCols', $indexes);
263
        $this->assertEquals([
264
            'type' => 'index',
265
            'columns' => ['Title', 'Content'],
266
        ], $indexes['IndexCols']);
267
268
        $this->assertArrayHasKey('IndexUnique', $indexes);
269
        $this->assertEquals([
270
            'type' => 'unique',
271
            'columns' => ['Number'],
272
        ], $indexes['IndexUnique']);
273
274
        $this->assertArrayHasKey('IndexNormal', $indexes);
275
        $this->assertEquals([
276
            'type' => 'index',
277
            'columns' => ['Title'],
278
        ], $indexes['IndexNormal']);
279
    }
280
281
    public function testCompositeDatabaseFieldIndexes()
282
    {
283
        $indexes = DataObject::getSchema()->databaseIndexes(HasComposites::class);
284
        $this->assertCount(3, $indexes);
285
        $this->assertArrayHasKey('RegularHasOneID', $indexes);
286
        $this->assertEquals([
287
            'type' => 'index',
288
            'columns' => ['RegularHasOneID']
289
        ], $indexes['RegularHasOneID']);
290
291
        $this->assertArrayHasKey('Polymorpheus', $indexes);
292
        $this->assertEquals([
293
            'type' => 'index',
294
            'columns' => ['PolymorpheusID', 'PolymorpheusClass']
295
        ], $indexes['Polymorpheus']);
296
297
        // Check that DBPolymorphicForeignKey's "Class" is not indexed on its own
298
        $this->assertArrayNotHasKey('PolymorpheusClass', $indexes);
299
    }
300
301
    public function testCompositeFieldsCanBeIndexedByDefaultConfiguration()
302
    {
303
        Config::modify()->set(DBMoney::class, 'index', true);
304
        $indexes = DataObject::getSchema()->databaseIndexes(HasComposites::class);
305
306
        $this->assertCount(4, $indexes);
307
        $this->assertArrayHasKey('Amount', $indexes);
308
        $this->assertEquals([
309
            'type' => 'index',
310
            'columns' => ['AmountCurrency', 'AmountAmount']
311
        ], $indexes['Amount']);
312
    }
313
314
    public function testIndexTypeIsConfigurable()
315
    {
316
        Config::modify()->set(DBMoney::class, 'index', 'unique');
317
318
        $indexes = DataObject::getSchema()->databaseIndexes(HasComposites::class);
319
        $this->assertCount(4, $indexes);
320
        $this->assertArrayHasKey('Amount', $indexes);
321
        $this->assertEquals([
322
            'type' => 'unique',
323
            'columns' => ['AmountCurrency', 'AmountAmount']
324
        ], $indexes['Amount']);
325
    }
326
327
    public function testFieldsCanBeIndexedFromFieldSpecs()
328
    {
329
        $indexes = DataObject::getSchema()->databaseIndexes(HasIndexesInFieldSpecs::class);
330
331
        $this->assertCount(3, $indexes);
332
        $this->assertArrayHasKey('ClassName', $indexes);
333
334
        $this->assertArrayHasKey('IndexedTitle', $indexes);
335
        $this->assertEquals([
336
            'type' => 'fulltext',
337
            'columns' => ['IndexedTitle']
338
        ], $indexes['IndexedTitle']);
339
340
        $this->assertArrayHasKey('IndexedMoney', $indexes);
341
        $this->assertEquals([
342
            'type' => 'index',
343
            'columns' => ['IndexedMoneyCurrency', 'IndexedMoneyAmount']
344
        ], $indexes['IndexedMoney']);
345
    }
346
347
    /**
348
     * Ensure that records with unique indexes can be written
349
     */
350
    public function testWriteUniqueIndexes()
351
    {
352
        // Create default object
353
        $zeroObject = new AllIndexes();
354
        $zeroObject->Number = 0;
355
        $zeroObject->write();
356
357
        $this->assertListEquals(
358
            [
359
                ['Number' => 0],
360
            ],
361
            AllIndexes::get()
362
        );
363
364
        // Test a new record can be created without clashing with default value
365
        $validObject = new AllIndexes();
366
        $validObject->Number = 1;
367
        $validObject->write();
368
369
        $this->assertListEquals(
370
            [
371
                ['Number' => 0],
372
                ['Number' => 1],
373
            ],
374
            AllIndexes::get()
375
        );
376
    }
377
}
378