-
Notifications
You must be signed in to change notification settings - Fork 3
/
Copy pathpaginator.php
423 lines (377 loc) · 10.2 KB
/
paginator.php
1
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
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
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
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
<?php namespace Laravel;
class Paginator {
/**
* The results for the current page.
*
* @var array
*/
public $results;
/**
* The current page.
*
* @var int
*/
public $page;
/**
* The last page available for the result set.
*
* @var int
*/
public $last;
/**
* The total number of results.
*
* @var int
*/
public $total;
/**
* The number of items per page.
*
* @var int
*/
public $per_page;
/**
* The values that should be appended to the end of the link query strings.
*
* @var array
*/
protected $appends;
/**
* The compiled appendage that will be appended to the links.
*
* This consists of a sprintf format with a page place-holder and query string.
*
* @var string
*/
protected $appendage;
/**
* The language that should be used when creating the pagination links.
*
* @var string
*/
protected $language;
/**
* The "dots" element used in the pagination slider.
*
* @var string
*/
protected $dots = '<li class="dots disabled"><a href="#">...</a></li>';
/**
* Create a new Paginator instance.
*
* @param array $results
* @param int $page
* @param int $total
* @param int $per_page
* @param int $last
* @return void
*/
protected function __construct($results, $page, $total, $per_page, $last)
{
$this->page = $page;
$this->last = $last;
$this->total = $total;
$this->results = $results;
$this->per_page = $per_page;
}
/**
* Create a new Paginator instance.
*
* @param array $results
* @param int $total
* @param int $per_page
* @return Paginator
*/
public static function make($results, $total, $per_page)
{
$page = static::page($total, $per_page);
$last = ceil($total / $per_page);
return new static($results, $page, $total, $per_page, $last);
}
/**
* Get the current page from the request query string.
*
* @param int $total
* @param int $per_page
* @return int
*/
public static function page($total, $per_page)
{
$page = Input::get('page', 1);
// The page will be validated and adjusted if it is less than one or greater
// than the last page. For example, if the current page is not an integer or
// less than one, one will be returned. If the current page is greater than
// the last page, the last page will be returned.
if (is_numeric($page) and $page > $last = ceil($total / $per_page))
{
return ($last > 0) ? $last : 1;
}
return (static::valid($page)) ? $page : 1;
}
/**
* Determine if a given page number is a valid page.
*
* A valid page must be greater than or equal to one and a valid integer.
*
* @param int $page
* @return bool
*/
protected static function valid($page)
{
return $page >= 1 and filter_var($page, FILTER_VALIDATE_INT) !== false;
}
/**
* Create the HTML pagination links.
*
* Typically, an intelligent, "sliding" window of links will be rendered based
* on the total number of pages, the current page, and the number of adjacent
* pages that should rendered. This creates a beautiful paginator similar to
* that of Google's.
*
* Example: 1 2 ... 23 24 25 [26] 27 28 29 ... 51 52
*
* If you wish to render only certain elements of the pagination control,
* explore some of the other public methods available on the instance.
*
* <code>
* // Render the pagination links
* echo $paginator->links();
*
* // Render the pagination links using a given window size
* echo $paginator->links(5);
* </code>
*
* @param int $adjacent
* @return string
*/
public function links($adjacent = 3)
{
if ($this->last <= 1) return '';
// The hard-coded seven is to account for all of the constant elements in a
// sliding range, such as the current page, the two ellipses, and the two
// beginning and ending pages.
//
// If there are not enough pages to make the creation of a slider possible
// based on the adjacent pages, we will simply display all of the pages.
// Otherwise, we will create a "truncating" sliding window.
if ($this->last < 7 + ($adjacent * 2))
{
$links = $this->range(1, $this->last);
}
else
{
$links = $this->slider($adjacent);
}
$content = '<ul>' . $this->previous() . $links . $this->next() . '</ul>';
return '<div class="pagination">'.$content.'</div>';
}
/**
* Build sliding list of HTML numeric page links.
*
* This method is very similar to the "links" method, only it does not
* render the "first" and "last" pagination links, but only the pages.
*
* <code>
* // Render the pagination slider
* echo $paginator->slider();
*
* // Render the pagination slider using a given window size
* echo $paginator->slider(5);
* </code>
*
* @param int $adjacent
* @return string
*/
public function slider($adjacent = 3)
{
$window = $adjacent * 2;
// If the current page is so close to the beginning that we do not have
// room to create a full sliding window, we will only show the first
// several pages, followed by the ending of the slider.
//
// Likewise, if the page is very close to the end, we will create the
// beginning of the slider, but just show the last several pages at
// the end of the slider. Otherwise, we'll build the range.
//
// Example: 1 [2] 3 4 5 6 ... 23 24
if ($this->page <= $window)
{
return $this->range(1, $window + 2).' '.$this->ending();
}
// Example: 1 2 ... 32 33 34 35 [36] 37
elseif ($this->page >= $this->last - $window)
{
return $this->beginning().' '.$this->range($this->last - $window - 2, $this->last);
}
// Example: 1 2 ... 23 24 25 [26] 27 28 29 ... 51 52
$content = $this->range($this->page - $adjacent, $this->page + $adjacent);
return $this->beginning().' '.$content.' '.$this->ending();
}
/**
* Generate the "previous" HTML link.
*
* <code>
* // Create the "previous" pagination element
* echo $paginator->previous();
*
* // Create the "previous" pagination element with custom text
* echo $paginator->previous('Go Back');
* </code>
*
* @param string $text
* @return string
*/
public function previous($text = null)
{
$disabled = function($page) { return $page <= 1; };
return $this->element(__FUNCTION__, $this->page - 1, $text, $disabled);
}
/**
* Generate the "next" HTML link.
*
* <code>
* // Create the "next" pagination element
* echo $paginator->next();
*
* // Create the "next" pagination element with custom text
* echo $paginator->next('Skip Forwards');
* </code>
*
* @param string $text
* @return string
*/
public function next($text = null)
{
$disabled = function($page, $last) { return $page >= $last; };
return $this->element(__FUNCTION__, $this->page + 1, $text, $disabled);
}
/**
* Create a chronological pagination element, such as a "previous" or "next" link.
*
* @param string $element
* @param int $page
* @param string $text
* @param Closure $disabled
* @return string
*/
protected function element($element, $page, $text, $disabled)
{
$class = "{$element}_page";
if (is_null($text))
{
$text = Lang::line("pagination.{$element}")->get($this->language);
}
// Each consumer of this method provides a "disabled" Closure which can
// be used to determine if the element should be a span element or an
// actual link. For example, if the current page is the first page,
// the "first" element should be a span instead of a link.
if ($disabled($this->page, $this->last))
{
return '<li'.HTML::attributes(array('class'=>"{$class} disabled")).'><a href="#">'.$text.'</a></li>';
}
else
{
return $this->link($page, $text, $class);
}
}
/**
* Build the first two page links for a sliding page range.
*
* @return string
*/
protected function beginning()
{
return $this->range(1, 2).' '.$this->dots;
}
/**
* Build the last two page links for a sliding page range.
*
* @return string
*/
protected function ending()
{
return $this->dots.' '.$this->range($this->last - 1, $this->last);
}
/**
* Build a range of numeric pagination links.
*
* For the current page, an HTML span element will be generated instead of a link.
*
* @param int $start
* @param int $end
* @return string
*/
protected function range($start, $end)
{
$pages = array();
// To generate the range of page links, we will iterate through each page
// and, if the current page matches the page, we will generate a span,
// otherwise we will generate a link for the page. The span elements
// will be assigned the "current" CSS class for convenient styling.
for ($page = $start; $page <= $end; $page++)
{
if ($this->page == $page)
{
$pages[] = '<li class="active"><a href="#">'.$page.'</a></li>';
}
else
{
$pages[] = $this->link($page, $page, null);
}
}
return implode(' ', $pages);
}
/**
* Create a HTML page link.
*
* @param int $page
* @param string $text
* @param string $class
* @return string
*/
protected function link($page, $text, $class)
{
$query = '?page='.$page.$this->appendage($this->appends);
return '<li'.HTML::attributes(array('class' => $class)).'>'. HTML::link(URI::current().$query, $text, array(), Request::secure()).'</li>';
}
/**
* Create the "appendage" to be attached to every pagination link.
*
* @param array $appends
* @return string
*/
protected function appendage($appends)
{
// The developer may assign an array of values that will be converted to a
// query string and attached to every pagination link. This allows simple
// implementation of sorting or other things the developer may need.
if ( ! is_null($this->appendage)) return $this->appendage;
if (count($appends) <= 0)
{
return $this->appendage = '';
}
return $this->appendage = '&'.http_build_query($appends);
}
/**
* Set the items that should be appended to the link query strings.
*
* @param array $values
* @return Paginator
*/
public function appends($values)
{
$this->appends = $values;
return $this;
}
/**
* Set the language that should be used when creating the pagination links.
*
* @param string $language
* @return Paginator
*/
public function speaks($language)
{
$this->language = $language;
return $this;
}
}