silverstripe-framework/src/ORM/FieldType/DBEnum.php

276 lines
7.6 KiB
PHP

<?php
namespace SilverStripe\ORM\FieldType;
use SilverStripe\Core\Config\Config;
use SilverStripe\Forms\DropdownField;
use SilverStripe\ORM\ArrayLib;
use SilverStripe\ORM\DB;
/**
* Class Enum represents an enumeration of a set of strings.
*
* See {@link DropdownField} for a {@link FormField} to select enum values.
*/
class DBEnum extends DBString
{
/**
* List of enum values
*
* @var array
*/
protected $enum = [];
/**
* Default value
*
* @var string|null
*/
protected $default = null;
private static $default_search_filter_class = 'ExactMatchFilter';
/**
* Internal cache for obsolete enum values. 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 enum values
*
* @var array
*/
protected static $enum_cache = [];
/**
* Clear all cached enum values.
*/
public static function flushCache()
{
self::$enum_cache = [];
}
/**
* Create a new Enum field, which is a value within a defined set, with an optional default.
*
* Example field specification strings:
*
* <code>
* "MyField" => "Enum('Val1, Val2, Val3')" // First item 'Val1' is default implicitly
* "MyField" => "Enum('Val1, Val2, Val3', 'Val2')" // 'Val2' is default explicitly
* "MyField" => "Enum('Val1, Val2, Val3', null)" // Force empty (no) default
* "MyField" => "Enum(['Val1', 'Val2', 'Val3'], 'Val1')" // Supports array notation as well
* </code>
*
* @param string $name
* @param string|array $enum A string containing a comma separated list of options or an array of Vals.
* @param string|int|null $default The default option, which is either NULL or one of the items in the enumeration.
* If passing in an integer (non-string) it will default to the index of that item in the list.
* Set to null or empty string to allow empty values
* @param array $options Optional parameters for this DB field
*/
public function __construct($name = null, $enum = null, $default = 0, $options = [])
{
if ($enum) {
$this->setEnum($enum);
$enum = $this->getEnum();
// If there's a default, then use this
if ($default && !is_int($default)) {
if (in_array($default, $enum)) {
$this->setDefault($default);
} else {
user_error(
"Enum::__construct() The default value '$default' does not match any item in the enumeration",
E_USER_ERROR
);
}
} elseif (is_int($default) && $default < count($enum)) {
// Set to specified index if given
$this->setDefault($enum[$default]);
} else {
// Set to null if specified
$this->setDefault(null);
}
}
parent::__construct($name, $options);
}
/**
* @return void
*/
public function requireField()
{
$charset = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'charset');
$collation = Config::inst()->get('SilverStripe\ORM\Connect\MySQLDatabase', 'collation');
$parts = [
'datatype' => 'enum',
'enums' => $this->getEnumObsolete(),
'character set' => $charset,
'collate' => $collation,
'default' => $this->getDefault(),
'table' => $this->getTable(),
'arrayValue' => $this->arrayValue
];
$values = [
'type' => 'enum',
'parts' => $parts
];
DB::require_field($this->getTable(), $this->getName(), $values);
}
/**
* Return a dropdown field suitable for editing this field.
*
* @param string $title
* @param string $name
* @param bool $hasEmpty
* @param string $value
* @param string $emptyString
* @return DropdownField
*/
public function formField($title = null, $name = null, $hasEmpty = false, $value = "", $emptyString = null)
{
if (!$title) {
$title = $this->getName();
}
if (!$name) {
$name = $this->getName();
}
$field = new DropdownField($name, $title, $this->enumValues(false), $value);
if ($hasEmpty) {
$field->setEmptyString($emptyString);
}
return $field;
}
public function scaffoldFormField($title = null, $params = null)
{
return $this->formField($title);
}
/**
* @param string
*
* @return DropdownField
*/
public function scaffoldSearchField($title = null)
{
$anyText = _t('SilverStripe\\ORM\\FieldType\\DBEnum.ANY', 'Any');
return $this->formField($title, null, true, '', "($anyText)");
}
/**
* Returns the values of this enum as an array, suitable for insertion into
* a {@link DropdownField}
*
* @param boolean
*
* @return array
*/
public function enumValues($hasEmpty = false)
{
return ($hasEmpty)
? array_merge(['' => ''], ArrayLib::valuekey($this->getEnum()))
: ArrayLib::valuekey($this->getEnum());
}
/**
* Get list of enum values
*
* @return array
*/
public function getEnum()
{
return $this->enum;
}
/**
* Get the list of enum values, including obsolete values still present in the database
*
* If table or name are not set, or if it is not a valid field on the given table,
* then only known enum values are returned.
*
* Values cached in this method can be cleared via `DBEnum::flushCache();`
*
* @return array
*/
public function getEnumObsolete()
{
// Without a table or field specified, we can only retrieve known enum values
$table = $this->getTable();
$name = $this->getName();
if (empty($table) || empty($name)) {
return $this->getEnum();
}
// Ensure the table level cache exists
if (empty(self::$enum_cache[$table])) {
self::$enum_cache[$table] = [];
}
// Check existing cache
if (!empty(self::$enum_cache[$table][$name])) {
return self::$enum_cache[$table][$name];
}
// Get all enum values
$enumValues = $this->getEnum();
if (DB::get_schema()->hasField($table, $name)) {
$existing = DB::query("SELECT DISTINCT \"{$name}\" FROM \"{$table}\"")->column();
$enumValues = array_unique(array_merge($enumValues, $existing));
}
// Cache and return
self::$enum_cache[$table][$name] = $enumValues;
return $enumValues;
}
/**
* Set enum options
*
* @param string|array $enum
* @return $this
*/
public function setEnum($enum)
{
if (!is_array($enum)) {
$enum = preg_split(
'/\s*,\s*/',
// trim commas only if they are on the right with a newline following it
ltrim(preg_replace('/,\s*\n\s*$/', '', $enum))
);
}
$this->enum = array_values($enum);
return $this;
}
/**
* Get default vwalue
*
* @return string|null
*/
public function getDefault()
{
return $this->default;
}
/**
* Set default value
*
* @param string $default
* @return $this
*/
public function setDefault($default)
{
$this->default = $default;
$this->setDefaultValue($default);
return $this;
}
}