2015-02-13 05:35:39 +01:00
|
|
|
<?php
|
|
|
|
|
2016-08-19 00:51:35 +02:00
|
|
|
namespace SilverStripe\Forms;
|
|
|
|
|
2016-06-15 06:03:16 +02:00
|
|
|
use SilverStripe\ORM\SS_List;
|
2016-09-09 08:43:05 +02:00
|
|
|
use SilverStripe\ORM\Map;
|
2016-08-19 00:51:35 +02:00
|
|
|
use ArrayAccess;
|
2016-06-15 06:03:16 +02:00
|
|
|
|
2015-02-13 05:35:39 +01:00
|
|
|
/**
|
|
|
|
* Represents a field that allows users to select one or more items from a list
|
|
|
|
*/
|
2016-11-29 00:31:16 +01:00
|
|
|
abstract class SelectField extends FormField
|
|
|
|
{
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Associative or numeric array of all dropdown items,
|
|
|
|
* with array key as the submitted field value, and the array value as a
|
|
|
|
* natural language description shown in the interface element.
|
|
|
|
*
|
|
|
|
* @var array|ArrayAccess
|
|
|
|
*/
|
|
|
|
protected $source;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* The values for items that should be disabled (greyed out) in the dropdown.
|
|
|
|
* This is a non-associative array
|
|
|
|
*
|
|
|
|
* @var array
|
|
|
|
*/
|
2020-04-20 19:58:09 +02:00
|
|
|
protected $disabledItems = [];
|
2016-11-29 00:31:16 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @param string $name The field name
|
|
|
|
* @param string $title The field title
|
|
|
|
* @param array|ArrayAccess $source A map of the dropdown items
|
|
|
|
* @param mixed $value The current value
|
|
|
|
*/
|
2020-04-20 19:58:09 +02:00
|
|
|
public function __construct($name, $title = null, $source = [], $value = null)
|
2016-11-29 00:31:16 +01:00
|
|
|
{
|
|
|
|
$this->setSource($source);
|
|
|
|
if (!isset($title)) {
|
|
|
|
$title = $name;
|
|
|
|
}
|
|
|
|
parent::__construct($name, $title, $value);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getSchemaStateDefaults()
|
|
|
|
{
|
|
|
|
$data = parent::getSchemaStateDefaults();
|
|
|
|
$disabled = $this->getDisabledItems();
|
|
|
|
|
|
|
|
// Add options to 'data'
|
|
|
|
$source = $this->getSource();
|
|
|
|
$data['source'] = (is_array($source))
|
|
|
|
? array_map(function ($value, $title) use ($disabled) {
|
|
|
|
return [
|
|
|
|
'value' => $value,
|
|
|
|
'title' => $title,
|
|
|
|
'disabled' => in_array($value, $disabled),
|
|
|
|
];
|
|
|
|
}, array_keys($source), $source)
|
|
|
|
: [];
|
|
|
|
|
|
|
|
return $data;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mark certain elements as disabled,
|
|
|
|
* regardless of the {@link setDisabled()} settings.
|
|
|
|
*
|
|
|
|
* These should be items that appear in the source list, not in addition to them.
|
|
|
|
*
|
|
|
|
* @param array|SS_List $items Collection of values or items
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setDisabledItems($items)
|
|
|
|
{
|
|
|
|
$this->disabledItems = $this->getListValues($items);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Non-associative list of disabled item values
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getDisabledItems()
|
|
|
|
{
|
|
|
|
return $this->disabledItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Check if the given value is disabled
|
|
|
|
*
|
|
|
|
* @param string $value
|
|
|
|
* @return bool
|
|
|
|
*/
|
|
|
|
protected function isDisabledValue($value)
|
|
|
|
{
|
|
|
|
if ($this->isDisabled()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return in_array($value, $this->getDisabledItems());
|
|
|
|
}
|
|
|
|
|
|
|
|
public function getAttributes()
|
|
|
|
{
|
|
|
|
return array_merge(
|
|
|
|
parent::getAttributes(),
|
2020-04-20 19:58:09 +02:00
|
|
|
['type' => null, 'value' => null]
|
2016-11-29 00:31:16 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Retrieve all values in the source array
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
protected function getSourceValues()
|
|
|
|
{
|
|
|
|
return array_keys($this->getSource());
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets all valid values for this field.
|
|
|
|
*
|
|
|
|
* Does not include "empty" value if specified
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function getValidValues()
|
|
|
|
{
|
|
|
|
$valid = array_diff($this->getSourceValues(), $this->getDisabledItems());
|
|
|
|
// Renumber indexes from 0
|
|
|
|
return array_values($valid);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Gets the source array not including any empty default values.
|
|
|
|
*
|
|
|
|
* @return array|ArrayAccess
|
|
|
|
*/
|
|
|
|
public function getSource()
|
|
|
|
{
|
|
|
|
return $this->source;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the source for this list
|
|
|
|
*
|
|
|
|
* @param mixed $source
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function setSource($source)
|
|
|
|
{
|
|
|
|
$this->source = $this->getListMap($source);
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a list of values, extract the associative map of id => title
|
|
|
|
*
|
|
|
|
* @param mixed $source
|
|
|
|
* @return array Associative array of ids and titles
|
|
|
|
*/
|
|
|
|
protected function getListMap($source)
|
|
|
|
{
|
|
|
|
// Extract source as an array
|
|
|
|
if ($source instanceof SS_List) {
|
|
|
|
$source = $source->map();
|
|
|
|
}
|
|
|
|
if ($source instanceof Map) {
|
|
|
|
$source = $source->toArray();
|
|
|
|
}
|
|
|
|
if (!is_array($source) && !($source instanceof ArrayAccess)) {
|
2020-09-25 02:09:37 +02:00
|
|
|
throw new \InvalidArgumentException('$source passed in as invalid type');
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
return $source;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Given a non-array collection, extract the non-associative list of ids
|
|
|
|
* If passing as array, treat the array values (not the keys) as the ids
|
|
|
|
*
|
|
|
|
* @param mixed $values
|
|
|
|
* @return array Non-associative list of values
|
|
|
|
*/
|
|
|
|
protected function getListValues($values)
|
|
|
|
{
|
|
|
|
// Empty values
|
|
|
|
if (empty($values)) {
|
2020-04-20 19:58:09 +02:00
|
|
|
return [];
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
// Direct array
|
|
|
|
if (is_array($values)) {
|
|
|
|
return array_values($values);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Extract lists
|
|
|
|
if ($values instanceof SS_List) {
|
|
|
|
return $values->column('ID');
|
|
|
|
}
|
|
|
|
|
2020-04-20 19:58:09 +02:00
|
|
|
return [trim($values)];
|
2016-11-29 00:31:16 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Determine if the current value of this field matches the given option value
|
|
|
|
*
|
|
|
|
* @param mixed $dataValue The value as extracted from the source of this field (or empty value if available)
|
|
|
|
* @param mixed $userValue The value as submitted by the user
|
|
|
|
* @return boolean True if the selected value matches the given option value
|
|
|
|
*/
|
|
|
|
public function isSelectedValue($dataValue, $userValue)
|
|
|
|
{
|
|
|
|
if ($dataValue === $userValue) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Allow null to match empty strings
|
|
|
|
if ($dataValue === '' && $userValue === null) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Safety check against casting arrays as strings in PHP>5.4
|
|
|
|
if (is_array($dataValue) || is_array($userValue)) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For non-falsey values do loose comparison
|
|
|
|
if ($dataValue) {
|
|
|
|
return $dataValue == $userValue;
|
|
|
|
}
|
|
|
|
|
|
|
|
// For empty values, use string comparison to perform visible value match
|
|
|
|
return ((string) $dataValue) === ((string) $userValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
public function performReadonlyTransformation()
|
|
|
|
{
|
|
|
|
/** @var LookupField $field */
|
|
|
|
$field = $this->castedCopy('SilverStripe\\Forms\\LookupField');
|
|
|
|
$field->setSource($this->getSource());
|
|
|
|
$field->setReadonly(true);
|
|
|
|
|
|
|
|
return $field;
|
|
|
|
}
|
|
|
|
|
|
|
|
public function performDisabledTransformation()
|
|
|
|
{
|
|
|
|
$clone = clone $this;
|
|
|
|
$clone->setDisabled(true);
|
|
|
|
return $clone;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns another instance of this field, but "cast" to a different class.
|
|
|
|
*
|
|
|
|
* @see FormField::castedCopy()
|
|
|
|
*
|
|
|
|
* @param String $classOrCopy
|
|
|
|
* @return FormField
|
|
|
|
*/
|
|
|
|
public function castedCopy($classOrCopy)
|
|
|
|
{
|
|
|
|
$field = parent::castedCopy($classOrCopy);
|
|
|
|
if ($field instanceof SelectField) {
|
|
|
|
$field->setSource($this->getSource());
|
|
|
|
}
|
|
|
|
return $field;
|
|
|
|
}
|
2015-02-13 05:35:39 +01:00
|
|
|
}
|