00001 <?php
00025 require_once 'Zend/Loader/PluginLoader.php';
00026
00030 require_once 'Zend/Json.php';
00031
00038 class Zend_Paginator implements Countable, IteratorAggregate
00039 {
00045 const INTERNAL_ADAPTER = 'Zend_Paginator_Adapter_Internal';
00046
00051 const CACHE_TAG_PREFIX = 'Zend_Paginator_';
00052
00058 protected static $_adapterLoader = null;
00059
00065 protected static $_config = null;
00066
00072 protected static $_defaultScrollingStyle = 'Sliding';
00073
00079 protected static $_defaultItemCountPerPage = 10;
00080
00086 protected static $_scrollingStyleLoader = null;
00087
00093 protected static $_cache;
00094
00100 protected $_cacheEnabled = true;
00101
00107 protected $_adapter = null;
00108
00114 protected $_currentItemCount = null;
00115
00121 protected $_currentItems = null;
00122
00128 protected $_currentPageNumber = 1;
00129
00135 protected $_filter = null;
00136
00142 protected $_itemCountPerPage = null;
00143
00149 protected $_pageCount = null;
00150
00157 protected $_pageRange = 10;
00158
00164 protected $_pages = null;
00165
00171 protected $_view = null;
00172
00179 public static function addAdapterPrefixPath($prefix, $path)
00180 {
00181 self::getAdapterLoader()->addPrefixPath($prefix, $path);
00182 }
00183
00197 public static function addAdapterPrefixPaths(array $prefixPaths)
00198 {
00199 if (isset($prefixPaths['prefix']) && isset($prefixPaths['path'])) {
00200 self::addAdapterPrefixPath($prefixPaths['prefix'], $prefixPaths['path']);
00201 } else {
00202 foreach ($prefixPaths as $prefix => $path) {
00203 if (is_array($path) && isset($path['prefix']) && isset($path['path'])) {
00204 $prefix = $path['prefix'];
00205 $path = $path['path'];
00206 }
00207
00208 self::addAdapterPrefixPath($prefix, $path);
00209 }
00210 }
00211 }
00212
00219 public static function addScrollingStylePrefixPath($prefix, $path)
00220 {
00221 self::getScrollingStyleLoader()->addPrefixPath($prefix, $path);
00222 }
00223
00237 public static function addScrollingStylePrefixPaths(array $prefixPaths)
00238 {
00239 if (isset($prefixPaths['prefix']) && isset($prefixPaths['path'])) {
00240 self::addScrollingStylePrefixPath($prefixPaths['prefix'], $prefixPaths['path']);
00241 } else {
00242 foreach ($prefixPaths as $prefix => $path) {
00243 if (is_array($path) && isset($path['prefix']) && isset($path['path'])) {
00244 $prefix = $path['prefix'];
00245 $path = $path['path'];
00246 }
00247
00248 self::addScrollingStylePrefixPath($prefix, $path);
00249 }
00250 }
00251 }
00252
00261 public static function factory($data, $adapter = self::INTERNAL_ADAPTER,
00262 array $prefixPaths = null)
00263 {
00264 if ($data instanceof Zend_Paginator_AdapterAggregate) {
00265 return new self($data->getPaginatorAdapter());
00266 } else {
00267 if ($adapter == self::INTERNAL_ADAPTER) {
00268 if (is_array($data)) {
00269 $adapter = 'Array';
00270 } else if ($data instanceof Zend_Db_Table_Select) {
00271 $adapter = 'DbTableSelect';
00272 } else if ($data instanceof Zend_Db_Select) {
00273 $adapter = 'DbSelect';
00274 } else if ($data instanceof Iterator) {
00275 $adapter = 'Iterator';
00276 } else if (is_integer($data)) {
00277 $adapter = 'Null';
00278 } else {
00279 $type = (is_object($data)) ? get_class($data) : gettype($data);
00280
00284 require_once 'Zend/Paginator/Exception.php';
00285
00286 throw new Zend_Paginator_Exception('No adapter for type ' . $type);
00287 }
00288 }
00289
00290 $pluginLoader = self::getAdapterLoader();
00291
00292 if (null !== $prefixPaths) {
00293 foreach ($prefixPaths as $prefix => $path) {
00294 $pluginLoader->addPrefixPath($prefix, $path);
00295 }
00296 }
00297
00298 $adapterClassName = $pluginLoader->load($adapter);
00299
00300 return new self(new $adapterClassName($data));
00301 }
00302 }
00303
00309 public static function getAdapterLoader()
00310 {
00311 if (self::$_adapterLoader === null) {
00312 self::$_adapterLoader = new Zend_Loader_PluginLoader(
00313 array('Zend_Paginator_Adapter' => 'Zend/Paginator/Adapter')
00314 );
00315 }
00316
00317 return self::$_adapterLoader;
00318 }
00319
00325 public static function setConfig(Zend_Config $config)
00326 {
00327 self::$_config = $config;
00328
00329 $adapterPaths = $config->get('adapterpaths');
00330
00331 if ($adapterPaths != null) {
00332 self::addAdapterPrefixPaths($adapterPaths->adapterpath->toArray());
00333 }
00334
00335 $prefixPaths = $config->get('prefixpaths');
00336
00337 if ($prefixPaths != null) {
00338 self::addScrollingStylePrefixPaths($prefixPaths->prefixpath->toArray());
00339 }
00340
00341 $scrollingStyle = $config->get('scrollingstyle');
00342
00343 if ($scrollingStyle != null) {
00344 self::setDefaultScrollingStyle($scrollingStyle);
00345 }
00346 }
00347
00353 public static function getDefaultScrollingStyle()
00354 {
00355 return self::$_defaultScrollingStyle;
00356 }
00357
00363 public static function getDefaultItemCountPerPage()
00364 {
00365 return self::$_defaultItemCountPerPage;
00366 }
00367
00373 public static function setDefaultItemCountPerPage($count)
00374 {
00375 self::$_defaultItemCountPerPage = (int) $count;
00376 }
00377
00383 public static function setCache(Zend_Cache_Core $cache)
00384 {
00385 self::$_cache = $cache;
00386 }
00387
00393 public static function setDefaultScrollingStyle($scrollingStyle = 'Sliding')
00394 {
00395 self::$_defaultScrollingStyle = $scrollingStyle;
00396 }
00397
00404 public static function getScrollingStyleLoader()
00405 {
00406 if (self::$_scrollingStyleLoader === null) {
00407 self::$_scrollingStyleLoader = new Zend_Loader_PluginLoader(
00408 array('Zend_Paginator_ScrollingStyle' => 'Zend/Paginator/ScrollingStyle')
00409 );
00410 }
00411
00412 return self::$_scrollingStyleLoader;
00413 }
00414
00420 public function __construct($adapter)
00421 {
00422 if ($adapter instanceof Zend_Paginator_Adapter_Interface) {
00423 $this->_adapter = $adapter;
00424 } else if ($adapter instanceof Zend_Paginator_AdapterAggregate) {
00425 $this->_adapter = $adapter->getPaginatorAdapter();
00426 } else {
00430 require_once 'Zend/Paginator/Exception.php';
00431
00432 throw new Zend_Paginator_Exception(
00433 'Zend_Paginator only accepts instances of the type ' .
00434 'Zend_Paginator_Adapter_Interface or Zend_Paginator_AdapterAggregate.'
00435 );
00436 }
00437
00438 $config = self::$_config;
00439
00440 if ($config != null) {
00441 $setupMethods = array('ItemCountPerPage', 'PageRange');
00442
00443 foreach ($setupMethods as $setupMethod) {
00444 $value = $config->get(strtolower($setupMethod));
00445
00446 if ($value != null) {
00447 $setupMethod = 'set' . $setupMethod;
00448 $this->$setupMethod($value);
00449 }
00450 }
00451 }
00452 }
00453
00459 public function __toString()
00460 {
00461 try {
00462 $return = $this->render();
00463 return $return;
00464 } catch (Exception $e) {
00465 trigger_error($e->getMessage(), E_USER_WARNING);
00466 }
00467
00468 return '';
00469 }
00470
00477 public function setCacheEnabled($enable)
00478 {
00479 $this->_cacheEnabled = (bool)$enable;
00480 return $this;
00481 }
00482
00488 public function count()
00489 {
00490 if (!$this->_pageCount) {
00491 $this->_pageCount = $this->_calculatePageCount();
00492 }
00493
00494 return $this->_pageCount;
00495 }
00496
00502 public function getTotalItemCount()
00503 {
00504 return count($this->getAdapter());
00505 }
00506
00513 public function clearPageItemCache($pageNumber = null)
00514 {
00515 if (!$this->_cacheEnabled()) {
00516 return $this;
00517 }
00518
00519 if (null === $pageNumber) {
00520 $cleanTags = self::CACHE_TAG_PREFIX;
00521 foreach (self::$_cache->getIdsMatchingTags(array($this->_getCacheInternalId())) as $id) {
00522 if (preg_match('|'.self::CACHE_TAG_PREFIX."(\d+)_.*|", $id, $page)) {
00523 self::$_cache->remove($this->_getCacheId($page[1]));
00524 }
00525 }
00526 } else {
00527 $cleanId = $this->_getCacheId($pageNumber);
00528 self::$_cache->remove($cleanId);
00529 }
00530 return $this;
00531 }
00532
00540 public function getAbsoluteItemNumber($relativeItemNumber, $pageNumber = null)
00541 {
00542 $relativeItemNumber = $this->normalizeItemNumber($relativeItemNumber);
00543
00544 if ($pageNumber == null) {
00545 $pageNumber = $this->getCurrentPageNumber();
00546 }
00547
00548 $pageNumber = $this->normalizePageNumber($pageNumber);
00549
00550 return (($pageNumber - 1) * $this->getItemCountPerPage()) + $relativeItemNumber;
00551 }
00552
00558 public function getAdapter()
00559 {
00560 return $this->_adapter;
00561 }
00562
00568 public function getCurrentItemCount()
00569 {
00570 if ($this->_currentItemCount === null) {
00571 $this->_currentItemCount = $this->getItemCount($this->getCurrentItems());
00572 }
00573
00574 return $this->_currentItemCount;
00575 }
00576
00582 public function getCurrentItems()
00583 {
00584 if ($this->_currentItems === null) {
00585 $this->_currentItems = $this->getItemsByPage($this->getCurrentPageNumber());
00586 }
00587
00588 return $this->_currentItems;
00589 }
00590
00596 public function getCurrentPageNumber()
00597 {
00598 return $this->normalizePageNumber($this->_currentPageNumber);
00599 }
00600
00607 public function setCurrentPageNumber($pageNumber)
00608 {
00609 $this->_currentPageNumber = (integer) $pageNumber;
00610 $this->_currentItems = null;
00611 $this->_currentItemCount = null;
00612
00613 return $this;
00614 }
00615
00621 public function getFilter()
00622 {
00623 return $this->_filter;
00624 }
00625
00632 public function setFilter(Zend_Filter_Interface $filter)
00633 {
00634 $this->_filter = $filter;
00635
00636 return $this;
00637 }
00638
00647 public function getItem($itemNumber, $pageNumber = null)
00648 {
00649 $itemNumber = $this->normalizeItemNumber($itemNumber);
00650
00651 if ($pageNumber == null) {
00652 $pageNumber = $this->getCurrentPageNumber();
00653 }
00654
00655 $page = $this->getItemsByPage($pageNumber);
00656 $itemCount = $this->getItemCount($page);
00657
00658 if ($itemCount == 0) {
00662 require_once 'Zend/Paginator/Exception.php';
00663
00664 throw new Zend_Paginator_Exception('Page ' . $pageNumber . ' does not exist');
00665 }
00666
00667 if ($itemNumber > $itemCount) {
00671 require_once 'Zend/Paginator/Exception.php';
00672
00673 throw new Zend_Paginator_Exception('Page ' . $pageNumber . ' does not'
00674 . ' contain item number ' . $itemNumber);
00675 }
00676
00677 return $page[$itemNumber - 1];
00678 }
00679
00685 public function getItemCountPerPage()
00686 {
00687 if (empty($this->_itemCountPerPage)) {
00688 $this->_itemCountPerPage = self::getDefaultItemCountPerPage();
00689 }
00690
00691 return $this->_itemCountPerPage;
00692 }
00693
00700 public function setItemCountPerPage($itemCountPerPage)
00701 {
00702 $this->_itemCountPerPage = (integer) $itemCountPerPage;
00703 if ($this->_itemCountPerPage < 1) {
00704 $this->_itemCountPerPage = $this->getItemCountPerPage();
00705 }
00706 $this->_pageCount = $this->_calculatePageCount();
00707 $this->_currentItems = null;
00708 $this->_currentItemCount = null;
00709
00710 return $this;
00711 }
00712
00719 public function getItemCount($items)
00720 {
00721 $itemCount = 0;
00722
00723 if (is_array($items) || $items instanceof Countable) {
00724 $itemCount = count($items);
00725 } else {
00726 $itemCount = iterator_count($items);
00727 }
00728
00729 return $itemCount;
00730 }
00731
00737 public function getItemsByPage($pageNumber)
00738 {
00739 $pageNumber = $this->normalizePageNumber($pageNumber);
00740
00741 if ($this->_cacheEnabled()) {
00742 $data = self::$_cache->load($this->_getCacheId($pageNumber));
00743 if ($data !== false) {
00744 return $data;
00745 }
00746 }
00747
00748 $offset = ($pageNumber - 1) * $this->getItemCountPerPage();
00749
00750 $items = $this->_adapter->getItems($offset, $this->getItemCountPerPage());
00751
00752 $filter = $this->getFilter();
00753
00754 if ($filter !== null) {
00755 $items = $filter->filter($items);
00756 }
00757
00758 if (!$items instanceof Traversable) {
00759 $items = new ArrayIterator($items);
00760 }
00761
00762 if ($this->_cacheEnabled()) {
00763 self::$_cache->save($items, $this->_getCacheId($pageNumber), array($this->_getCacheInternalId()));
00764 }
00765
00766 return $items;
00767 }
00768
00774 public function getIterator()
00775 {
00776 return $this->getCurrentItems();
00777 }
00778
00784 public function getPageRange()
00785 {
00786 return $this->_pageRange;
00787 }
00788
00795 public function setPageRange($pageRange)
00796 {
00797 $this->_pageRange = (integer) $pageRange;
00798
00799 return $this;
00800 }
00801
00808 public function getPages($scrollingStyle = null)
00809 {
00810 if ($this->_pages === null) {
00811 $this->_pages = $this->_createPages($scrollingStyle);
00812 }
00813
00814 return $this->_pages;
00815 }
00816
00824 public function getPagesInRange($lowerBound, $upperBound)
00825 {
00826 $lowerBound = $this->normalizePageNumber($lowerBound);
00827 $upperBound = $this->normalizePageNumber($upperBound);
00828
00829 $pages = array();
00830
00831 for ($pageNumber = $lowerBound; $pageNumber <= $upperBound; $pageNumber++) {
00832 $pages[$pageNumber] = $pageNumber;
00833 }
00834
00835 return $pages;
00836 }
00837
00843 public function getPageItemCache()
00844 {
00845 $data = array();
00846 if ($this->_cacheEnabled()) {
00847 foreach (self::$_cache->getIdsMatchingTags(array($this->_getCacheInternalId())) as $id) {
00848 if (preg_match('|'.self::CACHE_TAG_PREFIX."(\d+)_.*|", $id, $page)) {
00849 $data[$page[1]] = self::$_cache->load($this->_getCacheId($page[1]));
00850 }
00851 }
00852 }
00853 return $data;
00854 }
00855
00862 public function getView()
00863 {
00864 if ($this->_view === null) {
00868 require_once 'Zend/Controller/Action/HelperBroker.php';
00869
00870 $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');
00871 if ($viewRenderer->view === null) {
00872 $viewRenderer->initView();
00873 }
00874 $this->_view = $viewRenderer->view;
00875 }
00876
00877 return $this->_view;
00878 }
00879
00886 public function setView(Zend_View_Interface $view = null)
00887 {
00888 $this->_view = $view;
00889
00890 return $this;
00891 }
00892
00899 public function normalizeItemNumber($itemNumber)
00900 {
00901 if ($itemNumber < 1) {
00902 $itemNumber = 1;
00903 }
00904
00905 if ($itemNumber > $this->getItemCountPerPage()) {
00906 $itemNumber = $this->getItemCountPerPage();
00907 }
00908
00909 return $itemNumber;
00910 }
00911
00918 public function normalizePageNumber($pageNumber)
00919 {
00920 if ($pageNumber < 1) {
00921 $pageNumber = 1;
00922 }
00923
00924 $pageCount = $this->count();
00925
00926 if ($pageCount > 0 && $pageNumber > $pageCount) {
00927 $pageNumber = $pageCount;
00928 }
00929
00930 return $pageNumber;
00931 }
00932
00939 public function render(Zend_View_Interface $view = null)
00940 {
00941 if (null !== $view) {
00942 $this->setView($view);
00943 }
00944
00945 $view = $this->getView();
00946
00947 return $view->paginationControl($this);
00948 }
00949
00955 public function toJson()
00956 {
00957 $currentItems = $this->getCurrentItems();
00958
00959 if ($currentItems instanceof Zend_Db_Table_Rowset_Abstract) {
00960 return Zend_Json::encode($currentItems->toArray());
00961 } else {
00962 return Zend_Json::encode($currentItems);
00963 }
00964 }
00965
00972 protected function _cacheEnabled()
00973 {
00974 return ((self::$_cache !== null) && $this->_cacheEnabled);
00975 }
00976
00987 protected function _getCacheId($page = null)
00988 {
00989 if ($page === null) {
00990 $page = $this->getCurrentPageNumber();
00991 }
00992 return self::CACHE_TAG_PREFIX . $page . '_' . $this->_getCacheInternalId();
00993 }
00994
01003 protected function _getCacheInternalId()
01004 {
01005 return md5(serialize($this->getAdapter()) . $this->getItemCountPerPage());
01006 }
01007
01013 protected function _calculatePageCount()
01014 {
01015 return (integer) ceil($this->getAdapter()->count() / $this->getItemCountPerPage());
01016 }
01017
01024 protected function _createPages($scrollingStyle = null)
01025 {
01026 $pageCount = $this->count();
01027 $currentPageNumber = $this->getCurrentPageNumber();
01028
01029 $pages = new stdClass();
01030 $pages->pageCount = $pageCount;
01031 $pages->itemCountPerPage = $this->getItemCountPerPage();
01032 $pages->first = 1;
01033 $pages->current = $currentPageNumber;
01034 $pages->last = $pageCount;
01035
01036
01037 if ($currentPageNumber - 1 > 0) {
01038 $pages->previous = $currentPageNumber - 1;
01039 }
01040
01041 if ($currentPageNumber + 1 <= $pageCount) {
01042 $pages->next = $currentPageNumber + 1;
01043 }
01044
01045
01046 $scrollingStyle = $this->_loadScrollingStyle($scrollingStyle);
01047 $pages->pagesInRange = $scrollingStyle->getPages($this);
01048 $pages->firstPageInRange = min($pages->pagesInRange);
01049 $pages->lastPageInRange = max($pages->pagesInRange);
01050
01051
01052 if ($this->getCurrentItems() !== null) {
01053 $pages->currentItemCount = $this->getCurrentItemCount();
01054 $pages->itemCountPerPage = $this->getItemCountPerPage();
01055 $pages->totalItemCount = $this->getTotalItemCount();
01056 $pages->firstItemNumber = (($currentPageNumber - 1) * $this->getItemCountPerPage()) + 1;
01057 $pages->lastItemNumber = $pages->firstItemNumber + $pages->currentItemCount - 1;
01058 }
01059
01060 return $pages;
01061 }
01062
01069 protected function _loadScrollingStyle($scrollingStyle = null)
01070 {
01071 if ($scrollingStyle === null) {
01072 $scrollingStyle = self::$_defaultScrollingStyle;
01073 }
01074
01075 switch (strtolower(gettype($scrollingStyle))) {
01076 case 'object':
01077 if (!$scrollingStyle instanceof Zend_Paginator_ScrollingStyle_Interface) {
01081 require_once 'Zend/View/Exception.php';
01082
01083 throw new Zend_View_Exception('Scrolling style must implement ' .
01084 'Zend_Paginator_ScrollingStyle_Interface');
01085 }
01086
01087 return $scrollingStyle;
01088
01089 case 'string':
01090 $className = self::getScrollingStyleLoader()->load($scrollingStyle);
01091
01092 return new $className();
01093
01094 case 'null':
01095
01096
01097 default:
01101 require_once 'Zend/View/Exception.php';
01102
01103 throw new Zend_View_Exception('Scrolling style must be a class ' .
01104 'name or object implementing Zend_Paginator_ScrollingStyle_Interface');
01105 }
01106 }
01107 }