Permalink
Browse files

API CHANGE All SS_List implementators supports filter, exclude and so…

…rt methods
  • Loading branch information...
stojg authored and sminnee committed Dec 8, 2011
1 parent 2306ec9 commit aafdb8e01c47cf5aeccc2b5b7139e6fc7560a06b
View
@@ -291,33 +291,175 @@ public function canSortBy($by) {
* Sorts this list by one or more fields. You can either pass in a single
* field name and direction, or a map of field names to sort directions.
*
- * @param string|array $by
- * @param string $sortDirection
+ * @return DataList
* @see SS_List::sort()
- * @link http://php.net/manual/en/function.array-multisort.php
+ * @example $list->sort('Name'); // default ASC sorting
+ * @example $list->sort('Name DESC'); // DESC sorting
* @example $list->sort('Name', 'ASC');
- * @example $list->sort(array('Name'=>'ASC,'Age'=>'DESC');
+ * @example $list->sort(array('Name'=>'ASC,'Age'=>'DESC'));
*/
- public function sort($by, $sortDirection = 'ASC') {
- $sorts = array();
+ public function sort() {
+ $args = func_get_args();
+
+ if(count($args)==0){
+ return $this;
+ }
+ if(count($args)>2){
+ throw new InvalidArgumentException('This method takes zero, one or two arguments');
+ }
+
+ // One argument and it's a string
+ if(count($args)==1 && is_string($args[0])){
+ $column = $args[0];
+ if(strpos($column, ' ') !== false) throw new InvalidArgumentException("You can't pass SQL fragments to sort()");
+ $columnsToSort[$column] = SORT_ASC;
+
+ } else if(count($args)==2){
+ $columnsToSort[$args[0]]=(strtolower($args[1])=='desc')?SORT_DESC:SORT_ASC;
+
+ } else if(is_array($args[0])) {
+ foreach($args[0] as $column => $sort_order){
+ $columnsToSort[$column] = (strtolower($sort_order)=='desc')?SORT_DESC:SORT_ASC;
+ }
+ } else {
+ throw new InvalidArgumentException("Bad arguments passed to sort()");
+ }
+
+ // This the main sorting algorithm that supports infinite sorting params
+ $multisortArgs = array();
+ $values = array();
+ foreach($columnsToSort as $column => $direction ) {
+ // The reason these are added to columns is of the references, otherwise when the foreach
+ // is done, all $values and $direction look the same
+ $values[$column] = array();
+ $sortDirection[$column] = $direction;
+ // We need to subtract every value into a temporary array for sorting
+ foreach($this->items as $index => $item) {
+ $values[$column][] = $this->extractValue($item, $column);
+ }
+ // PHP 5.3 requires below arguments to be reference when using array_multisort together
+ // with call_user_func_array
+ // First argument is the 'value' array to be sorted
+ $multisortArgs[] = &$values[$column];
+ // First argument is the direction to be sorted,
+ $multisortArgs[] = &$sortDirection[$column];
+ }
+ // As the last argument we pass in a reference to the items that all the sorting will be
+ // applied upon
+ $multisortArgs[] = &$this->items;
+ call_user_func_array('array_multisort', $multisortArgs);
+ return $this;
+ }
+
+ /**
+ * Filter the list to include items with these charactaristics
+ *
+ * @return ArrayList
+ * @see SS_List::filter()
+ * @example $list->filter('Name', 'bob'); // only bob in the list
+ * @example $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
+ * @example $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the Age 21 in list
+ * @example $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob with the Age 21 or 43
+ * @example $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43))); // aziz with the age 21 or 43 and bob with the Age 21 or 43
+ */
+ public function filter() {
+ if(count(func_get_args())>2){
+ throw new InvalidArgumentException('filter takes one array or two arguments');
+ }
+
+ if(count(func_get_args()) == 1 && !is_array(func_get_arg(0))){
+ throw new InvalidArgumentException('filter takes one array or two arguments');
+ }
+
+ $keepUs = array();
+ if(count(func_get_args())==2){
+ $keepUs[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 => $value) {
+ $keepUs[$column] = $value;
+ }
+ }
+
+ $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;
+ }
+ }
+
+ $this->items = $itemsToKeep;
+ return $this;
+ }
- if(!is_array($by)) {
- $by = array($by => $sortDirection);
+ /**
+ * Exclude the list to not contain items with these charactaristics
+ *
+ * @return ArrayList
+ * @see SS_List::exclude()
+ * @example $list->exclude('Name', 'bob'); // exclude bob from list
+ * @example $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
+ * @example $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
+ * @example $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
+ * @example $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43))); // 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;
+ }
+ }
+
+ $itemsToKeep = array();
- foreach ($by as $field => $sortDirection) {
- $sortDirection = strtoupper($sortDirection) == 'DESC' ? SORT_DESC : SORT_ASC;
- $values = array();
- foreach($this->items as $item) {
- $values[] = $this->extractValue($item, $field);
+ $hitsRequiredToRemove = count($removeUs);
+ $matches = array();
+ foreach($removeUs as $column => $excludeValue) {
+ foreach($this->items as $key => $item){
+ if(!is_array($excludeValue) && $this->extractValue($item, $column) == $excludeValue) {
+ $matches[$key]=isset($matches[$key])?$matches[$key]+1:1;
+ } elseif(is_array($excludeValue) && in_array($this->extractValue($item, $column), $excludeValue)) {
+ $matches[$key]=isset($matches[$key])?$matches[$key]+1:1;
+ }
}
- $sorts[] = &$values;
- $sorts[] = &$sortDirection;
}
- $sorts[] = &$this->items;
- call_user_func_array('array_multisort', $sorts);
+
+ $keysToRemove = array_keys($matches,$hitsRequiredToRemove);
+ foreach($keysToRemove as $itemToRemoveIdx){
+ $this->remove($this->items[$itemToRemoveIdx]);
+ }
+ return;
+
+ return $this;
+ }
+
+ protected function shouldExclude($item, $args) {
+
}
+
/**
* Returns whether an item with $key exists
*
View
@@ -105,19 +105,6 @@ public function where($filter) {
return $this;
}
- /**
- * Set the sort order of this data list
- *
- * @param string $sort
- * @param string $direction
- * @return DataList
- */
- public function sort($sort, $direction = "ASC") {
- if($direction && strtoupper($direction) != 'ASC') $sort = "$sort $direction";
- $this->dataQuery->sort($sort);
- return $this;
- }
-
/**
* Returns true if this DataList can be sorted by the given field.
*
@@ -150,7 +137,165 @@ public function limit($limit) {
$this->dataQuery->limit($limit);
return $this;
}
+
+ /**
+ * Set the sort order of this data list
+ *
+ * @return DataList
+ * @see SS_List::sort()
+ * @example $list->sort('Name'); // default ASC sorting
+ * @example $list->sort('Name DESC'); // DESC sorting
+ * @example $list->sort('Name', 'ASC');
+ * @example $list->sort(array('Name'=>'ASC,'Age'=>'DESC'));
+ */
+ public function sort() {
+ if(count(func_get_args())==0){
+ return $this;
+ }
+ if(count(func_get_args())>2){
+ throw new InvalidArgumentException('This method takes zero, one or two arguments');
+ }
+
+ // sort('Name','Desc')
+ if(count(func_get_args())==2){
+ if(!in_array(strtolower(func_get_arg(1)),array('desc','asc'))){
+ user_error('Second argument to sort must be either ASC or DESC');
+ }
+ $this->dataQuery->sort(func_get_arg(0).' '.func_get_arg(1));
+ return $this;
+ }
+
+ // sort('Name') - default to ASC sorting if not specified
+ if(is_string(func_get_arg(0)) && func_get_arg(0)){
+ // sort('Name ASC')
+ if(stristr(func_get_arg(0), ' asc') || stristr(func_get_arg(0), ' desc')){
+ $this->dataQuery->sort(func_get_arg(0));
+ } else {
+ $this->dataQuery->sort(func_get_arg(0).' ASC');
+ }
+
+ return $this;
+ }
+
+ // sort(array('Name'=>'desc'));
+ $argumentArray = func_get_arg(0);
+ if(is_array($argumentArray)){
+ $sort = array();
+ foreach($argumentArray as $column => $direction) {
+ $sort []= '"'.$column.'" '.$direction;
+ }
+ $this->dataQuery->sort(implode(',', $sort));
+ return $this;
+ }
+
+ return $this;
+ }
+
+ /**
+ * Filter the list to include items with these charactaristics
+ *
+ * @return DataList
+ * @see SS_List::filter()
+ * @example $list->filter('Name', 'bob'); // only bob in the list
+ * @example $list->filter('Name', array('aziz', 'bob'); // aziz and bob in list
+ * @example $list->filter(array('Name'=>'bob, 'Age'=>21)); // bob with the age 21
+ * @example $list->filter(array('Name'=>'bob, 'Age'=>array(21, 43))); // bob with the Age 21 or 43
+ * @example $list->filter(array('Name'=>array('aziz','bob'), 'Age'=>array(21, 43))); // aziz with the age 21 or 43 and bob with the Age 21 or 43
+ *
+ * @todo extract the sql from $customQuery into a SQLGenerator class
+ */
+ public function filter() {
+ $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('Arguments passed to filter() is wrong');
+ }
+
+ $SQL_Statements = array();
+ foreach($whereArguments as $field => $value) {
+ if(is_array($value)) {
+ $customQuery = 'IN (\''.implode('\',\'',Convert::raw2sql($value)).'\')';
+ } else {
+ $customQuery = '= \''.Convert::raw2sql($value).'\'';
+ }
+
+ if(stristr($field,':')) {
+ $fieldArgs = explode(':',$field);
+ $field = array_shift($fieldArgs);
+ foreach($fieldArgs as $fieldArg){
+ $comparisor = $this->applyFilterContext($field, $fieldArg, $value);
+ }
+ } else {
+ $SQL_Statements[] = '"'.Convert::raw2sql($field).'" '.$customQuery;
+ }
+ }
+ if(count($SQL_Statements)) {
+ foreach($SQL_Statements as $SQL_Statement){
+ $this->dataQuery->where($SQL_Statement);
+ }
+ }
+ return $this;
+ }
+ /**
+ * Translates the comparisator to the sql query
+ *
+ * @param string $field - the fieldname in the db
+ * @param string $comparisators - example StartsWith, relates to a filtercontext
+ * @param string $value - the value that the filtercontext will use for matching
+ * @todo Deprecated SearchContexts and pull their functionality into the core of the ORM
+ */
+ private function applyFilterContext($field, $comparisators, $value) {
+ $t = singleton($this->dataClass())->dbObject($field);
+ $className = "{$comparisators}Filter";
+ if(!class_exists($className)){
+ throw new InvalidArgumentException('There are no '.$comparisators.' comparisator');
+ }
+ $t = new $className($field,$value);
+ $t->apply($this->dataQuery());
+ }
+
+ /**
+ * Exclude the list to not contain items with these charactaristics
+ *
+ * @return DataList
+ * @see SS_List::exclude()
+ * @example $list->exclude('Name', 'bob'); // exclude bob from list
+ * @example $list->exclude('Name', array('aziz', 'bob'); // exclude aziz and bob from list
+ * @example $list->exclude(array('Name'=>'bob, 'Age'=>21)); // exclude bob that has Age 21
+ * @example $list->exclude(array('Name'=>'bob, 'Age'=>array(21, 43))); // exclude bob with Age 21 or 43
+ * @example $list->exclude(array('Name'=>array('bob','phil'), 'Age'=>array(21, 43))); // bob age 21 or 43, phil age 21 or 43 would be excluded
+ *
+ * @todo extract the sql from this method into a SQLGenerator class
+ */
+ public function exclude(){
+ $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('Arguments passed to exclude() is wrong');
+ }
+
+ $SQL_Statements = array();
+ foreach($whereArguments as $fieldName => $value) {
+ if(is_array($value)){
+ $SQL_Statements[] = ('"'.$fieldName.'" NOT IN (\''.implode('\',\'', Convert::raw2sql($value)).'\')');
+ } else {
+ $SQL_Statements[] = ('"'.$fieldName.'" != \''.Convert::raw2sql($value).'\'');
+ }
+ }
+ $this->dataQuery->where(implode(' OR ', $SQL_Statements));
+ return $this;
+ }
+
/**
* Add an inner join clause to this data list's query.
*
@@ -647,6 +792,6 @@ public function offsetSet($key, $value) {
*/
public function offsetUnset($key) {
user_error("Can't alter items in a DataList using array-access", E_USER_ERROR);
- }
+ }
}
View
0 model/DataQuery.php 100755 → 100644
No changes.
Oops, something went wrong.

0 comments on commit aafdb8e

Please sign in to comment.