2011-05-03 16:06:42 +02:00
|
|
|
<?php
|
|
|
|
/**
|
|
|
|
* A list object that wraps around an array of objects or arrays.
|
|
|
|
*
|
|
|
|
* @package sapphire
|
|
|
|
* @subpackage model
|
|
|
|
*/
|
|
|
|
class ArrayList extends ViewableData implements SS_List {
|
|
|
|
|
|
|
|
/**
|
2011-12-07 02:35:30 +01:00
|
|
|
* Holds the items in the list
|
|
|
|
*
|
2011-05-03 16:06:42 +02:00
|
|
|
* @var array
|
|
|
|
*/
|
2011-05-05 16:30:45 +02:00
|
|
|
protected $items;
|
2011-12-07 02:35:30 +01:00
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Synonym of the constructor. Can be chained with literate methods.
|
|
|
|
* ArrayList::create("SiteTree")->sort("Title") is legal, but
|
|
|
|
* new ArrayList("SiteTree")->sort("Title") is not.
|
|
|
|
*
|
|
|
|
* @param array $items - an initial array to fill this object with
|
|
|
|
*/
|
|
|
|
public static function create(array $items = array()) {
|
|
|
|
return new ArrayList($items);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
*
|
|
|
|
* @param array $items - an initial array to fill this object with
|
|
|
|
*/
|
2011-05-05 16:30:45 +02:00
|
|
|
public function __construct(array $items = array()) {
|
|
|
|
$this->items = $items;
|
2011-05-03 16:06:42 +02:00
|
|
|
parent::__construct();
|
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Return the number of items in this list
|
|
|
|
*
|
|
|
|
* @return int
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function count() {
|
2011-05-05 16:30:45 +02:00
|
|
|
return count($this->items);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Returns true if this list has items
|
|
|
|
*
|
|
|
|
* @return bool
|
|
|
|
*/
|
2011-05-05 09:48:12 +02:00
|
|
|
public function exists() {
|
|
|
|
return (bool) count($this);
|
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Returns an Iterator for this ArrayList.
|
|
|
|
* This function allows you to use ArrayList in foreach loops
|
|
|
|
*
|
|
|
|
* @return ArrayIterator
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function getIterator() {
|
2011-05-05 16:30:45 +02:00
|
|
|
return new ArrayIterator($this->items);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Return an array of the actual items that this ArrayList contains.
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function toArray() {
|
2011-05-05 16:30:45 +02:00
|
|
|
return $this->items;
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-17 00:45:45 +01:00
|
|
|
public function debug() {
|
|
|
|
$val = "<h2>" . $this->class . "</h2><ul>";
|
|
|
|
foreach($this->toNestedArray() as $item) {
|
|
|
|
$val .= "<li style=\"list-style-type: disc; margin-left: 20px\">" . Debug::text($item) . "</li>";
|
|
|
|
}
|
|
|
|
$val .= "</ul>";
|
|
|
|
return $val;
|
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Return this list as an array and every object it as an sub array as well
|
|
|
|
*
|
|
|
|
* @return array
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function toNestedArray() {
|
|
|
|
$result = array();
|
|
|
|
|
2011-05-05 16:30:45 +02:00
|
|
|
foreach ($this->items as $item) {
|
2011-05-03 16:06:42 +02:00
|
|
|
if (is_object($item)) {
|
|
|
|
if (method_exists($item, 'toMap')) {
|
|
|
|
$result[] = $item->toMap();
|
|
|
|
} else {
|
|
|
|
$result[] = (array) $item;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
$result[] = $item;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Get a sub-range of this dataobjectset as an array
|
|
|
|
*
|
|
|
|
* @param int $offset
|
|
|
|
* @param int $length
|
|
|
|
* @return ArrayList
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function getRange($offset, $length) {
|
2011-05-05 16:30:45 +02:00
|
|
|
return new ArrayList(array_slice($this->items, $offset, $length));
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Add this $item into this list
|
|
|
|
*
|
|
|
|
* @param mixed $item
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function add($item) {
|
2011-05-05 09:48:12 +02:00
|
|
|
$this->push($item);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Remove this item from this list
|
|
|
|
*
|
|
|
|
* @param mixed $item
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function remove($item) {
|
2011-05-05 16:30:45 +02:00
|
|
|
foreach ($this->items as $key => $value) {
|
|
|
|
if ($item === $value) unset($this->items[$key]);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-05 09:48:12 +02:00
|
|
|
/**
|
|
|
|
* Replaces an item in this list with another item.
|
|
|
|
*
|
|
|
|
* @param array|object $item
|
|
|
|
* @param array|object $with
|
2011-09-27 00:12:35 +02:00
|
|
|
* @return void;
|
2011-05-05 09:48:12 +02:00
|
|
|
*/
|
|
|
|
public function replace($item, $with) {
|
2011-05-05 16:30:45 +02:00
|
|
|
foreach ($this->items as $key => $candidate) {
|
2011-05-05 09:48:12 +02:00
|
|
|
if ($candidate === $item) {
|
2011-05-05 16:30:45 +02:00
|
|
|
$this->items[$key] = $with;
|
2011-05-05 09:48:12 +02:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Merges with another array or list by pushing all the items in it onto the
|
|
|
|
* end of this list.
|
|
|
|
*
|
|
|
|
* @param array|object $with
|
|
|
|
*/
|
|
|
|
public function merge($with) {
|
|
|
|
foreach ($with as $item) $this->push($item);
|
|
|
|
}
|
|
|
|
|
2011-05-05 12:53:07 +02:00
|
|
|
/**
|
|
|
|
* Removes items from this list which have a duplicate value for a certain
|
|
|
|
* field. This is especially useful when combining lists.
|
|
|
|
*
|
|
|
|
* @param string $field
|
|
|
|
*/
|
|
|
|
public function removeDuplicates($field = 'ID') {
|
|
|
|
$seen = array();
|
|
|
|
|
2011-05-05 16:30:45 +02:00
|
|
|
foreach ($this->items as $key => $item) {
|
2011-09-27 00:12:35 +02:00
|
|
|
$value = $this->extractValue($item, $field);
|
2011-05-05 12:53:07 +02:00
|
|
|
|
|
|
|
if (array_key_exists($value, $seen)) {
|
2011-05-05 16:30:45 +02:00
|
|
|
unset($this->items[$key]);
|
2011-05-05 12:53:07 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
$seen[$value] = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-05-05 09:48:12 +02:00
|
|
|
/**
|
|
|
|
* Pushes an item onto the end of this list.
|
|
|
|
*
|
|
|
|
* @param array|object $item
|
|
|
|
*/
|
|
|
|
public function push($item) {
|
2011-05-05 16:30:45 +02:00
|
|
|
$this->items[] = $item;
|
2011-05-05 09:48:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Pops the last element off the end of the list and returns it.
|
|
|
|
*
|
|
|
|
* @return array|object
|
|
|
|
*/
|
|
|
|
public function pop() {
|
2011-05-05 16:30:45 +02:00
|
|
|
return array_pop($this->items);
|
2011-05-05 09:48:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2011-12-07 02:35:30 +01:00
|
|
|
* Add an item onto the beginning of the list.
|
2011-05-05 09:48:12 +02:00
|
|
|
*
|
|
|
|
* @param array|object $item
|
|
|
|
*/
|
|
|
|
public function unshift($item) {
|
2011-05-05 16:30:45 +02:00
|
|
|
array_unshift($this->items, $item);
|
2011-05-05 09:48:12 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Shifts the item off the beginning of the list and returns it.
|
|
|
|
*
|
|
|
|
* @return array|object
|
|
|
|
*/
|
|
|
|
public function shift() {
|
2011-05-05 16:30:45 +02:00
|
|
|
return array_shift($this->items);
|
2011-05-05 09:48:12 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Returns the first item in the list
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function first() {
|
2011-05-05 16:30:45 +02:00
|
|
|
return reset($this->items);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Returns the last item in the list
|
|
|
|
*
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function last() {
|
2011-05-05 16:30:45 +02:00
|
|
|
return end($this->items);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Returns a map of this list
|
|
|
|
*
|
|
|
|
* @param type $keyfield - the 'key' field of the result array
|
|
|
|
* @param type $titlefield - the value field of the result array
|
|
|
|
* @return array
|
|
|
|
*/
|
2011-10-22 15:42:46 +02:00
|
|
|
public function map($keyfield = 'ID', $titlefield = 'Title') {
|
2011-05-03 16:06:42 +02:00
|
|
|
$map = array();
|
2011-05-05 16:30:45 +02:00
|
|
|
foreach ($this->items as $item) {
|
2011-09-27 00:12:35 +02:00
|
|
|
$map[$this->extractValue($item, $keyfield)] = $this->extractValue($item, $titlefield);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
return $map;
|
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Find the first item of this list where the given key = value
|
|
|
|
*
|
|
|
|
* @param type $key
|
|
|
|
* @param type $value
|
|
|
|
* @return type
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function find($key, $value) {
|
2011-05-05 16:30:45 +02:00
|
|
|
foreach ($this->items as $item) {
|
2011-09-27 00:12:35 +02:00
|
|
|
if ($this->extractValue($item, $key) == $value) return $item;
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Returns an array of a single field value for all items in the list.
|
|
|
|
*
|
|
|
|
* @param string $colName
|
|
|
|
* @return array
|
|
|
|
*/
|
|
|
|
public function column($colName = 'ID') {
|
2011-05-03 16:06:42 +02:00
|
|
|
$result = array();
|
2011-05-05 16:30:45 +02:00
|
|
|
foreach ($this->items as $item) {
|
2011-12-07 02:35:30 +01:00
|
|
|
$result[] = $this->extractValue($item, $colName);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
return $result;
|
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* You can always sort a ArrayList
|
|
|
|
*
|
|
|
|
* @param string $by
|
|
|
|
* @return bool
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function canSortBy($by) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-05-05 16:24:33 +02:00
|
|
|
/**
|
|
|
|
* Sorts this list by one or more fields. You can either pass in a single
|
|
|
|
* field name and direction, or a map of field names to sort directions.
|
|
|
|
*
|
|
|
|
* @param string|array $by
|
2011-09-27 00:12:35 +02:00
|
|
|
* @param string $sortDirection
|
|
|
|
* @see SS_List::sort()
|
|
|
|
* @link http://php.net/manual/en/function.array-multisort.php
|
|
|
|
* @example $list->sort('Name', 'ASC');
|
|
|
|
* @example $list->sort(array('Name'=>'ASC,'Age'=>'DESC');
|
2011-05-05 16:24:33 +02:00
|
|
|
*/
|
2011-09-27 00:12:35 +02:00
|
|
|
public function sort($by, $sortDirection = 'ASC') {
|
2011-05-03 16:06:42 +02:00
|
|
|
$sorts = array();
|
|
|
|
|
2011-09-27 00:12:35 +02:00
|
|
|
if(!is_array($by)) {
|
|
|
|
$by = array($by => $sortDirection);
|
2011-05-05 16:24:33 +02:00
|
|
|
}
|
|
|
|
|
2011-09-27 00:12:35 +02:00
|
|
|
foreach ($by as $field => $sortDirection) {
|
|
|
|
$sortDirection = strtoupper($sortDirection) == 'DESC' ? SORT_DESC : SORT_ASC;
|
|
|
|
$values = array();
|
|
|
|
foreach($this->items as $item) {
|
|
|
|
$values[] = $this->extractValue($item, $field);
|
2011-05-05 16:24:33 +02:00
|
|
|
}
|
2011-09-27 00:12:35 +02:00
|
|
|
$sorts[] = &$values;
|
|
|
|
$sorts[] = &$sortDirection;
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
2011-05-05 16:30:45 +02:00
|
|
|
$sorts[] = &$this->items;
|
2011-05-05 16:24:33 +02:00
|
|
|
call_user_func_array('array_multisort', $sorts);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Returns whether an item with $key exists
|
|
|
|
*
|
|
|
|
* @param mixed $key
|
|
|
|
* @return bool
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function offsetExists($offset) {
|
2011-05-05 16:30:45 +02:00
|
|
|
return array_key_exists($offset, $this->items);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
2011-12-07 02:35:30 +01:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Returns item stored in list with index $key
|
|
|
|
*
|
|
|
|
* @param mixed $key
|
|
|
|
* @return DataObject
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function offsetGet($offset) {
|
2011-05-05 16:30:45 +02:00
|
|
|
if ($this->offsetExists($offset)) return $this->items[$offset];
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Set an item with the key in $key
|
|
|
|
*
|
|
|
|
* @param mixed $key
|
|
|
|
* @param mixed $value
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function offsetSet($offset, $value) {
|
2011-05-05 16:30:45 +02:00
|
|
|
$this->items[$offset] = $value;
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
2011-12-07 02:35:30 +01:00
|
|
|
/**
|
|
|
|
* Unset an item with the key in $key
|
|
|
|
*
|
|
|
|
* @param mixed $key
|
|
|
|
*/
|
2011-05-03 16:06:42 +02:00
|
|
|
public function offsetUnset($offset) {
|
2011-05-05 16:30:45 +02:00
|
|
|
unset($this->items[$offset]);
|
2011-05-03 16:06:42 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Extracts a value from an item in the list, where the item is either an
|
|
|
|
* object or array.
|
|
|
|
*
|
|
|
|
* @param array|object $item
|
|
|
|
* @param string $key
|
|
|
|
* @return mixed
|
|
|
|
*/
|
2011-09-27 00:12:35 +02:00
|
|
|
protected function extractValue($item, $key) {
|
2011-05-03 16:06:42 +02:00
|
|
|
if (is_object($item)) {
|
|
|
|
return $item->$key;
|
|
|
|
} else {
|
|
|
|
if (array_key_exists($key, $item)) return $item[$key];
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|