Merge pull request #3828 from dhensby/pulls/byids-arraylist

API `byIDs` added to `ArrayList`
This commit is contained in:
Damian Mooyman 2016-04-26 17:52:22 +12:00
commit da8f4a7eb6
5 changed files with 224 additions and 37 deletions

View File

@ -56,7 +56,7 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
* @return bool
*/
public function exists() {
return (bool) count($this);
return !empty($this->items);
}
/**
@ -484,6 +484,79 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
* // aziz with the age 21 or 43 and bob with the Age 21 or 43
*/
public function filter() {
$keepUs = call_user_func_array(array($this, 'normaliseFilterArgs'), func_get_args());
$itemsToKeep = array();
foreach($this->items as $item){
$keepItem = true;
foreach ($keepUs as $column => $value) {
if ((is_array($value) && !in_array($this->extractValue($item, $column), $value))
|| (!is_array($value) && $this->extractValue($item, $column) != $value)
) {
$keepItem = false;
break;
}
}
if($keepItem) {
$itemsToKeep[] = $item;
}
}
$list = clone $this;
$list->items = $itemsToKeep;
return $list;
}
/**
* Return a copy of this list which contains items matching any of these charactaristics.
*
* @example // only bob in the list
* $list = $list->filterAny('Name', 'bob');
* @example // azis or bob in the list
* $list = $list->filterAny('Name', array('aziz', 'bob');
* @example // bob or anyone aged 21 in the list
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>21));
* @example // bob or anyone aged 21 or 43 in the list
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>array(21, 43)));
* @example // all bobs, phils or anyone aged 21 or 43 in the list
* $list = $list->filterAny(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
*
* @param string|array See {@link filter()}
* @return DataList
*/
public function filterAny() {
$keepUs = call_user_func_array(array($this, 'normaliseFilterArgs'), func_get_args());
$itemsToKeep = array();
foreach ($this->items as $item) {
foreach ($keepUs as $column => $value) {
$extractedValue = $this->extractValue($item, $column);
$matches = is_array($value) ? in_array($extractedValue, $value) : $extractedValue == $value;
if ($matches) {
$itemsToKeep[] = $item;
break;
}
}
}
$list = clone $this;
$list->items = array_unique($itemsToKeep, SORT_REGULAR);
return $list;
}
/**
* Take the "standard" arguments that the filter/exclude functions take and return a single array with
* 'colum' => 'value'
*
* @param $column array|string The column name to filter OR an assosicative array of column => value
* @param $value array|string|null The values to filter the $column against
*
* @return array The normalised keyed array
*/
protected function normaliseFilterArgs($column, $value = null) {
if(count(func_get_args())>2){
throw new InvalidArgumentException('filter takes one array or two arguments');
}
@ -503,24 +576,18 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
}
}
$itemsToKeep = array();
foreach($this->items as $item){
$keepItem = true;
foreach($keepUs as $column => $value ) {
if(is_array($value) && !in_array($this->extractValue($item, $column), $value)) {
$keepItem = false;
} elseif(!is_array($value) && $this->extractValue($item, $column) != $value) {
$keepItem = false;
}
}
if($keepItem) {
$itemsToKeep[] = $item;
}
return $keepUs;
}
$list = clone $this;
$list->items = $itemsToKeep;
return $list;
/**
* Filter this list to only contain the given Primary IDs
*
* @param array $ids Array of integers, will be automatically cast/escaped.
* @return ArrayList
*/
public function byIDs($ids) {
$ids = array_map('intval', $ids); // sanitize
return $this->filter('ID', $ids);
}
public function byID($id) {
@ -570,25 +637,8 @@ class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sorta
* // bob age 21 or 43, phil age 21 or 43 would be excluded
*/
public function exclude() {
if(count(func_get_args())>2){
throw new InvalidArgumentException('exclude() takes one array or two arguments');
}
if(count(func_get_args()) == 1 && !is_array(func_get_arg(0))){
throw new InvalidArgumentException('exclude() takes one array or two arguments');
}
$removeUs = array();
if(count(func_get_args())==2){
$removeUs[func_get_arg(0)] = func_get_arg(1);
}
if(count(func_get_args())==1 && is_array(func_get_arg(0))){
foreach(func_get_arg(0) as $column => $excludeValue) {
$removeUs[$column] = $excludeValue;
}
}
$removeUs = call_user_func_array(array($this, 'normaliseFilterArgs'), func_get_args());
$hitsRequiredToRemove = count($removeUs);
$matches = array();

View File

@ -147,6 +147,32 @@ abstract class SS_ListDecorator extends ViewableData implements SS_List, SS_Sort
return call_user_func_array(array($this->list, 'filter'), $args);
}
/**
* Return a copy of this list which contains items matching any of these charactaristics.
*
* @example // only bob in the list
* $list = $list->filterAny('Name', 'bob');
* // SQL: WHERE "Name" = 'bob'
* @example // azis or bob in the list
* $list = $list->filterAny('Name', array('aziz', 'bob');
* // SQL: WHERE ("Name" IN ('aziz','bob'))
* @example // bob or anyone aged 21 in the list
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>21));
* // SQL: WHERE ("Name" = 'bob' OR "Age" = '21')
* @example // bob or anyone aged 21 or 43 in the list
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>array(21, 43)));
* // SQL: WHERE ("Name" = 'bob' OR ("Age" IN ('21', '43'))
* @example // all bobs, phils or anyone aged 21 or 43 in the list
* $list = $list->filterAny(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // SQL: WHERE (("Name" IN ('bob', 'phil')) OR ("Age" IN ('21', '43'))
*
* @param string|array See {@link filter()}
* @return DataList
*/
public function filterAny() {
return call_user_func_array(array($this->list, __FUNCTION__), func_get_args());
}
/**
* Note that, in the current implementation, the filtered list will be an ArrayList, but this may change in a
* future implementation.
@ -174,6 +200,26 @@ abstract class SS_ListDecorator extends ViewableData implements SS_List, SS_Sort
return $this->list->limit($limit, $offset);
}
/**
* Return the first item with the given ID
*
* @param int $id
* @return mixed
*/
public function byID($id) {
return $this->list->byID($id);
}
/**
* Filter this list to only contain the given Primary IDs
*
* @param array $ids Array of integers
* @return SS_List
*/
public function byIDs($ids) {
return $this->list->byIDs($ids);
}
/**
* Exclude the list to not contain items with these charactaristics
*

View File

@ -301,7 +301,7 @@ class UnsavedRelationList extends ArrayList {
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
}
public function byIDs() {
public function byIDs($ids) {
throw new LogicException(__FUNCTION__ . " can't be called on an UnsavedRelationList.");
}

View File

@ -564,6 +564,72 @@ class ArrayListTest extends SapphireTest {
$this->assertEquals($expected, $list->toArray(), 'List should only contain Steve and Steve and Clair');
}
public function testFilterAny() {
$list = new ArrayList(array(
$steve = array('Name' => 'Steve', 'ID' => 1, 'Age' => 21),
$bob = array('Name' => 'Bob', 'ID' => 2, 'Age' => 18),
$clair = array('Name' => 'Clair', 'ID' => 3, 'Age' => 21),
$phil = array('Name' => 'Phil', 'ID' => 4, 'Age' => 21),
$oscar = array('Name' => 'Oscar', 'ID' => 5, 'Age' => 52),
$mike = array('Name' => 'Mike', 'ID' => 6, 'Age' => 43),
));
// only bob in the list
//$list = $list->filterAny('Name', 'bob');
$filteredList = $list->filterAny('Name', 'Bob')->toArray();
$this->assertCount(1, $filteredList);
$this->assertContains($bob, $filteredList);
// azis or bob in the list
//$list = $list->filterAny('Name', array('aziz', 'bob');
$filteredList = $list->filterAny('Name', array('Aziz', 'Bob'))->toArray();
$this->assertCount(1, $filteredList);
$this->assertContains($bob, $filteredList);
$filteredList = $list->filterAny('Name', array('Steve', 'Bob'))->toArray();
$this->assertCount(2, $filteredList);
$this->assertContains($steve, $filteredList);
$this->assertContains($bob, $filteredList);
// bob or anyone aged 21 in the list
//$list = $list->filterAny(array('Name'=>'bob, 'Age'=>21));
$filteredList = $list->filterAny(array('Name' => 'Bob', 'Age' => 21))->toArray();
$this->assertCount(4, $filteredList);
$this->assertContains($bob, $filteredList);
$this->assertContains($steve, $filteredList);
$this->assertContains($clair, $filteredList);
$this->assertContains($phil, $filteredList);
// bob or anyone aged 21 or 43 in the list
// $list = $list->filterAny(array('Name'=>'bob, 'Age'=>array(21, 43)));
$filteredList = $list->filterAny(array('Name' => 'Bob', 'Age' => array(21, 43)))->toArray();
$this->assertCount(5, $filteredList);
$this->assertContains($bob, $filteredList);
$this->assertContains($steve, $filteredList);
$this->assertContains($clair, $filteredList);
$this->assertContains($mike, $filteredList);
$this->assertContains($phil, $filteredList);
// all bobs, phils or anyone aged 21 or 43 in the list
//$list = $list->filterAny(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
$filteredList = $list->filterAny(array('Name' => array('Bob', 'Phil'), 'Age' => array(21, 43)))->toArray();
$this->assertCount(5, $filteredList);
$this->assertContains($bob, $filteredList);
$this->assertContains($steve, $filteredList);
$this->assertContains($clair, $filteredList);
$this->assertContains($mike, $filteredList);
$this->assertContains($phil, $filteredList);
$filteredList = $list->filterAny(array('Name' => array('Bob', 'Nobody'), 'Age' => array(21, 43)))->toArray();
$this->assertCount(5, $filteredList);
$this->assertContains($bob, $filteredList);
$this->assertContains($steve, $filteredList);
$this->assertContains($clair, $filteredList);
$this->assertContains($mike, $filteredList);
$this->assertContains($phil, $filteredList);
}
/**
* $list = $list->filterByCallback(function($item, $list) { return $item->Age == 21; })
*/
@ -786,6 +852,21 @@ class ArrayListTest extends SapphireTest {
$this->assertNull($element);
}
public function testByIDs() {
$list = new ArrayList(array(
array('ID' => 1, 'Name' => 'Steve'),
array('ID' => 2, 'Name' => 'Bob'),
array('ID' => 3, 'Name' => 'John')
));
$knownIDs = $list->column('ID');
$removedID = array_pop($knownIDs);
$filteredItems = $list->byIDs($knownIDs);
foreach ($filteredItems as $item) {
$this->assertContains($item->ID, $knownIDs);
$this->assertNotEquals($removedID, $item->ID);
}
}
public function testByIDEmpty() {
$list = new ArrayList();

View File

@ -407,6 +407,16 @@ class DataListTest extends SapphireTest {
$this->assertNotContains('WHERE ("DataObjectTest_SubTeam"."ID" = ?)', $query);
}
public function testByIDs() {
$knownIDs = $this->allFixtureIDs('DataObjectTest_Player');
$removedID = array_pop($knownIDs);
$filteredPlayers = DataObjectTest_Player::get()->byIDs($knownIDs);
foreach ($filteredPlayers as $player) {
$this->assertContains($player->ID, $knownIDs);
$this->assertNotEquals($removedID, $player->ID);
}
}
/**
* Test DataList->removeByID()
*/