mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-10-22 14:05:37 +02:00
Merge pull request #11359 from creative-commoners/pulls/5/varchar-classname
NEW Create DBClassNameVarchar
This commit is contained in:
commit
b2a8baa931
@ -8,6 +8,8 @@ SilverStripe\Core\Injector\Injector:
|
|||||||
class: SilverStripe\ORM\FieldType\DBCurrency
|
class: SilverStripe\ORM\FieldType\DBCurrency
|
||||||
DBClassName:
|
DBClassName:
|
||||||
class: SilverStripe\ORM\FieldType\DBClassName
|
class: SilverStripe\ORM\FieldType\DBClassName
|
||||||
|
DBClassNameVarchar:
|
||||||
|
class: SilverStripe\ORM\FieldType\DBClassNameVarchar
|
||||||
Date:
|
Date:
|
||||||
class: SilverStripe\ORM\FieldType\DBDate
|
class: SilverStripe\ORM\FieldType\DBDate
|
||||||
Datetime:
|
Datetime:
|
||||||
|
@ -15,6 +15,7 @@ use SilverStripe\Dev\DevelopmentAdmin;
|
|||||||
use SilverStripe\ORM\Connect\DatabaseException;
|
use SilverStripe\ORM\Connect\DatabaseException;
|
||||||
use SilverStripe\ORM\Connect\TableBuilder;
|
use SilverStripe\ORM\Connect\TableBuilder;
|
||||||
use SilverStripe\ORM\FieldType\DBClassName;
|
use SilverStripe\ORM\FieldType\DBClassName;
|
||||||
|
use SilverStripe\ORM\FieldType\DBClassNameVarchar;
|
||||||
use SilverStripe\Security\Permission;
|
use SilverStripe\Security\Permission;
|
||||||
use SilverStripe\Security\Security;
|
use SilverStripe\Security\Security;
|
||||||
use SilverStripe\Versioned\Versioned;
|
use SilverStripe\Versioned\Versioned;
|
||||||
@ -460,7 +461,8 @@ class DatabaseAdmin extends Controller
|
|||||||
foreach ($dataClasses as $className) {
|
foreach ($dataClasses as $className) {
|
||||||
$fieldSpecs = $schema->fieldSpecs($className);
|
$fieldSpecs = $schema->fieldSpecs($className);
|
||||||
foreach ($fieldSpecs as $fieldName => $fieldSpec) {
|
foreach ($fieldSpecs as $fieldName => $fieldSpec) {
|
||||||
if (Injector::inst()->create($fieldSpec, 'Dummy') instanceof DBClassName) {
|
$dummy = Injector::inst()->create($fieldSpec, 'Dummy');
|
||||||
|
if ($dummy instanceof DBClassName || $dummy instanceof DBClassNameVarchar) {
|
||||||
$remapping[$className][] = $fieldName;
|
$remapping[$className][] = $fieldName;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,47 +2,15 @@
|
|||||||
|
|
||||||
namespace SilverStripe\ORM\FieldType;
|
namespace SilverStripe\ORM\FieldType;
|
||||||
|
|
||||||
use SilverStripe\Core\ClassInfo;
|
|
||||||
use SilverStripe\Core\Config\Config;
|
|
||||||
use SilverStripe\ORM\DataObject;
|
|
||||||
use SilverStripe\ORM\DB;
|
use SilverStripe\ORM\DB;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents a classname selector, which respects obsolete clasess.
|
* Represents a classname selector, which respects obsolete clasess.
|
||||||
*/
|
*/
|
||||||
class DBClassName extends DBEnum
|
class DBClassName extends DBEnum
|
||||||
{
|
{
|
||||||
|
use DBClassNameTrait;
|
||||||
/**
|
|
||||||
* Base classname of class to enumerate.
|
|
||||||
* If 'DataObject' then all classes are included.
|
|
||||||
* If empty, then the baseClass of the parent object will be used
|
|
||||||
*
|
|
||||||
* @var string|null
|
|
||||||
*/
|
|
||||||
protected $baseClass = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Parent object
|
|
||||||
*
|
|
||||||
* @var DataObject|null
|
|
||||||
*/
|
|
||||||
protected $record = null;
|
|
||||||
|
|
||||||
private static $index = true;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new DBClassName field
|
|
||||||
*
|
|
||||||
* @param string $name Name of field
|
|
||||||
* @param string|null $baseClass Optional base class to limit selections
|
|
||||||
* @param array $options Optional parameters for this DBField instance
|
|
||||||
*/
|
|
||||||
public function __construct($name = null, $baseClass = null, $options = [])
|
|
||||||
{
|
|
||||||
$this->setBaseClass($baseClass);
|
|
||||||
parent::__construct($name, null, null, $options);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return void
|
* @return void
|
||||||
@ -67,82 +35,6 @@ class DBClassName extends DBEnum
|
|||||||
DB::require_field($this->getTable(), $this->getName(), $values);
|
DB::require_field($this->getTable(), $this->getName(), $values);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the base dataclass for the list of subclasses
|
|
||||||
*
|
|
||||||
* @return string
|
|
||||||
*/
|
|
||||||
public function getBaseClass()
|
|
||||||
{
|
|
||||||
// Use explicit base class
|
|
||||||
if ($this->baseClass) {
|
|
||||||
return $this->baseClass;
|
|
||||||
}
|
|
||||||
// Default to the basename of the record
|
|
||||||
$schema = DataObject::getSchema();
|
|
||||||
if ($this->record) {
|
|
||||||
return $schema->baseDataClass($this->record);
|
|
||||||
}
|
|
||||||
// During dev/build only the table is assigned
|
|
||||||
$tableClass = $schema->tableClass($this->getTable());
|
|
||||||
if ($tableClass && ($baseClass = $schema->baseDataClass($tableClass))) {
|
|
||||||
return $baseClass;
|
|
||||||
}
|
|
||||||
// Fallback to global default
|
|
||||||
return DataObject::class;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the base name of the current class
|
|
||||||
* Useful as a non-fully qualified CSS Class name in templates.
|
|
||||||
*
|
|
||||||
* @return string|null
|
|
||||||
*/
|
|
||||||
public function getShortName()
|
|
||||||
{
|
|
||||||
$value = $this->getValue();
|
|
||||||
if (empty($value) || !ClassInfo::exists($value)) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
return ClassInfo::shortName($value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assign the base class
|
|
||||||
*
|
|
||||||
* @param string $baseClass
|
|
||||||
* @return $this
|
|
||||||
*/
|
|
||||||
public function setBaseClass($baseClass)
|
|
||||||
{
|
|
||||||
$this->baseClass = $baseClass;
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get list of classnames that should be selectable
|
|
||||||
*
|
|
||||||
* @return array
|
|
||||||
*/
|
|
||||||
public function getEnum()
|
|
||||||
{
|
|
||||||
$classNames = ClassInfo::subclassesFor($this->getBaseClass());
|
|
||||||
$dataobject = strtolower(DataObject::class);
|
|
||||||
unset($classNames[$dataobject]);
|
|
||||||
return array_values($classNames ?? []);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function setValue($value, $record = null, $markChanged = true)
|
|
||||||
{
|
|
||||||
parent::setValue($value, $record, $markChanged);
|
|
||||||
|
|
||||||
if ($record instanceof DataObject) {
|
|
||||||
$this->record = $record;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getDefault()
|
public function getDefault()
|
||||||
{
|
{
|
||||||
// Check for assigned default
|
// Check for assigned default
|
||||||
@ -151,15 +43,6 @@ class DBClassName extends DBEnum
|
|||||||
return $default;
|
return $default;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow classes to set default class
|
return $this->getDefaultClassName();
|
||||||
$baseClass = $this->getBaseClass();
|
|
||||||
$defaultClass = Config::inst()->get($baseClass, 'default_classname');
|
|
||||||
if ($defaultClass && class_exists($defaultClass ?? '')) {
|
|
||||||
return $defaultClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fallback to first option
|
|
||||||
$enum = $this->getEnum();
|
|
||||||
return reset($enum);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
131
src/ORM/FieldType/DBClassNameTrait.php
Normal file
131
src/ORM/FieldType/DBClassNameTrait.php
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ORM\FieldType;
|
||||||
|
|
||||||
|
use SilverStripe\Core\ClassInfo;
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
|
||||||
|
trait DBClassNameTrait
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Base classname of class to enumerate.
|
||||||
|
* If 'DataObject' then all classes are included.
|
||||||
|
* If empty, then the baseClass of the parent object will be used
|
||||||
|
*
|
||||||
|
* @var string|null
|
||||||
|
*/
|
||||||
|
protected $baseClass = null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parent object
|
||||||
|
*
|
||||||
|
* @var DataObject|null
|
||||||
|
*/
|
||||||
|
protected $record = null;
|
||||||
|
|
||||||
|
private static $index = true;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a new DBClassName field
|
||||||
|
*
|
||||||
|
* @param string $name Name of field
|
||||||
|
* @param string|null $baseClass Optional base class to limit selections
|
||||||
|
* @param array $options Optional parameters for this DBField instance
|
||||||
|
*/
|
||||||
|
public function __construct($name = null, $baseClass = null, $options = [])
|
||||||
|
{
|
||||||
|
$this->setBaseClass($baseClass);
|
||||||
|
parent::__construct($name, null, null, $options);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the base dataclass for the list of subclasses
|
||||||
|
*
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getBaseClass()
|
||||||
|
{
|
||||||
|
// Use explicit base class
|
||||||
|
if ($this->baseClass) {
|
||||||
|
return $this->baseClass;
|
||||||
|
}
|
||||||
|
// Default to the basename of the record
|
||||||
|
$schema = DataObject::getSchema();
|
||||||
|
if ($this->record) {
|
||||||
|
return $schema->baseDataClass($this->record);
|
||||||
|
}
|
||||||
|
// During dev/build only the table is assigned
|
||||||
|
$tableClass = $schema->tableClass($this->getTable());
|
||||||
|
if ($tableClass && ($baseClass = $schema->baseDataClass($tableClass))) {
|
||||||
|
return $baseClass;
|
||||||
|
}
|
||||||
|
// Fallback to global default
|
||||||
|
return DataObject::class;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the base name of the current class
|
||||||
|
* Useful as a non-fully qualified CSS Class name in templates.
|
||||||
|
*
|
||||||
|
* @return string|null
|
||||||
|
*/
|
||||||
|
public function getShortName()
|
||||||
|
{
|
||||||
|
$value = $this->getValue();
|
||||||
|
if (empty($value) || !ClassInfo::exists($value)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return ClassInfo::shortName($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assign the base class
|
||||||
|
*
|
||||||
|
* @param string $baseClass
|
||||||
|
* @return $this
|
||||||
|
*/
|
||||||
|
public function setBaseClass($baseClass)
|
||||||
|
{
|
||||||
|
$this->baseClass = $baseClass;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get list of classnames that should be selectable
|
||||||
|
*
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function getEnum()
|
||||||
|
{
|
||||||
|
$classNames = ClassInfo::subclassesFor($this->getBaseClass());
|
||||||
|
$dataobject = strtolower(DataObject::class);
|
||||||
|
unset($classNames[$dataobject]);
|
||||||
|
return array_values($classNames ?? []);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setValue($value, $record = null, $markChanged = true)
|
||||||
|
{
|
||||||
|
parent::setValue($value, $record, $markChanged);
|
||||||
|
|
||||||
|
if ($record instanceof DataObject) {
|
||||||
|
$this->record = $record;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getDefaultClassName()
|
||||||
|
{
|
||||||
|
// Allow classes to set default class
|
||||||
|
$baseClass = $this->getBaseClass();
|
||||||
|
$defaultClass = Config::inst()->get($baseClass, 'default_classname');
|
||||||
|
if ($defaultClass && class_exists($defaultClass ?? '')) {
|
||||||
|
return $defaultClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fallback to first option
|
||||||
|
$subClassNames = $this->getEnum();
|
||||||
|
return reset($subClassNames);
|
||||||
|
}
|
||||||
|
}
|
27
src/ORM/FieldType/DBClassNameVarchar.php
Normal file
27
src/ORM/FieldType/DBClassNameVarchar.php
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ORM\FieldType;
|
||||||
|
|
||||||
|
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An alternative to DBClassName that stores the class name as a varchar instead of an enum
|
||||||
|
* This will use more disk space, though will prevent issues with long dev/builds on
|
||||||
|
* very large database tables when a ALTER TABLE queries are required to update the enum.
|
||||||
|
*
|
||||||
|
* Use the following config to use this class in your project:
|
||||||
|
*
|
||||||
|
* <code>
|
||||||
|
* SilverStripe\ORM\DataObject:
|
||||||
|
* fixed_fields:
|
||||||
|
* ClassName: DBClassNameVarchar
|
||||||
|
*
|
||||||
|
* SilverStripe\ORM\FieldType\DBPolymorphicForeignKey:
|
||||||
|
* composite_db:
|
||||||
|
* Class: DBClassNameVarchar('SilverStripe\ORM\DataObject', ['index' => false])
|
||||||
|
* </code>
|
||||||
|
*/
|
||||||
|
class DBClassNameVarchar extends DBVarchar
|
||||||
|
{
|
||||||
|
use DBClassNameTrait;
|
||||||
|
}
|
35
tests/php/ORM/DBClassNameVarcharTest.php
Normal file
35
tests/php/ORM/DBClassNameVarcharTest.php
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace SilverStripe\ORM\Tests;
|
||||||
|
|
||||||
|
use SilverStripe\Core\Config\Config;
|
||||||
|
use SilverStripe\ORM\DataObject;
|
||||||
|
use SilverStripe\ORM\FieldType\DBClassNameVarchar;
|
||||||
|
use SilverStripe\ORM\FieldType\DBVarchar;
|
||||||
|
use SilverStripe\ORM\Tests\DataObjectSchemaTest\HasFields;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* These unit tests test will change DBClassName to a varchar column
|
||||||
|
* and then test that the tests in DataObjectSchemaTest still pass
|
||||||
|
*
|
||||||
|
* There's also a test that a ClassName of an arbitary DataObject is a Varchar
|
||||||
|
*/
|
||||||
|
class DBClassNameVarcharTest extends DataObjectSchemaTest
|
||||||
|
{
|
||||||
|
public function setup(): void
|
||||||
|
{
|
||||||
|
parent::setup();
|
||||||
|
$fixedFields = Config::inst()->get(DataObject::class, 'fixed_fields');
|
||||||
|
$fixedFields['ClassName'] = 'DBClassNameVarchar';
|
||||||
|
Config::modify()->set(DataObject::class, 'fixed_fields', $fixedFields);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testVarcharType(): void
|
||||||
|
{
|
||||||
|
/** @var DataObject $obj */
|
||||||
|
$obj = HasFields::create();
|
||||||
|
$class = get_class($obj->dbObject('ClassName'));
|
||||||
|
$this->assertSame(DBClassNameVarchar::class, $class);
|
||||||
|
$this->assertTrue(is_a($class, DBVarchar::class, true));
|
||||||
|
}
|
||||||
|
}
|
@ -200,7 +200,9 @@ class DataObjectSchemaGenerationTest extends SapphireTest
|
|||||||
DBEnum::flushCache();
|
DBEnum::flushCache();
|
||||||
$do1 = new TestObject();
|
$do1 = new TestObject();
|
||||||
$fields = $schema->databaseFields(TestObject::class, false);
|
$fields = $schema->databaseFields(TestObject::class, false);
|
||||||
$this->assertEquals("DBClassName", $fields['ClassName']);
|
// May be overridden from DBClassName to DBClassNameVarchar by config
|
||||||
|
$expectedClassName = DataObject::config()->get('fixed_fields')['ClassName'];
|
||||||
|
$this->assertEquals($expectedClassName, $fields['ClassName']);
|
||||||
$this->assertEquals(
|
$this->assertEquals(
|
||||||
[
|
[
|
||||||
TestObject::class,
|
TestObject::class,
|
||||||
|
@ -165,6 +165,11 @@ class DataObjectSchemaTest extends SapphireTest
|
|||||||
public function testFieldSpec(array $args, array $expected): void
|
public function testFieldSpec(array $args, array $expected): void
|
||||||
{
|
{
|
||||||
$schema = DataObject::getSchema();
|
$schema = DataObject::getSchema();
|
||||||
|
// May be overridden from DBClassName to DBClassNameVarchar by config
|
||||||
|
$expectedClassName = DataObject::config()->get('fixed_fields')['ClassName'];
|
||||||
|
if (array_key_exists('ClassName', $expected) && $expectedClassName !== 'DBClassName') {
|
||||||
|
$expected['ClassName'] = str_replace('DBClassName', $expectedClassName, $expected['ClassName']);
|
||||||
|
}
|
||||||
$this->assertEquals($expected, $schema->fieldSpecs(...$args));
|
$this->assertEquals($expected, $schema->fieldSpecs(...$args));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user