silverstripe-framework/tests/php/ORM/DataObjectSchemaTest.php
2022-08-22 17:47:14 +12:00

378 lines
13 KiB
PHP

<?php
namespace SilverStripe\ORM\Tests;
use InvalidArgumentException;
use SilverStripe\Core\ClassInfo;
use SilverStripe\Core\Config\Config;
use SilverStripe\Dev\SapphireTest;
use SilverStripe\ORM\FieldType\DBMoney;
use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\DataObjectSchema;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\AllIndexes;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\BaseClass;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\BaseDataClass;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\ChildClass;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\DefaultTableName;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\GrandChildClass;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\HasComposites;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\HasFields;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\HasIndexesInFieldSpecs;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\NoFields;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\WithCustomTable;
use SilverStripe\ORM\Tests\DataObjectSchemaTest\WithRelation;
/**
* Tests schema inspection of DataObjects
*
* @skipUpgrade
*/
class DataObjectSchemaTest extends SapphireTest
{
protected static $extra_dataobjects = [
// Classes in base namespace
BaseClass::class,
BaseDataClass::class,
ChildClass::class,
GrandChildClass::class,
HasFields::Class,
NoFields::class,
WithCustomTable::class,
WithRelation::class,
DefaultTableName::class,
AllIndexes::class,
];
/**
* Test table name generation
*/
public function testTableName()
{
$schema = DataObject::getSchema();
$this->assertEquals(
'DataObjectSchemaTest_WithRelation',
$schema->tableName(WithRelation::class)
);
$this->assertEquals(
'DOSTWithCustomTable',
$schema->tableName(WithCustomTable::class)
);
// Default table name is Vendor plus base class
$this->assertEquals(
'SilverStripe_DefaultTableName',
$schema->tableName(DefaultTableName::class)
);
}
/**
* Test that the class name is convertible from the table
*/
public function testClassNameForTable()
{
$schema = DataObject::getSchema();
// Tables that aren't classes
$this->assertNull($schema->tableClass('NotARealTable'));
// Non-namespaced tables
$this->assertEquals(
WithRelation::class,
$schema->tableClass('DataObjectSchemaTest_WithRelation')
);
$this->assertEquals(
WithCustomTable::class,
$schema->tableClass('DOSTWithCustomTable')
);
}
public function testTableForObjectField()
{
$schema = DataObject::getSchema();
$this->assertEquals(
'DataObjectSchemaTest_WithRelation',
$schema->tableForField(WithRelation::class, 'RelationID')
);
$this->assertEquals(
'DataObjectSchemaTest_WithRelation',
$schema->tableForField(WithRelation::class, 'RelationID')
);
$this->assertEquals(
'DataObjectSchemaTest_BaseDataClass',
$schema->tableForField(BaseDataClass::class, 'Title')
);
$this->assertEquals(
'DataObjectSchemaTest_BaseDataClass',
$schema->tableForField(HasFields::class, 'Title')
);
$this->assertEquals(
'DataObjectSchemaTest_BaseDataClass',
$schema->tableForField(NoFields::class, 'Title')
);
$this->assertEquals(
'DataObjectSchemaTest_BaseDataClass',
$schema->tableForField(NoFields::class, 'Title')
);
$this->assertEquals(
'DataObjectSchemaTest_HasFields',
$schema->tableForField(HasFields::Class, 'Description')
);
// Class and table differ for this model
$this->assertEquals(
'DOSTWithCustomTable',
$schema->tableForField(WithCustomTable::class, 'Description')
);
$this->assertEquals(
WithCustomTable::class,
$schema->classForField(WithCustomTable::class, 'Description')
);
$this->assertNull(
$schema->tableForField(WithCustomTable::class, 'NotAField')
);
$this->assertNull(
$schema->classForField(WithCustomTable::class, 'NotAField')
);
// Non-existent fields shouldn't match any table
$this->assertNull(
$schema->tableForField(BaseClass::class, 'Nonexist')
);
$this->assertNull(
$schema->tableForField(ClassInfo::class, 'Title')
);
// Test fixed fields
$this->assertEquals(
'DataObjectSchemaTest_BaseDataClass',
$schema->tableForField(HasFields::class, 'ID')
);
$this->assertEquals(
'DataObjectSchemaTest_BaseDataClass',
$schema->tableForField(NoFields::class, 'Created')
);
}
public function testFieldSpec()
{
$schema = DataObject::getSchema();
$this->assertEquals(
[
'ID' => 'PrimaryKey',
'ClassName' => 'DBClassName',
'LastEdited' => 'DBDatetime',
'Created' => 'DBDatetime',
'Title' => 'Varchar',
'Description' => 'Varchar',
'MoneyFieldCurrency' => 'Varchar(3)',
'MoneyFieldAmount' => 'Decimal(19,4)',
'MoneyField' => 'Money',
],
$schema->fieldSpecs(HasFields::class)
);
$this->assertEquals(
[
'ID' => DataObjectSchemaTest\HasFields::class . '.PrimaryKey',
'ClassName' => DataObjectSchemaTest\BaseDataClass::class . '.DBClassName',
'LastEdited' => DataObjectSchemaTest\BaseDataClass::class . '.DBDatetime',
'Created' => DataObjectSchemaTest\BaseDataClass::class . '.DBDatetime',
'Title' => DataObjectSchemaTest\BaseDataClass::class . '.Varchar',
'Description' => DataObjectSchemaTest\HasFields::class . '.Varchar',
'MoneyFieldCurrency' => DataObjectSchemaTest\HasFields::class . '.Varchar(3)',
'MoneyFieldAmount' => DataObjectSchemaTest\HasFields::class . '.Decimal(19,4)',
'MoneyField' => DataObjectSchemaTest\HasFields::class . '.Money',
],
$schema->fieldSpecs(HasFields::class, DataObjectSchema::INCLUDE_CLASS)
);
// DB_ONLY excludes composite field MoneyField
$this->assertEquals(
[
'ID' => DataObjectSchemaTest\HasFields::class . '.PrimaryKey',
'ClassName' => DataObjectSchemaTest\BaseDataClass::class . '.DBClassName',
'LastEdited' => DataObjectSchemaTest\BaseDataClass::class . '.DBDatetime',
'Created' => DataObjectSchemaTest\BaseDataClass::class . '.DBDatetime',
'Title' => DataObjectSchemaTest\BaseDataClass::class . '.Varchar',
'Description' => DataObjectSchemaTest\HasFields::class . '.Varchar',
'MoneyFieldCurrency' => DataObjectSchemaTest\HasFields::class . '.Varchar(3)',
'MoneyFieldAmount' => DataObjectSchemaTest\HasFields::class . '.Decimal(19,4)'
],
$schema->fieldSpecs(
HasFields::class,
DataObjectSchema::INCLUDE_CLASS | DataObjectSchema::DB_ONLY
)
);
// Use all options at once
$this->assertEquals(
[
'ID' => DataObjectSchemaTest\HasFields::class . '.PrimaryKey',
'Description' => DataObjectSchemaTest\HasFields::class . '.Varchar',
'MoneyFieldCurrency' => DataObjectSchemaTest\HasFields::class . '.Varchar(3)',
'MoneyFieldAmount' => DataObjectSchemaTest\HasFields::class . '.Decimal(19,4)',
],
$schema->fieldSpecs(
HasFields::class,
DataObjectSchema::INCLUDE_CLASS | DataObjectSchema::DB_ONLY | DataObjectSchema::UNINHERITED
)
);
}
/**
* @covers \SilverStripe\ORM\DataObjectSchema::baseDataClass()
*/
public function testBaseDataClass()
{
$schema = DataObject::getSchema();
$this->assertEquals(BaseClass::class, $schema->baseDataClass(BaseClass::class));
$this->assertEquals(BaseClass::class, $schema->baseDataClass(strtolower(BaseClass::class)));
$this->assertEquals(BaseClass::class, $schema->baseDataClass(ChildClass::class));
$this->assertEquals(BaseClass::class, $schema->baseDataClass(strtoupper(ChildClass::class)));
$this->assertEquals(BaseClass::class, $schema->baseDataClass(GrandChildClass::class));
$this->assertEquals(BaseClass::class, $schema->baseDataClass(ucfirst(GrandChildClass::class)));
$this->expectException(InvalidArgumentException::class);
$schema->baseDataClass(DataObject::class);
}
public function testDatabaseIndexes()
{
$indexes = DataObject::getSchema()->databaseIndexes(AllIndexes::class);
$this->assertCount(5, $indexes);
$this->assertArrayHasKey('ClassName', $indexes);
$this->assertEquals([
'type' => 'index',
'columns' => ['ClassName'],
], $indexes['ClassName']);
$this->assertArrayHasKey('Content', $indexes);
$this->assertEquals([
'type' => 'index',
'columns' => ['Content'],
], $indexes['Content']);
$this->assertArrayHasKey('IndexCols', $indexes);
$this->assertEquals([
'type' => 'index',
'columns' => ['Title', 'Content'],
], $indexes['IndexCols']);
$this->assertArrayHasKey('IndexUnique', $indexes);
$this->assertEquals([
'type' => 'unique',
'columns' => ['Number'],
], $indexes['IndexUnique']);
$this->assertArrayHasKey('IndexNormal', $indexes);
$this->assertEquals([
'type' => 'index',
'columns' => ['Title'],
], $indexes['IndexNormal']);
}
public function testCompositeDatabaseFieldIndexes()
{
$indexes = DataObject::getSchema()->databaseIndexes(HasComposites::class);
$this->assertCount(3, $indexes);
$this->assertArrayHasKey('RegularHasOneID', $indexes);
$this->assertEquals([
'type' => 'index',
'columns' => ['RegularHasOneID']
], $indexes['RegularHasOneID']);
$this->assertArrayHasKey('Polymorpheus', $indexes);
$this->assertEquals([
'type' => 'index',
'columns' => ['PolymorpheusID', 'PolymorpheusClass']
], $indexes['Polymorpheus']);
// Check that DBPolymorphicForeignKey's "Class" is not indexed on its own
$this->assertArrayNotHasKey('PolymorpheusClass', $indexes);
}
public function testCompositeFieldsCanBeIndexedByDefaultConfiguration()
{
Config::modify()->set(DBMoney::class, 'index', true);
$indexes = DataObject::getSchema()->databaseIndexes(HasComposites::class);
$this->assertCount(4, $indexes);
$this->assertArrayHasKey('Amount', $indexes);
$this->assertEquals([
'type' => 'index',
'columns' => ['AmountCurrency', 'AmountAmount']
], $indexes['Amount']);
}
public function testIndexTypeIsConfigurable()
{
Config::modify()->set(DBMoney::class, 'index', 'unique');
$indexes = DataObject::getSchema()->databaseIndexes(HasComposites::class);
$this->assertCount(4, $indexes);
$this->assertArrayHasKey('Amount', $indexes);
$this->assertEquals([
'type' => 'unique',
'columns' => ['AmountCurrency', 'AmountAmount']
], $indexes['Amount']);
}
public function testFieldsCanBeIndexedFromFieldSpecs()
{
$indexes = DataObject::getSchema()->databaseIndexes(HasIndexesInFieldSpecs::class);
$this->assertCount(3, $indexes);
$this->assertArrayHasKey('ClassName', $indexes);
$this->assertArrayHasKey('IndexedTitle', $indexes);
$this->assertEquals([
'type' => 'fulltext',
'columns' => ['IndexedTitle']
], $indexes['IndexedTitle']);
$this->assertArrayHasKey('IndexedMoney', $indexes);
$this->assertEquals([
'type' => 'index',
'columns' => ['IndexedMoneyCurrency', 'IndexedMoneyAmount']
], $indexes['IndexedMoney']);
}
/**
* Ensure that records with unique indexes can be written
*/
public function testWriteUniqueIndexes()
{
// Create default object
$zeroObject = new AllIndexes();
$zeroObject->Number = 0;
$zeroObject->write();
$this->assertListEquals(
[
['Number' => 0],
],
AllIndexes::get()
);
// Test a new record can be created without clashing with default value
$validObject = new AllIndexes();
$validObject->Number = 1;
$validObject->write();
$this->assertListEquals(
[
['Number' => 0],
['Number' => 1],
],
AllIndexes::get()
);
}
}