PageRenderTime 46ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/paginator/classes/Paginator.php

https://github.com/dari88/kohanaPress-Sample_Code_for_kohana_3.3
PHP | 883 lines | 426 code | 107 blank | 350 comment | 50 complexity | 4420686068ecc2b0a9a11ad1836d0ae4 MD5 | raw file
Possible License(s): LGPL-2.1, GPL-2.0
  1. <?php defined('SYSPATH') OR die('No direct script access.');
  2. /**
  3. * Zend Framework
  4. *
  5. * LICENSE
  6. *
  7. * This source file is subject to the new BSD license that is bundled
  8. * with this package in the file LICENSE.txt.
  9. * It is also available through the world-wide-web at this URL:
  10. * http://framework.zend.com/license/new-bsd
  11. * If you did not receive a copy of the license and are unable to
  12. * obtain it through the world-wide-web, please send an email
  13. * to license@zend.com so we can send you a copy immediately.
  14. *
  15. * @category Zend
  16. * @package Paginator
  17. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  18. * @license http://framework.zend.com/license/new-bsd New BSD License
  19. * @version $Id: Paginator.php 23775 2011-03-01 17:25:24Z ralph $
  20. */
  21. /**
  22. * @category Zend
  23. * @package Paginator
  24. * @copyright Copyright (c) 2005-2011 Zend Technologies USA Inc. (http://www.zend.com)
  25. * @license http://framework.zend.com/license/new-bsd New BSD License
  26. */
  27. /**
  28. * @category kohana
  29. * @package Paginator modified by dari88
  30. * @copyright Copyright (c) dari88
  31. * @license New BSD License
  32. */
  33. class Paginator implements Countable, IteratorAggregate {
  34. /**
  35. * The cache tag prefix used to namespace Paginator results in the cache
  36. *
  37. */
  38. const CACHE_TAG_PREFIX = 'Paginator_';
  39. /**
  40. * Default scrolling style
  41. *
  42. * @var string
  43. */
  44. protected static $_default_scrolling_style = 'Sliding';
  45. /**
  46. * Default item count per page
  47. *
  48. * @var int
  49. */
  50. protected static $_default_item_count_per_page = 10;
  51. /**
  52. * Default number of local pages (i.e., the number of discretes
  53. * page numbers that will be displayed, including the current
  54. * page number)
  55. *
  56. * @var int
  57. */
  58. protected static $_default_page_range = 10;
  59. /**
  60. * Cache object
  61. *
  62. * @var Cache_Core
  63. */
  64. protected static $_cache;
  65. /**
  66. * Enable or disable the cache by Paginator instance
  67. *
  68. * @var bool
  69. */
  70. protected $_cache_enabled = true;
  71. /**
  72. * Adapter
  73. *
  74. * @var Paginator_Interface
  75. */
  76. protected $_adapter = null;
  77. /**
  78. * Number of items in the current page
  79. *
  80. * @var integer
  81. */
  82. protected $_current_item_count = null;
  83. /**
  84. * Current page items
  85. *
  86. * @var Traversable
  87. */
  88. protected $_current_items = null;
  89. /**
  90. * Current page number (starting from 1)
  91. *
  92. * @var integer
  93. */
  94. protected $_current_page_number = 1;
  95. /**
  96. * Number of items per page
  97. *
  98. * @var integer
  99. */
  100. protected $_item_count_per_page = null;
  101. /**
  102. * Number of pages
  103. *
  104. * @var integer
  105. */
  106. protected $_page_count = null;
  107. /**
  108. * Number of local pages (i.e., the number of discrete page numbers
  109. * that will be displayed, including the current page number)
  110. *
  111. * @var integer
  112. */
  113. protected $_page_range = null;
  114. /**
  115. * Pages
  116. *
  117. * @var array
  118. */
  119. protected $_pages = null;
  120. /**
  121. * Default url, page option's name, url options.
  122. *
  123. * @var array
  124. */
  125. protected $_default_url = '';
  126. protected $_default_page_query_name = 'page';
  127. protected $_default_option_query = '';
  128. /**
  129. * url, page option's name, url options.
  130. *
  131. * @var array
  132. */
  133. protected $_url = null;
  134. protected $_page_query_name = null;
  135. protected $_option_query = null;
  136. /**
  137. * Constructor.
  138. *
  139. * @param Paginator_Interface|Paginator_AdapterAggregate $adapter
  140. */
  141. public function __construct($adapter)
  142. {
  143. if ($adapter instanceof Paginator_Iterator)
  144. {
  145. $this->_adapter = $adapter;
  146. }
  147. else
  148. {
  149. throw new Exception(
  150. 'Paginator only accepts instances of the type ' .
  151. 'Paginator_Iterator.'
  152. );
  153. }
  154. }
  155. /**
  156. * Factory.
  157. *
  158. * @param mixed $data
  159. * @param string $adapter
  160. * @param array $prefixPaths
  161. * @return Paginator
  162. */
  163. public static function factory($data)
  164. {
  165. return new self(new Paginator_Iterator($data));
  166. }
  167. /**
  168. * Returns the default scrolling style.
  169. *
  170. * @return string
  171. */
  172. public static function get_default_scrolling_style()
  173. {
  174. return self::$_default_scrolling_style;
  175. }
  176. /**
  177. * Get the default item count per page
  178. *
  179. * @return int
  180. */
  181. public static function get_default_item_count_per_page()
  182. {
  183. return self::$_default_item_count_per_page;
  184. }
  185. /**
  186. * Set the default item count per page
  187. *
  188. * @param int $count
  189. */
  190. public static function set_default_item_count_per_page($count)
  191. {
  192. self::$_default_item_count_per_page = (int) $count;
  193. }
  194. /**
  195. * Get the default page range
  196. *
  197. * @return int
  198. */
  199. public static function get_default_page_range()
  200. {
  201. return self::$_default_page_range;
  202. }
  203. /**
  204. * Set the default page range
  205. *
  206. * @param int $count
  207. */
  208. public static function set_default_page_range($count)
  209. {
  210. self::$_default_page_range = (int) $count;
  211. }
  212. /**
  213. * Sets a cache object
  214. *
  215. * @param Cache_Core $cache
  216. */
  217. public static function set_cache(Cache_Core $cache)
  218. {
  219. self::$_cache = $cache;
  220. }
  221. /**
  222. * Sets the default scrolling style.
  223. *
  224. * @param string $scrolling_style
  225. */
  226. public static function set_default_scrolling_style($scrolling_style = 'Sliding')
  227. {
  228. self::$_default_scrolling_style = $scrolling_style;
  229. }
  230. /**
  231. * Enables/Disables the cache for this instance
  232. *
  233. * @param bool $enable
  234. * @return Paginator
  235. */
  236. public function set_cache_enabled($enable)
  237. {
  238. $this->_cache_enabled = (bool) $enable;
  239. return $this;
  240. }
  241. /**
  242. * Returns the number of pages.
  243. *
  244. * @return integer
  245. */
  246. public function count()
  247. {
  248. if ( ! $this->_page_count)
  249. {
  250. $this->_page_count = $this->_calculate_page_count();
  251. }
  252. return $this->_page_count;
  253. }
  254. /**
  255. * Returns the total number of items available.
  256. *
  257. * @return integer
  258. */
  259. public function get_total_item_count()
  260. {
  261. return count($this->get_adapter());
  262. }
  263. /**
  264. * Clear the page item cache.
  265. *
  266. * @param int $page_number
  267. * @return Paginator
  268. */
  269. public function clear_page_item_cache($page_number = null)
  270. {
  271. if ( ! $this->_cache_enabled())
  272. {
  273. return $this;
  274. }
  275. if (null === $page_number)
  276. {
  277. foreach (self::$_cache->getIdsMatchingTags(array($this->_get_cache_internal_id())) as $id)
  278. {
  279. if (preg_match('|' . self::CACHE_TAG_PREFIX . "(\d+)_.*|", $id, $page))
  280. {
  281. self::$_cache->remove($this->_get_cache_id($page[1]));
  282. }
  283. }
  284. }
  285. else
  286. {
  287. $clean_id = $this->_get_cache_id($page_number);
  288. self::$_cache->remove($clean_id);
  289. }
  290. return $this;
  291. }
  292. /**
  293. * Returns the absolute item number for the specified item.
  294. *
  295. * @param integer $relative_item_number Relative item number
  296. * @param integer $page_number Page number
  297. * @return integer
  298. */
  299. public function get_absolute_item_number($relative_item_number, $page_number = null)
  300. {
  301. $relative_item_number = $this->normalize_item_number($relative_item_number);
  302. if ($page_number == null)
  303. {
  304. $page_number = $this->get_current_page_number();
  305. }
  306. $page_number = $this->normalize_page_number($page_number);
  307. return (($page_number - 1) * $this->get_item_count_per_page()) + $relative_item_number;
  308. }
  309. /**
  310. * Returns the adapter.
  311. *
  312. * @return Paginator_Interface
  313. */
  314. public function get_adapter()
  315. {
  316. return $this->_adapter;
  317. }
  318. /**
  319. * Returns the number of items for the current page.
  320. *
  321. * @return integer
  322. */
  323. public function get_current_item_count()
  324. {
  325. if ($this->_current_item_count === null)
  326. {
  327. $this->_current_item_count = $this->get_item_count($this->get_current_items());
  328. }
  329. return $this->_current_item_count;
  330. }
  331. /**
  332. * Returns the items for the current page.
  333. *
  334. * @return Traversable
  335. */
  336. public function get_current_items()
  337. {
  338. if ($this->_current_items === null)
  339. {
  340. $this->_current_items = $this->get_items_by_page($this->get_current_page_number());
  341. }
  342. return $this->_current_items;
  343. }
  344. /**
  345. * Returns the current page number.
  346. *
  347. * @return integer
  348. */
  349. public function get_current_page_number()
  350. {
  351. return $this->normalize_page_number($this->_current_page_number);
  352. }
  353. /**
  354. * Sets the current page number.
  355. *
  356. * @param integer $page_number Page number
  357. * @return Paginator $this
  358. */
  359. public function set_current_page_number($page_number)
  360. {
  361. $this->_current_page_number = (integer) $page_number;
  362. $this->_current_items = null;
  363. $this->_current_item_count = null;
  364. return $this;
  365. }
  366. /**
  367. * Returns an item from a page. The current page is used if there's no
  368. * page sepcified.
  369. *
  370. * @param integer $item_number Item number (1 to item_count_per_page)
  371. * @param integer $page_number
  372. * @return mixed
  373. */
  374. public function get_item($item_number, $page_number = null)
  375. {
  376. if ($page_number == null)
  377. {
  378. $page_number = $this->get_current_page_number();
  379. }
  380. else if ($page_number < 0)
  381. {
  382. $page_number = ($this->count() + 1) + $page_number;
  383. }
  384. $page = $this->get_items_by_page($page_number);
  385. $item_count = $this->get_item_count($page);
  386. if ($item_count == 0)
  387. {
  388. throw new Exception('Page ' . $page_number . ' does not exist');
  389. }
  390. if ($item_number < 0)
  391. {
  392. $item_number = ($item_count + 1) + $item_number;
  393. }
  394. $item_number = $this->normalize_item_number($item_number);
  395. if ($item_number > $item_count)
  396. {
  397. throw new Exception('Page ' . $page_number . ' does not'
  398. . ' contain item number ' . $item_number);
  399. }
  400. return $page[$item_number - 1];
  401. }
  402. /**
  403. * Returns the number of items per page.
  404. *
  405. * @return integer
  406. */
  407. public function get_item_count_per_page()
  408. {
  409. if (empty($this->_item_count_per_page))
  410. {
  411. $this->_item_count_per_page = self::get_default_item_count_per_page();
  412. }
  413. return $this->_item_count_per_page;
  414. }
  415. /**
  416. * Sets the number of items per page.
  417. *
  418. * @param integer $item_count_per_page
  419. * @return Paginator $this
  420. */
  421. public function set_item_count_per_page($item_count_per_page = -1)
  422. {
  423. $this->_item_count_per_page = (integer) $item_count_per_page;
  424. if ($this->_item_count_per_page < 1)
  425. {
  426. $this->_item_count_per_page = $this->get_total_item_count();
  427. }
  428. $this->_page_count = $this->_calculate_page_count();
  429. $this->_current_items = null;
  430. $this->_current_item_count = null;
  431. return $this;
  432. }
  433. /**
  434. * Returns the number of items in a collection.
  435. *
  436. * @param mixed $items Items
  437. * @return integer
  438. */
  439. public function get_item_count($items)
  440. {
  441. $item_count = 0;
  442. if (is_array($items) || $items instanceof Countable)
  443. {
  444. $item_count = count($items);
  445. }
  446. else
  447. { // $items is something like LimitIterator
  448. $item_count = iterator_count($items);
  449. }
  450. return $item_count;
  451. }
  452. /**
  453. * Returns the items for a given page.
  454. *
  455. * @return Traversable
  456. */
  457. public function get_items_by_page($page_number)
  458. {
  459. $page_number = $this->normalize_page_number($page_number);
  460. if ($this->_cache_enabled())
  461. {
  462. $data = self::$_cache->load($this->_get_cache_id($page_number));
  463. if ($data !== false)
  464. {
  465. return $data;
  466. }
  467. }
  468. $offset = ($page_number - 1) * $this->get_item_count_per_page();
  469. $items = $this->_adapter->get_items($offset, $this->get_item_count_per_page());
  470. if ( ! $items instanceof Traversable)
  471. {
  472. $items = new ArrayIterator($items);
  473. }
  474. if ($this->_cache_enabled())
  475. {
  476. self::$_cache->save($items, $this->_get_cache_id($page_number), array($this->_get_cache_internal_id()));
  477. }
  478. return $items;
  479. }
  480. /**
  481. * Returns a foreach-compatible iterator.
  482. *
  483. * @return Traversable
  484. */
  485. public function getIterator()
  486. {
  487. return $this->get_current_items();
  488. }
  489. /**
  490. * Returns the page range (see property declaration above).
  491. *
  492. * @return integer
  493. */
  494. public function get_page_range()
  495. {
  496. if (null === $this->_page_range)
  497. {
  498. $this->_page_range = self::get_default_page_range();
  499. }
  500. return $this->_page_range;
  501. }
  502. /**
  503. * Sets the page range (see property declaration above).
  504. *
  505. * @param integer $page_range
  506. * @return Paginator $this
  507. */
  508. public function set_page_range($page_range)
  509. {
  510. $this->_page_range = (integer) $page_range;
  511. return $this;
  512. }
  513. /**
  514. * Returns the page collection.
  515. *
  516. * @param string $scrolling_style Scrolling style
  517. * @return array
  518. */
  519. public function get_pages($scrolling_style = null)
  520. {
  521. if ($this->_pages === null)
  522. {
  523. $this->_pages = $this->_create_pages($scrolling_style);
  524. }
  525. return $this->_pages;
  526. }
  527. /**
  528. * Returns a subset of pages within a given range.
  529. *
  530. * @param integer $lower_bound Lower bound of the range
  531. * @param integer $upper_bound Upper bound of the range
  532. * @return array
  533. */
  534. public function get_pages_in_range($lower_bound, $upper_bound)
  535. {
  536. $lower_bound = $this->normalize_page_number($lower_bound);
  537. $upper_bound = $this->normalize_page_number($upper_bound);
  538. $pages = array();
  539. for ($page_number = $lower_bound; $page_number <= $upper_bound; $page_number ++ )
  540. {
  541. $pages[$page_number] = $page_number;
  542. }
  543. return $pages;
  544. }
  545. /**
  546. * Returns the page item cache.
  547. *
  548. * @return array
  549. */
  550. public function get_page_item_cache()
  551. {
  552. $data = array();
  553. if ($this->_cache_enabled())
  554. {
  555. foreach (self::$_cache->getIdsMatchingTags(array($this->_get_cache_internal_id())) as $id)
  556. {
  557. if (preg_match('|' . self::CACHE_TAG_PREFIX . "(\d+)_.*|", $id, $page))
  558. {
  559. $data[$page[1]] = self::$_cache->load($this->_get_cache_id($page[1]));
  560. }
  561. }
  562. }
  563. return $data;
  564. }
  565. /**
  566. * Brings the item number in range of the page.
  567. *
  568. * @param integer $item_number
  569. * @return integer
  570. */
  571. public function normalize_item_number($item_number)
  572. {
  573. $item_number = (integer) $item_number;
  574. if ($item_number < 1)
  575. {
  576. $item_number = 1;
  577. }
  578. if ($item_number > $this->get_item_count_per_page())
  579. {
  580. $item_number = $this->get_item_count_per_page();
  581. }
  582. return $item_number;
  583. }
  584. /**
  585. * Brings the page number in range of the paginator.
  586. *
  587. * @param integer $page_number
  588. * @return integer
  589. */
  590. public function normalize_page_number($page_number)
  591. {
  592. $page_number = (integer) $page_number;
  593. if ($page_number < 1)
  594. {
  595. $page_number = 1;
  596. }
  597. $page_count = $this->count();
  598. if ($page_count > 0 && $page_number > $page_count)
  599. {
  600. $page_number = $page_count;
  601. }
  602. return $page_number;
  603. }
  604. /**
  605. * Tells if there is an active cache object
  606. * and if the cache has not been desabled
  607. *
  608. * @return bool
  609. */
  610. protected function _cache_enabled()
  611. {
  612. return ((self::$_cache !== null) && $this->_cache_enabled);
  613. }
  614. /**
  615. * Makes an Id for the cache
  616. * Depends on the adapter object and the page number
  617. *
  618. * Used to store item in cache from that Paginator instance
  619. * and that current page
  620. *
  621. * @param int $page
  622. * @return string
  623. */
  624. protected function _get_cache_id($page = null)
  625. {
  626. if ($page === null)
  627. {
  628. $page = $this->get_current_page_number();
  629. }
  630. return self::CACHE_TAG_PREFIX . $page . '_' . $this->_get_cache_internal_id();
  631. }
  632. /**
  633. * Get the internal cache id
  634. * Depends on the adapter and the item count per page
  635. *
  636. * Used to tag that unique Paginator instance in cache
  637. *
  638. * @return string
  639. */
  640. protected function _get_cache_internal_id()
  641. {
  642. return md5(serialize(array(
  643. $this->get_adapter(),
  644. $this->get_item_count_per_page()
  645. )));
  646. }
  647. /**
  648. * Calculates the page count.
  649. *
  650. * @return integer
  651. */
  652. protected function _calculate_page_count()
  653. {
  654. return (integer) ceil($this->get_adapter()->count() / $this->get_item_count_per_page());
  655. }
  656. /**
  657. * Creates the page collection.
  658. *
  659. * @param string $scrolling_style Scrolling style
  660. * @return stdClass
  661. */
  662. protected function _create_pages($scrolling_style = null)
  663. {
  664. $page_count = $this->count();
  665. $current_page_number = $this->get_current_page_number();
  666. $pages = new stdClass;
  667. $pages->page_count = $page_count;
  668. $pages->item_count_per_page = $this->get_item_count_per_page();
  669. $pages->first = 1;
  670. $pages->current = $current_page_number;
  671. $pages->last = $page_count;
  672. // Previous and next
  673. if ($current_page_number - 1 > 0)
  674. {
  675. $pages->previous = $current_page_number - 1;
  676. }
  677. if ($current_page_number + 1 <= $page_count)
  678. {
  679. $pages->next = $current_page_number + 1;
  680. }
  681. // Pages in range
  682. $scrolling_style = $this->_load_scrolling_style($scrolling_style);
  683. $pages->pages_in_range = $scrolling_style->get_pages($this);
  684. $pages->first_page_in_range = min($pages->pages_in_range);
  685. $pages->last_page_in_range = max($pages->pages_in_range);
  686. // Item numbers
  687. if ($this->get_current_items() !== null)
  688. {
  689. $pages->current_item_count = $this->get_current_item_count();
  690. $pages->item_count_per_page = $this->get_item_count_per_page();
  691. $pages->total_item_count = $this->get_total_item_count();
  692. $pages->first_item_number = (($current_page_number - 1) * $this->get_item_count_per_page()) + 1;
  693. $pages->last_item_number = $pages->first_item_number + $pages->current_item_count - 1;
  694. }
  695. return $pages;
  696. }
  697. /**
  698. * Loads a scrolling style.
  699. *
  700. * @param string $scrolling_style
  701. * @return Paginator_Scrollingstyle_Interface
  702. */
  703. protected function _load_scrolling_style($scrolling_style = null)
  704. {
  705. if ($scrolling_style === null)
  706. {
  707. $scrolling_style = self::$_default_scrolling_style;
  708. }
  709. switch (strtolower($scrolling_style))
  710. {
  711. case 'all':
  712. case 'elastic':
  713. case 'jumping':
  714. case 'sliding':
  715. $className = 'Paginator_Scrollingstyle_' . ucfirst($scrolling_style);
  716. return new $className;
  717. case 'null':
  718. default:
  719. throw new Exception('Scrolling style must be a class ' .
  720. 'name or object implementing Paginator_Scrollingstyle_Interface');
  721. }
  722. }
  723. /**
  724. * Set URL and options
  725. * Default Page Query Name = 'page'
  726. * @param string $url
  727. * @param string $page_query_name
  728. * @param string $option_query
  729. * @return boolean true
  730. */
  731. public function set_option_queries($url = null, $page_query_name = null, $option_query = null)
  732. {
  733. $this->_url = $url ? $url : $this->_default_url;
  734. $this->_page_query_name = $page_query_name ? $page_query_name : $this->_default_page_query_name;
  735. $this->_option_query = $option_query ? $option_query : $this->_default_option_query;
  736. return true;
  737. }
  738. /**
  739. * Render the pagination.
  740. * Scrolling style: Sliding(default), Elastic, Jumping, All
  741. * @param string $scrolling_style = null
  742. * @return rendered_View
  743. */
  744. public function render($scrolling_style = null)
  745. {
  746. if ($this->_page_query_name == null)
  747. {
  748. $this->set_option_queries();
  749. }
  750. $pages = $this->get_pages($scrolling_style);
  751. if ($pages->page_count > 0)
  752. {
  753. $url1 = $this->_url . '?' . $this->_page_query_name . '=';
  754. $url2 = $this->_option_query ? '&' . $this->_option_query : '';
  755. $first = ($pages->first == $pages->current) ? '' : $url1 . $pages->first . $url2;
  756. $previous = ($pages->first == $pages->current) ? '' : $url1 . $pages->previous . $url2;
  757. $next = ($pages->last == $pages->current) ? '' : $url1 . $pages->next . $url2;
  758. $last = ($pages->last == $pages->current) ? '' : $url1 . $pages->last . $url2;
  759. foreach ($pages->pages_in_range as $key => $value)
  760. {
  761. $pages_in_range[$value] = ($value == $pages->current) ? '' : $url1 . $value . $url2;
  762. }
  763. }
  764. else
  765. {
  766. $first = $previous = $pages_in_range[1] = $next = $last = '';
  767. }
  768. $view = View::factory('paginator/pagination');
  769. $view->first = $first;
  770. $view->previous = $previous;
  771. $view->pages_in_range = $pages_in_range;
  772. $view->next = $next;
  773. $view->last = $last;
  774. return $view->render();
  775. }
  776. }