1
|
|
|
<?php |
2
|
|
|
/** |
3
|
|
|
* CakePHP(tm) : Rapid Development Framework (https://cakephp.org) |
4
|
|
|
* Copyright (c) Cake Software Foundation, Inc. (https://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. (https://cakefoundation.org) |
11
|
|
|
* @link https://cakephp.org CakePHP(tm) Project |
12
|
|
|
* @since 1.2.0 |
13
|
|
|
* @license https://opensource.org/licenses/mit-license.php MIT License |
14
|
|
|
*/ |
15
|
|
|
namespace Cake\View\Helper; |
16
|
|
|
|
17
|
|
|
use Cake\Utility\Hash; |
18
|
|
|
use Cake\Utility\Inflector; |
19
|
|
|
use Cake\View\Helper; |
20
|
|
|
use Cake\View\StringTemplateTrait; |
21
|
|
|
use Cake\View\View; |
22
|
|
|
|
23
|
|
|
/** |
24
|
|
|
* Pagination Helper class for easy generation of pagination links. |
25
|
|
|
* |
26
|
|
|
* PaginationHelper encloses all methods needed when working with pagination. |
27
|
|
|
* |
28
|
|
|
* @property \Cake\View\Helper\UrlHelper $Url |
29
|
|
|
* @property \Cake\View\Helper\NumberHelper $Number |
30
|
|
|
* @property \Cake\View\Helper\HtmlHelper $Html |
31
|
|
|
* @property \Cake\View\Helper\FormHelper $Form |
32
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html |
33
|
|
|
*/ |
34
|
|
|
class PaginatorHelper extends Helper |
35
|
|
|
{ |
36
|
|
|
use StringTemplateTrait; |
37
|
|
|
|
38
|
|
|
/** |
39
|
|
|
* List of helpers used by this helper |
40
|
|
|
* |
41
|
|
|
* @var array |
42
|
|
|
*/ |
43
|
|
|
public $helpers = ['Url', 'Number', 'Html', 'Form']; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Default config for this class |
47
|
|
|
* |
48
|
|
|
* Options: Holds the default options for pagination links |
49
|
|
|
* |
50
|
|
|
* The values that may be specified are: |
51
|
|
|
* |
52
|
|
|
* - `url` Url of the action. See Router::url() |
53
|
|
|
* - `url['sort']` the key that the recordset is sorted. |
54
|
|
|
* - `url['direction']` Direction of the sorting (default: 'asc'). |
55
|
|
|
* - `url['page']` Page number to use in links. |
56
|
|
|
* - `model` The name of the model. |
57
|
|
|
* - `escape` Defines if the title field for the link should be escaped (default: true). |
58
|
|
|
* |
59
|
|
|
* Templates: the templates used by this class |
60
|
|
|
* |
61
|
|
|
* @var array |
62
|
|
|
*/ |
63
|
|
|
protected $_defaultConfig = [ |
64
|
|
|
'options' => [], |
65
|
|
|
'templates' => [ |
66
|
|
|
'nextActive' => '<li class="next"><a rel="next" href="{{url}}">{{text}}</a></li>', |
67
|
|
|
'nextDisabled' => '<li class="next disabled"><a href="" onclick="return false;">{{text}}</a></li>', |
68
|
|
|
'prevActive' => '<li class="prev"><a rel="prev" href="{{url}}">{{text}}</a></li>', |
69
|
|
|
'prevDisabled' => '<li class="prev disabled"><a href="" onclick="return false;">{{text}}</a></li>', |
70
|
|
|
'counterRange' => '{{start}} - {{end}} of {{count}}', |
71
|
|
|
'counterPages' => '{{page}} of {{pages}}', |
72
|
|
|
'first' => '<li class="first"><a href="{{url}}">{{text}}</a></li>', |
73
|
|
|
'last' => '<li class="last"><a href="{{url}}">{{text}}</a></li>', |
74
|
|
|
'number' => '<li><a href="{{url}}">{{text}}</a></li>', |
75
|
|
|
'current' => '<li class="active"><a href="">{{text}}</a></li>', |
76
|
|
|
'ellipsis' => '<li class="ellipsis">…</li>', |
77
|
|
|
'sort' => '<a href="{{url}}">{{text}}</a>', |
78
|
|
|
'sortAsc' => '<a class="asc" href="{{url}}">{{text}}</a>', |
79
|
|
|
'sortDesc' => '<a class="desc" href="{{url}}">{{text}}</a>', |
80
|
|
|
'sortAscLocked' => '<a class="asc locked" href="{{url}}">{{text}}</a>', |
81
|
|
|
'sortDescLocked' => '<a class="desc locked" href="{{url}}">{{text}}</a>', |
82
|
|
|
], |
83
|
|
|
]; |
84
|
|
|
|
85
|
|
|
/** |
86
|
|
|
* Default model of the paged sets |
87
|
|
|
* |
88
|
|
|
* @var string |
89
|
|
|
*/ |
90
|
|
|
protected $_defaultModel; |
91
|
|
|
|
92
|
|
|
/** |
93
|
|
|
* Constructor. Overridden to merge passed args with URL options. |
94
|
|
|
* |
95
|
|
|
* @param \Cake\View\View $View The View this helper is being attached to. |
96
|
|
|
* @param array $config Configuration settings for the helper. |
97
|
|
|
*/ |
98
|
|
|
public function __construct(View $View, array $config = []) |
99
|
|
|
{ |
100
|
|
|
parent::__construct($View, $config); |
101
|
|
|
|
102
|
|
|
$query = $this->_View->getRequest()->getQueryParams(); |
103
|
|
|
unset($query['page'], $query['limit'], $query['sort'], $query['direction']); |
104
|
|
|
$this->setConfig( |
105
|
|
|
'options.url', |
106
|
|
|
array_merge($this->_View->getRequest()->getParam('pass', []), ['?' => $query]) |
107
|
|
|
); |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* Gets the current paging parameters from the resultset for the given model |
112
|
|
|
* |
113
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
114
|
|
|
* @return array The array of paging parameters for the paginated resultset. |
115
|
|
|
*/ |
116
|
|
|
public function params($model = null) |
117
|
|
|
{ |
118
|
|
|
$request = $this->_View->getRequest(); |
119
|
|
|
|
120
|
|
|
if (empty($model)) { |
121
|
|
|
$model = $this->defaultModel(); |
122
|
|
|
} |
123
|
|
|
if (!$request->getParam('paging') || !$request->getParam('paging.' . $model)) { |
124
|
|
|
return []; |
125
|
|
|
} |
126
|
|
|
|
127
|
|
|
return $request->getParam('paging.' . $model); |
128
|
|
|
} |
129
|
|
|
|
130
|
|
|
/** |
131
|
|
|
* Convenience access to any of the paginator params. |
132
|
|
|
* |
133
|
|
|
* @param string $key Key of the paginator params array to retrieve. |
134
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
135
|
|
|
* @return mixed Content of the requested param. |
136
|
|
|
*/ |
137
|
|
|
public function param($key, $model = null) |
138
|
|
|
{ |
139
|
|
|
$params = $this->params($model); |
140
|
|
|
if (!isset($params[$key])) { |
141
|
|
|
return null; |
142
|
|
|
} |
143
|
|
|
|
144
|
|
|
return $params[$key]; |
145
|
|
|
} |
146
|
|
|
|
147
|
|
|
/** |
148
|
|
|
* Sets default options for all pagination links |
149
|
|
|
* |
150
|
|
|
* @param array $options Default options for pagination links. |
151
|
|
|
* See PaginatorHelper::$options for list of keys. |
152
|
|
|
* @return void |
153
|
|
|
*/ |
154
|
|
|
public function options(array $options = []) |
155
|
|
|
{ |
156
|
|
|
$request = $this->_View->getRequest(); |
157
|
|
|
|
158
|
|
|
if (!empty($options['paging'])) { |
159
|
|
|
$request = $request->withParam( |
160
|
|
|
'paging', |
161
|
|
|
$options['paging'] + $request->getParam('paging', []) |
162
|
|
|
); |
163
|
|
|
unset($options['paging']); |
164
|
|
|
} |
165
|
|
|
|
166
|
|
|
$model = $this->defaultModel(); |
167
|
|
|
if (!empty($options[$model])) { |
168
|
|
|
$request = $request->withParam( |
169
|
|
|
'paging.' . $model, |
170
|
|
|
$options[$model] + (array)$request->getParam('paging.' . $model, []) |
171
|
|
|
); |
172
|
|
|
unset($options[$model]); |
173
|
|
|
} |
174
|
|
|
|
175
|
|
|
$this->_View->setRequest($request); |
176
|
|
|
|
177
|
|
|
$this->_config['options'] = array_filter($options + $this->_config['options']); |
178
|
|
|
if (empty($this->_config['options']['url'])) { |
179
|
|
|
$this->_config['options']['url'] = []; |
180
|
|
|
} |
181
|
|
|
if (!empty($this->_config['options']['model'])) { |
182
|
|
|
$this->defaultModel($this->_config['options']['model']); |
183
|
|
|
} |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
/** |
187
|
|
|
* Gets the current page of the recordset for the given model |
188
|
|
|
* |
189
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
190
|
|
|
* @return int The current page number of the recordset. |
191
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#checking-the-pagination-state |
192
|
|
|
*/ |
193
|
|
View Code Duplication |
public function current($model = null) |
194
|
|
|
{ |
195
|
|
|
$params = $this->params($model); |
196
|
|
|
|
197
|
|
|
if (isset($params['page'])) { |
198
|
|
|
return $params['page']; |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
return 1; |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Gets the total number of pages in the recordset for the given model. |
206
|
|
|
* |
207
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
208
|
|
|
* @return int The total pages for the recordset. |
209
|
|
|
*/ |
210
|
|
View Code Duplication |
public function total($model = null) |
211
|
|
|
{ |
212
|
|
|
$params = $this->params($model); |
213
|
|
|
|
214
|
|
|
if (isset($params['pageCount'])) { |
215
|
|
|
return $params['pageCount']; |
216
|
|
|
} |
217
|
|
|
|
218
|
|
|
return 0; |
219
|
|
|
} |
220
|
|
|
|
221
|
|
|
/** |
222
|
|
|
* Gets the current key by which the recordset is sorted |
223
|
|
|
* |
224
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
225
|
|
|
* @param array $options Options for pagination links. See #options for list of keys. |
226
|
|
|
* @return string|null The name of the key by which the recordset is being sorted, or |
227
|
|
|
* null if the results are not currently sorted. |
228
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-sort-links |
229
|
|
|
*/ |
230
|
|
|
public function sortKey($model = null, array $options = []) |
231
|
|
|
{ |
232
|
|
|
if (empty($options)) { |
233
|
|
|
$options = $this->params($model); |
234
|
|
|
} |
235
|
|
|
if (!empty($options['sort'])) { |
236
|
|
|
return $options['sort']; |
237
|
|
|
} |
238
|
|
|
|
239
|
|
|
return null; |
240
|
|
|
} |
241
|
|
|
|
242
|
|
|
/** |
243
|
|
|
* Gets the current direction the recordset is sorted |
244
|
|
|
* |
245
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
246
|
|
|
* @param array $options Options for pagination links. See #options for list of keys. |
247
|
|
|
* @return string The direction by which the recordset is being sorted, or |
248
|
|
|
* null if the results are not currently sorted. |
249
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-sort-links |
250
|
|
|
*/ |
251
|
|
|
public function sortDir($model = null, array $options = []) |
252
|
|
|
{ |
253
|
|
|
$dir = null; |
254
|
|
|
|
255
|
|
|
if (empty($options)) { |
256
|
|
|
$options = $this->params($model); |
257
|
|
|
} |
258
|
|
|
|
259
|
|
|
if (isset($options['direction'])) { |
260
|
|
|
$dir = strtolower($options['direction']); |
261
|
|
|
} |
262
|
|
|
|
263
|
|
|
if ($dir === 'desc') { |
264
|
|
|
return 'desc'; |
265
|
|
|
} |
266
|
|
|
|
267
|
|
|
return 'asc'; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/** |
271
|
|
|
* Generate an active/inactive link for next/prev methods. |
272
|
|
|
* |
273
|
|
|
* @param string|bool $text The enabled text for the link. |
274
|
|
|
* @param bool $enabled Whether or not the enabled/disabled version should be created. |
275
|
|
|
* @param array $options An array of options from the calling method. |
276
|
|
|
* @param array $templates An array of templates with the 'active' and 'disabled' keys. |
277
|
|
|
* @return string Generated HTML |
278
|
|
|
*/ |
279
|
|
|
protected function _toggledLink($text, $enabled, $options, $templates) |
280
|
|
|
{ |
281
|
|
|
$template = $templates['active']; |
282
|
|
|
if (!$enabled) { |
283
|
|
|
$text = $options['disabledTitle']; |
284
|
|
|
$template = $templates['disabled']; |
285
|
|
|
} |
286
|
|
|
|
287
|
|
|
if (!$enabled && $text === false) { |
288
|
|
|
return ''; |
289
|
|
|
} |
290
|
|
|
$text = $options['escape'] ? h($text) : $text; |
291
|
|
|
|
292
|
|
|
$templater = $this->templater(); |
293
|
|
|
$newTemplates = !empty($options['templates']) ? $options['templates'] : false; |
294
|
|
View Code Duplication |
if ($newTemplates) { |
295
|
|
|
$templater->push(); |
296
|
|
|
$templateMethod = is_string($options['templates']) ? 'load' : 'add'; |
297
|
|
|
$templater->{$templateMethod}($options['templates']); |
298
|
|
|
} |
299
|
|
|
|
300
|
|
|
if (!$enabled) { |
301
|
|
|
$out = $templater->format($template, [ |
302
|
|
|
'text' => $text, |
303
|
|
|
]); |
304
|
|
|
|
305
|
|
|
if ($newTemplates) { |
306
|
|
|
$templater->pop(); |
307
|
|
|
} |
308
|
|
|
|
309
|
|
|
return $out; |
310
|
|
|
} |
311
|
|
|
$paging = $this->params($options['model']); |
312
|
|
|
|
313
|
|
|
$url = array_merge( |
314
|
|
|
$options['url'], |
315
|
|
|
['page' => $paging['page'] + $options['step']] |
316
|
|
|
); |
317
|
|
|
$url = $this->generateUrl($url, $options['model']); |
318
|
|
|
|
319
|
|
|
$out = $templater->format($template, [ |
320
|
|
|
'url' => $url, |
321
|
|
|
'text' => $text, |
322
|
|
|
]); |
323
|
|
|
|
324
|
|
|
if ($newTemplates) { |
325
|
|
|
$templater->pop(); |
326
|
|
|
} |
327
|
|
|
|
328
|
|
|
return $out; |
329
|
|
|
} |
330
|
|
|
|
331
|
|
|
/** |
332
|
|
|
* Generates a "previous" link for a set of paged records |
333
|
|
|
* |
334
|
|
|
* ### Options: |
335
|
|
|
* |
336
|
|
|
* - `disabledTitle` The text to used when the link is disabled. This |
337
|
|
|
* defaults to the same text at the active link. Setting to false will cause |
338
|
|
|
* this method to return ''. |
339
|
|
|
* - `escape` Whether you want the contents html entity encoded, defaults to true |
340
|
|
|
* - `model` The model to use, defaults to PaginatorHelper::defaultModel() |
341
|
|
|
* - `url` An array of additional URL options to use for link generation. |
342
|
|
|
* - `templates` An array of templates, or template file name containing the |
343
|
|
|
* templates you'd like to use when generating the link for previous page. |
344
|
|
|
* The helper's original templates will be restored once prev() is done. |
345
|
|
|
* |
346
|
|
|
* @param string $title Title for the link. Defaults to '<< Previous'. |
347
|
|
|
* @param array $options Options for pagination link. See above for list of keys. |
348
|
|
|
* @return string A "previous" link or a disabled link. |
349
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-jump-links |
350
|
|
|
*/ |
351
|
|
View Code Duplication |
public function prev($title = '<< Previous', array $options = []) |
352
|
|
|
{ |
353
|
|
|
$defaults = [ |
354
|
|
|
'url' => [], |
355
|
|
|
'model' => $this->defaultModel(), |
356
|
|
|
'disabledTitle' => $title, |
357
|
|
|
'escape' => true, |
358
|
|
|
]; |
359
|
|
|
$options += $defaults; |
360
|
|
|
$options['step'] = -1; |
361
|
|
|
|
362
|
|
|
$enabled = $this->hasPrev($options['model']); |
363
|
|
|
$templates = [ |
364
|
|
|
'active' => 'prevActive', |
365
|
|
|
'disabled' => 'prevDisabled', |
366
|
|
|
]; |
367
|
|
|
|
368
|
|
|
return $this->_toggledLink($title, $enabled, $options, $templates); |
369
|
|
|
} |
370
|
|
|
|
371
|
|
|
/** |
372
|
|
|
* Generates a "next" link for a set of paged records |
373
|
|
|
* |
374
|
|
|
* ### Options: |
375
|
|
|
* |
376
|
|
|
* - `disabledTitle` The text to used when the link is disabled. This |
377
|
|
|
* defaults to the same text at the active link. Setting to false will cause |
378
|
|
|
* this method to return ''. |
379
|
|
|
* - `escape` Whether you want the contents html entity encoded, defaults to true |
380
|
|
|
* - `model` The model to use, defaults to PaginatorHelper::defaultModel() |
381
|
|
|
* - `url` An array of additional URL options to use for link generation. |
382
|
|
|
* - `templates` An array of templates, or template file name containing the |
383
|
|
|
* templates you'd like to use when generating the link for next page. |
384
|
|
|
* The helper's original templates will be restored once next() is done. |
385
|
|
|
* |
386
|
|
|
* @param string $title Title for the link. Defaults to 'Next >>'. |
387
|
|
|
* @param array $options Options for pagination link. See above for list of keys. |
388
|
|
|
* @return string A "next" link or $disabledTitle text if the link is disabled. |
389
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-jump-links |
390
|
|
|
*/ |
391
|
|
View Code Duplication |
public function next($title = 'Next >>', array $options = []) |
392
|
|
|
{ |
393
|
|
|
$defaults = [ |
394
|
|
|
'url' => [], |
395
|
|
|
'model' => $this->defaultModel(), |
396
|
|
|
'disabledTitle' => $title, |
397
|
|
|
'escape' => true, |
398
|
|
|
]; |
399
|
|
|
$options += $defaults; |
400
|
|
|
$options['step'] = 1; |
401
|
|
|
|
402
|
|
|
$enabled = $this->hasNext($options['model']); |
403
|
|
|
$templates = [ |
404
|
|
|
'active' => 'nextActive', |
405
|
|
|
'disabled' => 'nextDisabled', |
406
|
|
|
]; |
407
|
|
|
|
408
|
|
|
return $this->_toggledLink($title, $enabled, $options, $templates); |
409
|
|
|
} |
410
|
|
|
|
411
|
|
|
/** |
412
|
|
|
* Generates a sorting link. Sets named parameters for the sort and direction. Handles |
413
|
|
|
* direction switching automatically. |
414
|
|
|
* |
415
|
|
|
* ### Options: |
416
|
|
|
* |
417
|
|
|
* - `escape` Whether you want the contents html entity encoded, defaults to true. |
418
|
|
|
* - `model` The model to use, defaults to PaginatorHelper::defaultModel(). |
419
|
|
|
* - `direction` The default direction to use when this link isn't active. |
420
|
|
|
* - `lock` Lock direction. Will only use the default direction then, defaults to false. |
421
|
|
|
* |
422
|
|
|
* @param string $key The name of the key that the recordset should be sorted. |
423
|
|
|
* @param string|array|null $title Title for the link. If $title is null $key will be used |
424
|
|
|
* for the title and will be generated by inflection. It can also be an array |
425
|
|
|
* with keys `asc` and `desc` for specifying separate titles based on the direction. |
426
|
|
|
* @param array $options Options for sorting link. See above for list of keys. |
427
|
|
|
* @return string A link sorting default by 'asc'. If the resultset is sorted 'asc' by the specified |
428
|
|
|
* key the returned link will sort by 'desc'. |
429
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-sort-links |
430
|
|
|
*/ |
431
|
|
|
public function sort($key, $title = null, array $options = []) |
432
|
|
|
{ |
433
|
|
|
$options += ['url' => [], 'model' => null, 'escape' => true]; |
434
|
|
|
$url = $options['url']; |
435
|
|
|
unset($options['url']); |
436
|
|
|
|
437
|
|
|
if (empty($title)) { |
438
|
|
|
$title = $key; |
439
|
|
|
|
440
|
|
|
if (strpos($title, '.') !== false) { |
441
|
|
|
$title = str_replace('.', ' ', $title); |
442
|
|
|
} |
443
|
|
|
|
444
|
|
|
$title = __(Inflector::humanize(preg_replace('/_id$/', '', $title))); |
445
|
|
|
} |
446
|
|
|
|
447
|
|
|
$defaultDir = isset($options['direction']) ? strtolower($options['direction']) : 'asc'; |
448
|
|
|
unset($options['direction']); |
449
|
|
|
|
450
|
|
|
$locked = isset($options['lock']) ? $options['lock'] : false; |
451
|
|
|
unset($options['lock']); |
452
|
|
|
|
453
|
|
|
$sortKey = $this->sortKey($options['model']); |
454
|
|
|
$defaultModel = $this->defaultModel(); |
455
|
|
|
$model = $options['model'] ?: $defaultModel; |
456
|
|
|
list($table, $field) = explode('.', $key . '.'); |
457
|
|
|
if (!$field) { |
458
|
|
|
$field = $table; |
459
|
|
|
$table = $model; |
460
|
|
|
} |
461
|
|
|
$isSorted = ( |
462
|
|
|
$sortKey === $table . '.' . $field || |
463
|
|
|
$sortKey === $model . '.' . $key || |
464
|
|
|
$table . '.' . $field === $model . '.' . $sortKey |
465
|
|
|
); |
466
|
|
|
|
467
|
|
|
$template = 'sort'; |
468
|
|
|
$dir = $defaultDir; |
469
|
|
|
if ($isSorted) { |
470
|
|
|
if ($locked) { |
471
|
|
|
$template = $dir === 'asc' ? 'sortDescLocked' : 'sortAscLocked'; |
472
|
|
|
} else { |
473
|
|
|
$dir = $this->sortDir($options['model']) === 'asc' ? 'desc' : 'asc'; |
474
|
|
|
$template = $dir === 'asc' ? 'sortDesc' : 'sortAsc'; |
475
|
|
|
} |
476
|
|
|
} |
477
|
|
|
if (is_array($title) && array_key_exists($dir, $title)) { |
478
|
|
|
$title = $title[$dir]; |
479
|
|
|
} |
480
|
|
|
|
481
|
|
|
$url = array_merge( |
482
|
|
|
['sort' => $key, 'direction' => $dir, 'page' => 1], |
483
|
|
|
$url, |
484
|
|
|
['order' => null] |
485
|
|
|
); |
486
|
|
|
$vars = [ |
487
|
|
|
'text' => $options['escape'] ? h($title) : $title, |
488
|
|
|
'url' => $this->generateUrl($url, $options['model']), |
489
|
|
|
]; |
490
|
|
|
|
491
|
|
|
return $this->templater()->format($template, $vars); |
492
|
|
|
} |
493
|
|
|
|
494
|
|
|
/** |
495
|
|
|
* Merges passed URL options with current pagination state to generate a pagination URL. |
496
|
|
|
* |
497
|
|
|
* ### Url options: |
498
|
|
|
* |
499
|
|
|
* - `escape`: If false, the URL will be returned unescaped, do only use if it is manually |
500
|
|
|
* escaped afterwards before being displayed. |
501
|
|
|
* - `fullBase`: If true, the full base URL will be prepended to the result |
502
|
|
|
* |
503
|
|
|
* @param array $options Pagination/URL options array |
504
|
|
|
* @param string|null $model Which model to paginate on |
505
|
|
|
* @param array $urlOptions Array of options |
506
|
|
|
* The bool version of this argument is *deprecated* and will be removed in 4.0.0 |
507
|
|
|
* @return string By default, returns a full pagination URL string for use in non-standard contexts (i.e. JavaScript) |
508
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#generating-pagination-urls |
509
|
|
|
*/ |
510
|
|
|
public function generateUrl(array $options = [], $model = null, $urlOptions = []) |
511
|
|
|
{ |
512
|
|
|
if (is_bool($urlOptions)) { |
513
|
|
|
$urlOptions = ['fullBase' => $urlOptions]; |
514
|
|
|
deprecationWarning( |
515
|
|
|
'Passing a boolean value as third argument into PaginatorHelper::generateUrl() is deprecated ' . |
516
|
|
|
'and will be removed in 4.0.0 . ' . |
517
|
|
|
'Pass an array instead.' |
518
|
|
|
); |
519
|
|
|
} |
520
|
|
|
$urlOptions += [ |
521
|
|
|
'escape' => true, |
522
|
|
|
'fullBase' => false, |
523
|
|
|
]; |
524
|
|
|
|
525
|
|
|
return $this->Url->build($this->generateUrlParams($options, $model), $urlOptions); |
526
|
|
|
} |
527
|
|
|
|
528
|
|
|
/** |
529
|
|
|
* Merges passed URL options with current pagination state to generate a pagination URL. |
530
|
|
|
* |
531
|
|
|
* @param array $options Pagination/URL options array |
532
|
|
|
* @param string|null $model Which model to paginate on |
533
|
|
|
* @return array An array of URL parameters |
534
|
|
|
*/ |
535
|
|
|
public function generateUrlParams(array $options = [], $model = null) |
536
|
|
|
{ |
537
|
|
|
$paging = $this->params($model); |
538
|
|
|
$paging += ['page' => null, 'sort' => null, 'direction' => null, 'limit' => null]; |
539
|
|
|
|
540
|
|
View Code Duplication |
if (!empty($paging['sort']) && !empty($options['sort']) && strpos($options['sort'], '.') === false) { |
541
|
|
|
$paging['sort'] = $this->_removeAlias($paging['sort'], null); |
542
|
|
|
} |
543
|
|
View Code Duplication |
if (!empty($paging['sortDefault']) && !empty($options['sort']) && strpos($options['sort'], '.') === false) { |
544
|
|
|
$paging['sortDefault'] = $this->_removeAlias($paging['sortDefault'], $model); |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
$url = [ |
548
|
|
|
'page' => $paging['page'], |
549
|
|
|
'limit' => $paging['limit'], |
550
|
|
|
'sort' => $paging['sort'], |
551
|
|
|
'direction' => $paging['direction'], |
552
|
|
|
]; |
553
|
|
|
|
554
|
|
|
if (!empty($this->_config['options']['url'])) { |
555
|
|
|
$key = implode('.', array_filter(['options.url', Hash::get($paging, 'scope', null)])); |
556
|
|
|
$url = array_merge($url, Hash::get($this->_config, $key, [])); |
557
|
|
|
} |
558
|
|
|
|
559
|
|
|
$url = array_filter($url, function ($value) { |
560
|
|
|
return ($value || is_numeric($value) || $value === false); |
561
|
|
|
}); |
562
|
|
|
$url = array_merge($url, $options); |
563
|
|
|
|
564
|
|
|
if (!empty($url['page']) && $url['page'] == 1) { |
565
|
|
|
$url['page'] = false; |
566
|
|
|
} |
567
|
|
|
|
568
|
|
|
if ( |
569
|
|
|
isset($paging['sortDefault'], $paging['directionDefault'], $url['sort'], $url['direction']) && |
570
|
|
|
$url['sort'] === $paging['sortDefault'] && |
571
|
|
|
strtolower($url['direction']) === strtolower($paging['directionDefault']) |
572
|
|
|
) { |
573
|
|
|
$url['sort'] = $url['direction'] = null; |
574
|
|
|
} |
575
|
|
|
|
576
|
|
|
if (!empty($paging['scope'])) { |
577
|
|
|
$scope = $paging['scope']; |
578
|
|
|
$currentParams = $this->_config['options']['url']; |
579
|
|
|
|
580
|
|
|
if (isset($url['#'])) { |
581
|
|
|
$currentParams['#'] = $url['#']; |
582
|
|
|
unset($url['#']); |
583
|
|
|
} |
584
|
|
|
|
585
|
|
|
// Merge existing query parameters in the scope. |
586
|
|
|
if (isset($currentParams['?'][$scope]) && is_array($currentParams['?'][$scope])) { |
587
|
|
|
$url += $currentParams['?'][$scope]; |
588
|
|
|
unset($currentParams['?'][$scope]); |
589
|
|
|
} |
590
|
|
|
$url = [$scope => $url] + $currentParams; |
591
|
|
|
if (empty($url[$scope]['page'])) { |
592
|
|
|
unset($url[$scope]['page']); |
593
|
|
|
} |
594
|
|
|
} |
595
|
|
|
|
596
|
|
|
return $url; |
597
|
|
|
} |
598
|
|
|
|
599
|
|
|
/** |
600
|
|
|
* Remove alias if needed. |
601
|
|
|
* |
602
|
|
|
* @param string $field Current field |
603
|
|
|
* @param string|null $model Current model alias |
604
|
|
|
* @return string Unaliased field if applicable |
605
|
|
|
*/ |
606
|
|
|
protected function _removeAlias($field, $model = null) |
607
|
|
|
{ |
608
|
|
|
$currentModel = $model ?: $this->defaultModel(); |
609
|
|
|
|
610
|
|
|
if (strpos($field, '.') === false) { |
611
|
|
|
return $field; |
612
|
|
|
} |
613
|
|
|
|
614
|
|
|
list ($alias, $currentField) = explode('.', $field); |
615
|
|
|
|
616
|
|
|
if ($alias === $currentModel) { |
617
|
|
|
return $currentField; |
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
return $field; |
621
|
|
|
} |
622
|
|
|
|
623
|
|
|
/** |
624
|
|
|
* Returns true if the given result set is not at the first page |
625
|
|
|
* |
626
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
627
|
|
|
* @return bool True if the result set is not at the first page. |
628
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#checking-the-pagination-state |
629
|
|
|
*/ |
630
|
|
|
public function hasPrev($model = null) |
631
|
|
|
{ |
632
|
|
|
return $this->_hasPage($model, 'prev'); |
633
|
|
|
} |
634
|
|
|
|
635
|
|
|
/** |
636
|
|
|
* Returns true if the given result set is not at the last page |
637
|
|
|
* |
638
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
639
|
|
|
* @return bool True if the result set is not at the last page. |
640
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#checking-the-pagination-state |
641
|
|
|
*/ |
642
|
|
|
public function hasNext($model = null) |
643
|
|
|
{ |
644
|
|
|
return $this->_hasPage($model, 'next'); |
645
|
|
|
} |
646
|
|
|
|
647
|
|
|
/** |
648
|
|
|
* Returns true if the given result set has the page number given by $page |
649
|
|
|
* |
650
|
|
|
* @param string|null $model Optional model name. Uses the default if none is specified. |
651
|
|
|
* @param int $page The page number - if not set defaults to 1. |
652
|
|
|
* @return bool True if the given result set has the specified page number. |
653
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#checking-the-pagination-state |
654
|
|
|
*/ |
655
|
|
|
public function hasPage($model = null, $page = 1) |
656
|
|
|
{ |
657
|
|
|
if (is_numeric($model)) { |
658
|
|
|
$page = $model; |
659
|
|
|
$model = null; |
660
|
|
|
} |
661
|
|
|
$paging = $this->params($model); |
662
|
|
|
if ($paging === []) { |
663
|
|
|
return false; |
664
|
|
|
} |
665
|
|
|
|
666
|
|
|
return $page <= $paging['pageCount']; |
667
|
|
|
} |
668
|
|
|
|
669
|
|
|
/** |
670
|
|
|
* Does $model have $page in its range? |
671
|
|
|
* |
672
|
|
|
* @param string $model Model name to get parameters for. |
673
|
|
|
* @param int $page Page number you are checking. |
674
|
|
|
* @return bool Whether model has $page |
675
|
|
|
*/ |
676
|
|
|
protected function _hasPage($model, $page) |
677
|
|
|
{ |
678
|
|
|
$params = $this->params($model); |
679
|
|
|
|
680
|
|
|
return !empty($params) && $params[$page . 'Page']; |
681
|
|
|
} |
682
|
|
|
|
683
|
|
|
/** |
684
|
|
|
* Gets or sets the default model of the paged sets |
685
|
|
|
* |
686
|
|
|
* @param string|null $model Model name to set |
687
|
|
|
* @return string|null Model name or null if the pagination isn't initialized. |
688
|
|
|
*/ |
689
|
|
|
public function defaultModel($model = null) |
690
|
|
|
{ |
691
|
|
|
if ($model !== null) { |
692
|
|
|
$this->_defaultModel = $model; |
693
|
|
|
} |
694
|
|
|
if ($this->_defaultModel) { |
695
|
|
|
return $this->_defaultModel; |
696
|
|
|
} |
697
|
|
|
if (!$this->_View->getRequest()->getParam('paging')) { |
698
|
|
|
return null; |
699
|
|
|
} |
700
|
|
|
list($this->_defaultModel) = array_keys($this->_View->getRequest()->getParam('paging')); |
701
|
|
|
|
702
|
|
|
return $this->_defaultModel; |
703
|
|
|
} |
704
|
|
|
|
705
|
|
|
/** |
706
|
|
|
* Returns a counter string for the paged result set |
707
|
|
|
* |
708
|
|
|
* ### Options |
709
|
|
|
* |
710
|
|
|
* - `model` The model to use, defaults to PaginatorHelper::defaultModel(); |
711
|
|
|
* - `format` The format string you want to use, defaults to 'pages' Which generates output like '1 of 5' |
712
|
|
|
* set to 'range' to generate output like '1 - 3 of 13'. Can also be set to a custom string, containing |
713
|
|
|
* the following placeholders `{{page}}`, `{{pages}}`, `{{current}}`, `{{count}}`, `{{model}}`, `{{start}}`, `{{end}}` and any |
714
|
|
|
* custom content you would like. |
715
|
|
|
* |
716
|
|
|
* @param string|array $options Options for the counter string. See #options for list of keys. |
717
|
|
|
* If string it will be used as format. |
718
|
|
|
* @return string Counter string. |
719
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-a-page-counter |
720
|
|
|
*/ |
721
|
|
|
public function counter($options = []) |
722
|
|
|
{ |
723
|
|
|
if (is_string($options)) { |
724
|
|
|
$options = ['format' => $options]; |
725
|
|
|
} |
726
|
|
|
|
727
|
|
|
$options += [ |
728
|
|
|
'model' => $this->defaultModel(), |
729
|
|
|
'format' => 'pages', |
730
|
|
|
]; |
731
|
|
|
|
732
|
|
|
$paging = $this->params($options['model']); |
733
|
|
|
if (!$paging['pageCount']) { |
734
|
|
|
$paging['pageCount'] = 1; |
735
|
|
|
} |
736
|
|
|
|
737
|
|
|
switch ($options['format']) { |
738
|
|
|
case 'range': |
739
|
|
|
case 'pages': |
740
|
|
|
$template = 'counter' . ucfirst($options['format']); |
741
|
|
|
break; |
742
|
|
|
default: |
743
|
|
|
$template = 'counterCustom'; |
744
|
|
|
$this->templater()->add([$template => $options['format']]); |
745
|
|
|
} |
746
|
|
|
$map = array_map([$this->Number, 'format'], [ |
747
|
|
|
'page' => $paging['page'], |
748
|
|
|
'pages' => $paging['pageCount'], |
749
|
|
|
'current' => $paging['current'], |
750
|
|
|
'count' => $paging['count'], |
751
|
|
|
'start' => $paging['start'], |
752
|
|
|
'end' => $paging['end'], |
753
|
|
|
]); |
754
|
|
|
|
755
|
|
|
$map += [ |
756
|
|
|
'model' => strtolower(Inflector::humanize(Inflector::tableize($options['model']))), |
757
|
|
|
]; |
758
|
|
|
|
759
|
|
|
return $this->templater()->format($template, $map); |
760
|
|
|
} |
761
|
|
|
|
762
|
|
|
/** |
763
|
|
|
* Returns a set of numbers for the paged result set |
764
|
|
|
* uses a modulus to decide how many numbers to show on each side of the current page (default: 8). |
765
|
|
|
* |
766
|
|
|
* ``` |
767
|
|
|
* $this->Paginator->numbers(['first' => 2, 'last' => 2]); |
768
|
|
|
* ``` |
769
|
|
|
* |
770
|
|
|
* Using the first and last options you can create links to the beginning and end of the page set. |
771
|
|
|
* |
772
|
|
|
* ### Options |
773
|
|
|
* |
774
|
|
|
* - `before` Content to be inserted before the numbers, but after the first links. |
775
|
|
|
* - `after` Content to be inserted after the numbers, but before the last links. |
776
|
|
|
* - `model` Model to create numbers for, defaults to PaginatorHelper::defaultModel() |
777
|
|
|
* - `modulus` How many numbers to include on either side of the current page, defaults to 8. |
778
|
|
|
* Set to `false` to disable and to show all numbers. |
779
|
|
|
* - `first` Whether you want first links generated, set to an integer to define the number of 'first' |
780
|
|
|
* links to generate. If a string is set a link to the first page will be generated with the value |
781
|
|
|
* as the title. |
782
|
|
|
* - `last` Whether you want last links generated, set to an integer to define the number of 'last' |
783
|
|
|
* links to generate. If a string is set a link to the last page will be generated with the value |
784
|
|
|
* as the title. |
785
|
|
|
* - `templates` An array of templates, or template file name containing the templates you'd like to |
786
|
|
|
* use when generating the numbers. The helper's original templates will be restored once |
787
|
|
|
* numbers() is done. |
788
|
|
|
* - `url` An array of additional URL options to use for link generation. |
789
|
|
|
* |
790
|
|
|
* The generated number links will include the 'ellipsis' template when the `first` and `last` options |
791
|
|
|
* and the number of pages exceed the modulus. For example if you have 25 pages, and use the first/last |
792
|
|
|
* options and a modulus of 8, ellipsis content will be inserted after the first and last link sets. |
793
|
|
|
* |
794
|
|
|
* @param array $options Options for the numbers. |
795
|
|
|
* @return string|false Numbers string. |
796
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-page-number-links |
797
|
|
|
*/ |
798
|
|
|
public function numbers(array $options = []) |
799
|
|
|
{ |
800
|
|
|
$defaults = [ |
801
|
|
|
'before' => null, 'after' => null, 'model' => $this->defaultModel(), |
802
|
|
|
'modulus' => 8, 'first' => null, 'last' => null, 'url' => [], |
803
|
|
|
]; |
804
|
|
|
$options += $defaults; |
805
|
|
|
|
806
|
|
|
$params = (array)$this->params($options['model']) + ['page' => 1]; |
807
|
|
|
if ($params['pageCount'] <= 1) { |
808
|
|
|
return false; |
809
|
|
|
} |
810
|
|
|
|
811
|
|
|
$templater = $this->templater(); |
812
|
|
View Code Duplication |
if (isset($options['templates'])) { |
813
|
|
|
$templater->push(); |
814
|
|
|
$method = is_string($options['templates']) ? 'load' : 'add'; |
815
|
|
|
$templater->{$method}($options['templates']); |
816
|
|
|
} |
817
|
|
|
|
818
|
|
|
if ($options['modulus'] !== false && $params['pageCount'] > $options['modulus']) { |
819
|
|
|
$out = $this->_modulusNumbers($templater, $params, $options); |
820
|
|
|
} else { |
821
|
|
|
$out = $this->_numbers($templater, $params, $options); |
822
|
|
|
} |
823
|
|
|
|
824
|
|
|
if (isset($options['templates'])) { |
825
|
|
|
$templater->pop(); |
826
|
|
|
} |
827
|
|
|
|
828
|
|
|
return $out; |
829
|
|
|
} |
830
|
|
|
|
831
|
|
|
/** |
832
|
|
|
* Calculates the start and end for the pagination numbers. |
833
|
|
|
* |
834
|
|
|
* @param array $params Params from the numbers() method. |
835
|
|
|
* @param array $options Options from the numbers() method. |
836
|
|
|
* @return array An array with the start and end numbers. |
837
|
|
|
*/ |
838
|
|
|
protected function _getNumbersStartAndEnd($params, $options) |
839
|
|
|
{ |
840
|
|
|
$half = (int)($options['modulus'] / 2); |
841
|
|
|
$end = max(1 + $options['modulus'], $params['page'] + $half); |
842
|
|
|
$start = min($params['pageCount'] - $options['modulus'], $params['page'] - $half - $options['modulus'] % 2); |
843
|
|
|
|
844
|
|
|
if ($options['first']) { |
845
|
|
|
$first = is_int($options['first']) ? $options['first'] : 1; |
846
|
|
|
|
847
|
|
|
if ($start <= $first + 2) { |
848
|
|
|
$start = 1; |
849
|
|
|
} |
850
|
|
|
} |
851
|
|
|
|
852
|
|
|
if ($options['last']) { |
853
|
|
|
$last = is_int($options['last']) ? $options['last'] : 1; |
854
|
|
|
|
855
|
|
|
if ($end >= $params['pageCount'] - $last - 1) { |
856
|
|
|
$end = $params['pageCount']; |
857
|
|
|
} |
858
|
|
|
} |
859
|
|
|
|
860
|
|
|
$end = min($params['pageCount'], $end); |
861
|
|
|
$start = max(1, $start); |
862
|
|
|
|
863
|
|
|
return [$start, $end]; |
864
|
|
|
} |
865
|
|
|
|
866
|
|
|
/** |
867
|
|
|
* Formats a number for the paginator number output. |
868
|
|
|
* |
869
|
|
|
* @param \Cake\View\StringTemplate $templater StringTemplate instance. |
870
|
|
|
* @param array $options Options from the numbers() method. |
871
|
|
|
* @return string |
872
|
|
|
*/ |
873
|
|
|
protected function _formatNumber($templater, $options) |
874
|
|
|
{ |
875
|
|
|
$url = array_merge($options['url'], ['page' => $options['page']]); |
876
|
|
|
$vars = [ |
877
|
|
|
'text' => $options['text'], |
878
|
|
|
'url' => $this->generateUrl($url, $options['model']), |
879
|
|
|
]; |
880
|
|
|
|
881
|
|
|
return $templater->format('number', $vars); |
882
|
|
|
} |
883
|
|
|
|
884
|
|
|
/** |
885
|
|
|
* Generates the numbers for the paginator numbers() method. |
886
|
|
|
* |
887
|
|
|
* @param \Cake\View\StringTemplate $templater StringTemplate instance. |
888
|
|
|
* @param array $params Params from the numbers() method. |
889
|
|
|
* @param array $options Options from the numbers() method. |
890
|
|
|
* @return string Markup output. |
891
|
|
|
*/ |
892
|
|
|
protected function _modulusNumbers($templater, $params, $options) |
893
|
|
|
{ |
894
|
|
|
$out = ''; |
895
|
|
|
$ellipsis = $templater->format('ellipsis', []); |
896
|
|
|
|
897
|
|
|
list($start, $end) = $this->_getNumbersStartAndEnd($params, $options); |
898
|
|
|
|
899
|
|
|
$out .= $this->_firstNumber($ellipsis, $params, $start, $options); |
900
|
|
|
$out .= $options['before']; |
901
|
|
|
|
902
|
|
|
for ($i = $start; $i < $params['page']; $i++) { |
903
|
|
|
$out .= $this->_formatNumber($templater, [ |
904
|
|
|
'text' => $this->Number->format($i), |
905
|
|
|
'page' => $i, |
906
|
|
|
'model' => $options['model'], |
907
|
|
|
'url' => $options['url'], |
908
|
|
|
]); |
909
|
|
|
} |
910
|
|
|
|
911
|
|
|
$url = array_merge($options['url'], ['page' => $params['page']]); |
912
|
|
|
$out .= $templater->format('current', [ |
913
|
|
|
'text' => $this->Number->format($params['page']), |
914
|
|
|
'url' => $this->generateUrl($url, $options['model']), |
915
|
|
|
]); |
916
|
|
|
|
917
|
|
|
$start = $params['page'] + 1; |
918
|
|
|
$i = $start; |
919
|
|
|
while ($i < $end) { |
920
|
|
|
$out .= $this->_formatNumber($templater, [ |
921
|
|
|
'text' => $this->Number->format($i), |
922
|
|
|
'page' => $i, |
923
|
|
|
'model' => $options['model'], |
924
|
|
|
'url' => $options['url'], |
925
|
|
|
]); |
926
|
|
|
$i++; |
927
|
|
|
} |
928
|
|
|
|
929
|
|
|
if ($end != $params['page']) { |
930
|
|
|
$out .= $this->_formatNumber($templater, [ |
931
|
|
|
'text' => $this->Number->format($i), |
932
|
|
|
'page' => $end, |
933
|
|
|
'model' => $options['model'], |
934
|
|
|
'url' => $options['url'], |
935
|
|
|
]); |
936
|
|
|
} |
937
|
|
|
|
938
|
|
|
$out .= $options['after']; |
939
|
|
|
$out .= $this->_lastNumber($ellipsis, $params, $end, $options); |
940
|
|
|
|
941
|
|
|
return $out; |
942
|
|
|
} |
943
|
|
|
|
944
|
|
|
/** |
945
|
|
|
* Generates the first number for the paginator numbers() method. |
946
|
|
|
* |
947
|
|
|
* @param string $ellipsis Ellipsis character. |
948
|
|
|
* @param array $params Params from the numbers() method. |
949
|
|
|
* @param int $start Start number. |
950
|
|
|
* @param array $options Options from the numbers() method. |
951
|
|
|
* @return string Markup output. |
952
|
|
|
*/ |
953
|
|
|
protected function _firstNumber($ellipsis, $params, $start, $options) |
954
|
|
|
{ |
955
|
|
|
$out = ''; |
956
|
|
|
$first = is_int($options['first']) ? $options['first'] : 0; |
957
|
|
|
if ($options['first'] && $start > 1) { |
958
|
|
|
$offset = ($start <= $first) ? $start - 1 : $options['first']; |
959
|
|
|
$out .= $this->first($offset, $options); |
960
|
|
|
if ($first < $start - 1) { |
961
|
|
|
$out .= $ellipsis; |
962
|
|
|
} |
963
|
|
|
} |
964
|
|
|
|
965
|
|
|
return $out; |
966
|
|
|
} |
967
|
|
|
|
968
|
|
|
/** |
969
|
|
|
* Generates the last number for the paginator numbers() method. |
970
|
|
|
* |
971
|
|
|
* @param string $ellipsis Ellipsis character. |
972
|
|
|
* @param array $params Params from the numbers() method. |
973
|
|
|
* @param int $end End number. |
974
|
|
|
* @param array $options Options from the numbers() method. |
975
|
|
|
* @return string Markup output. |
976
|
|
|
*/ |
977
|
|
|
protected function _lastNumber($ellipsis, $params, $end, $options) |
978
|
|
|
{ |
979
|
|
|
$out = ''; |
980
|
|
|
$last = is_int($options['last']) ? $options['last'] : 0; |
981
|
|
|
if ($options['last'] && $end < $params['pageCount']) { |
982
|
|
|
$offset = ($params['pageCount'] < $end + $last) ? $params['pageCount'] - $end : $options['last']; |
983
|
|
|
if ($offset <= $options['last'] && $params['pageCount'] - $end > $last) { |
984
|
|
|
$out .= $ellipsis; |
985
|
|
|
} |
986
|
|
|
$out .= $this->last($offset, $options); |
987
|
|
|
} |
988
|
|
|
|
989
|
|
|
return $out; |
990
|
|
|
} |
991
|
|
|
|
992
|
|
|
/** |
993
|
|
|
* Generates the numbers for the paginator numbers() method. |
994
|
|
|
* |
995
|
|
|
* @param \Cake\View\StringTemplate $templater StringTemplate instance. |
996
|
|
|
* @param array $params Params from the numbers() method. |
997
|
|
|
* @param array $options Options from the numbers() method. |
998
|
|
|
* @return string Markup output. |
999
|
|
|
*/ |
1000
|
|
|
protected function _numbers($templater, $params, $options) |
1001
|
|
|
{ |
1002
|
|
|
$out = ''; |
1003
|
|
|
$out .= $options['before']; |
1004
|
|
|
for ($i = 1; $i <= $params['pageCount']; $i++) { |
1005
|
|
|
$url = array_merge($options['url'], ['page' => $i]); |
1006
|
|
|
if ($i == $params['page']) { |
1007
|
|
|
$out .= $templater->format('current', [ |
1008
|
|
|
'text' => $this->Number->format($params['page']), |
1009
|
|
|
'url' => $this->generateUrl($url, $options['model']), |
1010
|
|
|
]); |
1011
|
|
|
} else { |
1012
|
|
|
$vars = [ |
1013
|
|
|
'text' => $this->Number->format($i), |
1014
|
|
|
'url' => $this->generateUrl($url, $options['model']), |
1015
|
|
|
]; |
1016
|
|
|
$out .= $templater->format('number', $vars); |
1017
|
|
|
} |
1018
|
|
|
} |
1019
|
|
|
$out .= $options['after']; |
1020
|
|
|
|
1021
|
|
|
return $out; |
1022
|
|
|
} |
1023
|
|
|
|
1024
|
|
|
/** |
1025
|
|
|
* Returns a first or set of numbers for the first pages. |
1026
|
|
|
* |
1027
|
|
|
* ``` |
1028
|
|
|
* echo $this->Paginator->first('< first'); |
1029
|
|
|
* ``` |
1030
|
|
|
* |
1031
|
|
|
* Creates a single link for the first page. Will output nothing if you are on the first page. |
1032
|
|
|
* |
1033
|
|
|
* ``` |
1034
|
|
|
* echo $this->Paginator->first(3); |
1035
|
|
|
* ``` |
1036
|
|
|
* |
1037
|
|
|
* Will create links for the first 3 pages, once you get to the third or greater page. Prior to that |
1038
|
|
|
* nothing will be output. |
1039
|
|
|
* |
1040
|
|
|
* ### Options: |
1041
|
|
|
* |
1042
|
|
|
* - `model` The model to use defaults to PaginatorHelper::defaultModel() |
1043
|
|
|
* - `escape` Whether or not to HTML escape the text. |
1044
|
|
|
* - `url` An array of additional URL options to use for link generation. |
1045
|
|
|
* |
1046
|
|
|
* @param string|int $first if string use as label for the link. If numeric, the number of page links |
1047
|
|
|
* you want at the beginning of the range. |
1048
|
|
|
* @param array $options An array of options. |
1049
|
|
|
* @return string|false Numbers string. |
1050
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-jump-links |
1051
|
|
|
*/ |
1052
|
|
|
public function first($first = '<< first', array $options = []) |
1053
|
|
|
{ |
1054
|
|
|
$options += [ |
1055
|
|
|
'url' => [], |
1056
|
|
|
'model' => $this->defaultModel(), |
1057
|
|
|
'escape' => true, |
1058
|
|
|
]; |
1059
|
|
|
|
1060
|
|
|
$params = $this->params($options['model']); |
1061
|
|
|
|
1062
|
|
|
if ($params['pageCount'] <= 1) { |
1063
|
|
|
return false; |
1064
|
|
|
} |
1065
|
|
|
|
1066
|
|
|
$out = ''; |
1067
|
|
|
|
1068
|
|
|
if (is_int($first) && $params['page'] >= $first) { |
1069
|
|
View Code Duplication |
for ($i = 1; $i <= $first; $i++) { |
1070
|
|
|
$url = array_merge($options['url'], ['page' => $i]); |
1071
|
|
|
$out .= $this->templater()->format('number', [ |
1072
|
|
|
'url' => $this->generateUrl($url, $options['model']), |
1073
|
|
|
'text' => $this->Number->format($i), |
1074
|
|
|
]); |
1075
|
|
|
} |
1076
|
|
|
} elseif ($params['page'] > 1 && is_string($first)) { |
1077
|
|
|
$first = $options['escape'] ? h($first) : $first; |
1078
|
|
|
$out .= $this->templater()->format('first', [ |
1079
|
|
|
'url' => $this->generateUrl(['page' => 1], $options['model']), |
1080
|
|
|
'text' => $first, |
1081
|
|
|
]); |
1082
|
|
|
} |
1083
|
|
|
|
1084
|
|
|
return $out; |
1085
|
|
|
} |
1086
|
|
|
|
1087
|
|
|
/** |
1088
|
|
|
* Returns a last or set of numbers for the last pages. |
1089
|
|
|
* |
1090
|
|
|
* ``` |
1091
|
|
|
* echo $this->Paginator->last('last >'); |
1092
|
|
|
* ``` |
1093
|
|
|
* |
1094
|
|
|
* Creates a single link for the last page. Will output nothing if you are on the last page. |
1095
|
|
|
* |
1096
|
|
|
* ``` |
1097
|
|
|
* echo $this->Paginator->last(3); |
1098
|
|
|
* ``` |
1099
|
|
|
* |
1100
|
|
|
* Will create links for the last 3 pages. Once you enter the page range, no output will be created. |
1101
|
|
|
* |
1102
|
|
|
* ### Options: |
1103
|
|
|
* |
1104
|
|
|
* - `model` The model to use defaults to PaginatorHelper::defaultModel() |
1105
|
|
|
* - `escape` Whether or not to HTML escape the text. |
1106
|
|
|
* - `url` An array of additional URL options to use for link generation. |
1107
|
|
|
* |
1108
|
|
|
* @param string|int $last if string use as label for the link, if numeric print page numbers |
1109
|
|
|
* @param array $options Array of options |
1110
|
|
|
* @return string|false Numbers string. |
1111
|
|
|
* @link https://book.cakephp.org/3/en/views/helpers/paginator.html#creating-jump-links |
1112
|
|
|
*/ |
1113
|
|
|
public function last($last = 'last >>', array $options = []) |
1114
|
|
|
{ |
1115
|
|
|
$options += [ |
1116
|
|
|
'model' => $this->defaultModel(), |
1117
|
|
|
'escape' => true, |
1118
|
|
|
'url' => [], |
1119
|
|
|
]; |
1120
|
|
|
$params = $this->params($options['model']); |
1121
|
|
|
|
1122
|
|
|
if ($params['pageCount'] <= 1) { |
1123
|
|
|
return false; |
1124
|
|
|
} |
1125
|
|
|
|
1126
|
|
|
$out = ''; |
1127
|
|
|
$lower = (int)$params['pageCount'] - (int)$last + 1; |
1128
|
|
|
|
1129
|
|
|
if (is_int($last) && $params['page'] <= $lower) { |
1130
|
|
View Code Duplication |
for ($i = $lower; $i <= $params['pageCount']; $i++) { |
1131
|
|
|
$url = array_merge($options['url'], ['page' => $i]); |
1132
|
|
|
$out .= $this->templater()->format('number', [ |
1133
|
|
|
'url' => $this->generateUrl($url, $options['model']), |
1134
|
|
|
'text' => $this->Number->format($i), |
1135
|
|
|
]); |
1136
|
|
|
} |
1137
|
|
|
} elseif ($params['page'] < $params['pageCount'] && is_string($last)) { |
1138
|
|
|
$last = $options['escape'] ? h($last) : $last; |
1139
|
|
|
$out .= $this->templater()->format('last', [ |
1140
|
|
|
'url' => $this->generateUrl(['page' => $params['pageCount']], $options['model']), |
1141
|
|
|
'text' => $last, |
1142
|
|
|
]); |
1143
|
|
|
} |
1144
|
|
|
|
1145
|
|
|
return $out; |
1146
|
|
|
} |
1147
|
|
|
|
1148
|
|
|
/** |
1149
|
|
|
* Returns the meta-links for a paginated result set. |
1150
|
|
|
* |
1151
|
|
|
* ``` |
1152
|
|
|
* echo $this->Paginator->meta(); |
1153
|
|
|
* ``` |
1154
|
|
|
* |
1155
|
|
|
* Echos the links directly, will output nothing if there is neither a previous nor next page. |
1156
|
|
|
* |
1157
|
|
|
* ``` |
1158
|
|
|
* $this->Paginator->meta(['block' => true]); |
1159
|
|
|
* ``` |
1160
|
|
|
* |
1161
|
|
|
* Will append the output of the meta function to the named block - if true is passed the "meta" |
1162
|
|
|
* block is used. |
1163
|
|
|
* |
1164
|
|
|
* ### Options: |
1165
|
|
|
* |
1166
|
|
|
* - `model` The model to use defaults to PaginatorHelper::defaultModel() |
1167
|
|
|
* - `block` The block name to append the output to, or false/absent to return as a string |
1168
|
|
|
* - `prev` (default True) True to generate meta for previous page |
1169
|
|
|
* - `next` (default True) True to generate meta for next page |
1170
|
|
|
* - `first` (default False) True to generate meta for first page |
1171
|
|
|
* - `last` (default False) True to generate meta for last page |
1172
|
|
|
* |
1173
|
|
|
* @param array $options Array of options |
1174
|
|
|
* @return string|null Meta links |
1175
|
|
|
*/ |
1176
|
|
|
public function meta(array $options = []) |
1177
|
|
|
{ |
1178
|
|
|
$options += [ |
1179
|
|
|
'model' => null, |
1180
|
|
|
'block' => false, |
1181
|
|
|
'prev' => true, |
1182
|
|
|
'next' => true, |
1183
|
|
|
'first' => false, |
1184
|
|
|
'last' => false, |
1185
|
|
|
]; |
1186
|
|
|
|
1187
|
|
|
$model = isset($options['model']) ? $options['model'] : null; |
1188
|
|
|
$params = $this->params($model); |
1189
|
|
|
$links = []; |
1190
|
|
|
|
1191
|
|
View Code Duplication |
if ($options['prev'] && $this->hasPrev()) { |
1192
|
|
|
$links[] = $this->Html->meta( |
1193
|
|
|
'prev', |
1194
|
|
|
$this->generateUrl(['page' => $params['page'] - 1], null, ['escape' => false, 'fullBase' => true]) |
1195
|
|
|
); |
1196
|
|
|
} |
1197
|
|
|
|
1198
|
|
View Code Duplication |
if ($options['next'] && $this->hasNext()) { |
1199
|
|
|
$links[] = $this->Html->meta( |
1200
|
|
|
'next', |
1201
|
|
|
$this->generateUrl(['page' => $params['page'] + 1], null, ['escape' => false, 'fullBase' => true]) |
1202
|
|
|
); |
1203
|
|
|
} |
1204
|
|
|
|
1205
|
|
|
if ($options['first']) { |
1206
|
|
|
$links[] = $this->Html->meta( |
1207
|
|
|
'first', |
1208
|
|
|
$this->generateUrl(['page' => 1], null, ['escape' => false, 'fullBase' => true]) |
1209
|
|
|
); |
1210
|
|
|
} |
1211
|
|
|
|
1212
|
|
|
if ($options['last']) { |
1213
|
|
|
$links[] = $this->Html->meta( |
1214
|
|
|
'last', |
1215
|
|
|
$this->generateUrl(['page' => $params['pageCount']], null, ['escape' => false, 'fullBase' => true]) |
1216
|
|
|
); |
1217
|
|
|
} |
1218
|
|
|
|
1219
|
|
|
$out = implode($links); |
1220
|
|
|
|
1221
|
|
|
if ($options['block'] === true) { |
1222
|
|
|
$options['block'] = __FUNCTION__; |
1223
|
|
|
} |
1224
|
|
|
|
1225
|
|
|
if ($options['block']) { |
1226
|
|
|
$this->_View->append($options['block'], $out); |
1227
|
|
|
|
1228
|
|
|
return null; |
1229
|
|
|
} |
1230
|
|
|
|
1231
|
|
|
return $out; |
1232
|
|
|
} |
1233
|
|
|
|
1234
|
|
|
/** |
1235
|
|
|
* Event listeners. |
1236
|
|
|
* |
1237
|
|
|
* @return array |
1238
|
|
|
*/ |
1239
|
|
|
public function implementedEvents() |
1240
|
|
|
{ |
1241
|
|
|
return []; |
1242
|
|
|
} |
1243
|
|
|
|
1244
|
|
|
/** |
1245
|
|
|
* Dropdown select for pagination limit. |
1246
|
|
|
* This will generate a wrapping form. |
1247
|
|
|
* |
1248
|
|
|
* @param array $limits The options array. |
1249
|
|
|
* @param int|null $default Default option for pagination limit. Defaults to `$this->param('perPage')`. |
1250
|
|
|
* @param array $options Options for Select tag attributes like class, id or event |
1251
|
|
|
* @return string html output. |
1252
|
|
|
*/ |
1253
|
|
|
public function limitControl(array $limits = [], $default = null, array $options = []) |
1254
|
|
|
{ |
1255
|
|
|
$out = $this->Form->create(null, ['type' => 'get']); |
1256
|
|
|
|
1257
|
|
|
if (empty($default) || !is_numeric($default)) { |
1258
|
|
|
$default = $this->param('perPage'); |
1259
|
|
|
} |
1260
|
|
|
|
1261
|
|
|
if (empty($limits)) { |
1262
|
|
|
$limits = [ |
1263
|
|
|
'20' => '20', |
1264
|
|
|
'50' => '50', |
1265
|
|
|
'100' => '100', |
1266
|
|
|
]; |
1267
|
|
|
} |
1268
|
|
|
|
1269
|
|
|
$out .= $this->Form->control('limit', $options + [ |
1270
|
|
|
'type' => 'select', |
1271
|
|
|
'label' => __('View'), |
1272
|
|
|
'default' => $default, |
1273
|
|
|
'value' => $this->_View->getRequest()->getQuery('limit'), |
1274
|
|
|
'options' => $limits, |
1275
|
|
|
'onChange' => 'this.form.submit()', |
1276
|
|
|
]); |
1277
|
|
|
$out .= $this->Form->end(); |
1278
|
|
|
|
1279
|
|
|
return $out; |
1280
|
|
|
} |
1281
|
|
|
} |
1282
|
|
|
|