2011-10-29 14:04:17 +13:00
|
|
|
<?php
|
|
|
|
|
2016-06-15 16:03:16 +12:00
|
|
|
namespace SilverStripe\ORM;
|
|
|
|
|
|
|
|
use ArrayAccess;
|
|
|
|
use Countable;
|
2018-03-17 15:05:40 +00:00
|
|
|
use Iterator;
|
2016-06-15 16:03:16 +12:00
|
|
|
use IteratorAggregate;
|
|
|
|
|
2011-10-29 14:04:17 +13:00
|
|
|
/**
|
|
|
|
* Creates a map from an SS_List by defining a key column and a value column.
|
|
|
|
*/
|
2016-11-29 12:31:16 +13:00
|
|
|
class Map implements ArrayAccess, Countable, IteratorAggregate
|
|
|
|
{
|
|
|
|
|
|
|
|
protected $list, $keyField, $valueField;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @see Map::unshift()
|
|
|
|
*
|
|
|
|
* @var array $firstItems
|
|
|
|
*/
|
2020-04-20 18:58:09 +01:00
|
|
|
protected $firstItems = [];
|
2016-11-29 12:31:16 +13:00
|
|
|
|
|
|
|
/**
|
|
|
|
* @see Map::push()
|
|
|
|
*
|
|
|
|
* @var array $lastItems
|
|
|
|
*/
|
2020-04-20 18:58:09 +01:00
|
|
|
protected $lastItems = [];
|
2016-11-29 12:31:16 +13:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Construct a new map around an SS_list.
|
|
|
|
*
|
|
|
|
* @param SS_List $list The list to build a map from
|
|
|
|
* @param string $keyField The field to use as the key of each map entry
|
|
|
|
* @param string $valueField The field to use as the value of each map entry
|
|
|
|
*/
|
|
|
|
public function __construct(SS_List $list, $keyField = "ID", $valueField = "Title")
|
|
|
|
{
|
|
|
|
$this->list = $list;
|
|
|
|
$this->keyField = $keyField;
|
|
|
|
$this->valueField = $valueField;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the key field for this map.
|
|
|
|
*
|
|
|
|
* @var string $keyField
|
|
|
|
*/
|
|
|
|
public function setKeyField($keyField)
|
|
|
|
{
|
|
|
|
$this->keyField = $keyField;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set the value field for this map.
|
|
|
|
*
|
|
|
|
* @var string $valueField
|
|
|
|
*/
|
|
|
|
public function setValueField($valueField)
|
|
|
|
{
|
|
|
|
$this->valueField = $valueField;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return an array equivalent to this map.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function toArray()
|
|
|
|
{
|
2020-04-20 18:58:09 +01:00
|
|
|
$array = [];
|
2016-11-29 12:31:16 +13:00
|
|
|
|
|
|
|
foreach ($this as $k => $v) {
|
|
|
|
$array[$k] = $v;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $array;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return all the keys of this map.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function keys()
|
|
|
|
{
|
2022-04-14 13:12:59 +12:00
|
|
|
return array_keys($this->toArray() ?? []);
|
2016-11-29 12:31:16 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Return all the values of this map.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function values()
|
|
|
|
{
|
2022-04-14 13:12:59 +12:00
|
|
|
return array_values($this->toArray() ?? []);
|
2016-11-29 12:31:16 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Unshift an item onto the start of the map.
|
|
|
|
*
|
|
|
|
* Stores the value in addition to the {@link DataQuery} for the map.
|
|
|
|
*
|
|
|
|
* @var string $key
|
|
|
|
* @var mixed $value
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function unshift($key, $value)
|
|
|
|
{
|
|
|
|
$oldItems = $this->firstItems;
|
2020-04-20 18:58:09 +01:00
|
|
|
$this->firstItems = [
|
2016-11-29 12:31:16 +13:00
|
|
|
$key => $value
|
2020-04-20 18:58:09 +01:00
|
|
|
];
|
2016-11-29 12:31:16 +13:00
|
|
|
|
|
|
|
if ($oldItems) {
|
|
|
|
$this->firstItems = $this->firstItems + $oldItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pushes an item onto the end of the map.
|
|
|
|
*
|
|
|
|
* @var string $key
|
|
|
|
* @var mixed $value
|
|
|
|
* @return $this
|
|
|
|
*/
|
|
|
|
public function push($key, $value)
|
|
|
|
{
|
|
|
|
$oldItems = $this->lastItems;
|
|
|
|
|
2020-04-20 18:58:09 +01:00
|
|
|
$this->lastItems = [
|
2016-11-29 12:31:16 +13:00
|
|
|
$key => $value
|
2020-04-20 18:58:09 +01:00
|
|
|
];
|
2016-11-29 12:31:16 +13:00
|
|
|
|
|
|
|
if ($oldItems) {
|
|
|
|
$this->lastItems = $this->lastItems + $oldItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
return $this;
|
|
|
|
}
|
|
|
|
|
|
|
|
// ArrayAccess
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var string $key
|
|
|
|
*
|
|
|
|
* @return boolean
|
|
|
|
*/
|
2022-04-14 13:12:59 +12:00
|
|
|
#[\ReturnTypeWillChange]
|
2016-11-29 12:31:16 +13:00
|
|
|
public function offsetExists($key)
|
|
|
|
{
|
|
|
|
if (isset($this->firstItems[$key])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($this->lastItems[$key])) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
$record = $this->list->find($this->keyField, $key);
|
|
|
|
|
|
|
|
return $record != null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* @var string $key
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2022-04-14 13:12:59 +12:00
|
|
|
#[\ReturnTypeWillChange]
|
2016-11-29 12:31:16 +13:00
|
|
|
public function offsetGet($key)
|
|
|
|
{
|
|
|
|
if (isset($this->firstItems[$key])) {
|
|
|
|
return $this->firstItems[$key];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($this->lastItems[$key])) {
|
|
|
|
return $this->lastItems[$key];
|
|
|
|
}
|
|
|
|
|
|
|
|
$record = $this->list->find($this->keyField, $key);
|
|
|
|
|
|
|
|
if ($record) {
|
|
|
|
$col = $this->valueField;
|
|
|
|
|
|
|
|
return $record->$col;
|
|
|
|
}
|
|
|
|
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Sets a value in the map by a given key that has been set via
|
|
|
|
* {@link Map::push()} or {@link Map::unshift()}
|
|
|
|
*
|
|
|
|
* Keys in the map cannot be set since these values are derived from a
|
|
|
|
* {@link DataQuery} instance. In this case, use {@link Map::toArray()}
|
|
|
|
* and manipulate the resulting array.
|
|
|
|
*
|
|
|
|
* @var string $key
|
|
|
|
* @var mixed $value
|
|
|
|
*/
|
2022-04-14 13:12:59 +12:00
|
|
|
#[\ReturnTypeWillChange]
|
2016-11-29 12:31:16 +13:00
|
|
|
public function offsetSet($key, $value)
|
|
|
|
{
|
|
|
|
if (isset($this->firstItems[$key])) {
|
|
|
|
$this->firstItems[$key] = $value;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($this->lastItems[$key])) {
|
|
|
|
$this->lastItems[$key] = $value;
|
|
|
|
}
|
|
|
|
|
2020-09-24 17:09:37 -07:00
|
|
|
throw new \BadMethodCallException('Map is read-only. Please use $map->push($key, $value) to append values');
|
2016-11-29 12:31:16 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Removes a value in the map by a given key which has been added to the map
|
|
|
|
* via {@link Map::push()} or {@link Map::unshift()}
|
|
|
|
*
|
|
|
|
* Keys in the map cannot be unset since these values are derived from a
|
|
|
|
* {@link DataQuery} instance. In this case, use {@link Map::toArray()}
|
|
|
|
* and manipulate the resulting array.
|
|
|
|
*
|
|
|
|
* @var string $key
|
|
|
|
* @var mixed $value
|
|
|
|
*/
|
2022-04-14 13:12:59 +12:00
|
|
|
#[\ReturnTypeWillChange]
|
2016-11-29 12:31:16 +13:00
|
|
|
public function offsetUnset($key)
|
|
|
|
{
|
|
|
|
if (isset($this->firstItems[$key])) {
|
|
|
|
unset($this->firstItems[$key]);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (isset($this->lastItems[$key])) {
|
|
|
|
unset($this->lastItems[$key]);
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-09-24 17:09:37 -07:00
|
|
|
throw new \BadMethodCallException(
|
|
|
|
'Map is read-only. Unset cannot be called on keys derived from the DataQuery'
|
2016-11-29 12:31:16 +13:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2018-03-17 15:05:40 +00:00
|
|
|
* Returns an Iterator for iterating over the complete set
|
2016-11-29 12:31:16 +13:00
|
|
|
* of items in the map.
|
|
|
|
*
|
2018-03-17 15:05:40 +00:00
|
|
|
* Satisfies the IteratorAggregate interface.
|
|
|
|
*/
|
|
|
|
public function getIterator(): Iterator
|
|
|
|
{
|
|
|
|
$keyField = $this->keyField;
|
|
|
|
$valueField = $this->valueField;
|
|
|
|
|
|
|
|
foreach ($this->firstItems as $k => $v) {
|
|
|
|
yield $k => $v;
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->list as $record) {
|
|
|
|
if (isset($this->firstItems[$record->$keyField])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (isset($this->lastItems[$record->$keyField])) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
yield $this->extractValue($record, $this->keyField) => $this->extractValue($record, $this->valueField);
|
|
|
|
}
|
|
|
|
|
|
|
|
foreach ($this->lastItems as $k => $v) {
|
|
|
|
yield $k => $v;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extracts a value from an item in the list, where the item is either an
|
|
|
|
* object or array.
|
2016-11-29 12:31:16 +13:00
|
|
|
*
|
2018-03-17 15:05:40 +00:00
|
|
|
* @param array|object $item
|
|
|
|
* @param string $key
|
|
|
|
* @return mixed
|
2016-11-29 12:31:16 +13:00
|
|
|
*/
|
2018-03-17 15:05:40 +00:00
|
|
|
protected function extractValue($item, $key)
|
2016-11-29 12:31:16 +13:00
|
|
|
{
|
2018-03-17 15:05:40 +00:00
|
|
|
if (is_object($item)) {
|
|
|
|
if (method_exists($item, 'hasMethod') && $item->hasMethod($key)) {
|
|
|
|
return $item->{$key}();
|
|
|
|
}
|
|
|
|
return $item->{$key};
|
|
|
|
} else {
|
|
|
|
if (array_key_exists($key, $item)) {
|
|
|
|
return $item[$key];
|
|
|
|
}
|
|
|
|
}
|
2016-11-29 12:31:16 +13:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns the count of items in the list including the additional items set
|
|
|
|
* through {@link Map::push()} and {@link Map::unshift}.
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
2022-04-14 13:12:59 +12:00
|
|
|
#[\ReturnTypeWillChange]
|
2016-11-29 12:31:16 +13:00
|
|
|
public function count()
|
|
|
|
{
|
|
|
|
return $this->list->count() +
|
2022-04-14 13:12:59 +12:00
|
|
|
count($this->firstItems ?? []) +
|
|
|
|
count($this->lastItems ?? []);
|
2016-11-29 12:31:16 +13:00
|
|
|
}
|
2011-10-29 14:04:17 +13:00
|
|
|
}
|