PaginatorHelper.php 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 1.2.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\View\Helper;
  16. use Cake\Utility\Inflector;
  17. use Cake\View\Helper;
  18. use Cake\View\View;
  19. /**
  20. * Pagination Helper class for easy generation of pagination links.
  21. *
  22. * PaginationHelper encloses all methods needed when working with pagination.
  23. *
  24. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html
  25. */
  26. class PaginatorHelper extends Helper {
  27. use StringTemplateTrait;
  28. /**
  29. * List of helpers used by this helper
  30. *
  31. * @var array
  32. */
  33. public $helpers = ['Url', 'Number'];
  34. /**
  35. * Defualt config for this class
  36. *
  37. * Options: Holds the default options for pagination links
  38. *
  39. * The values that may be specified are:
  40. *
  41. * - `url` Url of the action. See Router::url()
  42. * - `url['sort']` the key that the recordset is sorted.
  43. * - `url['direction']` Direction of the sorting (default: 'asc').
  44. * - `url['page']` Page number to use in links.
  45. * - `model` The name of the model.
  46. * - `escape` Defines if the title field for the link should be escaped (default: true).
  47. *
  48. * Templates: the templates used by this class
  49. *
  50. * @var array
  51. */
  52. protected $_defaultConfig = [
  53. 'options' => [],
  54. 'templates' => [
  55. 'nextActive' => '<li class="next"><a rel="next" href="{{url}}">{{text}}</a></li>',
  56. 'nextDisabled' => '<li class="next disabled"><a href="">{{text}}</a></li>',
  57. 'prevActive' => '<li class="prev"><a rel="prev" href="{{url}}">{{text}}</a></li>',
  58. 'prevDisabled' => '<li class="prev disabled"><a href="">{{text}}</a></li>',
  59. 'counterRange' => '{{start}} - {{end}} of {{count}}',
  60. 'counterPages' => '{{page}} of {{pages}}',
  61. 'first' => '<li class="first"><a href="{{url}}">{{text}}</a></li>',
  62. 'last' => '<li class="last"><a href="{{url}}">{{text}}</a></li>',
  63. 'number' => '<li><a href="{{url}}">{{text}}</a></li>',
  64. 'current' => '<li class="active"><a href="">{{text}}</a></li>',
  65. 'ellipsis' => '<li class="ellipsis">...</li>',
  66. 'sort' => '<a href="{{url}}">{{text}}</a>',
  67. 'sortAsc' => '<a class="asc" href="{{url}}">{{text}}</a>',
  68. 'sortDesc' => '<a class="desc" href="{{url}}">{{text}}</a>',
  69. 'sortAscLocked' => '<a class="asc locked" href="{{url}}">{{text}}</a>',
  70. 'sortDescLocked' => '<a class="desc locked" href="{{url}}">{{text}}</a>',
  71. ]
  72. ];
  73. /**
  74. * Constructor. Overridden to merge passed args with URL options.
  75. *
  76. * @param \Cake\View\View $View The View this helper is being attached to.
  77. * @param array $config Configuration settings for the helper.
  78. */
  79. public function __construct(View $View, array $config = array()) {
  80. parent::__construct($View, $config);
  81. $this->config(
  82. 'options.url',
  83. array_merge($this->request->params['pass'], $this->request->query)
  84. );
  85. }
  86. /**
  87. * Gets the current paging parameters from the resultset for the given model
  88. *
  89. * @param string $model Optional model name. Uses the default if none is specified.
  90. * @return array The array of paging parameters for the paginated resultset.
  91. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::params
  92. */
  93. public function params($model = null) {
  94. if (empty($model)) {
  95. $model = $this->defaultModel();
  96. }
  97. if (!isset($this->request->params['paging']) || empty($this->request->params['paging'][$model])) {
  98. return [];
  99. }
  100. return $this->request->params['paging'][$model];
  101. }
  102. /**
  103. * Convenience access to any of the paginator params.
  104. *
  105. * @param string $key Key of the paginator params array to retrieve.
  106. * @param string $model Optional model name. Uses the default if none is specified.
  107. * @return mixed Content of the requested param.
  108. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::params
  109. */
  110. public function param($key, $model = null) {
  111. $params = $this->params($model);
  112. if (!isset($params[$key])) {
  113. return null;
  114. }
  115. return $params[$key];
  116. }
  117. /**
  118. * Sets default options for all pagination links
  119. *
  120. * @param array $options Default options for pagination links.
  121. * See PaginatorHelper::$options for list of keys.
  122. * @return void
  123. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::options
  124. */
  125. public function options(array $options = array()) {
  126. if (!empty($options['paging'])) {
  127. if (!isset($this->request->params['paging'])) {
  128. $this->request->params['paging'] = array();
  129. }
  130. $this->request->params['paging'] = array_merge($this->request->params['paging'], $options['paging']);
  131. unset($options['paging']);
  132. }
  133. $model = $this->defaultModel();
  134. if (!empty($options[$model])) {
  135. if (!isset($this->request->params['paging'][$model])) {
  136. $this->request->params['paging'][$model] = array();
  137. }
  138. $this->request->params['paging'][$model] = array_merge(
  139. $this->request->params['paging'][$model], $options[$model]
  140. );
  141. unset($options[$model]);
  142. }
  143. $this->_config['options'] = array_filter(array_merge($this->_config['options'], $options));
  144. }
  145. /**
  146. * Gets the current page of the recordset for the given model
  147. *
  148. * @param string $model Optional model name. Uses the default if none is specified.
  149. * @return string The current page number of the recordset.
  150. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::current
  151. */
  152. public function current($model = null) {
  153. $params = $this->params($model);
  154. if (isset($params['page'])) {
  155. return $params['page'];
  156. }
  157. return 1;
  158. }
  159. /**
  160. * Gets the current key by which the recordset is sorted
  161. *
  162. * @param string $model Optional model name. Uses the default if none is specified.
  163. * @param array $options Options for pagination links. See #options for list of keys.
  164. * @return string The name of the key by which the recordset is being sorted, or
  165. * null if the results are not currently sorted.
  166. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::sortKey
  167. */
  168. public function sortKey($model = null, array $options = array()) {
  169. if (empty($options)) {
  170. $options = $this->params($model);
  171. }
  172. if (!empty($options['sort'])) {
  173. return $options['sort'];
  174. }
  175. return null;
  176. }
  177. /**
  178. * Gets the current direction the recordset is sorted
  179. *
  180. * @param string $model Optional model name. Uses the default if none is specified.
  181. * @param array $options Options for pagination links. See #options for list of keys.
  182. * @return string The direction by which the recordset is being sorted, or
  183. * null if the results are not currently sorted.
  184. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::sortDir
  185. */
  186. public function sortDir($model = null, array $options = array()) {
  187. $dir = null;
  188. if (empty($options)) {
  189. $options = $this->params($model);
  190. }
  191. if (isset($options['direction'])) {
  192. $dir = strtolower($options['direction']);
  193. }
  194. if ($dir === 'desc') {
  195. return 'desc';
  196. }
  197. return 'asc';
  198. }
  199. /**
  200. * Generate an active/inactive link for next/prev methods.
  201. *
  202. * @param string $text The enabled text for the link.
  203. * @param bool $enabled Whether or not the enabled/disabled version should be created.
  204. * @param array $options An array of options from the calling method.
  205. * @param array $templates An array of templates with the 'active' and 'disabled' keys.
  206. * @return string Generated HTML
  207. */
  208. protected function _toggledLink($text, $enabled, $options, $templates) {
  209. $template = $templates['active'];
  210. if (!$enabled) {
  211. $text = $options['disabledTitle'];
  212. $template = $templates['disabled'];
  213. }
  214. if (!$enabled && $text === false) {
  215. return '';
  216. }
  217. $text = $options['escape'] ? h($text) : $text;
  218. if (!$enabled) {
  219. return $this->templater()->format($template, [
  220. 'text' => $text,
  221. ]);
  222. }
  223. $paging = $this->params($options['model']);
  224. $url = array_merge(
  225. $options['url'],
  226. ['page' => $paging['page'] + $options['step']]
  227. );
  228. $url = $this->generateUrl($url, $options['model']);
  229. return $this->templater()->format($template, [
  230. 'url' => $url,
  231. 'text' => $text,
  232. ]);
  233. }
  234. /**
  235. * Generates a "previous" link for a set of paged records
  236. *
  237. * ### Options:
  238. *
  239. * - `disabledTitle` The text to used when the link is disabled. This
  240. * defaults to the same text at the active link. Setting to false will cause
  241. * this method to return ''.
  242. * - `escape` Whether you want the contents html entity encoded, defaults to true
  243. * - `model` The model to use, defaults to PaginatorHelper::defaultModel()
  244. * - `url` Additional URL parameters to use in the generated URL.
  245. *
  246. * @param string $title Title for the link. Defaults to '<< Previous'.
  247. * @param array $options Options for pagination link. See above for list of keys.
  248. * @return string A "previous" link or a disabled link.
  249. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::prev
  250. */
  251. public function prev($title = '<< Previous', array $options = []) {
  252. $defaults = [
  253. 'url' => [],
  254. 'model' => $this->defaultModel(),
  255. 'disabledTitle' => $title,
  256. 'escape' => true,
  257. ];
  258. $options += $defaults;
  259. $options['step'] = -1;
  260. $enabled = $this->hasPrev($options['model']);
  261. $templates = [
  262. 'active' => 'prevActive',
  263. 'disabled' => 'prevDisabled'
  264. ];
  265. return $this->_toggledLink($title, $enabled, $options, $templates);
  266. }
  267. /**
  268. * Generates a "next" link for a set of paged records
  269. *
  270. * ### Options:
  271. *
  272. * - `disabledTitle` The text to used when the link is disabled. This
  273. * defaults to the same text at the active link. Setting to false will cause
  274. * this method to return ''.
  275. * - `escape` Whether you want the contents html entity encoded, defaults to true
  276. * - `model` The model to use, defaults to PaginatorHelper::defaultModel()
  277. * - `url` Additional URL parameters to use in the generated URL.
  278. *
  279. * @param string $title Title for the link. Defaults to 'Next >>'.
  280. * @param array $options Options for pagination link. See above for list of keys.
  281. * @return string A "next" link or $disabledTitle text if the link is disabled.
  282. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::next
  283. */
  284. public function next($title = 'Next >>', array $options = []) {
  285. $defaults = [
  286. 'url' => [],
  287. 'model' => $this->defaultModel(),
  288. 'disabledTitle' => $title,
  289. 'escape' => true,
  290. ];
  291. $options += $defaults;
  292. $options['step'] = 1;
  293. $enabled = $this->hasNext($options['model']);
  294. $templates = [
  295. 'active' => 'nextActive',
  296. 'disabled' => 'nextDisabled'
  297. ];
  298. return $this->_toggledLink($title, $enabled, $options, $templates);
  299. }
  300. /**
  301. * Generates a sorting link. Sets named parameters for the sort and direction. Handles
  302. * direction switching automatically.
  303. *
  304. * ### Options:
  305. *
  306. * - `escape` Whether you want the contents html entity encoded, defaults to true.
  307. * - `model` The model to use, defaults to PaginatorHelper::defaultModel().
  308. * - `direction` The default direction to use when this link isn't active.
  309. * - `lock` Lock direction. Will only use the default direction then, defaults to false.
  310. *
  311. * @param string $key The name of the key that the recordset should be sorted.
  312. * @param string $title Title for the link. If $title is null $key will be used
  313. * for the title and will be generated by inflection.
  314. * @param array $options Options for sorting link. See above for list of keys.
  315. * @return string A link sorting default by 'asc'. If the resultset is sorted 'asc' by the specified
  316. * key the returned link will sort by 'desc'.
  317. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::sort
  318. */
  319. public function sort($key, $title = null, array $options = []) {
  320. $options += ['url' => array(), 'model' => null, 'escape' => true];
  321. $url = $options['url'];
  322. unset($options['url']);
  323. if (empty($title)) {
  324. $title = $key;
  325. if (strpos($title, '.') !== false) {
  326. $title = str_replace('.', ' ', $title);
  327. }
  328. $title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)));
  329. }
  330. $defaultDir = isset($options['direction']) ? $options['direction'] : 'asc';
  331. unset($options['direction']);
  332. $locked = isset($options['lock']) ? $options['lock'] : false;
  333. unset($options['lock']);
  334. $sortKey = $this->sortKey($options['model']);
  335. $defaultModel = $this->defaultModel();
  336. $isSorted = (
  337. $sortKey === $key ||
  338. $sortKey === $defaultModel . '.' . $key ||
  339. $key === $defaultModel . '.' . $sortKey
  340. );
  341. $template = 'sort';
  342. $dir = $defaultDir;
  343. if ($isSorted) {
  344. if ($locked) {
  345. $template = $dir === 'asc' ? 'sortDescLocked' : 'sortAscLocked';
  346. } else {
  347. $dir = $this->sortDir($options['model']) === 'asc' ? 'desc' : 'asc';
  348. $template = $dir === 'asc' ? 'sortDesc' : 'sortAsc';
  349. }
  350. }
  351. if (is_array($title) && array_key_exists($dir, $title)) {
  352. $title = $title[$dir];
  353. }
  354. $url = array_merge(
  355. ['sort' => $key, 'direction' => $dir],
  356. $url,
  357. ['order' => null]
  358. );
  359. $vars = [
  360. 'text' => $options['escape'] ? h($title) : $title,
  361. 'url' => $this->generateUrl($url, $options['model']),
  362. ];
  363. return $this->templater()->format($template, $vars);
  364. }
  365. /**
  366. * Merges passed URL options with current pagination state to generate a pagination URL.
  367. *
  368. * @param array $options Pagination/URL options array
  369. * @param string $model Which model to paginate on
  370. * @param bool $full If true, the full base URL will be prepended to the result
  371. * @return mixed By default, returns a full pagination URL string for use in non-standard contexts (i.e. JavaScript)
  372. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::url
  373. */
  374. public function generateUrl(array $options = array(), $model = null, $full = false) {
  375. $paging = $this->params($model);
  376. $paging += ['page' => null, 'sort' => null, 'direction' => null, 'limit' => null];
  377. $url = [
  378. 'page' => $paging['page'],
  379. 'limit' => $paging['limit'],
  380. 'sort' => $paging['sort'],
  381. 'direction' => $paging['direction'],
  382. ];
  383. if (!empty($this->_config['options']['url'])) {
  384. $url = array_merge($this->_config['options']['url'], $url);
  385. }
  386. $url = array_merge(array_filter($url), $options);
  387. if (!empty($url['page']) && $url['page'] == 1) {
  388. $url['page'] = null;
  389. }
  390. if (
  391. isset($paging['sortDefault'], $paging['directionDefault'], $url['sort'], $url['direction']) &&
  392. $url['sort'] === $paging['sortDefault'] &&
  393. $url['direction'] === $paging['directionDefault']
  394. ) {
  395. $url['sort'] = $url['direction'] = null;
  396. }
  397. return $this->Url->build($url, $full);
  398. }
  399. /**
  400. * Returns true if the given result set is not at the first page
  401. *
  402. * @param string $model Optional model name. Uses the default if none is specified.
  403. * @return bool True if the result set is not at the first page.
  404. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::hasPrev
  405. */
  406. public function hasPrev($model = null) {
  407. return $this->_hasPage($model, 'prev');
  408. }
  409. /**
  410. * Returns true if the given result set is not at the last page
  411. *
  412. * @param string $model Optional model name. Uses the default if none is specified.
  413. * @return bool True if the result set is not at the last page.
  414. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::hasNext
  415. */
  416. public function hasNext($model = null) {
  417. return $this->_hasPage($model, 'next');
  418. }
  419. /**
  420. * Returns true if the given result set has the page number given by $page
  421. *
  422. * @param string $model Optional model name. Uses the default if none is specified.
  423. * @param int $page The page number - if not set defaults to 1.
  424. * @return bool True if the given result set has the specified page number.
  425. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::hasPage
  426. */
  427. public function hasPage($model = null, $page = 1) {
  428. if (is_numeric($model)) {
  429. $page = $model;
  430. $model = null;
  431. }
  432. $paging = $this->params($model);
  433. if ($paging === []) {
  434. return false;
  435. }
  436. return $page <= $paging['pageCount'];
  437. }
  438. /**
  439. * Does $model have $page in its range?
  440. *
  441. * @param string $model Model name to get parameters for.
  442. * @param int $page Page number you are checking.
  443. * @return bool Whether model has $page
  444. */
  445. protected function _hasPage($model, $page) {
  446. $params = $this->params($model);
  447. return !empty($params) && $params[$page . 'Page'];
  448. }
  449. /**
  450. * Gets the default model of the paged sets
  451. *
  452. * @return string Model name or null if the pagination isn't initialized.
  453. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::defaultModel
  454. */
  455. public function defaultModel() {
  456. if ($this->_defaultModel) {
  457. return $this->_defaultModel;
  458. }
  459. if (empty($this->request->params['paging'])) {
  460. return null;
  461. }
  462. list($this->_defaultModel) = array_keys($this->request->params['paging']);
  463. return $this->_defaultModel;
  464. }
  465. /**
  466. * Returns a counter string for the paged result set
  467. *
  468. * ### Options
  469. *
  470. * - `model` The model to use, defaults to PaginatorHelper::defaultModel();
  471. * - `format` The format string you want to use, defaults to 'pages' Which generates output like '1 of 5'
  472. * set to 'range' to generate output like '1 - 3 of 13'. Can also be set to a custom string, containing
  473. * the following placeholders `{{page}}`, `{{pages}}`, `{{current}}`, `{{count}}`, `{{model}}`, `{{start}}`, `{{end}}` and any
  474. * custom content you would like.
  475. *
  476. * @param string|array $options Options for the counter string. See #options for list of keys.
  477. * If string it will be used as format.
  478. * @return string Counter string.
  479. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::counter
  480. */
  481. public function counter($options = []) {
  482. if (is_string($options)) {
  483. $options = array('format' => $options);
  484. }
  485. $options += [
  486. 'model' => $this->defaultModel(),
  487. 'format' => 'pages',
  488. ];
  489. $paging = $this->params($options['model']);
  490. if (!$paging['pageCount']) {
  491. $paging['pageCount'] = 1;
  492. }
  493. $start = 0;
  494. if ($paging['count'] >= 1) {
  495. $start = (($paging['page'] - 1) * $paging['perPage']) + 1;
  496. }
  497. $end = $start + $paging['perPage'] - 1;
  498. if ($paging['count'] < $end) {
  499. $end = $paging['count'];
  500. }
  501. switch ($options['format']) {
  502. case 'range':
  503. case 'pages':
  504. $template = 'counter' . ucfirst($options['format']);
  505. break;
  506. default:
  507. $template = 'counterCustom';
  508. $this->templater()->add([$template => $options['format']]);
  509. }
  510. $map = array_map([$this->Number, 'format'], [
  511. 'page' => $paging['page'],
  512. 'pages' => $paging['pageCount'],
  513. 'current' => $paging['current'],
  514. 'count' => $paging['count'],
  515. 'start' => $start,
  516. 'end' => $end
  517. ]);
  518. $map += [
  519. 'model' => strtolower(Inflector::humanize(Inflector::tableize($options['model'])))
  520. ];
  521. return $this->templater()->format($template, $map);
  522. }
  523. /**
  524. * Returns a set of numbers for the paged result set
  525. * uses a modulus to decide how many numbers to show on each side of the current page (default: 8).
  526. *
  527. * `$this->Paginator->numbers(array('first' => 2, 'last' => 2));`
  528. *
  529. * Using the first and last options you can create links to the beginning and end of the page set.
  530. *
  531. * ### Options
  532. *
  533. * - `before` Content to be inserted before the numbers, but after the first links.
  534. * - `after` Content to be inserted after the numbers, but before the last links.
  535. * - `model` Model to create numbers for, defaults to PaginatorHelper::defaultModel()
  536. * - `modulus` how many numbers to include on either side of the current page, defaults to 8.
  537. * - `first` Whether you want first links generated, set to an integer to define the number of 'first'
  538. * links to generate.
  539. * - `last` Whether you want last links generated, set to an integer to define the number of 'last'
  540. * links to generate.
  541. * - `templates` An array of templates, or template file name containing the templates you'd like to
  542. * use when generating the numbers. The helper's original templates will be restored once
  543. * numbers() is done.
  544. *
  545. * The generated number links will include the 'ellipsis' template when the `first` and `last` options
  546. * and the number of pages exceed the modulus. For example if you have 25 pages, and use the first/last
  547. * options and a modulus of 8, ellipsis content will be inserted after the first and last link sets.
  548. *
  549. * @param array $options Options for the numbers.
  550. * @return string numbers string.
  551. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::numbers
  552. */
  553. public function numbers(array $options = array()) {
  554. $defaults = array(
  555. 'before' => null, 'after' => null, 'model' => $this->defaultModel(),
  556. 'modulus' => 8, 'first' => null, 'last' => null,
  557. );
  558. $options += $defaults;
  559. $params = (array)$this->params($options['model']) + array('page' => 1);
  560. if ($params['pageCount'] <= 1) {
  561. return false;
  562. }
  563. $templater = $this->templater();
  564. if (isset($options['templates'])) {
  565. $templater->push();
  566. $method = is_string($options['templates']) ? 'load' : 'add';
  567. $templater->{$method}($options['templates']);
  568. }
  569. $out = '';
  570. $ellipsis = $templater->format('ellipsis', []);
  571. if ($options['modulus'] && $params['pageCount'] > $options['modulus']) {
  572. $half = (int)($options['modulus'] / 2);
  573. $end = $params['page'] + $half;
  574. if ($end > $params['pageCount']) {
  575. $end = $params['pageCount'];
  576. }
  577. $start = $params['page'] - ($options['modulus'] - ($end - $params['page']));
  578. if ($start <= 1) {
  579. $start = 1;
  580. $end = $params['page'] + ($options['modulus'] - $params['page']) + 1;
  581. }
  582. if ($options['first'] && $start > 1) {
  583. $offset = ($start <= (int)$options['first']) ? $start - 1 : $options['first'];
  584. $out .= $this->first($offset);
  585. if ($offset < $start - 1) {
  586. $out .= $ellipsis;
  587. }
  588. }
  589. $out .= $options['before'];
  590. for ($i = $start; $i < $params['page']; $i++) {
  591. $vars = [
  592. 'text' => $i,
  593. 'url' => $this->generateUrl(['page' => $i], $options['model']),
  594. ];
  595. $out .= $templater->format('number', $vars);
  596. }
  597. $out .= $templater->format('current', [
  598. 'text' => $params['page'],
  599. 'url' => $this->generateUrl(['page' => $params['page']], $options['model']),
  600. ]);
  601. $start = $params['page'] + 1;
  602. for ($i = $start; $i < $end; $i++) {
  603. $vars = [
  604. 'text' => $i,
  605. 'url' => $this->generateUrl(['page' => $i], $options['model']),
  606. ];
  607. $out .= $templater->format('number', $vars);
  608. }
  609. if ($end != $params['page']) {
  610. $vars = [
  611. 'text' => $i,
  612. 'url' => $this->generateUrl(['page' => $end], $options['model']),
  613. ];
  614. $out .= $templater->format('number', $vars);
  615. }
  616. $out .= $options['after'];
  617. if ($options['last'] && $end < $params['pageCount']) {
  618. $offset = ($params['pageCount'] < $end + (int)$options['last']) ? $params['pageCount'] - $end : $options['last'];
  619. if ($offset <= $options['last'] && $params['pageCount'] - $end > $offset) {
  620. $out .= $ellipsis;
  621. }
  622. $out .= $this->last($offset);
  623. }
  624. } else {
  625. $out .= $options['before'];
  626. for ($i = 1; $i <= $params['pageCount']; $i++) {
  627. if ($i == $params['page']) {
  628. $out .= $templater->format('current', [
  629. 'text' => $params['page'],
  630. 'url' => $this->generateUrl(['page' => $params['page']], $options['model']),
  631. ]);
  632. } else {
  633. $vars = [
  634. 'text' => $i,
  635. 'url' => $this->generateUrl(['page' => $i], $options['model']),
  636. ];
  637. $out .= $templater->format('number', $vars);
  638. }
  639. }
  640. $out .= $options['after'];
  641. }
  642. if (isset($options['templates'])) {
  643. $templater->pop();
  644. }
  645. return $out;
  646. }
  647. /**
  648. * Returns a first or set of numbers for the first pages.
  649. *
  650. * `echo $this->Paginator->first('< first');`
  651. *
  652. * Creates a single link for the first page. Will output nothing if you are on the first page.
  653. *
  654. * `echo $this->Paginator->first(3);`
  655. *
  656. * Will create links for the first 3 pages, once you get to the third or greater page. Prior to that
  657. * nothing will be output.
  658. *
  659. * ### Options:
  660. *
  661. * - `model` The model to use defaults to PaginatorHelper::defaultModel()
  662. * - `escape` Whether or not to HTML escape the text.
  663. *
  664. * @param string|int $first if string use as label for the link. If numeric, the number of page links
  665. * you want at the beginning of the range.
  666. * @param array $options An array of options.
  667. * @return string numbers string.
  668. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::first
  669. */
  670. public function first($first = '<< first', array $options = []) {
  671. $options += ['model' => $this->defaultModel(), 'escape' => true];
  672. $params = $this->params($options['model']);
  673. if ($params['pageCount'] <= 1) {
  674. return false;
  675. }
  676. $out = '';
  677. if (is_int($first) && $params['page'] >= $first) {
  678. for ($i = 1; $i <= $first; $i++) {
  679. $out .= $this->templater()->format('number', [
  680. 'url' => $this->generateUrl(['page' => $i], $options['model']),
  681. 'text' => $i
  682. ]);
  683. }
  684. } elseif ($params['page'] > 1 && is_string($first)) {
  685. $first = $options['escape'] ? h($first) : $first;
  686. $out .= $this->templater()->format('first', [
  687. 'url' => $this->generateUrl(['page' => 1], $options['model']),
  688. 'text' => $first
  689. ]);
  690. }
  691. return $out;
  692. }
  693. /**
  694. * Returns a last or set of numbers for the last pages.
  695. *
  696. * `echo $this->Paginator->last('last >');`
  697. *
  698. * Creates a single link for the last page. Will output nothing if you are on the last page.
  699. *
  700. * `echo $this->Paginator->last(3);`
  701. *
  702. * Will create links for the last 3 pages. Once you enter the page range, no output will be created.
  703. *
  704. * ### Options:
  705. *
  706. * - `model` The model to use defaults to PaginatorHelper::defaultModel()
  707. * - `escape` Whether or not to HTML escape the text.
  708. *
  709. * @param string|int $last if string use as label for the link, if numeric print page numbers
  710. * @param array $options Array of options
  711. * @return string numbers string.
  712. * @link http://book.cakephp.org/2.0/en/core-libraries/helpers/paginator.html#PaginatorHelper::last
  713. */
  714. public function last($last = 'last >>', array $options = array()) {
  715. $options += ['model' => $this->defaultModel(), 'escape' => true];
  716. $params = $this->params($options['model']);
  717. if ($params['pageCount'] <= 1) {
  718. return false;
  719. }
  720. $out = '';
  721. $lower = $params['pageCount'] - $last + 1;
  722. if (is_int($last) && $params['page'] <= $lower) {
  723. for ($i = $lower; $i <= $params['pageCount']; $i++) {
  724. $out .= $this->templater()->format('number', [
  725. 'url' => $this->generateUrl(['page' => $i], $options['model']),
  726. 'text' => $i
  727. ]);
  728. }
  729. } elseif ($params['page'] < $params['pageCount'] && is_string($last)) {
  730. $last = $options['escape'] ? h($last) : $last;
  731. $out .= $this->templater()->format('last', [
  732. 'url' => $this->generateUrl(['page' => $params['pageCount']], $options['model']),
  733. 'text' => $last
  734. ]);
  735. }
  736. return $out;
  737. }
  738. /**
  739. * Event listeners.
  740. *
  741. * @return array
  742. */
  743. public function implementedEvents() {
  744. return [];
  745. }
  746. }