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
<?php
/**
* @link http://www.yiiframework.com/
* @copyright Copyright (c) 2008 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\data;
use Yii;
use yii\base\Object;
use yii\web\Link;
use yii\web\Linkable;
use yii\web\Request;
/**
* Pagination represents information relevant to pagination of data items.
*
* When data needs to be rendered in multiple pages, Pagination can be used to
* represent information such as [[totalCount|total item count]], [[pageSize|page size]],
* [[page|current page]], etc. These information can be passed to [[\yii\widgets\LinkPager|pagers]]
* to render pagination buttons or links.
*
* The following example shows how to create a pagination object and feed it
* to a pager.
*
* Controller action:
*
* ~~~
* function actionIndex()
* {
* $query = Article::find()->where(['status' => 1]);
* $countQuery = clone $query;
* $pages = new Pagination(['totalCount' => $countQuery->count()]);
* $models = $query->offset($pages->offset)
* ->limit($pages->limit)
* ->all();
*
* return $this->render('index', [
* 'models' => $models,
* 'pages' => $pages,
* ]);
* }
* ~~~
*
* View:
*
* ~~~
* foreach ($models as $model) {
* // display $model here
* }
*
* // display pagination
* echo LinkPager::widget([
* 'pagination' => $pages,
* ]);
* ~~~
*
* @property integer $limit The limit of the data. This may be used to set the LIMIT value for a SQL statement
* for fetching the current page of data. Note that if the page size is infinite, a value -1 will be returned.
* This property is read-only.
* @property array $links The links for navigational purpose. The array keys specify the purpose of the links
* (e.g. [[LINK_FIRST]]), and the array values are the corresponding URLs. This property is read-only.
* @property integer $offset The offset of the data. This may be used to set the OFFSET value for a SQL
* statement for fetching the current page of data. This property is read-only.
* @property integer $page The zero-based current page number.
* @property integer $pageCount Number of pages. This property is read-only.
* @property integer $pageSize The number of items per page.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
*/
class Pagination extends Object implements Linkable
{
const LINK_NEXT = 'next';
const LINK_PREV = 'prev';
const LINK_FIRST = 'first';
const LINK_LAST = 'last';
/**
* @var string name of the parameter storing the current page index.
* @see params
*/
public $pageParam = 'page';
/**
* @var string name of the parameter storing the page size.
* @see params
*/
public $pageSizeParam = 'per-page';
/**
* @var boolean whether to always have the page parameter in the URL created by [[createUrl()]].
* If false and [[page]] is 0, the page parameter will not be put in the URL.
*/
public $forcePageParam = true;
/**
* @var string the route of the controller action for displaying the paged contents.
* If not set, it means using the currently requested route.
*/
public $route;
/**
* @var array parameters (name => value) that should be used to obtain the current page number
* and to create new pagination URLs. If not set, all parameters from $_GET will be used instead.
*
* In order to add hash to all links use `array_merge($_GET, ['#' => 'my-hash'])`.
*
* The array element indexed by [[pageParam]] is considered to be the current page number (defaults to 0);
* while the element indexed by [[pageSizeParam]] is treated as the page size (defaults to [[defaultPageSize]]).
*/
public $params;
/**
* @var \yii\web\UrlManager the URL manager used for creating pagination URLs. If not set,
* the "urlManager" application component will be used.
*/
public $urlManager;
/**
* @var boolean whether to check if [[page]] is within valid range.
* When this property is true, the value of [[page]] will always be between 0 and ([[pageCount]]-1).
* Because [[pageCount]] relies on the correct value of [[totalCount]] which may not be available
* in some cases (e.g. MongoDB), you may want to set this property to be false to disable the page
* number validation. By doing so, [[page]] will return the value indexed by [[pageParam]] in [[params]].
*/
public $validatePage = true;
/**
* @var integer total number of items.
*/
public $totalCount = 0;
/**
* @var integer the default page size. This property will be returned by [[pageSize]] when page size
* cannot be determined by [[pageSizeParam]] from [[params]].
*/
public $defaultPageSize = 20;
/**
* @var array|boolean the page size limits. The first array element stands for the minimal page size, and the second
* the maximal page size. If this is false, it means [[pageSize]] should always return the value of [[defaultPageSize]].
*/
public $pageSizeLimit = [1, 50];
/**
* @var integer number of items on each page.
* If it is less than 1, it means the page size is infinite, and thus a single page contains all items.
*/
private $_pageSize;
/**
* @return integer number of pages
*/
public function getPageCount()
{
$pageSize = $this->getPageSize();
if ($pageSize < 1) {
return $this->totalCount > 0 ? 1 : 0;
} else {
$totalCount = $this->totalCount < 0 ? 0 : (int) $this->totalCount;
return (int) (($totalCount + $pageSize - 1) / $pageSize);
}
}
private $_page;
/**
* Returns the zero-based current page number.
* @param boolean $recalculate whether to recalculate the current page based on the page size and item count.
* @return integer the zero-based current page number.
*/
public function getPage($recalculate = false)
{
if ($this->_page === null || $recalculate) {
$page = (int) $this->getQueryParam($this->pageParam, 1) - 1;
$this->setPage($page, true);
}
return $this->_page;
}
/**
* Sets the current page number.
* @param integer $value the zero-based index of the current page.
* @param boolean $validatePage whether to validate the page number. Note that in order
* to validate the page number, both [[validatePage]] and this parameter must be true.
*/
public function setPage($value, $validatePage = false)
{
if ($value === null) {
$this->_page = null;
} else {
$value = (int) $value;
if ($validatePage && $this->validatePage) {
$pageCount = $this->getPageCount();
if ($value >= $pageCount) {
$value = $pageCount - 1;
}
}
if ($value < 0) {
$value = 0;
}
$this->_page = $value;
}
}
/**
* Returns the number of items per page.
* By default, this method will try to determine the page size by [[pageSizeParam]] in [[params]].
* If the page size cannot be determined this way, [[defaultPageSize]] will be returned.
* @return integer the number of items per page.
* @see pageSizeLimit
*/
public function getPageSize()
{
if ($this->_pageSize === null) {
if (empty($this->pageSizeLimit)) {
$pageSize = $this->defaultPageSize;
$this->setPageSize($pageSize);
} else {
$pageSize = (int) $this->getQueryParam($this->pageSizeParam, $this->defaultPageSize);
$this->setPageSize($pageSize, true);
}
}
return $this->_pageSize;
}
/**
* @param integer $value the number of items per page.
* @param boolean $validatePageSize whether to validate page size.
*/
public function setPageSize($value, $validatePageSize = false)
{
if ($value === null) {
$this->_pageSize = null;
} else {
$value = (int) $value;
if ($validatePageSize && count($this->pageSizeLimit) === 2 && isset($this->pageSizeLimit[0], $this->pageSizeLimit[1])) {
if ($value < $this->pageSizeLimit[0]) {
$value = $this->pageSizeLimit[0];
} elseif ($value > $this->pageSizeLimit[1]) {
$value = $this->pageSizeLimit[1];
}
}
$this->_pageSize = $value;
}
}
/**
* Creates the URL suitable for pagination with the specified page number.
* This method is mainly called by pagers when creating URLs used to perform pagination.
* @param integer $page the zero-based page number that the URL should point to.
* @param boolean $absolute whether to create an absolute URL. Defaults to `false`.
* @return string the created URL
* @see params
* @see forcePageParam
*/
public function createUrl($page, $absolute = false)
{
if (($params = $this->params) === null) {
$request = Yii::$app->getRequest();
$params = $request instanceof Request ? $request->getQueryParams() : [];
}
if ($page > 0 || $page >= 0 && $this->forcePageParam) {
$params[$this->pageParam] = $page + 1;
} else {
unset($params[$this->pageParam]);
}
$pageSize = $this->getPageSize();
if ($pageSize != $this->defaultPageSize) {
$params[$this->pageSizeParam] = $pageSize;
} else {
unset($params[$this->pageSizeParam]);
}
$params[0] = $this->route === null ? Yii::$app->controller->getRoute() : $this->route;
$urlManager = $this->urlManager === null ? Yii::$app->getUrlManager() : $this->urlManager;
if ($absolute) {
return $urlManager->createAbsoluteUrl($params);
} else {
return $urlManager->createUrl($params);
}
}
/**
* @return integer the offset of the data. This may be used to set the
* OFFSET value for a SQL statement for fetching the current page of data.
*/
public function getOffset()
{
$pageSize = $this->getPageSize();
return $pageSize < 1 ? 0 : $this->getPage() * $pageSize;
}
/**
* @return integer the limit of the data. This may be used to set the
* LIMIT value for a SQL statement for fetching the current page of data.
* Note that if the page size is infinite, a value -1 will be returned.
*/
public function getLimit()
{
$pageSize = $this->getPageSize();
return $pageSize < 1 ? -1 : $pageSize;
}
/**
* Returns a whole set of links for navigating to the first, last, next and previous pages.
* @param boolean $absolute whether the generated URLs should be absolute.
* @return array the links for navigational purpose. The array keys specify the purpose of the links (e.g. [[LINK_FIRST]]),
* and the array values are the corresponding URLs.
*/
public function getLinks($absolute = false)
{
$currentPage = $this->getPage();
$pageCount = $this->getPageCount();
$links = [
Link::REL_SELF => $this->createUrl($currentPage, $absolute),
];
if ($currentPage > 0) {
$links[self::LINK_FIRST] = $this->createUrl(0, $absolute);
$links[self::LINK_PREV] = $this->createUrl($currentPage - 1, $absolute);
}
if ($currentPage < $pageCount - 1) {
$links[self::LINK_NEXT] = $this->createUrl($currentPage + 1, $absolute);
$links[self::LINK_LAST] = $this->createUrl($pageCount - 1, $absolute);
}
return $links;
}
/**
* Returns the value of the specified query parameter.
* This method returns the named parameter value from [[params]]. Null is returned if the value does not exist.
* @param string $name the parameter name
* @param string $defaultValue the value to be returned when the specified parameter does not exist in [[params]].
* @return string the parameter value
*/
protected function getQueryParam($name, $defaultValue = null)
{
if (($params = $this->params) === null) {
$request = Yii::$app->getRequest();
$params = $request instanceof Request ? $request->getQueryParams() : [];
}
return isset($params[$name]) && is_scalar($params[$name]) ? $params[$name] : $defaultValue;
}
}