Skip to content

Commit

Permalink
API Making ArrayList (and others) more consistent with DataList
Browse files Browse the repository at this point in the history
  • Loading branch information
dhensby committed Sep 2, 2015
1 parent fe2be0c commit f6fe142
Show file tree
Hide file tree
Showing 5 changed files with 224 additions and 37 deletions.
122 changes: 86 additions & 36 deletions model/ArrayList.php
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ public function count() {
* @return bool
*/
public function exists() {
return (bool) count($this);
return !empty($this->items);
}

/**
Expand Down Expand Up @@ -477,6 +477,79 @@ public function canFilterBy($by) {
* // 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');
}
Expand All @@ -496,24 +569,18 @@ public function filter() {
}
}

$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) {
Expand Down Expand Up @@ -563,25 +630,8 @@ public function filterByCallback($callback) {
* // 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();
Expand Down
46 changes: 46 additions & 0 deletions model/ListDecorator.php
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,32 @@ public function filter(){
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.
Expand Down Expand Up @@ -174,6 +200,26 @@ public function limit($limit, $offset = 0) {
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
*
Expand Down
2 changes: 1 addition & 1 deletion model/UnsavedRelationList.php
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ public function avg() {
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.");
}

Expand Down
81 changes: 81 additions & 0 deletions tests/model/ArrayListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -541,6 +541,72 @@ public function testMultipleWithArrayFilterAdvanced() {
$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; })
*/
Expand Down Expand Up @@ -763,6 +829,21 @@ public function testByID() {
$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();

Expand Down
10 changes: 10 additions & 0 deletions tests/model/DataListTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -394,6 +394,16 @@ public function testByID() {
$this->assertEquals('Team 2', $team->Title);
}

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()
*/
Expand Down

0 comments on commit f6fe142

Please sign in to comment.