sort("Title") is legal, but * new DataList("SiteTree")->sort("Title") is not. * * @param string $dataClass - The DataObject class to query. */ public static function create($dataClass) { return new DataList($dataClass); } /** * Create a new DataList. * No querying is done on construction, but the initial query schema is set up. * * @param string $dataClass - The DataObject class to query. */ public function __construct($dataClass) { $this->dataClass = $dataClass; $this->dataQuery = new DataQuery($this->dataClass); parent::__construct(); } /** * Set the DataModel * * @param DataModel $model */ public function setModel(DataModel $model) { $this->model = $model; } /** * Get the dataClass name for this DataList, ie the DataObject ClassName * * @return string */ public function dataClass() { return $this->dataClass; } /** * When cloning this object, clone the dataQuery object as well */ public function __clone() { $this->dataQuery = clone $this->dataQuery; } /** * Return the internal {@link DataQuery} object for direct manipulation * * @return DataQuery */ public function dataQuery() { return $this->dataQuery; } /** * Returns the SQL query that will be used to get this DataList's records. Good for debugging. :-) * * @return SQLQuery */ public function sql() { return $this->dataQuery->query()->sql(); } /** * Add a WHERE clause to the query. * * @param string $filter * @return DataList */ public function where($filter) { $this->dataQuery->where($filter); return $this; } /** * Returns true if this DataList can be sorted by the given field. * * @param string $fieldName * @return boolean */ public function canSortBy($fieldName) { return $this->dataQuery()->query()->canSortBy($fieldName); } /** * * @param string $fieldName * @return boolean */ public function canFilterBy($fieldName) { if($t = singleton($this->dataClass)->hasDatabaseField($fieldName)){ return true; } return false; } /** * Add an join clause to this data list's query. * * @param type $join * @return DataList * @deprecated 3.0 */ public function join($join) { Deprecation::notice('3.0', 'Use innerJoin() or leftJoin() instead.'); $this->dataQuery->join($join); return $this; } /** * Restrict the records returned in this query by a limit clause * * @param string $limit */ public function limit($limit, $offset = 0) { if(!$limit && !$offset) { return $this; } if($limit && !is_numeric($limit)) { Deprecation::notice('3.0', 'Please pass limits as 2 arguments, rather than an array or SQL fragment.'); } $this->dataQuery->limit($limit, $offset); 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[]= ''.$this->getRelationName($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 a Object relation name to a Database name and apply the relation join to * the query * * @param string $field * @return string */ public function getRelationName($field) { if(strpos($field,'.') === false) { return '"'.$field.'"'; } $relations = explode('.', $field); $fieldName = array_pop($relations); $relationModelName = $this->dataQuery->applyRelation($field); return '"'.$relationModelName.'"."'.$fieldName.'"'; } /** * 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->whereAny($SQL_Statements); return $this; } /** * This method returns a list does not contain any DataObjects that exists in $list * * It does not return the resulting list, it only adds the constraints on the database to exclude * objects from $list. * The $list passed needs to contain the same dataclass as $this * * @param SS_List $list * @return DataList * @throws BadMethodCallException */ public function subtract(SS_List $list) { if($this->dataclass() != $list->dataclass()) { throw new InvalidArgumentException('The list passed must have the same dataclass as this class'); } $newlist = clone $this; $newlist->dataQuery->subtract($list->dataQuery()); return $newlist; } /** * Add an inner join clause to this data list's query. * * @param string $table * @param string $onClause * @param string $alias - if you want this table to be aliased under another name * @return DataList */ public function innerJoin($table, $onClause, $alias = null) { $this->dataQuery->innerJoin($table, $onClause, $alias); return $this; } /** * Add an left join clause to this data list's query. * * @param string $table * @param string $onClause * @param string $alias - if you want this table to be aliased under another name * @return DataList */ public function leftJoin($table, $onClause, $alias = null) { $this->dataQuery->leftJoin($table, $onClause, $alias); return $this; } /** * Return an array of the actual items that this DataList contains at this stage. * This is when the query is actually executed. * * @return array */ public function toArray() { $query = $this->dataQuery->query(); $rows = $query->execute(); $results = array(); foreach($rows as $row) { $results[] = $this->createDataObject($row); } return $results; } /** * Return this list as an array and every object it as an sub array as well * * @return type */ public function toNestedArray() { $result = array(); foreach($this as $item) { $result[] = $item->toMap(); } return $result; } public function debug() { $val = "