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