* @link https://github.com/pradosoft/prado * @copyright Copyright © 2005-2015 The PRADO Group * @license https://github.com/pradosoft/prado/blob/master/COPYRIGHT * @package System.Collections */ /** * TPagedList class * * TPagedList implements a list with paging functionality. * * TPagedList works in one of two modes, managed paging or customized paging, * specified by {@link setCustomPaging CustomPaging}. * - Managed paging ({@link setCustomPaging CustomPaging}=false) : * the list is assumed to contain all data and it will manage which page * of data are available to user. * - Customized paging ({@link setCustomPaging CustomPaging}=true) : * the list is assumed to contain only one page of data. An {@link onFetchData OnFetchData} * event will be raised if the list changes to a different page. * Developers can attach a handler to the event and supply the needed data. * The event handler can be written as follows, * * public function fetchData($sender,$param) * { * $offset=$param->Offset; // beginning index of the data needed * $limit=$param->Limit; // maximum number of data items needed * // get data according to the above two parameters * $param->Data=$data; * } * * * Data in TPagedList can be accessed like an integer-indexed array and can * be traversed using foreach. For example, * * $count=$list->Count; * for($index=0;$index<$count;++$index) * echo $list[$index]; * foreach($list as $index=>$item) // traverse each item in the list * * * The {@link setPageSize PageSize} property specifies the number of items in each page. * To access different page of data in the list, set {@link setCurrentPageIndex CurrentPageIndex} * or call {@link nextPage()}, {@link previousPage()}, or {@link gotoPage()}. * The total number of pages can be obtained by {@link getPageCount() PageCount}. * * * @author Qiang Xue * @package System.Collections * @since 3.0 */ class TPagedList extends TList { /** * @var boolean whether to allow custom paging */ private $_customPaging=false; /** * @var integer number of items in each page */ private $_pageSize=10; /** * @var integer current page index */ private $_currentPageIndex=-1; /** * @var integer user-assigned number of items in data source */ private $_virtualCount=-1; /** * Constructor. * @param array|Iterator the initial data. Default is null, meaning no initialization. * @param boolean whether the list is read-only. Always true for paged list. */ public function __construct($data=null,$readOnly=false) { parent::__construct($data,true); } /** * @return boolean whether to use custom paging. Defaults to false. */ public function getCustomPaging() { return $this->_customPaging; } /** * @param boolean whether to allow custom paging */ public function setCustomPaging($value) { $this->_customPaging=TPropertyValue::ensureBoolean($value); } /** * @return integer number of items in each page. Defaults to 10. */ public function getPageSize() { return $this->_pageSize; } /** * @param integer number of items in each page */ public function setPageSize($value) { if(($value=TPropertyValue::ensureInteger($value))>0) $this->_pageSize=$value; else throw new TInvalidDataValueException('pagedlist_pagesize_invalid'); } /** * @return integer current page index. Defaults to 0. */ public function getCurrentPageIndex() { return $this->_currentPageIndex; } /** * @param integer current page index * @throws TInvalidDataValueException if the page index is out of range */ public function setCurrentPageIndex($value) { if($this->gotoPage($value=TPropertyValue::ensureInteger($value))===false) throw new TInvalidDataValueException('pagedlist_currentpageindex_invalid'); } /** * Raises OnPageIndexChanged event. * This event is raised each time when the list changes to a different page. * @param TPagedListPageChangedEventParameter event parameter */ public function onPageIndexChanged($param) { $this->raiseEvent('OnPageIndexChanged',$this,$param); } /** * Raises OnFetchData event. * This event is raised each time when the list changes to a different page * and needs the new page of data. This event can only be raised when * {@link setCustomPaging CustomPaging} is true. * @param TPagedListFetchDataEventParameter event parameter */ public function onFetchData($param) { $this->raiseEvent('OnFetchData',$this,$param); } /** * Changes to a page with the specified page index. * @param integer page index * @return integer|boolean the new page index, false if page index is out of range. */ public function gotoPage($pageIndex) { if($pageIndex===$this->_currentPageIndex) return $pageIndex; if($this->_customPaging) { if($pageIndex>=0 && ($this->_virtualCount<0 || $pageIndex<$this->getPageCount())) { $param=new TPagedListFetchDataEventParameter($pageIndex,$this->_pageSize*$pageIndex,$this->_pageSize); $this->onFetchData($param); if(($data=$param->getData())!==null) { $this->setReadOnly(false); $this->copyFrom($data); $this->setReadOnly(true); $oldPage=$this->_currentPageIndex; $this->_currentPageIndex=$pageIndex; $this->onPageIndexChanged(new TPagedListPageChangedEventParameter($oldPage)); return $pageIndex; } else return false; } else return false; } else { if($pageIndex>=0 && $pageIndex<$this->getPageCount()) { $this->_currentPageIndex=$pageIndex; $this->onPageIndexChanged(null); return $pageIndex; } else return false; } } /** * Switches to the next page. * @return integer|boolean the new page index, false if next page is not available. */ public function nextPage() { return $this->gotoPage($this->_currentPageIndex+1); } /** * Switches to the previous page. * @return integer|boolean the new page index, false if previous page is not available. */ public function previousPage() { return $this->gotoPage($this->_currentPageIndex-1); } /** * @return integer user-assigned number of items in data source. Defaults to 0. */ public function getVirtualCount() { return $this->_virtualCount; } /** * @param integer user-assigned number of items in data source */ public function setVirtualCount($value) { if(($value=TPropertyValue::ensureInteger($value))<0) $value=-1; $this->_virtualCount=$value; } /** * @return integer number of pages, -1 if under custom paging mode and {@link setVirtualCount VirtualCount} is not set. */ public function getPageCount() { if($this->_customPaging) { if($this->_virtualCount>=0) return (int)(($this->_virtualCount+$this->_pageSize-1)/$this->_pageSize); else return -1; } else return (int)((parent::getCount()+$this->_pageSize-1)/$this->_pageSize); } /** * @return boolean whether the current page is the first page */ public function getIsFirstPage() { return $this->_currentPageIndex===0; } /** * @return boolean whether the current page is the last page */ public function getIsLastPage() { return $this->_currentPageIndex===$this->getPageCount()-1; } /** * @return integer the number of items in current page */ public function getCount() { if($this->_customPaging) return parent::getCount(); else { if($this->_currentPageIndex===$this->getPageCount()-1) return parent::getCount()-$this->_pageSize*$this->_currentPageIndex; else return $this->_pageSize; } } /** * @return Iterator iterator */ public function getIterator() { if($this->_customPaging) return parent::getIterator(); else { $data=$this->toArray(); return new TListIterator($data); } } /** * Returns the item at the specified offset. * This method is exactly the same as {@link offsetGet}. * @param integer the index of the item * @return mixed the item at the index * @throws TInvalidDataValueException if the index is out of the range */ public function itemAt($index) { if($this->_customPaging) return parent::itemAt($index); else return parent::itemAt($this->_pageSize*$this->_currentPageIndex+$index); } /** * @param mixed the item * @return integer the index of the item in the list (0 based), -1 if not found. */ public function indexOf($item) { $c=$this->getCount(); for($i=0;$i<$c;++$i) if($this->itemAt($i)===$item) return $i; return -1; } /** * Returns whether there is an item at the specified offset. * This method is required by the interface ArrayAccess. * @param integer the offset to check on * @return boolean */ public function offsetExists($offset) { return ($offset>=0 && $offset<$this->getCount()); } /** * Returns the item at the specified offset. * This method is required by the interface ArrayAccess. * @param integer the offset to retrieve item. * @return mixed the item at the offset * @throws TInvalidDataValueException if the offset is invalid */ public function offsetGet($offset) { return $this->itemAt($offset); } /** * @return array the list of items in array */ public function toArray() { $c=$this->getCount(); $array=array(); for($i=0;$i<$c;++$i) $array[$i]=$this->itemAt($i); return $array; } } /** * TPagedListPageChangedEventParameter class. * TPagedListPageChangedEventParameter is used as the parameter for * {@link TPagedList::onPageChanged OnPageChanged} event. * To obtain the page index before it was changed, use {@link getOldPageIndex OldPageIndex}. * * @author Qiang Xue * @package System.Collections * @since 3.0 */ class TPagedListPageChangedEventParameter extends TEventParameter { private $_oldPage; /** * Constructor. * @param integer old page index */ public function __construct($oldPage) { $this->_oldPage=$oldPage; } /** * @return integer the index of the page before the list changed to the new page */ public function getOldPageIndex() { return $this->_oldPage; } } /** * TPagedListFetchDataEventParameter class. * * TPagedListFetchDataEventParameter is used as the parameter for * {@link TPagedList::onFetchData OnFetchData} event. * To obtain the new page index, use {@link getNewPageIndex NewPageIndex}. * The {@link getOffset Offset} property refers to the index * of the first item in the new page, while {@link getLimit Limit} * specifies how many items are requested for the page. * Newly fetched data should be saved in {@link setData Data} property. * * @author Qiang Xue * @package System.Collections * @since 3.0 */ class TPagedListFetchDataEventParameter extends TEventParameter { private $_pageIndex; private $_offset; private $_limit; private $_data=null; /** * Constructor. * @param integer new page index * @param integer offset of the first item in the new page * @param integer number of items in the new page desired */ public function __construct($pageIndex,$offset,$limit) { $this->_pageIndex=$pageIndex; $this->_offset=$offset; $this->_limit=$limit; } /** * @return integer the zero-based index of the new page */ public function getNewPageIndex() { return $this->_pageIndex; } /** * @return integer offset of the first item in the new page */ public function getOffset() { return $this->_offset; } /** * @return integer number of items in the new page */ public function getLimit() { return $this->_limit; } /** * @return mixed new page data */ public function getData() { return $this->_data; } /** * @param mixed new page data */ public function setData($value) { $this->_data=$value; } }