Skip to content

Commit

Permalink
FEATURE: implement SS_Sortable->reverse()
Browse files Browse the repository at this point in the history
API CHANGE: SQLQuery:: now an array object rather than string. Existing strings will continue to work
  • Loading branch information
wilr authored and Sam Minnee committed Apr 27, 2012
1 parent cbc5d3c commit 9938b64
Show file tree
Hide file tree
Showing 11 changed files with 456 additions and 172 deletions.
13 changes: 12 additions & 1 deletion model/ArrayList.php
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,18 @@ public function column($colName = 'ID') {
public function canSortBy($by) {
return true;
}


/**
* Reverses an {@link ArrayList}
*
* @return ArrayList
*/
public function reverse() {
$this->items = array_reverse($this->items);

return $this;
}

/**
* 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.
Expand Down
75 changes: 48 additions & 27 deletions model/DataList.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortab
public function __construct($dataClass) {
$this->dataClass = $dataClass;
$this->dataQuery = new DataQuery($this->dataClass);

parent::__construct();
}

Expand Down Expand Up @@ -148,51 +149,48 @@ public function limit($limit, $offset = 0) {
/**
* Set the sort order of this data list
*
* @return DataList
* @see SS_List::sort()
* @see SQLQuery::orderby
*
* @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'));
*
* @return DataList
*/
public function sort() {
if(count(func_get_args())==0){
if(count(func_get_args()) == 0) {
return $this;
}
if(count(func_get_args())>2){

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(count(func_get_args()) == 2) {
// sort('Name','Desc')
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;

$this->dataQuery->sort(func_get_arg(0), func_get_arg(1));
}

// sort('Name') - default to ASC sorting if not specified
if(is_string(func_get_arg(0)) && func_get_arg(0)){
else 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')){
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');
$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;
else if(is_array(func_get_arg(0))) {
// sort(array('Name'=>'desc'));
$this->dataQuery->sort(null, null); // wipe the sort

foreach(func_get_arg(0) as $col => $dir) {
$this->dataQuery->sort($this->getRelationName($col), $dir, false);
}
$this->dataQuery->sort(implode(',', $sort));
return $this;
}

return $this;
Expand All @@ -201,15 +199,17 @@ public function sort() {
/**
* 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
*
* @return DataList
*/
public function filter() {
$numberFuncArgs = count(func_get_args());
Expand Down Expand Up @@ -284,9 +284,8 @@ private function applyFilterContext($field, $comparisators, $value) {
}

/**
* Exclude the list to not contain items with these charactaristics
* Exclude the list to not contain items with these characteristics
*
* @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
Expand All @@ -295,6 +294,8 @@ private function applyFilterContext($field, $comparisators, $value) {
* @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
*
* @return DataList
*/
public function exclude(){
$numberFuncArgs = count(func_get_args());
Expand Down Expand Up @@ -338,6 +339,7 @@ public function subtract(SS_List $list) {

$newlist = clone $this;
$newlist->dataQuery->subtract($list->dataQuery());

return $newlist;
}

Expand All @@ -351,6 +353,7 @@ public function subtract(SS_List $list) {
*/
public function innerJoin($table, $onClause, $alias = null) {
$this->dataQuery->innerJoin($table, $onClause, $alias);

return $this;
}

Expand All @@ -364,6 +367,7 @@ public function innerJoin($table, $onClause, $alias = null) {
*/
public function leftJoin($table, $onClause, $alias = null) {
$this->dataQuery->leftJoin($table, $onClause, $alias);

return $this;
}

Expand All @@ -377,9 +381,11 @@ public function toArray() {
$query = $this->dataQuery->query();
$rows = $query->execute();
$results = array();

foreach($rows as $row) {
$results[] = $this->createDataObject($row);
}

return $results;
}

Expand All @@ -390,6 +396,7 @@ public function toArray() {
*/
public function toNestedArray() {
$result = array();

foreach($this as $item) {
$result[] = $item->toMap();
}
Expand All @@ -399,6 +406,7 @@ public function toNestedArray() {

public function debug() {
$val = "<h2>" . $this->class . "</h2><ul>";

foreach($this->toNestedArray() as $item) {
$val .= "<li style=\"list-style-type: disc; margin-left: 20px\">" . Debug::text($item) . "</li>";
}
Expand Down Expand Up @@ -574,6 +582,7 @@ public function find($key, $value) {
public function byIDs(array $ids) {
$baseClass = ClassInfo::baseDataClass($this->dataClass);
$this->where("\"$baseClass\".\"ID\" IN (" . implode(',', $ids) .")");

return $this;
}

Expand All @@ -586,6 +595,7 @@ public function byIDs(array $ids) {
public function byID($id) {
$baseClass = ClassInfo::baseDataClass($this->dataClass);
$clone = clone $this;

return $clone->where("\"$baseClass\".\"ID\" = " . (int)$id)->First();
}

Expand Down Expand Up @@ -755,7 +765,18 @@ public function removeByID($itemID) {
$item = $this->byID($itemID);
if($item) return $item->delete();
}


/**
* Reverses a list of items.
*
* @return DataList
*/
public function reverse() {
$this->dataQuery->reverseSort();

return $this;
}

/**
* This method won't function on DataLists due to the specific query that it represent
*
Expand Down
98 changes: 64 additions & 34 deletions model/DataQuery.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,31 @@
/**
* An object representing a query of data from the DataObject's supporting database.
* Acts as a wrapper over {@link SQLQuery} and performs all of the query generation.
* Used extensively by DataList.
* Used extensively by {@link DataList}.
*
* @subpackage model
* @package sapphire
*/
class DataQuery {

/**
* @var String
*/
protected $dataClass;

/**
* @var SQLQuery
*/
protected $query;

/**
* @var array
*/
protected $collidingFields = array();

/**
* @var Boolean
*/
private $queryFinalised = false;

// TODO: replace subclass_access with this
Expand All @@ -20,7 +37,8 @@ class DataQuery {

/**
* Create a new DataQuery.
* @param $dataClass The name of the DataObject class that you wish to query
*
* @param String The name of the DataObject class that you wish to query
*/
function __construct($dataClass) {
$this->dataClass = $dataClass;
Expand Down Expand Up @@ -172,49 +190,50 @@ protected function ensureSelectContainsOrderbyColumns($query) {
$baseClass = array_shift($tableClasses);

if($query->orderby) {
$orderByFields = explode(',', $query->orderby);
foreach($orderByFields as $ob => $col) {
$col = trim($col);

// don't touch functions in the ORDER BY or function calls selected as fields
if(strpos($col, '(') !== false || preg_match('/_SortColumn/', $col)) continue;

$columnParts = explode(' ', $col);
if (count($columnParts) == 2) {
$col = $columnParts[0];
$dir = $columnParts[1];
} else {
$dir = 'ASC';
}
$orderby = $query->getOrderBy();

$orderByFields[$ob] = $col . ' ' . $dir;
$col = str_replace('"', '', $col);
foreach($orderby as $k => $dir) {
// don't touch functions in the ORDER BY or function calls
// selected as fields
if(strpos($k, '(') !== false || preg_match('/_SortColumn/', $k))
continue;

$col = str_replace('"', '', trim($k));
$parts = explode('.', $col);

if(count($parts) == 1) {
$databaseFields = DataObject::database_fields($baseClass);
// database_fields() doesn't return ID, so we need to manually add it here

// database_fields() doesn't return ID, so we need to
// manually add it here
$databaseFields['ID'] = true;

if(isset($databaseFields[$parts[0]])) {
$qualCol = "\"$baseClass\".\"{$parts[0]}\"";
$orderByFields[$ob] = trim($qualCol . " " . $dir);

// remove original sort
unset($orderby[$k]);

// add new columns sort
$orderby[$qualCol] = $dir;

} else {
$qualCol = "\"$parts[0]\"";
}

if(!isset($query->select[$parts[0]]) && !in_array($qualCol, $query->select)) {
if(!isset($query->select[$col]) && !in_array($qualCol, $query->select)) {
$query->select[] = $qualCol;
}
} else {
$qualCol = '"' . implode('"."', $parts) . '"';

if(!in_array($qualCol, $query->select)) {
$query->select[] = $qualCol;
}
}
}

$query->orderby = implode(',', $orderByFields);
$query->orderby = $orderby;
}
}

Expand Down Expand Up @@ -357,17 +376,28 @@ function whereAny($filter) {

/**
* Set the ORDER BY clause of this query
*
* @see SQLQuery::orderby()
*
* @return DataQuery
*/
function sort($sort) {
if($sort) {
$clone = $this;
// Add quoting to sort expression if it's a simple column name
if(!is_array($sort) && preg_match('/^[A-Z][A-Z0-9_]*$/i', $sort)) $sort = "\"$sort\"";
$clone->query->orderby($sort);
return $clone;
} else {
return $this;
}
function sort($sort = null, $direction = null, $clear = true) {
$clone = $this;
$clone->query->orderby($sort, $direction, $clear);

return $clone;
}

/**
* Reverse order by clause
*
* @return DataQuery
*/
function reverseSort() {
$clone = $this;

$clone->query->reverseOrderBy();
return $clone;
}

/**
Expand Down
Loading

0 comments on commit 9938b64

Please sign in to comment.