Merge pull request #870 from chillu/pulls/datalist-filterany

NEW DataList->filterAny()
This commit is contained in:
Simon Welsh 2012-11-02 16:17:48 -07:00
commit 8a0f234acf
3 changed files with 168 additions and 3 deletions

View File

@ -161,12 +161,30 @@ Then there is the most complex task when you want to find Sam and Sig that has e
'FirstName' => array('Sam', 'Sig'),
'Age' => array(17, 74)
));
// SQL: WHERE ("FirstName" IN ('Sam', 'Sig) AND "Age" IN ('17', '74))
This would be equivalent to a SQL query of
In case you want to match multiple criteria non-exclusively (with an "OR" disjunctive),
use the `filterAny()` method instead:
:::
... WHERE ("FirstName" IN ('Sam', 'Sig) AND "Age" IN ('17', '74));
:::php
$members = Member::get()->filterAny(array(
'FirstName' => 'Sam',
'Age' => 17,
));
// SQL: WHERE ("FirstName" = 'Sam' OR "Age" = '17')
You can also combine both conjunctive ("AND") and disjunctive ("OR") statements.
:::php
$members = Member::get()
->filter(array(
'LastName' => 'Minnée'
))
->filterAny(array(
'FirstName' => 'Sam',
'Age' => 17,
));
// SQL: WHERE ("LastName" = 'Minnée' AND ("FirstName" = 'Sam' OR "Age" = '17'))
### Exclude

View File

@ -385,6 +385,68 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
return $this;
}
/**
* Return a copy of this list which does not contain items matching any of these charactaristics.
*
* @example // filter bob from list
* $list = $list->filterAny('Name', 'bob');
* // SQL: WHERE "Name" = 'bob'
* @example // filter aziz and bob from list
* $list = $list->filterAny('Name', array('aziz', 'bob');
* // SQL: WHERE ("Name" IN ('aziz','bob'))
* @example // filter by bob or anybody aged 21
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>21));
* // SQL: WHERE ("Name" = 'bob' OR "Age" = '21')
* @example // filter by bob or anybody aged 21 or 43
* $list = $list->filterAny(array('Name'=>'bob, 'Age'=>array(21, 43)));
* // SQL: WHERE ("Name" = 'bob' OR ("Age" IN ('21', '43'))
* @example // bob age 21 or 43, phil age 21 or 43 would be excluded
* $list = $list->filterAny(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43)));
* // SQL: WHERE (("Name" IN ('bob', 'phil')) OR ("Age" IN ('21', '43'))
*
* @todo extract the sql from this method into a SQLGenerator class
*
* @param string|array See {@link filter()}
* @return DataList
*/
public function filterAny() {
$numberFuncArgs = count(func_get_args());
$whereArguments = array();
if($numberFuncArgs == 1 && is_array(func_get_arg(0))) {
$whereArguments = func_get_arg(0);
} elseif($numberFuncArgs == 2) {
$whereArguments[func_get_arg(0)] = func_get_arg(1);
} else {
throw new InvalidArgumentException('Incorrect number of arguments passed to exclude()');
}
return $this->alterDataQuery(function($query, $list) use ($whereArguments) {
$subquery = $query->disjunctiveGroup();
foreach($whereArguments as $field => $value) {
$fieldArgs = explode(':', $field);
$field = array_shift($fieldArgs);
$filterType = array_shift($fieldArgs);
$modifiers = $fieldArgs;
// This is here since PHP 5.3 can't call protected/private methods in a closure.
$t = singleton($list->dataClass())->dbObject($field);
if($filterType) {
$className = "{$filterType}Filter";
} else {
$className = 'ExactMatchFilter';
}
if(!class_exists($className)){
$className = 'ExactMatchFilter';
array_unshift($modifiers, $filterType);
}
$t = new $className($field, $value, $modifiers);
$t->apply($subquery);
}
});
}
/**
* Filter this DataList by a callback function.
* The function will be passed each record of the DataList in turn, and must return true for the record to be

View File

@ -457,6 +457,91 @@ class DataListTest extends SapphireTest {
$this->assertEquals(2, $gtList->count());
}
public function testFilterAny() {
$list = DataObjectTest_TeamComment::get();
$list = $list->filterAny('Name', 'Bob');
$this->assertEquals(1, $list->count());
}
public function testFilterAnyMultipleArray() {
$list = DataObjectTest_TeamComment::get();
$list = $list->filterAny(array('Name'=>'Bob', 'Comment'=>'This is a team comment by Bob'));
$this->assertEquals(1, $list->count());
$this->assertEquals('Bob', $list->first()->Name, 'Only comment should be from Bob');
}
public function testFilterAnyOnFilter() {
$list = DataObjectTest_TeamComment::get();
$list = $list->filter(array(
'TeamID'=>$this->idFromFixture('DataObjectTest_Team', 'team1')
));
$list = $list->filterAny(array(
'Name'=>array('Phil', 'Joe'),
'Comment'=>'This is a team comment by Bob'
));
$list = $list->sort('Name');
$this->assertEquals(2, $list->count());
$this->assertEquals(
'Bob',
$list->offsetGet(0)->Name,
'Results should include comments from Bob, matched by comment and team'
);
$this->assertEquals(
'Joe',
$list->offsetGet(1)->Name,
'Results should include comments by Joe, matched by name and team (not by comment)'
);
$list = DataObjectTest_TeamComment::get();
$list = $list->filter(array(
'TeamID'=>$this->idFromFixture('DataObjectTest_Team', 'team1')
));
$list = $list->filterAny(array(
'Name'=>array('Phil', 'Joe'),
'Comment'=>'This is a team comment by Bob'
));
$list = $list->sort('Name');
$list = $list->filter(array('Name' => 'Bob'));
$this->assertEquals(1, $list->count());
$this->assertEquals(
'Bob',
$list->offsetGet(0)->Name,
'Results should include comments from Bob, matched by name and team'
);
}
public function testFilterAnyMultipleWithArrayFilter() {
$list = DataObjectTest_TeamComment::get();
$list = $list->filterAny(array('Name'=>array('Bob','Phil')));
$this->assertEquals(2, $list->count(), 'There should be two comments');
$this->assertEquals('Bob', $list->first()->Name, 'First comment should be from Bob');
$this->assertEquals('Phil', $list->last()->Name, 'Last comment should be from Phil');
}
public function testFilterAnyArrayInArray() {
$list = DataObjectTest_TeamComment::get();
$list = $list->filterAny(array(
'Name'=>array('Bob','Phil'),
'TeamID'=>array($this->idFromFixture('DataObjectTest_Team', 'team1'))))
->sort('Name');
$this->assertEquals(3, $list->count());
$this->assertEquals(
'Bob',
$list->offsetGet(0)->Name,
'Results should include comments from Bob, matched by name and team'
);
$this->assertEquals(
'Joe',
$list->offsetGet(1)->Name,
'Results should include comments by Joe, matched by team (not by name)'
);
$this->assertEquals(
'Phil',
$list->offsetGet(2)->Name,
'Results should include comments from Phil, matched by name (even if he\'s not in Team1)'
);
}
public function testFilterAndExcludeById() {
$id = $this->idFromFixture('DataObjectTest_SubTeam', 'subteam1');
$list = DataObjectTest_SubTeam::get()->filter('ID', $id);