diff --git a/core/PaginatedList.php b/core/PaginatedList.php index 779021d6e..6a2718bc4 100644 --- a/core/PaginatedList.php +++ b/core/PaginatedList.php @@ -149,7 +149,7 @@ class PaginatedList extends SS_ListDecorator { */ public function getIterator() { return new IteratorIterator( - $this->list->getRange($this->getPageStart(), $this->pageLength) + $this->list->limit($this->pageLength, $this->getPageStart()) ); } diff --git a/forms/TableListField.php b/forms/TableListField.php index bd20da389..05cc6b5bd 100644 --- a/forms/TableListField.php +++ b/forms/TableListField.php @@ -430,7 +430,7 @@ JS $SQL_start = 0; } - $items = $items->getRange($SQL_start, $SQL_limit); + $items = $items->limit($SQL_limit, $SQL_start); } return $items; diff --git a/forms/gridfield/GridField.php b/forms/gridfield/GridField.php index ac1fbbd39..7f9628ebe 100755 --- a/forms/gridfield/GridField.php +++ b/forms/gridfield/GridField.php @@ -323,7 +323,7 @@ class GridField extends FormField { foreach($this->components as $item) { if($item instanceof GridField_HTMLProvider) { $fragments = $item->getHTMLFragments($this); - foreach($fragments as $k => $v) { + if($fragments) foreach($fragments as $k => $v) { $k = strtolower($k); if(!isset($content[$k])) $content[$k] = ""; $content[$k] .= $v . "\n"; @@ -650,7 +650,7 @@ class GridField extends FormField { continue; } - if(in_array($actionName, array_map('strtolower', $component->getActions($this)))) { + if(is_array($component->getActions($this)) && in_array($actionName, array_map('strtolower', $component->getActions($this)))) { return $component->handleAction($this, $actionName, $args, $data); } } diff --git a/forms/gridfield/GridFieldConfig.php b/forms/gridfield/GridFieldConfig.php index 76ad37a6b..b6da4213f 100755 --- a/forms/gridfield/GridFieldConfig.php +++ b/forms/gridfield/GridFieldConfig.php @@ -132,10 +132,14 @@ class GridFieldConfig_Base extends GridFieldConfig { */ public function __construct($itemsPerPage=null) { $this->addComponent(new GridFieldToolbarHeader()); - $this->addComponent(new GridFieldSortableHeader()); - $this->addComponent(new GridFieldFilterHeader()); + $this->addComponent($sort = new GridFieldSortableHeader()); + $this->addComponent($filter = new GridFieldFilterHeader()); $this->addComponent(new GridFieldDataColumns()); - $this->addComponent(new GridFieldPaginator($itemsPerPage)); + $this->addComponent($pagination = new GridFieldPaginator($itemsPerPage)); + + $sort->throwExceptionOnBadDataType(false); + $filter->throwExceptionOnBadDataType(false); + $pagination->throwExceptionOnBadDataType(false); } } @@ -159,13 +163,17 @@ class GridFieldConfig_RecordEditor extends GridFieldConfig { */ public function __construct($itemsPerPage=null) { $this->addComponent(new GridFieldToolbarHeader()); - $this->addComponent(new GridFieldSortableHeader()); - $this->addComponent(new GridFieldFilterHeader()); + $this->addComponent($sort = new GridFieldSortableHeader()); + $this->addComponent($filter = new GridFieldFilterHeader()); $this->addComponent(new GridFieldDataColumns()); $this->addComponent(new GridFieldEditButton()); $this->addComponent(new GridFieldDeleteAction()); - $this->addComponent(new GridFieldPaginator($itemsPerPage)); + $this->addComponent($pagination = new GridFieldPaginator($itemsPerPage)); $this->addComponent(new GridFieldDetailForm()); + + $sort->throwExceptionOnBadDataType(false); + $filter->throwExceptionOnBadDataType(false); + $pagination->throwExceptionOnBadDataType(false); } } @@ -202,12 +210,16 @@ class GridFieldConfig_RelationEditor extends GridFieldConfig { public function __construct($itemsPerPage=null) { $this->addComponent(new GridFieldToolbarHeader()); $this->addComponent(new GridFieldAddExistingAutocompleter()); - $this->addComponent(new GridFieldSortableHeader()); - $this->addComponent(new GridFieldFilterHeader()); + $this->addComponent($sort = new GridFieldSortableHeader()); + $this->addComponent($filter = new GridFieldFilterHeader()); $this->addComponent(new GridFieldDataColumns()); $this->addComponent(new GridFieldEditButton()); - $this->addComponent(new GridFieldDeleteAction(true)); - $this->addComponent(new GridFieldPaginator($itemsPerPage)); + $this->addComponent(new GridFieldDeleteAction()); + $this->addComponent($pagination = new GridFieldPaginator($itemsPerPage)); $this->addComponent(new GridFieldDetailForm()); + + $sort->throwExceptionOnBadDataType(false); + $filter->throwExceptionOnBadDataType(false); + $pagination->throwExceptionOnBadDataType(false); } } diff --git a/forms/gridfield/GridFieldFilterHeader.php b/forms/gridfield/GridFieldFilterHeader.php index 4990efe60..b59c38ee6 100644 --- a/forms/gridfield/GridFieldFilterHeader.php +++ b/forms/gridfield/GridFieldFilterHeader.php @@ -8,17 +8,54 @@ * @subpackage fields-relational */ class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider { + + /** + * See {@link throwExceptionOnBadDataType()} + */ + protected $throwExceptionOnBadDataType = true; + /** + * Determine what happens when this component is used with a list that isn't {@link SS_Filterable}. + * + * - true: An exception is thrown + * - false: This component will be ignored - it won't make any changes to the GridField. + * + * By default, this is set to true so that it's clearer what's happening, but the predefined + * {@link GridFieldConfig} subclasses set this to false for flexibility. + */ + public function throwExceptionOnBadDataType($throwExceptionOnBadDataType) { + $this->throwExceptionOnBadDataType = $throwExceptionOnBadDataType; + } + + /** + * Check that this dataList is of the right data type. + * Returns false if it's a bad data type, and if appropriate, throws an exception. + */ + protected function checkDataType($dataList) { + if($dataList instanceof SS_Filterable) { + return true; + } else { + if($this->throwExceptionOnBadDataType) { + throw new LogicException(get_class($this) . " expects an SS_Filterable list to be passed to the GridField."); + } + return false; + } + } + /** * * @param GridField $gridField * @return array */ public function getActions($gridField) { + if(!$this->checkDataType($gridField->getList())) return; + return array('filter', 'reset'); } function handleAction(GridField $gridField, $actionName, $arguments, $data) { + if(!$this->checkDataType($gridField->getList())) return; + $state = $gridField->State->GridFieldFilterHeader; if($actionName === 'filter') { if(isset($data['filter'])){ @@ -39,6 +76,8 @@ class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataMan * @return SS_List */ public function getManipulatedData(GridField $gridField, SS_List $dataList) { + if(!$this->checkDataType($dataList)) return $dataList; + $state = $gridField->State->GridFieldFilterHeader; if(!isset($state->Columns)) { return $dataList; @@ -54,6 +93,8 @@ class GridFieldFilterHeader implements GridField_HTMLProvider, GridField_DataMan } public function getHTMLFragments($gridField) { + if(!$this->checkDataType($gridField->getList())) return; + $forTemplate = new ArrayData(array()); $forTemplate->Fields = new ArrayList; diff --git a/forms/gridfield/GridFieldPaginator.php b/forms/gridfield/GridFieldPaginator.php index 7805f227d..282d50495 100755 --- a/forms/gridfield/GridFieldPaginator.php +++ b/forms/gridfield/GridFieldPaginator.php @@ -20,6 +20,11 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu */ protected $itemClass = 'GridFieldPaginator_Row'; + /** + * See {@link throwExceptionOnBadDataType()} + */ + protected $throwExceptionOnBadDataType = true; + /** * * @param int $itemsPerPage - How many items should be displayed per page @@ -27,6 +32,34 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu public function __construct($itemsPerPage=null) { if($itemsPerPage) $this->itemsPerPage = $itemsPerPage; } + + /** + * Determine what happens when this component is used with a list that isn't {@link SS_Filterable}. + * + * - true: An exception is thrown + * - false: This component will be ignored - it won't make any changes to the GridField. + * + * By default, this is set to true so that it's clearer what's happening, but the predefined + * {@link GridFieldConfig} subclasses set this to false for flexibility. + */ + public function throwExceptionOnBadDataType($throwExceptionOnBadDataType) { + $this->throwExceptionOnBadDataType = $throwExceptionOnBadDataType; + } + + /** + * Check that this dataList is of the right data type. + * Returns false if it's a bad data type, and if appropriate, throws an exception. + */ + protected function checkDataType($dataList) { + if($dataList instanceof SS_Limitable) { + return true; + } else { + if($this->throwExceptionOnBadDataType) { + throw new LogicException(get_class($this) . " expects an SS_Limitable list to be passed to the GridField."); + } + return false; + } + } /** * @@ -34,6 +67,8 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu * @return array */ public function getActions($gridField) { + if(!$this->checkDataType($gridField->getList())) return; + return array('paginate'); } @@ -46,12 +81,16 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu * @return void */ public function handleAction(GridField $gridField, $actionName, $arguments, $data) { + if(!$this->checkDataType($gridField->getList())) return; + if($actionName !== 'paginate') { return; } $state = $gridField->State->GridFieldPaginator; $state->currentPage = (int)$arguments; } + + protected $totalItems = 0; /** * @@ -60,18 +99,23 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu * @return SS_List */ public function getManipulatedData(GridField $gridField, SS_List $dataList) { + if(!$this->checkDataType($dataList)) return $dataList; + + $this->totalItems = $gridField->getList()->count(); + $state = $gridField->State->GridFieldPaginator; - if(!is_int($state->currentPage)) + if(!is_int($state->currentPage)) { $state->currentPage = 1; + } - if(!$this->getListPaginatable($dataList)) { + if(!($dataList instanceof SS_Limitable)) { return $dataList; } if(!$state->currentPage) { - return $dataList->getRange(0, (int)$this->itemsPerPage); + return $dataList->limit((int)$this->itemsPerPage); } $startRow = $this->itemsPerPage * ($state->currentPage - 1); - return $dataList->getRange((int)$startRow, (int)$this->itemsPerPage); + return $dataList->limit((int)$this->itemsPerPage, (int)$startRow); } /** @@ -80,13 +124,14 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu * @return array */ public function getHTMLFragments($gridField) { + if(!$this->checkDataType($gridField->getList())) return; + $state = $gridField->State->GridFieldPaginator; if(!is_int($state->currentPage)) $state->currentPage = 1; // Figure out which page and record range we're on - $countList = clone $gridField->List; - $totalRows = $countList->limit(null)->count(); + $totalRows = $this->totalItems; if(!$totalRows) return array(); $totalPages = ceil($totalRows/$this->itemsPerPage); @@ -159,14 +204,4 @@ class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipu return $this->itemsPerPage; } - /** Duck check to see if list support methods we need to paginate */ - protected function getListPaginatable(SS_List $list) { - // If no list yet, not paginatable - if (!$list) return false; - // Check for methods we use - if(!method_exists($list, 'getRange')) return false; - if(!method_exists($list, 'limit')) return false; - // Default it true - return true; - } } diff --git a/forms/gridfield/GridFieldSortableHeader.php b/forms/gridfield/GridFieldSortableHeader.php index 130cadc46..e52138864 100644 --- a/forms/gridfield/GridFieldSortableHeader.php +++ b/forms/gridfield/GridFieldSortableHeader.php @@ -8,11 +8,46 @@ * @subpackage fields-relational */ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider { + + /** + * See {@link throwExceptionOnBadDataType()} + */ + protected $throwExceptionOnBadDataType = true; + + /** + * Determine what happens when this component is used with a list that isn't {@link SS_Filterable}. + * + * - true: An exception is thrown + * - false: This component will be ignored - it won't make any changes to the GridField. + * + * By default, this is set to true so that it's clearer what's happening, but the predefined + * {@link GridFieldConfig} subclasses set this to false for flexibility. + */ + public function throwExceptionOnBadDataType($throwExceptionOnBadDataType) { + $this->throwExceptionOnBadDataType = $throwExceptionOnBadDataType; + } + + /** + * Check that this dataList is of the right data type. + * Returns false if it's a bad data type, and if appropriate, throws an exception. + */ + protected function checkDataType($dataList) { + if($dataList instanceof SS_Sortable) { + return true; + } else { + if($this->throwExceptionOnBadDataType) { + throw new LogicException(get_class($this) . " expects an SS_Sortable list to be passed to the GridField."); + } + return false; + } + } /** * Returns the header row providing titles with sort buttons */ public function getHTMLFragments($gridField) { + if(!$this->checkDataType($gridField->getList())) return; + $forTemplate = new ArrayData(array()); $forTemplate->Fields = new ArrayList; @@ -58,10 +93,14 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM * @return array */ public function getActions($gridField) { + if(!$this->checkDataType($gridField->getList())) return; + return array('sortasc', 'sortdesc'); } function handleAction(GridField $gridField, $actionName, $arguments, $data) { + if(!$this->checkDataType($gridField->getList())) return; + $state = $gridField->State->GridFieldSortableHeader; switch($actionName) { case 'sortasc': @@ -77,6 +116,8 @@ class GridFieldSortableHeader implements GridField_HTMLProvider, GridField_DataM } public function getManipulatedData(GridField $gridField, SS_List $dataList) { + if(!$this->checkDataType($dataList)) return $dataList; + $state = $gridField->State->GridFieldSortableHeader; if ($state->SortColumn == "") { return $dataList; diff --git a/model/ArrayList.php b/model/ArrayList.php index 0df9e7c2f..fe7bd91f2 100644 --- a/model/ArrayList.php +++ b/model/ArrayList.php @@ -5,7 +5,7 @@ * @package sapphire * @subpackage model */ -class ArrayList extends ViewableData implements SS_List { +class ArrayList extends ViewableData implements SS_List, SS_Filterable, SS_Sortable, SS_Limitable { /** * Holds the items in the list @@ -119,6 +119,18 @@ class ArrayList extends ViewableData implements SS_List { * @return ArrayList */ public function getRange($offset, $length) { + Deprecation::notice("3.0", 'getRange($offset, $length) is deprecated. Use limit($length, $offset) instead. Note the new argument order.'); + return $this->limit($length, $offset); + } + + /** + * Get a sub-range of this dataobjectset as an array + * + * @param int $offset + * @param int $length + * @return ArrayList + */ + public function limit($length, $offset = 0) { return new ArrayList(array_slice($this->items, $offset, $length)); } @@ -357,6 +369,15 @@ class ArrayList extends ViewableData implements SS_List { call_user_func_array('array_multisort', $multisortArgs); return $this; } + + /** + * Returns true if the given column can be used to filter the records. + * + * It works by checking the fields available in the first record of the list. + */ + public function canFilterBy($by) { + return array_key_exists($by, $this->first()); + } /** * Filter the list to include items with these charactaristics diff --git a/model/DataList.php b/model/DataList.php index 5e83a5d7a..1984af0df 100644 --- a/model/DataList.php +++ b/model/DataList.php @@ -6,7 +6,7 @@ * @package sapphire * @subpackage model */ -class DataList extends ViewableData implements SS_List { +class DataList extends ViewableData implements SS_List, SS_Filterable, SS_Sortable, SS_Limitable { /** * The DataObject class name that this data list is querying * @@ -145,8 +145,14 @@ class DataList extends ViewableData implements SS_List { * * @param string $limit */ - public function limit($limit) { - $this->dataQuery->limit($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; } @@ -545,7 +551,8 @@ class DataList extends ViewableData implements SS_List { * @return DataList */ public function getRange($offset, $length) { - return $this->limit(array('start' => $offset, 'limit' => $length)); + Deprecation::notice("3.0", 'getRange($offset, $length) is deprecated. Use limit($length, $offset) instead. Note the new argument order.'); + return $this->limit($length, $offset); } /** @@ -813,7 +820,7 @@ class DataList extends ViewableData implements SS_List { * @return bool */ public function offsetExists($key) { - return ($this->getRange($key, 1)->First() != null); + return ($this->limit(1,$key)->First() != null); } /** @@ -823,7 +830,7 @@ class DataList extends ViewableData implements SS_List { * @return DataObject */ public function offsetGet($key) { - return $this->getRange($key, 1)->First(); + return $this->limit(1, $key)->First(); } /** diff --git a/model/DataObject.php b/model/DataObject.php index ca972f6b2..c684ea7af 100644 --- a/model/DataObject.php +++ b/model/DataObject.php @@ -1309,7 +1309,7 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity * * @return HasManyList The components of the one-to-many relationship. */ - public function getComponents($componentName, $filter = "", $sort = "", $join = "", $limit = "") { + public function getComponents($componentName, $filter = "", $sort = "", $join = "", $limit = null) { $result = null; if(!$componentClass = $this->has_many($componentName)) { @@ -2479,11 +2479,17 @@ class DataObject extends ViewableData implements DataObjectInterface, i18nEntity * * @return mixed The objects matching the filter, in the class specified by $containerClass */ - public static function get($callerClass, $filter = "", $sort = "", $join = "", $limit = "", $containerClass = "DataList") { + public static function get($callerClass, $filter = "", $sort = "", $join = "", $limit = null, $containerClass = "DataList") { // Todo: Determine if we can deprecate for 3.0.0 and use DI or something instead // Todo: Make the $containerClass method redundant if($containerClass != "DataList") user_error("The DataObject::get() \$containerClass argument has been deprecated", E_USER_NOTICE); - $result = DataList::create($callerClass)->where($filter)->sort($sort)->limit($limit); + $result = DataList::create($callerClass)->where($filter)->sort($sort); + if($limit && strpos($limit, ',') !== false) { + $limitArguments = explode(',', $limit); + $result->limit($limitArguments[1],$limitArguments[0]); + } elseif($limit) { + $result->limit($limit); + } if($join) $result = $result->join($join); $result->setModel(DataModel::inst()); return $result; diff --git a/model/DataQuery.php b/model/DataQuery.php index a11dca4e0..de6514e5a 100644 --- a/model/DataQuery.php +++ b/model/DataQuery.php @@ -373,9 +373,9 @@ class DataQuery { /** * Set the limit of this query */ - function limit($limit) { + function limit($limit, $offset = 0) { $clone = $this; - $clone->query->limit($limit); + $clone->query->limit($limit, $offset); return $clone; } diff --git a/model/Filterable.php b/model/Filterable.php new file mode 100644 index 000000000..5fcdda99b --- /dev/null +++ b/model/Filterable.php @@ -0,0 +1,40 @@ +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 + */ + public function filter(); + + /** + * Exclude the list to not contain items with these charactaristics + * + * @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(); + +} \ No newline at end of file diff --git a/model/Limitable.php b/model/Limitable.php new file mode 100644 index 000000000..1c0560b66 --- /dev/null +++ b/model/Limitable.php @@ -0,0 +1,19 @@ +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(); - - /** - * Filter the list to include items with these charactaristics - * - * @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 - */ - public function filter(); - - /** - * Exclude the list to not contain items with these charactaristics - * - * @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(); - } \ No newline at end of file diff --git a/model/ListDecorator.php b/model/ListDecorator.php index a5d706cd2..19fb11150 100644 --- a/model/ListDecorator.php +++ b/model/ListDecorator.php @@ -7,7 +7,7 @@ * @package sapphire * @subpackage model */ -abstract class SS_ListDecorator extends ViewableData implements SS_List { +abstract class SS_ListDecorator extends ViewableData implements SS_List, SS_Sortable, SS_Filterable, SS_Limitable { protected $list; @@ -61,10 +61,6 @@ abstract class SS_ListDecorator extends ViewableData implements SS_List { $this->list->remove($itemObject); } - public function getRange($offset, $length) { - return $this->list->getRange($offset, $length); - } - public function getIterator() { return $this->list->getIterator(); } @@ -123,6 +119,10 @@ abstract class SS_ListDecorator extends ViewableData implements SS_List { return call_user_func_array(array($this->list, 'sort'), $args); } + public function canFilterBy($by) { + return $this->list->canFilterBy($by); + } + /** * Filter the list to include items with these charactaristics * @@ -136,6 +136,10 @@ abstract class SS_ListDecorator extends ViewableData implements SS_List { return call_user_func_array(array($this->list, 'filter'), $args); } + public function limit($limit, $offset = 0) { + return $this->list->getRange($length, $offset); + } + /** * Exclude the list to not contain items with these charactaristics * diff --git a/model/SQLQuery.php b/model/SQLQuery.php index f818ceff1..c283474a5 100644 --- a/model/SQLQuery.php +++ b/model/SQLQuery.php @@ -252,10 +252,10 @@ class SQLQuery { * @param string|array $limit * @return SQLQuery This instance */ - public function limit($limit) { + public function limit($limit, $offset = 0) { if($limit && is_numeric($limit)) { $this->limit = array( - 'start' => 0, + 'start' => $offset, 'limit' => $limit, ); } else if($limit && is_string($limit)) { @@ -667,7 +667,7 @@ class SQLQuery { function firstRow() { $query = clone $this; $offset = $this->limit ? $this->limit['start'] : 0; - $query->limit(array('start' => $offset, 'limit' => 1)); + $query->limit(1, $offset); return $query; } @@ -677,7 +677,7 @@ class SQLQuery { function lastRow() { $query = clone $this; $offset = $this->limit ? $this->limit['start'] : 0; - $query->limit(array('start' => $this->count() + $offset - 1, 'limit' => 1)); + $query->limit(1, $this->count() + $offset - 1); return $query; } diff --git a/model/Sortable.php b/model/Sortable.php new file mode 100644 index 000000000..e916f3529 --- /dev/null +++ b/model/Sortable.php @@ -0,0 +1,29 @@ +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(); + +} \ No newline at end of file diff --git a/tests/forms/GridFieldTest.php b/tests/forms/GridFieldTest.php index 8012554d6..24334b0ed 100644 --- a/tests/forms/GridFieldTest.php +++ b/tests/forms/GridFieldTest.php @@ -30,12 +30,15 @@ class GridFieldTest extends SapphireTest { $expectedComponents = new ArrayList(array( new GridFieldToolbarHeader(), - new GridFieldSortableHeader, - new GridFieldFilterHeader, - new GridFieldDataColumns, - new GridFieldPaginator, - new GridState_Component, + $sort = new GridFieldSortableHeader(), + $filter = new GridFieldFilterHeader(), + new GridFieldDataColumns(), + $pagination = new GridFieldPaginator(), + new GridState_Component(), )); + $sort->throwExceptionOnBadDataType(false); + $filter->throwExceptionOnBadDataType(false); + $pagination->throwExceptionOnBadDataType(false); $this->assertEquals($expectedComponents, $obj->getConfig()->getComponents(), 'Testing default Config'); } diff --git a/tests/forms/gridfield/GridFieldToolbarHeaderTest.php b/tests/forms/gridfield/GridFieldToolbarHeaderTest.php index 2f67df433..ae6558e1d 100644 --- a/tests/forms/gridfield/GridFieldToolbarHeaderTest.php +++ b/tests/forms/gridfield/GridFieldToolbarHeaderTest.php @@ -1,6 +1,11 @@ logInWithPermission('ADMIN'); @@ -8,7 +13,7 @@ class GridFieldToolbarHeaderTest extends SapphireTest { $config = new GridFieldConfig(); $config->addComponent($titleField = new GridFieldToolbarHeader()); $actions = new FieldList(); - $grid = new GridField('TestField', 'Test Field', new DataList('Company'),$config); + $grid = new GridField('TestField', 'Test Field', new DataList('GridFieldToolbarHeaderTest_Company'),$config); $fields = new FieldList($rootTab = new TabSet("Root",$tabMain = new Tab('Main',$grid))); $form = new Form(Controller::curr(), "TestForm", $fields, $actions); @@ -23,7 +28,7 @@ class GridFieldToolbarHeaderTest extends SapphireTest { $config = new GridFieldConfig(); $config->addComponent($titleField = new GridFieldToolbarHeader()); $actions = new FieldList(); - $grid = new GridField('TestField', 'Test Field', new DataList('Company'),$config); + $grid = new GridField('TestField', 'Test Field', new DataList('GridFieldToolbarHeaderTest_Company'),$config); $fields = new FieldList($rootTab = new TabSet("Root",$tabMain = new Tab('Main',$grid))); $form = new Form(Controller::curr(), "TestForm", $fields, $actions); @@ -36,11 +41,18 @@ class GridFieldToolbarHeaderTest extends SapphireTest { if(Member::currentUser()) { Member::currentUser()->logOut(); } $config = new GridFieldConfig(); $config->addComponent($titleField = new GridFieldToolbarHeader()); - $grid = new GridField('TestField', 'Test Field', new DataList('Company'),$config); + $grid = new GridField('TestField', 'Test Field', new DataList('GridFieldToolbarHeaderTest_Company'),$config); $fields = new FieldList(new TabSet("Root",$tabMain = new Tab('Main',$grid))); $form = new Form(Controller::curr(), "TestForm", $fields, new FieldList()); $html = $form->forTemplate(); $this->assertNotContains('data-icon="add"', $html, "HTML should not contain the 'add new' button"); } -} \ No newline at end of file +} +class GridFieldToolbarHeaderTest_Company extends DataObject implements TestOnly { + + public static $db = array( + 'Name' => 'Varchar(100)' + ); + +} diff --git a/tests/forms/gridfield/GridFieldToolbarHeaderTest.yml b/tests/forms/gridfield/GridFieldToolbarHeaderTest.yml new file mode 100644 index 000000000..88156d5df --- /dev/null +++ b/tests/forms/gridfield/GridFieldToolbarHeaderTest.yml @@ -0,0 +1,5 @@ +GridFieldToolbarHeaderTest_Company: + test-company-1: + Name: Test company 1 + test-company-2: + Name: Test company 2 diff --git a/tests/model/ArrayListTest.php b/tests/model/ArrayListTest.php index bf84fff9f..f06f1a312 100644 --- a/tests/model/ArrayListTest.php +++ b/tests/model/ArrayListTest.php @@ -61,11 +61,11 @@ class ArrayListTest extends SapphireTest { )); } - public function testGetRange() { + public function testLimit() { $list = new ArrayList(array( array('Key' => 1), array('Key' => 2), array('Key' => 3) )); - $this->assertEquals($list->getRange(1, 2)->toArray(), array( + $this->assertEquals($list->limit(2,1)->toArray(), array( array('Key' => 2), array('Key' => 3) )); } diff --git a/tests/model/DataListTest.php b/tests/model/DataListTest.php index 86850b26d..bec99455c 100755 --- a/tests/model/DataListTest.php +++ b/tests/model/DataListTest.php @@ -50,7 +50,7 @@ class DataListTest extends SapphireTest { $this->assertEquals(array('Bob', 'Joe', 'Phil'), $list->column('Name')); // We can also restrict the output to a range - $this->assertEquals(array('Joe', 'Phil'), $list->getRange(1,2)->column('Name')); + $this->assertEquals(array('Joe', 'Phil'), $list->limit(2, 1)->column('Name')); } function testDataClass() { diff --git a/tests/model/DataObjectTest.php b/tests/model/DataObjectTest.php index 6e44a1b92..056c8f2c9 100644 --- a/tests/model/DataObjectTest.php +++ b/tests/model/DataObjectTest.php @@ -197,18 +197,12 @@ class DataObjectTest extends SapphireTest { // There's 4 records in total $this->assertEquals(4, $players->count()); - // Testing "## offset ##" syntax - $this->assertEquals(4, $players->limit("20 OFFSET 0")->count()); - $this->assertEquals(0, $players->limit("20 OFFSET 20")->count()); - $this->assertEquals(2, $players->limit("2 OFFSET 0")->count()); - $this->assertEquals(1, $players->limit("5 OFFSET 3")->count()); - // Testing "##, ##" syntax - $this->assertEquals(4, $players->limit("20")->count()); - $this->assertEquals(4, $players->limit("0, 20")->count()); - $this->assertEquals(0, $players->limit("20, 20")->count()); - $this->assertEquals(2, $players->limit("0, 2")->count()); - $this->assertEquals(1, $players->limit("3, 5")->count()); + $this->assertEquals(4, $players->limit(20)->count()); + $this->assertEquals(4, $players->limit(20, 0)->count()); + $this->assertEquals(0, $players->limit(20, 20)->count()); + $this->assertEquals(2, $players->limit(2, 0)->count()); + $this->assertEquals(1, $players->limit(5, 3)->count()); } /** diff --git a/tests/model/SQLQueryTest.php b/tests/model/SQLQueryTest.php index 8d92021b4..556bdef8e 100755 --- a/tests/model/SQLQueryTest.php +++ b/tests/model/SQLQueryTest.php @@ -87,19 +87,13 @@ class SQLQueryTest extends SapphireTest { // numeric limit $query = new SQLQuery(); $query->from[] = "MyTable"; - $query->limit("99"); + $query->limit(99); $this->assertEquals("SELECT * FROM MyTable LIMIT 99", $query->sql()); - // array limit - $query = new SQLQuery(); - $query->from[] = "MyTable"; - $query->limit(array('limit'=>99)); - $this->assertEquals("SELECT * FROM MyTable LIMIT 99", $query->sql()); - // array limit with start (MySQL specific) $query = new SQLQuery(); $query->from[] = "MyTable"; - $query->limit(array('limit'=>99, 'start'=>97)); + $query->limit(99, 97); $this->assertEquals("SELECT * FROM MyTable LIMIT 99 OFFSET 97", $query->sql()); } }