mirror of
https://github.com/silverstripe/silverstripe-framework
synced 2024-08-28 13:36:12 +02:00
API: Deprecate SS_Datetime. The DBField subclasses are have all been renamed to start with “DB” and be in the SilverStripe\Model\FieldType namespace. To keep DataObject definitions concise, the original short variations of their names are preserved as service definitions. Most of the field generation code doesn’t need to change, but where field classes are referenced directly, changes will be needed. SS_Datetime, which is commonly referenced outside the model system itself, has been preserved as a subclass of DBDatetime. This has been marked as deprecated and can be removed in SilverStripe 5. A few places that referred to $db and $casting values weren’t using the Injector to instantiate the relevant classes. This meant that the remapping we have created as part of moving classes into a namespace didn’t work.
217 lines
5.1 KiB
PHP
217 lines
5.1 KiB
PHP
<?php
|
|
|
|
namespace SilverStripe\Model\FieldType;
|
|
|
|
use DB;
|
|
use DataObject;
|
|
use ClassInfo;
|
|
use Config;
|
|
|
|
/**
|
|
* Represents a classname selector, which respects obsolete clasess.
|
|
*
|
|
* @package framework
|
|
* @subpackage model
|
|
*/
|
|
class DBClassName extends DBEnum {
|
|
|
|
/**
|
|
* 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;
|
|
|
|
/**
|
|
* Classname spec cache for obsolete classes. The top level keys are the table, each of which contains
|
|
* nested arrays with keys mapped to field names. The values of the lowest level array are the classnames
|
|
*
|
|
* @var array
|
|
*/
|
|
protected static $classname_cache = array();
|
|
|
|
/**
|
|
* Clear all cached classname specs. It's necessary to clear all cached subclassed names
|
|
* for any classes if a new class manifest is generated.
|
|
*/
|
|
public static function clear_classname_cache() {
|
|
self::$classname_cache = array();
|
|
}
|
|
|
|
/**
|
|
* Create a new DBClassName field
|
|
*
|
|
* @param string $name Name of field
|
|
* @param string|null $baseClass Optional base class to limit selections
|
|
*/
|
|
public function __construct($name = null, $baseClass = null) {
|
|
$this->setBaseClass($baseClass);
|
|
parent::__construct($name);
|
|
}
|
|
|
|
/**
|
|
* @return void
|
|
*/
|
|
public function requireField() {
|
|
$parts = array(
|
|
'datatype' => 'enum',
|
|
'enums' => $this->getEnumObsolete(),
|
|
'character set' => 'utf8',
|
|
'collate' => 'utf8_general_ci',
|
|
'default' => $this->getDefault(),
|
|
'table' => $this->getTable(),
|
|
'arrayValue' => $this->arrayValue
|
|
);
|
|
|
|
$values = array(
|
|
'type' => 'enum',
|
|
'parts' => $parts
|
|
);
|
|
|
|
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
|
|
if($this->record) {
|
|
return ClassInfo::baseDataClass($this->record);
|
|
}
|
|
// During dev/build only the table is assigned
|
|
$tableClass = $this->getClassNameFromTable($this->getTable());
|
|
if($tableClass) {
|
|
return $tableClass;
|
|
}
|
|
// Fallback to global default
|
|
return 'DataObject';
|
|
}
|
|
|
|
/**
|
|
* Assign the base class
|
|
*
|
|
* @param string $baseClass
|
|
* @return $this
|
|
*/
|
|
public function setBaseClass($baseClass) {
|
|
$this->baseClass = $baseClass;
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Given a table name, find the base data class
|
|
*
|
|
* @param string $table
|
|
* @return string|null
|
|
*/
|
|
protected function getClassNameFromTable($table) {
|
|
if(empty($table)) {
|
|
return null;
|
|
}
|
|
$class = ClassInfo::baseDataClass($table);
|
|
if($class) {
|
|
return $class;
|
|
}
|
|
// If there is no class for this table, strip table modifiers (_Live / _versions) off the end
|
|
if(preg_match('/^(?<class>.+)(_[^_]+)$/i', $table, $matches)) {
|
|
return $this->getClassNameFromTable($matches['class']);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Get list of classnames that should be selectable
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getEnum() {
|
|
$classNames = ClassInfo::subclassesFor($this->getBaseClass());
|
|
unset($classNames['DataObject']);
|
|
return $classNames;
|
|
}
|
|
|
|
/**
|
|
* Get the list of classnames, including obsolete classes.
|
|
*
|
|
* If table or name are not set, or if it is not a valid field on the given table,
|
|
* then only known classnames are returned.
|
|
*
|
|
* Values cached in this method can be cleared via `DBClassName::clear_classname_cache();`
|
|
*
|
|
* @return array
|
|
*/
|
|
public function getEnumObsolete() {
|
|
// Without a table or field specified, we can only retrieve known classes
|
|
$table = $this->getTable();
|
|
$name = $this->getName();
|
|
if(empty($table) || empty($name)) {
|
|
return $this->getEnum();
|
|
}
|
|
|
|
// Ensure the table level cache exists
|
|
if(empty(self::$classname_cache[$table])) {
|
|
self::$classname_cache[$table] = array();
|
|
}
|
|
|
|
// Check existing cache
|
|
if(!empty(self::$classname_cache[$table][$name])) {
|
|
return self::$classname_cache[$table][$name];
|
|
}
|
|
|
|
// Get all class names
|
|
$classNames = $this->getEnum();
|
|
if(DB::get_schema()->hasField($table, $name)) {
|
|
$existing = DB::query("SELECT DISTINCT \"{$name}\" FROM \"{$table}\"")->column();
|
|
$classNames = array_unique(array_merge($classNames, $existing));
|
|
}
|
|
|
|
// Cache and return
|
|
self::$classname_cache[$table][$name] = $classNames;
|
|
return $classNames;
|
|
}
|
|
|
|
public function setValue($value, $record = null, $markChanged = true) {
|
|
parent::setValue($value, $record, $markChanged);
|
|
|
|
if($record instanceof DataObject) {
|
|
$this->record = $record;
|
|
}
|
|
}
|
|
|
|
public function getDefault() {
|
|
// Check for assigned default
|
|
$default = parent::getDefault();
|
|
if($default) {
|
|
return $default;
|
|
}
|
|
|
|
// 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
|
|
$enum = $this->getEnum();
|
|
return reset($enum);
|
|
}
|
|
}
|