Passed
Pull Request — 4 (#8209)
by Ingo
09:07
created

GridFieldPaginator::getHTMLFragments()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
1
<?php
2
3
namespace SilverStripe\Forms\GridField;
4
5
use SilverStripe\Core\Config\Configurable;
6
use SilverStripe\ORM\Limitable;
7
use SilverStripe\ORM\SS_List;
8
use SilverStripe\ORM\UnsavedRelationList;
9
use SilverStripe\View\ArrayData;
10
use SilverStripe\View\SSViewer;
11
use LogicException;
12
13
/**
14
 * GridFieldPaginator paginates the {@link GridField} list and adds controls
15
 * to the bottom of the {@link GridField}.
16
 */
17
class GridFieldPaginator implements GridField_HTMLProvider, GridField_DataManipulator, GridField_ActionProvider
18
{
19
    use Configurable;
20
21
    /**
22
     * Specifies default items per page
23
     *
24
     * @config
25
     * @var int
26
     */
27
    private static $default_items_per_page = 15;
0 ignored issues
show
introduced by
The private property $default_items_per_page is not used, and could be removed.
Loading history...
28
29
    /**
30
     * @var int
31
     */
32
    protected $itemsPerPage;
33
34
    /**
35
     * See {@link setThrowExceptionOnBadDataType()}
36
     */
37
    protected $throwExceptionOnBadDataType = true;
38
39
    /**
40
     *
41
     * @param int $itemsPerPage - How many items should be displayed per page
42
     */
43
    public function __construct($itemsPerPage = null)
44
    {
45
        $this->itemsPerPage = $itemsPerPage
46
            ?: GridFieldPaginator::config()->uninherited('default_items_per_page');
0 ignored issues
show
Coding Style introduced by
As per coding style, self should be used for accessing local static members.

This check looks for accesses to local static members using the fully qualified name instead of self::.

<?php

class Certificate {
    const TRIPLEDES_CBC = 'ASDFGHJKL';

    private $key;

    public function __construct()
    {
        $this->key = Certificate::TRIPLEDES_CBC;
    }
}

While this is perfectly valid, the fully qualified name of Certificate::TRIPLEDES_CBC could just as well be replaced by self::TRIPLEDES_CBC. Referencing local members with self:: assured the access will still work when the class is renamed, makes it perfectly clear that the member is in fact local and will usually be shorter.

Loading history...
47
    }
48
49
    /**
50
     * Determine what happens when this component is used with a list that isn't {@link SS_Filterable}.
51
     *
52
     *  - true: An exception is thrown
53
     *  - false: This component will be ignored - it won't make any changes to the GridField.
54
     *
55
     * By default, this is set to true so that it's clearer what's happening, but the predefined
56
     * {@link GridFieldConfig} subclasses set this to false for flexibility.
57
     *
58
     * @param bool $throwExceptionOnBadDataType
59
     * @return $this
60
     */
61
    public function setThrowExceptionOnBadDataType($throwExceptionOnBadDataType)
62
    {
63
        $this->throwExceptionOnBadDataType = $throwExceptionOnBadDataType;
64
        return $this;
65
    }
66
67
    /**
68
     * See {@link setThrowExceptionOnBadDataType()}
69
     *
70
     * @return bool
71
     */
72
    public function getThrowExceptionOnBadDataType()
73
    {
74
        return $this->throwExceptionOnBadDataType;
75
    }
76
77
    /**
78
     * Check that this dataList is of the right data type.
79
     * Returns false if it's a bad data type, and if appropriate, throws an exception.
80
     *
81
     * @param SS_List $dataList
82
     * @return bool
83
     */
84
    protected function checkDataType($dataList)
85
    {
86
        if ($dataList instanceof Limitable) {
87
            return true;
88
        } else {
89
            if ($this->throwExceptionOnBadDataType) {
90
                throw new LogicException(
91
                    static::class . " expects an SS_Limitable list to be passed to the GridField."
92
                );
93
            }
94
            return false;
95
        }
96
    }
97
98
    /**
99
     *
100
     * @param GridField $gridField
101
     * @return array
102
     */
103
    public function getActions($gridField)
104
    {
105
        if (!$this->checkDataType($gridField->getList())) {
106
            return [];
107
        }
108
109
        return array('paginate');
110
    }
111
112
    /**
113
     *
114
     * @param GridField $gridField
115
     * @param string $actionName
116
     * @param string $arguments
117
     * @param array $data
118
     * @return void
119
     */
120
    public function handleAction(GridField $gridField, $actionName, $arguments, $data)
121
    {
122
        if (!$this->checkDataType($gridField->getList())) {
123
            return;
124
        }
125
126
        if ($actionName !== 'paginate') {
127
            return;
128
        }
129
        $state = $this->getGridPagerState($gridField);
130
        $state->currentPage = (int)$arguments;
0 ignored issues
show
Bug Best Practice introduced by
The property currentPage does not exist on SilverStripe\Forms\GridField\GridState_Data. Since you implemented __set, consider adding a @property annotation.
Loading history...
131
    }
132
133
    protected $totalItems = 0;
134
135
    /**
136
     * Retrieves/Sets up the state object used to store and retrieve information
137
     * about the current paging details of this GridField
138
     * @param GridField $gridField
139
     * @return GridState_Data
140
     */
141
    protected function getGridPagerState(GridField $gridField)
142
    {
143
        $state = $gridField->State->GridFieldPaginator;
0 ignored issues
show
Bug Best Practice introduced by
The property GridFieldPaginator does not exist on SilverStripe\Forms\GridField\GridState_Data. Since you implemented __get, consider adding a @property annotation.
Loading history...
144
145
        // Force the state to the initial page if none is set
146
        $state->currentPage(1);
147
148
        return $state;
149
    }
150
151
    /**
152
     *
153
     * @param GridField $gridField
154
     * @param SS_List $dataList
155
     * @return SS_List
156
     */
157
    public function getManipulatedData(GridField $gridField, SS_List $dataList)
158
    {
159
        if (!$this->checkDataType($dataList)) {
160
            return $dataList;
161
        }
162
163
        $state = $this->getGridPagerState($gridField);
164
165
        // Update item count prior to filter. GridFieldPageCount will rely on this value
166
        $this->totalItems = $dataList->count();
167
168
        $startRow = $this->itemsPerPage * ($state->currentPage - 1);
0 ignored issues
show
Bug Best Practice introduced by
The property currentPage does not exist on SilverStripe\Forms\GridField\GridState_Data. Since you implemented __get, consider adding a @property annotation.
Loading history...
169
170
        // Prevent visiting a page with an offset higher than the total number of items
171
        if ($startRow >= $this->totalItems) {
172
            $state->currentPage = 1;
0 ignored issues
show
Bug Best Practice introduced by
The property currentPage does not exist on SilverStripe\Forms\GridField\GridState_Data. Since you implemented __set, consider adding a @property annotation.
Loading history...
173
            $startRow = 0;
174
        }
175
176
        if (!($dataList instanceof Limitable) || ($dataList instanceof UnsavedRelationList)) {
177
            return $dataList;
178
        }
179
180
        return $dataList->limit((int)$this->itemsPerPage, (int)$startRow);
181
    }
182
183
    /**
184
     * Determines arguments to be passed to the template for building this field
185
     *
186
     * @param GridField $gridField
187
     * @return ArrayData If paging is available this will be an ArrayData
188
     * object of paging details with these parameters:
189
     * <ul>
190
     *  <li>OnlyOnePage:                boolean - Is there only one page?</li>
191
     *  <li>FirstShownRecord:           integer - Number of the first record displayed</li>
192
     *  <li>LastShownRecord:            integer - Number of the last record displayed</li>
193
     *  <li>NumRecords:                 integer - Total number of records</li>
194
     *  <li>NumPages:                   integer - The number of pages</li>
195
     *  <li>CurrentPageNum (optional):  integer - If OnlyOnePage is false, the number of the current page</li>
196
     *  <li>FirstPage (optional):       GridField_FormAction - Button to go to the first page</li>
197
     *  <li>PreviousPage (optional):    GridField_FormAction - Button to go to the previous page</li>
198
     *  <li>NextPage (optional):        GridField_FormAction - Button to go to the next page</li>
199
     *  <li>LastPage (optional):        GridField_FormAction - Button to go to last page</li>
200
     * </ul>
201
     */
202
    public function getTemplateParameters(GridField $gridField)
203
    {
204
        if (!$this->checkDataType($gridField->getList())) {
205
            return null;
206
        }
207
208
        $state = $this->getGridPagerState($gridField);
209
210
        // Figure out which page and record range we're on
211
        $totalRows = $this->totalItems;
212
        if (!$totalRows) {
213
            return null;
214
        }
215
216
        $totalPages = 1;
217
        $firstShownRecord = 1;
218
        $lastShownRecord = $totalRows;
219
        if ($itemsPerPage = $this->getItemsPerPage()) {
220
            $totalPages = (int)ceil($totalRows / $itemsPerPage);
221
            if ($totalPages == 0) {
222
                $totalPages = 1;
223
            }
224
            $firstShownRecord = ($state->currentPage - 1) * $itemsPerPage + 1;
0 ignored issues
show
Bug Best Practice introduced by
The property currentPage does not exist on SilverStripe\Forms\GridField\GridState_Data. Since you implemented __get, consider adding a @property annotation.
Loading history...
225
            if ($firstShownRecord > $totalRows) {
226
                $firstShownRecord = $totalRows;
227
            }
228
            $lastShownRecord = $state->currentPage * $itemsPerPage;
229
            if ($lastShownRecord > $totalRows) {
230
                $lastShownRecord = $totalRows;
231
            }
232
        }
233
234
        // If there is only 1 page for all the records in list, we don't need to go further
235
        // to sort out those first page, last page, pre and next pages, etc
236
        // we are not render those in to the paginator.
237
        if ($totalPages === 1) {
238
            return new ArrayData(array(
239
                'OnlyOnePage' => true,
240
                'FirstShownRecord' => $firstShownRecord,
241
                'LastShownRecord' => $lastShownRecord,
242
                'NumRecords' => $totalRows,
243
                'NumPages' => $totalPages
244
            ));
245
        } else {
246
            // First page button
247
            $firstPage = new GridField_FormAction($gridField, 'pagination_first', 'First', 'paginate', 1);
0 ignored issues
show
Bug introduced by
1 of type integer is incompatible with the type array expected by parameter $args of SilverStripe\Forms\GridF...rmAction::__construct(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

247
            $firstPage = new GridField_FormAction($gridField, 'pagination_first', 'First', 'paginate', /** @scrutinizer ignore-type */ 1);
Loading history...
248
            $firstPage->addExtraClass('btn btn-secondary btn--hide-text btn-sm font-icon-angle-double-left ss-gridfield-firstpage');
249
            if ($state->currentPage == 1) {
250
                $firstPage = $firstPage->performDisabledTransformation();
251
            }
252
253
            // Previous page button
254
            $previousPageNum = $state->currentPage <= 1 ? 1 : $state->currentPage - 1;
255
            $previousPage = new GridField_FormAction(
256
                $gridField,
257
                'pagination_prev',
258
                'Previous',
259
                'paginate',
260
                $previousPageNum
261
            );
262
            $previousPage->addExtraClass('btn btn-secondary btn--hide-text btn-sm font-icon-angle-left ss-gridfield-previouspage');
263
            if ($state->currentPage == 1) {
264
                $previousPage = $previousPage->performDisabledTransformation();
265
            }
266
267
            // Next page button
268
            $nextPageNum = $state->currentPage >= $totalPages ? $totalPages : $state->currentPage + 1;
269
            $nextPage = new GridField_FormAction(
270
                $gridField,
271
                'pagination_next',
272
                'Next',
273
                'paginate',
274
                $nextPageNum
275
            );
276
            $nextPage->addExtraClass('btn btn-secondary btn--hide-text btn-sm font-icon-angle-right ss-gridfield-nextpage');
277
            if ($state->currentPage == $totalPages) {
278
                $nextPage = $nextPage->performDisabledTransformation();
279
            }
280
281
            // Last page button
282
            $lastPage = new GridField_FormAction($gridField, 'pagination_last', 'Last', 'paginate', $totalPages);
283
            $lastPage->addExtraClass('btn btn-secondary btn--hide-text btn-sm font-icon-angle-double-right ss-gridfield-lastpage');
284
            if ($state->currentPage == $totalPages) {
285
                $lastPage = $lastPage->performDisabledTransformation();
286
            }
287
288
            // Render in template
289
            return new ArrayData(array(
290
                'OnlyOnePage' => false,
291
                'FirstPage' => $firstPage,
292
                'PreviousPage' => $previousPage,
293
                'CurrentPageNum' => $state->currentPage,
294
                'NumPages' => $totalPages,
295
                'NextPage' => $nextPage,
296
                'LastPage' => $lastPage,
297
                'FirstShownRecord' => $firstShownRecord,
298
                'LastShownRecord' => $lastShownRecord,
299
                'NumRecords' => $totalRows
300
            ));
301
        }
302
    }
303
304
    /**
305
     *
306
     * @param GridField $gridField
307
     * @return array
308
     */
309
    public function getHTMLFragments($gridField)
310
    {
311
        $forTemplate = $this->getTemplateParameters($gridField);
312
        if (!$forTemplate) {
0 ignored issues
show
introduced by
$forTemplate is of type SilverStripe\View\ArrayData, thus it always evaluated to true.
Loading history...
313
            return null;
314
        }
315
        $template = SSViewer::get_templates_by_class($this, '_Row', __CLASS__);
316
        return array(
317
            'footer' => $forTemplate->renderWith(
318
                $template,
319
                array('Colspan' => count($gridField->getColumns()))
320
            )
321
        );
322
    }
323
324
    /**
325
     * @param int $num
326
     * @return $this
327
     */
328
    public function setItemsPerPage($num)
329
    {
330
        $this->itemsPerPage = $num;
331
        return $this;
332
    }
333
334
    /**
335
     * @return Int
336
     */
337
    public function getItemsPerPage()
338
    {
339
        return $this->itemsPerPage;
340
    }
341
}
342