silverstripe-framework/Forms/GridField/GridFieldPaginator.php

307 lines
9.0 KiB
PHP
Raw Normal View History

<?php
namespace SilverStripe\Forms\GridField;
use SilverStripe\Core\Config\Configurable;
2016-09-09 08:43:05 +02:00
use SilverStripe\ORM\Limitable;
use SilverStripe\ORM\SS_List;
use SilverStripe\ORM\UnsavedRelationList;
use SilverStripe\View\ArrayData;
use SilverStripe\View\SSViewer;
use LogicException;
/**
2014-08-15 08:53:05 +02:00
* GridFieldPaginator paginates the {@link GridField} list and adds controls
* to the bottom of the {@link GridField}.
*/
class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider {
use Configurable;
2014-08-15 08:53:05 +02:00
/**
* Specifies default items per page
2014-08-15 08:53:05 +02:00
*
* @config
* @var int
*/
private static $default_items_per_page = 15;
2014-08-15 08:53:05 +02:00
/**
* @var int
*/
protected $itemsPerPage;
/**
* See {@link setThrowExceptionOnBadDataType()}
*/
protected $throwExceptionOnBadDataType = true;
/**
*
* @param int $itemsPerPage - How many items should be displayed per page
*/
public function __construct($itemsPerPage=null) {
$this->itemsPerPage = $itemsPerPage ?: GridFieldPaginator::config()->get('default_items_per_page');
}
2014-08-15 08:53:05 +02:00
/**
* Determine what happens when this component is used with a list that isn't {@link SS_Filterable}.
2014-08-15 08:53:05 +02:00
*
* - true: An exception is thrown
* - false: This component will be ignored - it won't make any changes to the GridField.
2014-08-15 08:53:05 +02:00
*
* 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.
*
* @param bool $throwExceptionOnBadDataType
* @return $this
*/
public function setThrowExceptionOnBadDataType($throwExceptionOnBadDataType) {
2014-08-15 08:53:05 +02:00
$this->throwExceptionOnBadDataType = $throwExceptionOnBadDataType;
return $this;
}
/**
* See {@link setThrowExceptionOnBadDataType()}
*
* @return bool
*/
public function getThrowExceptionOnBadDataType() {
return $this->throwExceptionOnBadDataType;
}
2014-08-15 08:53:05 +02:00
/**
* 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.
*
* @param SS_List $dataList
* @return bool
*/
protected function checkDataType($dataList) {
2016-09-09 08:43:05 +02:00
if($dataList instanceof 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;
}
}
/**
*
* @param GridField $gridField
* @return array
*/
public function getActions($gridField) {
if(!$this->checkDataType($gridField->getList())) {
return [];
}
2014-08-15 08:53:05 +02:00
return array('paginate');
}
/**
*
* @param GridField $gridField
* @param string $actionName
* @param string $arguments
* @param array $data
* @return void
*/
public function handleAction(GridField $gridField, $actionName, $arguments, $data) {
if(!$this->checkDataType($gridField->getList())) return;
2014-08-15 08:53:05 +02:00
if($actionName !== 'paginate') {
return;
}
$state = $this->getGridPagerState($gridField);
$state->currentPage = (int)$arguments;
}
2014-08-15 08:53:05 +02:00
protected $totalItems = 0;
2014-08-15 08:53:05 +02:00
/**
* Retrieves/Sets up the state object used to store and retrieve information
* about the current paging details of this GridField
* @param GridField $gridField
2014-08-15 08:53:05 +02:00
* @return GridState_Data
*/
protected function getGridPagerState(GridField $gridField) {
$state = $gridField->State->GridFieldPaginator;
2014-08-15 08:53:05 +02:00
// Force the state to the initial page if none is set
$state->currentPage(1);
2014-08-15 08:53:05 +02:00
return $state;
}
/**
*
* @param GridField $gridField
* @param SS_List $dataList
2014-08-15 08:53:05 +02:00
* @return SS_List
*/
public function getManipulatedData(GridField $gridField, SS_List $dataList) {
if(!$this->checkDataType($dataList)) return $dataList;
2014-08-15 08:53:05 +02:00
$state = $this->getGridPagerState($gridField);
2014-08-15 08:53:05 +02:00
// Update item count prior to filter. GridFieldPageCount will rely on this value
$this->totalItems = $dataList->count();
$startRow = $this->itemsPerPage * ($state->currentPage - 1);
// Prevent visiting a page with an offset higher than the total number of items
if($startRow >= $this->totalItems) {
$state->currentPage = 1;
$startRow = 0;
}
2016-09-09 08:43:05 +02:00
if(!($dataList instanceof Limitable) || ($dataList instanceof UnsavedRelationList)) {
return $dataList;
}
2014-08-15 08:53:05 +02:00
return $dataList->limit((int)$this->itemsPerPage, (int)$startRow);
}
2014-08-15 08:53:05 +02:00
/**
* Determines arguments to be passed to the template for building this field
*
* @param GridField $gridField
* @return ArrayData If paging is available this will be an ArrayData
* object of paging details with these parameters:
* <ul>
* <li>OnlyOnePage: boolean - Is there only one page?</li>
* <li>FirstShownRecord: integer - Number of the first record displayed</li>
* <li>LastShownRecord: integer - Number of the last record displayed</li>
* <li>NumRecords: integer - Total number of records</li>
* <li>NumPages: integer - The number of pages</li>
* <li>CurrentPageNum (optional): integer - If OnlyOnePage is false, the number of the current page</li>
* <li>FirstPage (optional): GridField_FormAction - Button to go to the first page</li>
* <li>PreviousPage (optional): GridField_FormAction - Button to go to the previous page</li>
* <li>NextPage (optional): GridField_FormAction - Button to go to the next page</li>
* <li>LastPage (optional): GridField_FormAction - Button to go to last page</li>
* </ul>
*/
public function getTemplateParameters(GridField $gridField) {
if(!$this->checkDataType($gridField->getList())) {
return null;
}
2014-08-15 08:53:05 +02:00
$state = $this->getGridPagerState($gridField);
// Figure out which page and record range we're on
$totalRows = $this->totalItems;
if (!$totalRows) {
return null;
}
$totalPages = (int)ceil($totalRows/$this->itemsPerPage);
if($totalPages == 0) {
$totalPages = 1;
}
$firstShownRecord = ($state->currentPage - 1) * $this->itemsPerPage + 1;
if($firstShownRecord > $totalRows) {
$firstShownRecord = $totalRows;
}
$lastShownRecord = $state->currentPage * $this->itemsPerPage;
if($lastShownRecord > $totalRows) {
$lastShownRecord = $totalRows;
}
2014-08-15 08:53:05 +02:00
// If there is only 1 page for all the records in list, we don't need to go further
// to sort out those first page, last page, pre and next pages, etc
// we are not render those in to the paginator.
if($totalPages === 1){
return new ArrayData(array(
'OnlyOnePage' => true,
'FirstShownRecord' => $firstShownRecord,
'LastShownRecord' => $lastShownRecord,
'NumRecords' => $totalRows,
'NumPages' => $totalPages
));
} else {
// First page button
$firstPage = new GridField_FormAction($gridField, 'pagination_first', 'First', 'paginate', 1);
2016-07-01 03:37:29 +02:00
$firstPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-double-left ss-gridfield-firstpage');
if($state->currentPage == 1) {
$firstPage = $firstPage->performDisabledTransformation();
}
// Previous page button
$previousPageNum = $state->currentPage <= 1 ? 1 : $state->currentPage - 1;
$previousPage = new GridField_FormAction($gridField, 'pagination_prev', 'Previous',
'paginate', $previousPageNum);
2016-07-01 03:37:29 +02:00
$previousPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-left ss-gridfield-previouspage');
if($state->currentPage == 1)
$previousPage = $previousPage->performDisabledTransformation();
// Next page button
$nextPageNum = $state->currentPage >= $totalPages ? $totalPages : $state->currentPage + 1;
$nextPage = new GridField_FormAction($gridField, 'pagination_next', 'Next',
'paginate', $nextPageNum);
2016-07-01 03:37:29 +02:00
$nextPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-right ss-gridfield-nextpage');
if($state->currentPage == $totalPages)
$nextPage = $nextPage->performDisabledTransformation();
// Last page button
$lastPage = new GridField_FormAction($gridField, 'pagination_last', 'Last', 'paginate', $totalPages);
2016-07-01 03:37:29 +02:00
$lastPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-double-right ss-gridfield-lastpage');
if($state->currentPage == $totalPages) {
$lastPage = $lastPage->performDisabledTransformation();
}
// Render in template
return new ArrayData(array(
'OnlyOnePage' => false,
'FirstPage' => $firstPage,
'PreviousPage' => $previousPage,
'CurrentPageNum' => $state->currentPage,
'NumPages' => $totalPages,
'NextPage' => $nextPage,
'LastPage' => $lastPage,
'FirstShownRecord' => $firstShownRecord,
'LastShownRecord' => $lastShownRecord,
'NumRecords' => $totalRows
));
}
}
/**
*
* @param GridField $gridField
* @return array
*/
public function getHTMLFragments($gridField) {
$forTemplate = $this->getTemplateParameters($gridField);
if(!$forTemplate) {
return null;
}
$template = SSViewer::get_templates_by_class($this, '_Row', __CLASS__);
return array(
'footer' => $forTemplate->renderWith(
$template,
array('Colspan' => count($gridField->getColumns()))
)
);
}
/**
* @param int $num
* @return $this
*/
public function setItemsPerPage($num) {
$this->itemsPerPage = $num;
return $this;
}
/**
* @return Int
*/
public function getItemsPerPage() {
return $this->itemsPerPage;
}
}