Paginator.php 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650
  1. <?php
  2. /**
  3. *
  4. * Cube Framework $Id$ GHkFsY924WuZ5fm6xe+YaiNUC0nyI20owzzQvVpDfHw=
  5. *
  6. * @link http://codecu.be/framework
  7. * @copyright Copyright (c) 2015 CodeCube SRL
  8. * @license http://codecu.be/framework/license Commercial License
  9. *
  10. * @version 1.4
  11. */
  12. namespace Cube;
  13. use Cube\Controller\Front;
  14. /**
  15. * paginator class
  16. *
  17. * the paginator will support foreach because it implements \IteratorAggregate
  18. * the method that will be called by a foreach function is $paginator->getIterator()
  19. *
  20. * Class Paginator
  21. *
  22. * @package Cube
  23. */
  24. class Paginator implements \Countable, \IteratorAggregate
  25. {
  26. const ITEM_COUNT_PER_PAGE = 20;
  27. const PAGE_RANGE = 5;
  28. const SCROLLING_STYLE = 'Sliding';
  29. /**
  30. *
  31. * paginator adapter
  32. * (will always be of type \Countable)
  33. *
  34. * @var \Cube\Paginator\Adapter\AdapterInterface
  35. */
  36. protected $_adapter;
  37. /**
  38. *
  39. * number of items in the current page
  40. *
  41. * @var integer
  42. */
  43. protected $_currentItemCount = null;
  44. /**
  45. *
  46. * current page items
  47. *
  48. * @var \Traversable
  49. */
  50. protected $_currentItems = null;
  51. /**
  52. * current page number
  53. * (default: 1)
  54. *
  55. * @var integer
  56. */
  57. protected $_currentPageNumber = 1;
  58. /**
  59. *
  60. * number of items per page
  61. *
  62. * @var integer
  63. */
  64. protected $_itemCountPerPage = null;
  65. /**
  66. * Number of pages
  67. *
  68. * @var integer
  69. */
  70. protected $_pageCount;
  71. /**
  72. *
  73. * number of page numbers to will be displayed
  74. *
  75. * @var integer
  76. */
  77. protected $_pageRange = null;
  78. /**
  79. *
  80. * paginator pages
  81. *
  82. * @var array
  83. */
  84. protected $_pages = null;
  85. /**
  86. *
  87. * scrolling style object
  88. *
  89. * @var \Cube\Paginator\ScrollingStyle\ScrollingStyleInterface
  90. */
  91. protected $_scrollingStyle = null;
  92. /**
  93. *
  94. * view object
  95. *
  96. * @var \Cube\View
  97. */
  98. protected $_view = null;
  99. /**
  100. *
  101. * class constructor
  102. *
  103. * @param \Cube\Paginator\Adapter\AdapterInterface $adapter
  104. */
  105. public function __construct(Paginator\Adapter\AdapterInterface $adapter)
  106. {
  107. $this->_adapter = $adapter;
  108. }
  109. /**
  110. *
  111. * get pagination adapter
  112. *
  113. * @return \Cube\Paginator\Adapter\AdapterInterface
  114. */
  115. public function getAdapter()
  116. {
  117. return $this->_adapter;
  118. }
  119. /**
  120. *
  121. * set adapter
  122. *
  123. * @param \Cube\Paginator\Adapter\AdapterInterface $adapter
  124. *
  125. * @return \Cube\Paginator
  126. */
  127. public function setAdapter(Paginator\Adapter\AdapterInterface $adapter)
  128. {
  129. $this->_adapter = $adapter;
  130. return $this;
  131. }
  132. /**
  133. *
  134. * get the number of items for the current page
  135. *
  136. * @return integer
  137. */
  138. public function getCurrentItemCount()
  139. {
  140. if ($this->_currentItemCount === null) {
  141. $this->_currentItemCount = $this->getItemCount($this->getCurrentItems());
  142. }
  143. return $this->_currentItemCount;
  144. }
  145. /**
  146. *
  147. * get the items corresponding to the current page
  148. *
  149. * @return \Traversable
  150. */
  151. public function getCurrentItems()
  152. {
  153. if ($this->_currentItems === null) {
  154. $this->_currentItems = $this->getItemsByPage($this->getCurrentPageNumber());
  155. }
  156. return $this->_currentItems;
  157. }
  158. /**
  159. *
  160. * get the number of the current page
  161. *
  162. * @return integer
  163. */
  164. public function getCurrentPageNumber()
  165. {
  166. return $this->normalizePageNumber($this->_currentPageNumber);
  167. }
  168. /**
  169. *
  170. * set the current page number
  171. *
  172. * @param integer $currentPageNumber
  173. *
  174. * @return \Cube\Paginator
  175. */
  176. public function setCurrentPageNumber($currentPageNumber)
  177. {
  178. $this->_currentPageNumber = (integer)$currentPageNumber;
  179. $this->_currentItems = null;
  180. $this->_currentItemCount = null;
  181. return $this;
  182. }
  183. /**
  184. *
  185. * get the number of items per page
  186. *
  187. * @return integer
  188. */
  189. public function getItemCountPerPage()
  190. {
  191. if ($this->_itemCountPerPage === null) {
  192. $this->setItemCountPerPage(self::ITEM_COUNT_PER_PAGE);
  193. }
  194. return $this->_itemCountPerPage;
  195. }
  196. /**
  197. *
  198. * set number of items per page
  199. * (if the value is lower than 1, include all items in the current page)
  200. *
  201. * @param integer $itemCountPerPage
  202. *
  203. * @return \Cube\Paginator
  204. */
  205. public function setItemCountPerPage($itemCountPerPage)
  206. {
  207. $this->_itemCountPerPage = (integer)$itemCountPerPage;
  208. if ($this->_itemCountPerPage < 1) {
  209. $this->_itemCountPerPage = count($this->getAdapter());
  210. }
  211. $this->_pageCount = $this->_calculatePageCount();
  212. $this->_currentItems = null;
  213. $this->_currentItemCount = null;
  214. return $this;
  215. }
  216. /**
  217. *
  218. * get the number of pages to be displayed
  219. *
  220. * @return integer
  221. */
  222. public function getPageRange()
  223. {
  224. if ($this->_pageRange === null) {
  225. $this->setPageRange(self::PAGE_RANGE);
  226. }
  227. return $this->_pageRange;
  228. }
  229. /**
  230. *
  231. * set the number of pages to be displayed
  232. *
  233. * @param integer $pageRange
  234. *
  235. * @return \Cube\Paginator
  236. */
  237. public function setPageRange($pageRange)
  238. {
  239. $this->_pageRange = (integer)$pageRange;
  240. return $this;
  241. }
  242. /**
  243. *
  244. * get all pages from the paginator
  245. *
  246. * @return array
  247. */
  248. public function getPages()
  249. {
  250. if ($this->_pages === null) {
  251. $this->_pages = $this->_createPages();
  252. }
  253. return $this->_pages;
  254. }
  255. /**
  256. *
  257. * set pages
  258. *
  259. * @param array $pages
  260. *
  261. * @return $this
  262. */
  263. public function setPages($pages)
  264. {
  265. $this->_pages = $pages;
  266. return $this;
  267. }
  268. /**
  269. *
  270. * get scrolling style object
  271. *
  272. * @return \Cube\Paginator\ScrollingStyle\ScrollingStyleInterface
  273. */
  274. public function getScrollingStyle()
  275. {
  276. if ($this->_scrollingStyle === null) {
  277. $this->setScrollingStyle(self::SCROLLING_STYLE);
  278. }
  279. return $this->_scrollingStyle;
  280. }
  281. /**
  282. *
  283. * set paginator scrolling style
  284. *
  285. * @param mixed $scrollingStyle
  286. *
  287. * @return $this
  288. * @throws \RuntimeException
  289. */
  290. public function setScrollingStyle($scrollingStyle)
  291. {
  292. if (!$scrollingStyle instanceof Paginator\ScrollingStyle\ScrollingStyleInterface) {
  293. $className = '\\Cube\\Paginator\\ScrollingStyle\\' . ucfirst($scrollingStyle);
  294. if (class_exists($className)) {
  295. $scrollingStyle = new $className();
  296. }
  297. else {
  298. throw new \RuntimeException("Invalid paginator scrolling type class/string given");
  299. }
  300. }
  301. $this->_scrollingStyle = $scrollingStyle;
  302. return $this;
  303. }
  304. /**
  305. *
  306. * get the view object
  307. *
  308. * @return \Cube\View
  309. */
  310. public function getView()
  311. {
  312. if ($this->_view === null) {
  313. $this->setView();
  314. }
  315. return $this->_view;
  316. }
  317. /**
  318. * set the view object
  319. *
  320. * @param \Cube\View $view
  321. *
  322. * @return \Cube\Form
  323. */
  324. public function setView(View $view = null)
  325. {
  326. if (!$view instanceof View) {
  327. $bootstrap = Front::getInstance()->getBootstrap();
  328. if ($bootstrap->hasResource('view')) {
  329. $view = $bootstrap->getResource('view');
  330. }
  331. else {
  332. $view = new View();
  333. }
  334. }
  335. $this->_view = $view;
  336. return $this;
  337. }
  338. /**
  339. *
  340. * get the number of pages
  341. *
  342. * @return integer
  343. */
  344. public function count()
  345. {
  346. if ($this->_pageCount === null) {
  347. $this->_pageCount = $this->_calculatePageCount();
  348. }
  349. return $this->_pageCount;
  350. }
  351. /**
  352. *
  353. * get iterator
  354. *
  355. * @return \Traversable
  356. */
  357. public function getIterator()
  358. {
  359. return $this->getCurrentItems();
  360. }
  361. /**
  362. *
  363. * get an item from a page. The current page is used when no page is specified
  364. *
  365. * @param integer $itemNumber Item number (1 to itemCountPerPage)
  366. * @param integer $pageNumber
  367. *
  368. * @throws \InvalidArgumentException
  369. * @return mixed
  370. */
  371. public function getItem($itemNumber, $pageNumber = null)
  372. {
  373. if ($pageNumber == null) {
  374. $pageNumber = $this->getCurrentPageNumber();
  375. }
  376. else if ($pageNumber < 0) {
  377. $pageNumber = ($this->count() + 1) + $pageNumber;
  378. }
  379. $page = $this->getItemsByPage($pageNumber);
  380. $itemCount = $this->getItemCount($page);
  381. if ($itemCount == 0) {
  382. throw new \InvalidArgumentException(
  383. sprintf("Page '%s' does not exist.", $pageNumber));
  384. }
  385. if ($itemNumber < 0) {
  386. $itemNumber = ($itemCount + 1) + $itemNumber;
  387. }
  388. $itemNumber = $this->normalizeItemNumber($itemNumber);
  389. if ($itemNumber > $itemCount) {
  390. throw new \InvalidArgumentException(
  391. sprintf("Page '%2' does not contain item number '%s'", $pageNumber, $itemNumber));
  392. }
  393. return $page[$itemNumber - 1];
  394. }
  395. /**
  396. *
  397. * get the number of items in a collection
  398. *
  399. * @param mixed $items
  400. *
  401. * @return integer
  402. */
  403. public function getItemCount($items)
  404. {
  405. if (is_array($items) || $items instanceof \Countable) {
  406. $itemCount = count($items);
  407. }
  408. else { // if we have \Traversable
  409. $itemCount = iterator_count($items);
  410. }
  411. return $itemCount;
  412. }
  413. /**
  414. *
  415. * get all page numbers in the requested range
  416. *
  417. * @param integer $min
  418. * @param integer $max
  419. *
  420. * @return array
  421. */
  422. public function getPagesInRange($min, $max)
  423. {
  424. $min = $this->normalizePageNumber($min);
  425. $max = $this->normalizePageNumber($max);
  426. $pages = array();
  427. for ($number = $min; $number <= $max; $number++) {
  428. $pages[$number] = $number;
  429. }
  430. return $pages;
  431. }
  432. /**
  433. * get the items corresponding to a given page number
  434. *
  435. * @param int $pageNumber
  436. *
  437. * @return \Traversable
  438. */
  439. public function getItemsByPage($pageNumber)
  440. {
  441. $pageNumber = $this->normalizePageNumber($pageNumber);
  442. $offset = ($pageNumber - 1) * $this->getItemCountPerPage();
  443. $items = $this->_adapter->getItems($offset, $this->getItemCountPerPage());
  444. if (!$items instanceof \Traversable) {
  445. $items = new \ArrayIterator($items);
  446. }
  447. return $items;
  448. }
  449. /**
  450. *
  451. * set item number in respect to the current page
  452. *
  453. * @param integer $itemNumber
  454. *
  455. * @return integer
  456. */
  457. public function normalizeItemNumber($itemNumber)
  458. {
  459. $itemNumber = (integer)$itemNumber;
  460. if ($itemNumber < 1) {
  461. $itemNumber = 1;
  462. }
  463. if ($itemNumber > $this->getItemCountPerPage()) {
  464. $itemNumber = $this->getItemCountPerPage();
  465. }
  466. return $itemNumber;
  467. }
  468. /**
  469. *
  470. * set page number in the range of the paginator
  471. *
  472. * @param integer $pageNumber
  473. *
  474. * @return integer
  475. */
  476. public function normalizePageNumber($pageNumber)
  477. {
  478. $pageNumber = (integer)$pageNumber;
  479. if ($pageNumber < 1) {
  480. $pageNumber = 1;
  481. }
  482. $pageCount = $this->count();
  483. if ($pageCount > 0 && $pageNumber > $pageCount) {
  484. $pageNumber = $pageCount;
  485. }
  486. return $pageNumber;
  487. }
  488. /**
  489. * calculates the page count
  490. *
  491. * @return integer
  492. */
  493. protected function _calculatePageCount()
  494. {
  495. return (integer)ceil($this->getAdapter()->count() / $this->getItemCountPerPage());
  496. }
  497. /**
  498. *
  499. * creates the pages based on the adapter and the scrolling style
  500. *
  501. * @return \stdClass
  502. */
  503. protected function _createPages()
  504. {
  505. $scrollingStyle = $this->getScrollingStyle();
  506. $pageCount = $this->count();
  507. $currentPageNumber = $this->getCurrentPageNumber();
  508. $pages = new \stdClass();
  509. $pages->pageCount = $pageCount;
  510. $pages->itemCountPerPage = $this->getItemCountPerPage();
  511. $pages->first = 1;
  512. $pages->current = $currentPageNumber;
  513. $pages->last = $pageCount;
  514. // Previous and next
  515. if ($currentPageNumber - 1 > 0) {
  516. $pages->previous = $currentPageNumber - 1;
  517. }
  518. if ($currentPageNumber + 1 <= $pageCount) {
  519. $pages->next = $currentPageNumber + 1;
  520. }
  521. // Pages in range
  522. $pages->pagesInRange = $scrollingStyle->getPages($this);
  523. $pages->firstPageInRange = min($pages->pagesInRange);
  524. $pages->lastPageInRange = max($pages->pagesInRange);
  525. // Item numbers
  526. if ($this->getCurrentItems() !== null) {
  527. $pages->currentItemCount = $this->getCurrentItemCount();
  528. $pages->itemCountPerPage = $this->getItemCountPerPage();
  529. $pages->totalItemCount = count($this->getAdapter());
  530. $pages->firstItemNumber = (($currentPageNumber - 1) * $this->getItemCountPerPage()) + 1;
  531. $pages->lastItemNumber = $pages->firstItemNumber + $pages->currentItemCount - 1;
  532. }
  533. return $pages;
  534. }
  535. /**
  536. *
  537. * renders the paginator
  538. *
  539. * @return string
  540. */
  541. public function render()
  542. {
  543. $view = $this->getView();
  544. return $view->pagination($this);
  545. }
  546. /**
  547. *
  548. * serialize the object as a string
  549. *
  550. * @return string
  551. */
  552. public function __toString()
  553. {
  554. return $this->render();
  555. }
  556. }