diff --git a/src/ORM/DataList.php b/src/ORM/DataList.php index 4766db9f8..f9d1e87cf 100644 --- a/src/ORM/DataList.php +++ b/src/ORM/DataList.php @@ -587,9 +587,8 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li } /** - * Return a copy of this list which does not contain any items with these charactaristics + * Return a copy of this list which does not contain any items that match all params * - * @see SS_List::exclude() * @example $list = $list->exclude('Name', 'bob'); // exclude bob from list * @example $list = $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list * @example $list = $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21 @@ -599,7 +598,9 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li * * @todo extract the sql from this method into a SQLGenerator class * - * @param string|array Escaped SQL statement. If passed as array, all keys and values will be escaped internally + * @param string|array + * @param string [optional] + * * @return $this */ public function exclude() @@ -625,6 +626,43 @@ class DataList extends ViewableData implements SS_List, Filterable, Sortable, Li }); } + /** + * Return a copy of this list which does not contain any items with any of these params + * + * @example $list = $list->excludeAny('Name', 'bob'); // exclude bob from list + * @example $list = $list->excludeAny('Name', array('aziz', 'bob'); // exclude aziz and bob from list + * @example $list = $list->excludeAny(array('Name'=>'bob, 'Age'=>21)); // exclude bob or Age 21 + * @example $list = $list->excludeAny(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob or Age 21 or 43 + * @example $list = $list->excludeAny(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43))); + * // bob, phil, 21 or 43 would be excluded + * + * @param string|array + * @param string [optional] + * + * @return $this + */ + public function excludeAny() + { + $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 excludeAny()'); + } + + return $this->alterDataQuery(function (DataQuery $dataQuery) use ($whereArguments) { + foreach ($whereArguments as $field => $value) { + $filter = $this->createSearchFilter($field, $value); + $filter->exclude($dataQuery); + } + return $dataQuery; + }); + } + /** * This method returns a copy of this list that does not contain any DataObjects that exists in $list * diff --git a/tests/php/ORM/DataListTest.php b/tests/php/ORM/DataListTest.php index 2bf84821b..baecfeaf7 100755 --- a/tests/php/ORM/DataListTest.php +++ b/tests/php/ORM/DataListTest.php @@ -1509,14 +1509,61 @@ class DataListTest extends SapphireTest $this->assertEquals(2, $list->count()); } + /** + * Test doesn't exclude if only matches one + * $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21 + */ + public function testMultipleExcludeMultipleMatches() + { + $list = TeamComment::get(); + $list = $list->exclude(array('Name'=>'Bob', 'Comment'=>'Phil is a unique guy, and comments on team2')); + $this->assertCount(3, $list); + } + + /** + * // exclude only those that match both + */ + public function testMultipleExcludeArraysMultipleMatches() + { + $list = TeamComment::get(); + $list = $list->exclude(array( + 'Name'=> array('Bob', 'Phil'), + 'Comment'=> array( + 'This is a team comment by Bob', + 'Phil is a unique guy, and comments on team2' + ) + )); + $this->assertListEquals([['Name' => 'Joe']], $list); + } + + /** + * Exclude only which matches both params + */ + public function testMultipleExcludeArraysMultipleMatchesOneMiss() + { + $list = TeamComment::get(); + $list = $list->exclude(array( + 'Name' => array('Bob', 'Phil'), + 'Comment' => array( + 'Does not match any comments', + 'Phil is a unique guy, and comments on team2' + ) + )); + $list = $list->sort('Name'); + $this->assertListEquals( + [ + ['Name' => 'Bob'], + ['Name' => 'Joe'], + ], + $list + ); + } + /** * Test that if an exclude() is applied to a filter(), the filter() is still preserved. */ public function testExcludeOnFilter() { - /** - * @var DataList $list -*/ $list = TeamComment::get(); $list = $list->filter('Comment', 'Phil is a unique guy, and comments on team2'); $list = $list->exclude('Name', 'Bob'); @@ -1528,6 +1575,58 @@ class DataListTest extends SapphireTest $sql ); $this->assertEquals(array('Phil is a unique guy, and comments on team2', 'Bob'), $parameters); + $this->assertListEquals([['Name' => 'Phil']], $list); + } + + /** + * Test that if a complicated exclude() is applied to a filter(), the filter() is still preserved. + */ + public function testComplicatedExcludeOnFilter() + { + $list = TeamComment::get(); + $list = $list->filter('Name', array('Phil', 'Bob')); + $list = $list->exclude('Name', array('Bob', 'Joe')); + + $sql = $list->sql($parameters); + $this->assertSQLContains( + 'WHERE ("DataObjectTest_TeamComment"."Name" IN (?, ?)) AND (("DataObjectTest_TeamComment"."Name" NOT IN (?, ?) ' + . 'OR "DataObjectTest_TeamComment"."Name" IS NULL))', + $sql + ); + $this->assertEquals(array('Phil', 'Bob', 'Bob', 'Joe'), $parameters); + $this->assertListEquals([['Name' => 'Phil']], $list); + } + + /** + * Test that if a very complicated exclude() is applied to a filter(), the filter() is still preserved. + */ + public function testVeryComplicatedExcludeOnFilter() + { + $list = TeamComment::get(); + $list = $list->filter('Name', array('Phil', 'Bob')); + $list = $list->exclude(array( + 'Name' => array('Joe', 'Phil'), + 'Comment' => array('Matches no comments', 'Not a matching comment') + )); + + $sql = $list->sql($parameters); + $this->assertSQLContains( + 'WHERE ("DataObjectTest_TeamComment"."Name" IN (?, ?)) ' + . 'AND (("DataObjectTest_TeamComment"."Name" NOT IN (?, ?) ' + . 'OR "DataObjectTest_TeamComment"."Name" IS NULL) ' + . 'OR ("DataObjectTest_TeamComment"."Comment" NOT IN (?, ?) ' + . 'OR "DataObjectTest_TeamComment"."Comment" IS NULL))', + $sql + ); + $this->assertEquals(array('Phil', 'Bob', 'Joe', 'Phil', 'Matches no comments', 'Not a matching comment'), $parameters); + $list = $list->sort('Name'); + $this->assertListEquals( + [ + ['Name' => 'Bob'], + ['Name' => 'Phil'], + ], + $list + ); } public function testExcludeWithSearchFilter() @@ -1540,6 +1639,45 @@ class DataListTest extends SapphireTest $this->assertEquals(array('Bob'), $parameters); } + /** + * Test that Bob and Phil are excluded (one match each) + */ + public function testExcludeAny() + { + $list = TeamComment::get(); + $list = $list->excludeAny(array( + 'Name' => 'Bob', + 'Comment' => 'Phil is a unique guy, and comments on team2' + )); + $this->assertListEquals([['Name' => 'Joe']], $list); + } + + /** + * Test that Bob and Phil are excluded by Name + */ + public function testExcludeAnyArrays() + { + $list = TeamComment::get(); + $list = $list->excludeAny(array( + 'Name' => array('Bob', 'Phil'), + 'Comment' => 'No matching comments' + )); + $this->assertListEquals([['Name' => 'Joe']], $list); + } + + /** + * Test that Bob is excluded by Name, Phil by comment + */ + public function testExcludeAnyMultiArrays() + { + $list = TeamComment::get(); + $list = $list->excludeAny(array( + 'Name' => array('Bob', 'Fred'), + 'Comment' => array('No matching comments', 'Phil is a unique guy, and comments on team2') + )); + $this->assertListEquals([['Name' => 'Joe']], $list); + } + /** * Test exact match filter with empty array items *