Skip to content

Commit

Permalink
FEATURE: Added ArrayList to allow an array of arrays or objects to be…
Browse files Browse the repository at this point in the history
… handled as a list.
  • Loading branch information
ajshort committed May 3, 2011
1 parent 577be1e commit 7e7677b
Show file tree
Hide file tree
Showing 2 changed files with 279 additions and 0 deletions.
141 changes: 141 additions & 0 deletions model/ArrayList.php
@@ -0,0 +1,141 @@
<?php
/**
* A list object that wraps around an array of objects or arrays.
*
* @package sapphire
* @subpackage model
*/
class ArrayList extends ViewableData implements SS_List {

/**
* @var array
*/
protected $array;

public function __construct(array $array = array()) {
$this->array = $array;
parent::__construct();
}

public function count() {
return count($this->array);
}

public function getIterator() {
return new ArrayIterator($this->array);
}

public function toArray() {
return $this->array;
}

public function toNestedArray() {
$result = array();

foreach ($this->array as $item) {
if (is_object($item)) {
if (method_exists($item, 'toMap')) {
$result[] = $item->toMap();
} else {
$result[] = (array) $item;

This comment has been minimized.

Copy link
@dhensby

dhensby Oct 10, 2013

Contributor

Hey, what was the reason to cast as an array at this point? I'm currently doing Debug::show(FieldList) and which results in toNestedArray being called, but an (array) cast on a FormField results in an infinite loop... Removing this line fixes the infinite loop..

Perhaps fixing the infinite loop caused by casting as an array is a better approach.. but not sure what's causing that

This comment has been minimized.

Copy link
@ajshort

ajshort Oct 10, 2013

Author Contributor

The toNestedArray call seems to indicate to me that items should be casted to nested arrays.

This comment has been minimized.

Copy link
@dhensby

dhensby Oct 10, 2013

Contributor

An interesting thought, though (array) only casts one level deep, so this doesn't turn it into a nested array infinitely.

It looks like this has infinite recursion because $form private variable gets set as a property of the array which itself contains a pointer to field which contains the original field being cast as an array and so it goes on.

So, the other solution is to implement toMap() on FormField - though not sure what information is useful to be included in that function.

However, still we don't solve the key issue of recursion due to the $form variable - though fixing that may take a big change in how forms work...

}
} else {
$result[] = $item;
}
}

return $result;
}

public function getRange($offset, $length) {
return array_slice($this->array, $offset, $length);
}

public function add($item) {
$this->array[] = $item;
}

public function remove($item) {
foreach ($this->array as $key => $value) {
if ($item === $value) unset($this->array[$key]);
}
}

public function first() {
return reset($this->array);
}

public function last() {
return end($this->array);
}

public function map($keyfield, $titlefield) {
$map = array();
foreach ($this->array as $item) {
$map[$this->extract($item, $keyfield)] = $this->extract($item, $titlefield);
}
return $map;
}

public function find($key, $value) {
foreach ($this->array as $item) {
if ($this->extract($item, $key) == $value) return $item;
}
}

public function column($field) {
$result = array();
foreach ($this->array as $item) {
$result[] = $this->extract($item, $field);
}
return $result;
}

public function canSortBy($by) {
return true;
}

public function sort($by, $dir = 'ASC') {
$sorts = array();
$dir = strtoupper($dir) == 'DESC' ? SORT_DESC : SORT_ASC;

foreach ($this->array as $item) {
$sorts[] = $this->extract($item, $by);
}

array_multisort($sorts, $dir, $this->array);
}

public function offsetExists($offset) {
return array_key_exists($offset, $this->array);
}

public function offsetGet($offset) {
if ($this->offsetExists($offset)) return $this->array[$offset];
}

public function offsetSet($offset, $value) {
$this->array[$offset] = $value;
}

public function offsetUnset($offset) {
unset($this->array[$offset]);
}

/**
* 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
*/
protected function extract($item, $key) {
if (is_object($item)) {
return $item->$key;
} else {
if (array_key_exists($key, $item)) return $item[$key];
}
}

}
138 changes: 138 additions & 0 deletions tests/model/ArrayListTest.php
@@ -0,0 +1,138 @@
<?php
/**
* @package sapphire
* @subpackage tests
*/
class ArrayListTest extends SapphireTest {

public function testCount() {
$list = new ArrayList();
$this->assertEquals(0, $list->count());
$list = new ArrayList(array(1, 2, 3));
$this->assertEquals(3, $list->count());
}

public function testToNestedArray() {
$list = new ArrayList(array(
array('First' => 'FirstFirst', 'Second' => 'FirstSecond'),
(object) array('First' => 'SecondFirst', 'Second' => 'SecondSecond'),
new ArrayListTest_Object('ThirdFirst', 'ThirdSecond')
));

$this->assertEquals($list->toNestedArray(), array(
array('First' => 'FirstFirst', 'Second' => 'FirstSecond'),
array('First' => 'SecondFirst', 'Second' => 'SecondSecond'),
array('First' => 'ThirdFirst', 'Second' => 'ThirdSecond')
));
}

public function testGetRange() {
$list = new ArrayList(array(
array('Key' => 1), array('Key' => 2), array('Key' => 3)
));
$this->assertEquals($list->getRange(1, 2), array(
array('Key' => 2), array('Key' => 3)
));
}

public function testAddRemove() {
$list = new ArrayList(array(
array('Key' => 1), array('Key' => 2)
));

$list->add(array('Key' => 3));
$this->assertEquals($list->toArray(), array(
array('Key' => 1), array('Key' => 2), array('Key' => 3)
));

$list->remove(array('Key' => 2));
$this->assertEquals(array_values($list->toArray()), array(
array('Key' => 1), array('Key' => 3)
));
}

public function testFirstLast() {
$list = new ArrayList(array(
array('Key' => 1), array('Key' => 2), array('Key' => 3)
));
$this->assertEquals($list->first(), array('Key' => 1));
$this->assertEquals($list->last(), array('Key' => 3));
}

public function testMap() {
$list = new ArrayList(array(
array('ID' => 1, 'Name' => 'Steve',),
(object) array('ID' => 3, 'Name' => 'Bob'),
array('ID' => 5, 'Name' => 'John')
));
$this->assertEquals($list->map('ID', 'Name'), array(
1 => 'Steve',
3 => 'Bob',
5 => 'John'
));
}

public function testFind() {
$list = new ArrayList(array(
array('Name' => 'Steve'),
(object) array('Name' => 'Bob'),
array('Name' => 'John')
));
$this->assertEquals($list->find('Name', 'Bob'), (object) array(
'Name' => 'Bob'
));
}

public function testColumn() {
$list = new ArrayList(array(
array('Name' => 'Steve'),
(object) array('Name' => 'Bob'),
array('Name' => 'John')
));
$this->assertEquals($list->column('Name'), array(
'Steve', 'Bob', 'John'
));
}

public function testSort() {
$list = new ArrayList(array(
array('Name' => 'Steve'),
(object) array('Name' => 'Bob'),
array('Name' => 'John')
));

$list->sort('Name');
$this->assertEquals($list->toArray(), array(
(object) array('Name' => 'Bob'),
array('Name' => 'John'),
array('Name' => 'Steve')
));

$list->sort('Name', 'DESC');
$this->assertEquals($list->toArray(), array(
array('Name' => 'Steve'),
array('Name' => 'John'),
(object) array('Name' => 'Bob')
));
}

}

/**
* @ignore
*/
class ArrayListTest_Object {

public $First;
public $Second;

public function __construct($first, $second) {
$this->First = $first;
$this->Second = $second;
}

public function toMap() {
return array('First' => $this->First, 'Second' => $this->Second);
}

}

0 comments on commit 7e7677b

Please sign in to comment.