1 | <?php |
||
2 | /** |
||
3 | * @link https://www.yiiframework.com/ |
||
4 | * @copyright Copyright (c) 2008 Yii Software LLC |
||
5 | * @license https://www.yiiframework.com/license/ |
||
6 | */ |
||
7 | |||
8 | namespace yii\widgets; |
||
9 | |||
10 | use Yii; |
||
11 | use yii\base\InvalidConfigException; |
||
12 | use yii\base\Widget; |
||
13 | use yii\data\Pagination; |
||
14 | use yii\helpers\ArrayHelper; |
||
15 | use yii\helpers\Html; |
||
16 | |||
17 | /** |
||
18 | * LinkPager displays a list of hyperlinks that lead to different pages of target. |
||
19 | * |
||
20 | * LinkPager works with a [[Pagination]] object which specifies the total number |
||
21 | * of pages and the current page number. |
||
22 | * |
||
23 | * Note that LinkPager only generates the necessary HTML markups. In order for it |
||
24 | * to look like a real pager, you should provide some CSS styles for it. |
||
25 | * With the default configuration, LinkPager should look good using Twitter Bootstrap CSS framework. |
||
26 | * |
||
27 | * For more details and usage information on LinkPager, see the [guide article on pagination](guide:output-pagination). |
||
28 | * |
||
29 | * @author Qiang Xue <[email protected]> |
||
30 | * @since 2.0 |
||
31 | */ |
||
32 | class LinkPager extends Widget |
||
33 | { |
||
34 | /** |
||
35 | * @var Pagination the pagination object that this pager is associated with. |
||
36 | * You must set this property in order to make LinkPager work. |
||
37 | */ |
||
38 | public $pagination; |
||
39 | /** |
||
40 | * @var array HTML attributes for the pager container tag. |
||
41 | * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. |
||
42 | */ |
||
43 | public $options = ['class' => 'pagination']; |
||
44 | /** |
||
45 | * @var array HTML attributes which will be applied to all link containers |
||
46 | * @since 2.0.13 |
||
47 | */ |
||
48 | public $linkContainerOptions = []; |
||
49 | /** |
||
50 | * @var array HTML attributes for the link in a pager container tag. |
||
51 | * @see \yii\helpers\Html::renderTagAttributes() for details on how attributes are being rendered. |
||
52 | */ |
||
53 | public $linkOptions = []; |
||
54 | /** |
||
55 | * @var string the CSS class for the each page button. |
||
56 | * @since 2.0.7 |
||
57 | */ |
||
58 | public $pageCssClass; |
||
59 | /** |
||
60 | * @var string the CSS class for the "first" page button. |
||
61 | */ |
||
62 | public $firstPageCssClass = 'first'; |
||
63 | /** |
||
64 | * @var string the CSS class for the "last" page button. |
||
65 | */ |
||
66 | public $lastPageCssClass = 'last'; |
||
67 | /** |
||
68 | * @var string the CSS class for the "previous" page button. |
||
69 | */ |
||
70 | public $prevPageCssClass = 'prev'; |
||
71 | /** |
||
72 | * @var string the CSS class for the "next" page button. |
||
73 | */ |
||
74 | public $nextPageCssClass = 'next'; |
||
75 | /** |
||
76 | * @var string the CSS class for the active (currently selected) page button. |
||
77 | */ |
||
78 | public $activePageCssClass = 'active'; |
||
79 | /** |
||
80 | * @var string the CSS class for the disabled page buttons. |
||
81 | */ |
||
82 | public $disabledPageCssClass = 'disabled'; |
||
83 | /** |
||
84 | * @var array the options for the disabled tag to be generated inside the disabled list element. |
||
85 | * In order to customize the html tag, please use the tag key. |
||
86 | * |
||
87 | * ```php |
||
88 | * $disabledListItemSubTagOptions = ['tag' => 'div', 'class' => 'disabled-div']; |
||
89 | * ``` |
||
90 | * @since 2.0.11 |
||
91 | */ |
||
92 | public $disabledListItemSubTagOptions = []; |
||
93 | /** |
||
94 | * @var int maximum number of page buttons that can be displayed. Defaults to 10. |
||
95 | */ |
||
96 | public $maxButtonCount = 10; |
||
97 | /** |
||
98 | * @var string|bool the label for the "next" page button. Note that this will NOT be HTML-encoded. |
||
99 | * If this property is false, the "next" page button will not be displayed. |
||
100 | */ |
||
101 | public $nextPageLabel = '»'; |
||
102 | /** |
||
103 | * @var string|bool the text label for the "previous" page button. Note that this will NOT be HTML-encoded. |
||
104 | * If this property is false, the "previous" page button will not be displayed. |
||
105 | */ |
||
106 | public $prevPageLabel = '«'; |
||
107 | /** |
||
108 | * @var string|bool the text label for the "first" page button. Note that this will NOT be HTML-encoded. |
||
109 | * If it's specified as true, page number will be used as label. |
||
110 | * Default is false that means the "first" page button will not be displayed. |
||
111 | */ |
||
112 | public $firstPageLabel = false; |
||
113 | /** |
||
114 | * @var string|bool the text label for the "last" page button. Note that this will NOT be HTML-encoded. |
||
115 | * If it's specified as true, page number will be used as label. |
||
116 | * Default is false that means the "last" page button will not be displayed. |
||
117 | */ |
||
118 | public $lastPageLabel = false; |
||
119 | /** |
||
120 | * @var bool whether to register link tags in the HTML header for prev, next, first and last page. |
||
121 | * Defaults to `false` to avoid conflicts when multiple pagers are used on one page. |
||
122 | * @see https://www.w3.org/TR/html401/struct/links.html#h-12.1.2 |
||
123 | * @see registerLinkTags() |
||
124 | */ |
||
125 | public $registerLinkTags = false; |
||
126 | /** |
||
127 | * @var bool Hide widget when only one page exist. |
||
128 | */ |
||
129 | public $hideOnSinglePage = true; |
||
130 | /** |
||
131 | * @var bool whether to render current page button as disabled. |
||
132 | * @since 2.0.12 |
||
133 | */ |
||
134 | public $disableCurrentPageButton = false; |
||
135 | |||
136 | |||
137 | /** |
||
138 | * Initializes the pager. |
||
139 | */ |
||
140 | 18 | public function init() |
|
141 | { |
||
142 | 18 | parent::init(); |
|
143 | |||
144 | 18 | if ($this->pagination === null) { |
|
145 | throw new InvalidConfigException('The "pagination" property must be set.'); |
||
146 | } |
||
147 | } |
||
148 | |||
149 | /** |
||
150 | * Executes the widget. |
||
151 | * This overrides the parent implementation by displaying the generated page buttons. |
||
152 | */ |
||
153 | 18 | public function run() |
|
154 | { |
||
155 | 18 | if ($this->registerLinkTags) { |
|
156 | $this->registerLinkTags(); |
||
157 | } |
||
158 | 18 | echo $this->renderPageButtons(); |
|
159 | } |
||
160 | |||
161 | /** |
||
162 | * Registers relational link tags in the html header for prev, next, first and last page. |
||
163 | * These links are generated using [[\yii\data\Pagination::getLinks()]]. |
||
164 | * @see https://www.w3.org/TR/html401/struct/links.html#h-12.1.2 |
||
165 | */ |
||
166 | protected function registerLinkTags() |
||
167 | { |
||
168 | $view = $this->getView(); |
||
169 | foreach ($this->pagination->getLinks() as $rel => $href) { |
||
170 | $view->registerLinkTag(['rel' => $rel, 'href' => $href], $rel); |
||
171 | } |
||
172 | } |
||
173 | |||
174 | /** |
||
175 | * Renders the page buttons. |
||
176 | * @return string the rendering result |
||
177 | */ |
||
178 | 18 | protected function renderPageButtons() |
|
179 | { |
||
180 | 18 | $pageCount = $this->pagination->getPageCount(); |
|
181 | 18 | if ($pageCount < 2 && $this->hideOnSinglePage) { |
|
182 | 11 | return ''; |
|
183 | } |
||
184 | |||
185 | 7 | $buttons = []; |
|
186 | 7 | $currentPage = $this->pagination->getPage(); |
|
187 | |||
188 | // first page |
||
189 | 7 | $firstPageLabel = $this->firstPageLabel === true ? '1' : $this->firstPageLabel; |
|
190 | 7 | if ($firstPageLabel !== false) { |
|
191 | 1 | $buttons[] = $this->renderPageButton($firstPageLabel, 0, $this->firstPageCssClass, $currentPage <= 0, false); |
|
192 | } |
||
193 | |||
194 | // prev page |
||
195 | 7 | if ($this->prevPageLabel !== false) { |
|
196 | 7 | if (($page = $currentPage - 1) < 0) { |
|
197 | 2 | $page = 0; |
|
198 | } |
||
199 | 7 | $buttons[] = $this->renderPageButton($this->prevPageLabel, $page, $this->prevPageCssClass, $currentPage <= 0, false); |
|
0 ignored issues
–
show
Bug
introduced
by
![]() |
|||
200 | } |
||
201 | |||
202 | // internal pages |
||
203 | 7 | list($beginPage, $endPage) = $this->getPageRange(); |
|
204 | 7 | for ($i = $beginPage; $i <= $endPage; ++$i) { |
|
205 | 7 | $buttons[] = $this->renderPageButton($i + 1, $i, null, $this->disableCurrentPageButton && $i == $currentPage, $i == $currentPage); |
|
206 | } |
||
207 | |||
208 | // next page |
||
209 | 7 | if ($this->nextPageLabel !== false) { |
|
210 | 7 | if (($page = $currentPage + 1) >= $pageCount - 1) { |
|
211 | $page = $pageCount - 1; |
||
212 | } |
||
213 | 7 | $buttons[] = $this->renderPageButton($this->nextPageLabel, $page, $this->nextPageCssClass, $currentPage >= $pageCount - 1, false); |
|
214 | } |
||
215 | |||
216 | // last page |
||
217 | 7 | $lastPageLabel = $this->lastPageLabel === true ? $pageCount : $this->lastPageLabel; |
|
218 | 7 | if ($lastPageLabel !== false) { |
|
219 | 1 | $buttons[] = $this->renderPageButton($lastPageLabel, $pageCount - 1, $this->lastPageCssClass, $currentPage >= $pageCount - 1, false); |
|
220 | } |
||
221 | |||
222 | 7 | $options = $this->options; |
|
223 | 7 | $tag = ArrayHelper::remove($options, 'tag', 'ul'); |
|
224 | 7 | return Html::tag($tag, implode("\n", $buttons), $options); |
|
225 | } |
||
226 | |||
227 | /** |
||
228 | * Renders a page button. |
||
229 | * You may override this method to customize the generation of page buttons. |
||
230 | * @param string $label the text label for the button |
||
231 | * @param int $page the page number |
||
232 | * @param string $class the CSS class for the page button. |
||
233 | * @param bool $disabled whether this page button is disabled |
||
234 | * @param bool $active whether this page button is active |
||
235 | * @return string the rendering result |
||
236 | */ |
||
237 | 7 | protected function renderPageButton($label, $page, $class, $disabled, $active) |
|
238 | { |
||
239 | 7 | $options = $this->linkContainerOptions; |
|
240 | 7 | $linkWrapTag = ArrayHelper::remove($options, 'tag', 'li'); |
|
241 | 7 | Html::addCssClass($options, empty($class) ? $this->pageCssClass : $class); |
|
242 | |||
243 | 7 | if ($active) { |
|
244 | 7 | Html::addCssClass($options, $this->activePageCssClass); |
|
245 | } |
||
246 | 7 | if ($disabled) { |
|
247 | 3 | Html::addCssClass($options, $this->disabledPageCssClass); |
|
248 | 3 | $disabledItemOptions = $this->disabledListItemSubTagOptions; |
|
249 | 3 | $tag = ArrayHelper::remove($disabledItemOptions, 'tag', 'span'); |
|
250 | |||
251 | 3 | return Html::tag($linkWrapTag, Html::tag($tag, $label, $disabledItemOptions), $options); |
|
252 | } |
||
253 | 7 | $linkOptions = $this->linkOptions; |
|
254 | 7 | $linkOptions['data-page'] = $page; |
|
255 | |||
256 | 7 | return Html::tag($linkWrapTag, Html::a($label, $this->pagination->createUrl($page), $linkOptions), $options); |
|
257 | } |
||
258 | |||
259 | /** |
||
260 | * @return array the begin and end pages that need to be displayed. |
||
261 | */ |
||
262 | 7 | protected function getPageRange() |
|
263 | { |
||
264 | 7 | $currentPage = $this->pagination->getPage(); |
|
265 | 7 | $pageCount = $this->pagination->getPageCount(); |
|
266 | |||
267 | 7 | $beginPage = max(0, $currentPage - (int) ($this->maxButtonCount / 2)); |
|
268 | 7 | if (($endPage = $beginPage + $this->maxButtonCount - 1) >= $pageCount) { |
|
269 | $endPage = $pageCount - 1; |
||
270 | $beginPage = max(0, $endPage - $this->maxButtonCount + 1); |
||
271 | } |
||
272 | |||
273 | 7 | return [$beginPage, $endPage]; |
|
274 | } |
||
275 | } |
||
276 |