1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
20:
21: 22: 23: 24: 25: 26: 27: 28: 29:
30: class PaginatorHelper extends AppHelper {
31:
32: 33: 34: 35: 36:
37: var $helpers = array('Html');
38:
39: 40: 41: 42: 43:
44: var $__defaultModel = null;
45:
46: 47: 48: 49: 50:
51: var $_ajaxHelperClass = 'Js';
52:
53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76:
77: var $options = array();
78:
79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89:
90: function __construct($config = array()) {
91: parent::__construct($config);
92: $ajaxProvider = isset($config['ajax']) ? $config['ajax'] : 'Js';
93: $this->helpers[] = $ajaxProvider;
94: $this->_ajaxHelperClass = $ajaxProvider;
95:
96: App::import('Helper', $ajaxProvider);
97: $classname = $ajaxProvider . 'Helper';
98: if (!is_callable(array($classname, 'link'))) {
99: trigger_error(sprintf(__('%s does not implement a link() method, it is incompatible with PaginatorHelper', true), $classname), E_USER_WARNING);
100: }
101: }
102:
103: 104: 105: 106: 107: 108:
109: function beforeRender() {
110: $this->options['url'] = array_merge($this->params['pass'], $this->params['named']);
111:
112: parent::beforeRender();
113: }
114:
115: 116: 117: 118: 119: 120: 121:
122: function params($model = null) {
123: if (empty($model)) {
124: $model = $this->defaultModel();
125: }
126: if (!isset($this->params['paging']) || empty($this->params['paging'][$model])) {
127: return null;
128: }
129: return $this->params['paging'][$model];
130: }
131:
132: 133: 134: 135: 136: 137: 138: 139:
140: function options($options = array()) {
141: if (is_string($options)) {
142: $options = array('update' => $options);
143: }
144:
145: if (!empty($options['paging'])) {
146: if (!isset($this->params['paging'])) {
147: $this->params['paging'] = array();
148: }
149: $this->params['paging'] = array_merge($this->params['paging'], $options['paging']);
150: unset($options['paging']);
151: }
152: $model = $this->defaultModel();
153:
154: if (!empty($options[$model])) {
155: if (!isset($this->params['paging'][$model])) {
156: $this->params['paging'][$model] = array();
157: }
158: $this->params['paging'][$model] = array_merge(
159: $this->params['paging'][$model], $options[$model]
160: );
161: unset($options[$model]);
162: }
163: $this->options = array_filter(array_merge($this->options, $options));
164: }
165:
166: 167: 168: 169: 170: 171: 172:
173: function current($model = null) {
174: $params = $this->params($model);
175:
176: if (isset($params['page'])) {
177: return $params['page'];
178: }
179: return 1;
180: }
181:
182: 183: 184: 185: 186: 187: 188: 189: 190:
191: function sortKey($model = null, $options = array()) {
192: if (empty($options)) {
193: $params = $this->params($model);
194: $options = array_merge($params['defaults'], $params['options']);
195: }
196:
197: if (isset($options['sort']) && !empty($options['sort'])) {
198: return $options['sort'];
199: } elseif (isset($options['order']) && is_array($options['order'])) {
200: return key($options['order']);
201: } elseif (isset($options['order']) && is_string($options['order'])) {
202: return $options['order'];
203: }
204: return null;
205: }
206:
207: 208: 209: 210: 211: 212: 213: 214: 215:
216: function sortDir($model = null, $options = array()) {
217: $dir = null;
218:
219: if (empty($options)) {
220: $params = $this->params($model);
221: $options = array_merge($params['defaults'], $params['options']);
222: }
223:
224: if (isset($options['direction'])) {
225: $dir = strtolower($options['direction']);
226: } elseif (isset($options['order']) && is_array($options['order'])) {
227: $dir = strtolower(current($options['order']));
228: }
229:
230: if ($dir == 'desc') {
231: return 'desc';
232: }
233: return 'asc';
234: }
235:
236: 237: 238: 239: 240: 241: 242: 243: 244: 245: 246: 247: 248: 249: 250: 251:
252: function prev($title = '<< Previous', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
253: return $this->__pagingLink('Prev', $title, $options, $disabledTitle, $disabledOptions);
254: }
255:
256: 257: 258: 259: 260: 261: 262: 263: 264: 265: 266: 267: 268: 269: 270: 271:
272: function next($title = 'Next >>', $options = array(), $disabledTitle = null, $disabledOptions = array()) {
273: return $this->__pagingLink('Next', $title, $options, $disabledTitle, $disabledOptions);
274: }
275:
276: 277: 278: 279: 280: 281: 282: 283: 284: 285: 286: 287: 288: 289: 290: 291: 292:
293: function sort($title, $key = null, $options = array()) {
294: $options = array_merge(array('url' => array(), 'model' => null), $options);
295: $url = $options['url'];
296: unset($options['url']);
297:
298: if (empty($key)) {
299: $key = $title;
300: $title = __(Inflector::humanize(preg_replace('/_id$/', '', $title)), true);
301: }
302: $dir = isset($options['direction']) ? $options['direction'] : 'asc';
303: unset($options['direction']);
304:
305: $sortKey = $this->sortKey($options['model']);
306: $defaultModel = $this->defaultModel();
307: $isSorted = (
308: $sortKey === $key ||
309: $sortKey === $defaultModel . '.' . $key ||
310: $key === $defaultModel . '.' . $sortKey
311: );
312:
313: if ($isSorted) {
314: $dir = $this->sortDir($options['model']) === 'asc' ? 'desc' : 'asc';
315: $class = $dir === 'asc' ? 'desc' : 'asc';
316: if (!empty($options['class'])) {
317: $options['class'] .= ' ' . $class;
318: } else {
319: $options['class'] = $class;
320: }
321: }
322: if (is_array($title) && array_key_exists($dir, $title)) {
323: $title = $title[$dir];
324: }
325:
326: $url = array_merge(array('sort' => $key, 'direction' => $dir), $url, array('order' => null));
327: return $this->link($title, $url, $options);
328: }
329:
330: 331: 332: 333: 334: 335: 336: 337: 338: 339: 340: 341: 342: 343: 344: 345:
346: function link($title, $url = array(), $options = array()) {
347: $options = array_merge(array('model' => null, 'escape' => true), $options);
348: $model = $options['model'];
349: unset($options['model']);
350:
351: if (!empty($this->options)) {
352: $options = array_merge($this->options, $options);
353: }
354: if (isset($options['url'])) {
355: $url = array_merge((array)$options['url'], (array)$url);
356: unset($options['url']);
357: }
358: $url = $this->url($url, true, $model);
359:
360: $obj = isset($options['update']) ? $this->_ajaxHelperClass : 'Html';
361: $url = array_merge(array('page' => $this->current($model)), $url);
362: $url = array_merge(Set::filter($url, true), array_intersect_key($url, array('plugin' => true)));
363: return $this->{$obj}->link($title, $url, $options);
364: }
365:
366: 367: 368: 369: 370: 371: 372: 373: 374:
375: function url($options = array(), $asArray = false, $model = null) {
376: $paging = $this->params($model);
377: $url = array_merge(array_filter(Set::diff(array_merge(
378: $paging['defaults'], $paging['options']), $paging['defaults'])), $options
379: );
380:
381: if (isset($url['order'])) {
382: $sort = $direction = null;
383: if (is_array($url['order'])) {
384: list($sort, $direction) = array($this->sortKey($model, $url), current($url['order']));
385: }
386: unset($url['order']);
387: $url = array_merge($url, compact('sort', 'direction'));
388: }
389:
390: if ($asArray) {
391: return $url;
392: }
393: return parent::url($url);
394: }
395:
396: 397: 398: 399: 400:
401: function __pagingLink($which, $title = null, $options = array(), $disabledTitle = null, $disabledOptions = array()) {
402: $check = 'has' . $which;
403: $_defaults = array(
404: 'url' => array(), 'step' => 1, 'escape' => true,
405: 'model' => null, 'tag' => 'span', 'class' => strtolower($which)
406: );
407: $options = array_merge($_defaults, (array)$options);
408: $paging = $this->params($options['model']);
409: if (empty($disabledOptions)) {
410: $disabledOptions = $options;
411: }
412:
413: if (!$this->{$check}($options['model']) && (!empty($disabledTitle) || !empty($disabledOptions))) {
414: if (!empty($disabledTitle) && $disabledTitle !== true) {
415: $title = $disabledTitle;
416: }
417: $options = array_merge($_defaults, (array)$disabledOptions);
418: } elseif (!$this->{$check}($options['model'])) {
419: return null;
420: }
421:
422: foreach (array_keys($_defaults) as $key) {
423: ${$key} = $options[$key];
424: unset($options[$key]);
425: }
426: $url = array_merge(array('page' => $paging['page'] + ($which == 'Prev' ? $step * -1 : $step)), $url);
427:
428: if ($this->{$check}($model)) {
429: return $this->Html->tag($tag, $this->link($title, $url, array_merge($options, compact('escape', 'class'))));
430: } else {
431: return $this->Html->tag($tag, $title, array_merge($options, compact('escape', 'class')));
432: }
433: }
434:
435: 436: 437: 438: 439: 440: 441:
442: function hasPrev($model = null) {
443: return $this->__hasPage($model, 'prev');
444: }
445:
446: 447: 448: 449: 450: 451: 452:
453: function hasNext($model = null) {
454: return $this->__hasPage($model, 'next');
455: }
456:
457: 458: 459: 460: 461: 462: 463: 464:
465: function hasPage($model = null, $page = 1) {
466: if (is_numeric($model)) {
467: $page = $model;
468: $model = null;
469: }
470: $paging = $this->params($model);
471: return $page <= $paging['pageCount'];
472: }
473:
474: 475: 476: 477: 478: 479: 480: 481:
482: function __hasPage($model, $page) {
483: $params = $this->params($model);
484: if (!empty($params)) {
485: if ($params["{$page}Page"] == true) {
486: return true;
487: }
488: }
489: return false;
490: }
491:
492: 493: 494: 495: 496: 497:
498: function defaultModel() {
499: if ($this->__defaultModel != null) {
500: return $this->__defaultModel;
501: }
502: if (empty($this->params['paging'])) {
503: return null;
504: }
505: list($this->__defaultModel) = array_keys($this->params['paging']);
506: return $this->__defaultModel;
507: }
508:
509: 510: 511: 512: 513: 514: 515: 516: 517: 518: 519: 520: 521: 522: 523: 524:
525: function counter($options = array()) {
526: if (is_string($options)) {
527: $options = array('format' => $options);
528: }
529:
530: $options = array_merge(
531: array(
532: 'model' => $this->defaultModel(),
533: 'format' => 'pages',
534: 'separator' => __(' of ', true)
535: ),
536: $options);
537:
538: $paging = $this->params($options['model']);
539: if ($paging['pageCount'] == 0) {
540: $paging['pageCount'] = 1;
541: }
542: $start = 0;
543: if ($paging['count'] >= 1) {
544: $start = (($paging['page'] - 1) * $paging['options']['limit']) + 1;
545: }
546: $end = $start + $paging['options']['limit'] - 1;
547: if ($paging['count'] < $end) {
548: $end = $paging['count'];
549: }
550:
551: switch ($options['format']) {
552: case 'range':
553: if (!is_array($options['separator'])) {
554: $options['separator'] = array(' - ', $options['separator']);
555: }
556: $out = $start . $options['separator'][0] . $end . $options['separator'][1];
557: $out .= $paging['count'];
558: break;
559: case 'pages':
560: $out = $paging['page'] . $options['separator'] . $paging['pageCount'];
561: break;
562: default:
563: $map = array(
564: '%page%' => $paging['page'],
565: '%pages%' => $paging['pageCount'],
566: '%current%' => $paging['current'],
567: '%count%' => $paging['count'],
568: '%start%' => $start,
569: '%end%' => $end
570: );
571: $out = str_replace(array_keys($map), array_values($map), $options['format']);
572:
573: $newKeys = array(
574: '{:page}', '{:pages}', '{:current}', '{:count}', '{:start}', '{:end}'
575: );
576: $out = str_replace($newKeys, array_values($map), $out);
577: break;
578: }
579: return $out;
580: }
581:
582: 583: 584: 585: 586: 587: 588: 589: 590: 591: 592: 593: 594: 595: 596: 597: 598: 599: 600: 601: 602:
603: function numbers($options = array()) {
604: if ($options === true) {
605: $options = array(
606: 'before' => ' | ', 'after' => ' | ', 'first' => 'first', 'last' => 'last'
607: );
608: }
609:
610: $defaults = array(
611: 'tag' => 'span', 'before' => null, 'after' => null, 'model' => $this->defaultModel(),
612: 'modulus' => '8', 'separator' => ' | ', 'first' => null, 'last' => null,
613: );
614: $options += $defaults;
615:
616: $params = (array)$this->params($options['model']) + array('page'=> 1);
617: unset($options['model']);
618:
619: if ($params['pageCount'] <= 1) {
620: return false;
621: }
622:
623: extract($options);
624: unset($options['tag'], $options['before'], $options['after'], $options['model'],
625: $options['modulus'], $options['separator'], $options['first'], $options['last']);
626:
627: $out = '';
628:
629: if ($modulus && $params['pageCount'] > $modulus) {
630: $half = intval($modulus / 2);
631: $end = $params['page'] + $half;
632:
633: if ($end > $params['pageCount']) {
634: $end = $params['pageCount'];
635: }
636: $start = $params['page'] - ($modulus - ($end - $params['page']));
637: if ($start <= 1) {
638: $start = 1;
639: $end = $params['page'] + ($modulus - $params['page']) + 1;
640: }
641:
642: if ($first && $start > 1) {
643: $offset = ($start <= (int)$first) ? $start - 1 : $first;
644: if ($offset < $start - 1) {
645: $out .= $this->first($offset, array('tag' => $tag, 'separator' => $separator));
646: } else {
647: $out .= $this->first($offset, array('tag' => $tag, 'after' => $separator, 'separator' => $separator));
648: }
649: }
650:
651: $out .= $before;
652:
653: for ($i = $start; $i < $params['page']; $i++) {
654: $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options))
655: . $separator;
656: }
657:
658: $out .= $this->Html->tag($tag, $params['page'], array('class' => 'current'));
659: if ($i != $params['pageCount']) {
660: $out .= $separator;
661: }
662:
663: $start = $params['page'] + 1;
664: for ($i = $start; $i < $end; $i++) {
665: $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options))
666: . $separator;
667: }
668:
669: if ($end != $params['page']) {
670: $out .= $this->Html->tag($tag, $this->link($i, array('page' => $end), $options));
671: }
672:
673: $out .= $after;
674:
675: if ($last && $end < $params['pageCount']) {
676: $offset = ($params['pageCount'] < $end + (int)$last) ? $params['pageCount'] - $end : $last;
677: if ($offset <= $last && $params['pageCount'] - $end > $offset) {
678: $out .= $this->last($offset, array('tag' => $tag, 'separator' => $separator));
679: } else {
680: $out .= $this->last($offset, array('tag' => $tag, 'before' => $separator, 'separator' => $separator));
681: }
682: }
683:
684: } else {
685: $out .= $before;
686:
687: for ($i = 1; $i <= $params['pageCount']; $i++) {
688: if ($i == $params['page']) {
689: $out .= $this->Html->tag($tag, $i, array('class' => 'current'));
690: } else {
691: $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
692: }
693: if ($i != $params['pageCount']) {
694: $out .= $separator;
695: }
696: }
697:
698: $out .= $after;
699: }
700:
701: return $out;
702: }
703:
704: 705: 706: 707: 708: 709: 710: 711: 712: 713: 714: 715: 716: 717: 718:
719: function first($first = '<< first', $options = array()) {
720: $options = array_merge(
721: array(
722: 'tag' => 'span',
723: 'after'=> null,
724: 'model' => $this->defaultModel(),
725: 'separator' => ' | ',
726: ),
727: (array)$options);
728:
729: $params = array_merge(array('page'=> 1), (array)$this->params($options['model']));
730: unset($options['model']);
731:
732: if ($params['pageCount'] <= 1) {
733: return false;
734: }
735: extract($options);
736: unset($options['tag'], $options['after'], $options['model'], $options['separator']);
737:
738: $out = '';
739:
740: if (is_int($first) && $params['page'] > $first) {
741: if ($after === null) {
742: $after = '...';
743: }
744: for ($i = 1; $i <= $first; $i++) {
745: $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
746: if ($i != $first) {
747: $out .= $separator;
748: }
749: }
750: $out .= $after;
751: } elseif ($params['page'] > 1) {
752: $out = $this->Html->tag($tag, $this->link($first, array('page' => 1), $options))
753: . $after;
754: }
755: return $out;
756: }
757:
758: 759: 760: 761: 762: 763: 764: 765: 766: 767: 768: 769: 770: 771: 772:
773: function last($last = 'last >>', $options = array()) {
774: $options = array_merge(
775: array(
776: 'tag' => 'span',
777: 'before'=> null,
778: 'model' => $this->defaultModel(),
779: 'separator' => ' | ',
780: ),
781: (array)$options);
782:
783: $params = array_merge(array('page'=> 1), (array)$this->params($options['model']));
784: unset($options['model']);
785:
786: if ($params['pageCount'] <= 1) {
787: return false;
788: }
789:
790: extract($options);
791: unset($options['tag'], $options['before'], $options['model'], $options['separator']);
792:
793: $out = '';
794: $lower = $params['pageCount'] - $last + 1;
795:
796: if (is_int($last) && $params['page'] < $lower) {
797: if ($before === null) {
798: $before = '...';
799: }
800: for ($i = $lower; $i <= $params['pageCount']; $i++) {
801: $out .= $this->Html->tag($tag, $this->link($i, array('page' => $i), $options));
802: if ($i != $params['pageCount']) {
803: $out .= $separator;
804: }
805: }
806: $out = $before . $out;
807: } elseif ($params['page'] < $params['pageCount']) {
808: $out = $before . $this->Html->tag(
809: $tag, $this->link($last, array('page' => $params['pageCount']), $options
810: ));
811: }
812: return $out;
813: }
814: }
815: