Completed
Push — master ( 87fbd5...f3b6bb )
by Daniel
11:18
created

GridFieldPaginator::setItemsPerPage()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 5
rs 9.4285
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;
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 ?: GridFieldPaginator::config()->get('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...
46
    }
47
48
    /**
49
     * Determine what happens when this component is used with a list that isn't {@link SS_Filterable}.
50
     *
51
     *  - true: An exception is thrown
52
     *  - false: This component will be ignored - it won't make any changes to the GridField.
53
     *
54
     * By default, this is set to true so that it's clearer what's happening, but the predefined
55
     * {@link GridFieldConfig} subclasses set this to false for flexibility.
56
     *
57
     * @param bool $throwExceptionOnBadDataType
58
     * @return $this
59
     */
60
    public function setThrowExceptionOnBadDataType($throwExceptionOnBadDataType)
61
    {
62
        $this->throwExceptionOnBadDataType = $throwExceptionOnBadDataType;
63
        return $this;
64
    }
65
66
    /**
67
     * See {@link setThrowExceptionOnBadDataType()}
68
     *
69
     * @return bool
70
     */
71
    public function getThrowExceptionOnBadDataType()
72
    {
73
        return $this->throwExceptionOnBadDataType;
74
    }
75
76
    /**
77
     * Check that this dataList is of the right data type.
78
     * Returns false if it's a bad data type, and if appropriate, throws an exception.
79
     *
80
     * @param SS_List $dataList
81
     * @return bool
82
     */
83
    protected function checkDataType($dataList)
84
    {
85
        if ($dataList instanceof Limitable) {
86
            return true;
87
        } else {
88
            if ($this->throwExceptionOnBadDataType) {
89
                throw new LogicException(
90
                    get_class($this) . " expects an SS_Limitable list to be passed to the GridField."
91
                );
92
            }
93
            return false;
94
        }
95
    }
96
97
    /**
98
     *
99
     * @param GridField $gridField
100
     * @return array
101
     */
102
    public function getActions($gridField)
103
    {
104
        if (!$this->checkDataType($gridField->getList())) {
105
            return [];
106
        }
107
108
        return array('paginate');
109
    }
110
111
    /**
112
     *
113
     * @param GridField $gridField
114
     * @param string $actionName
115
     * @param string $arguments
116
     * @param array $data
117
     * @return void
118
     */
119
    public function handleAction(GridField $gridField, $actionName, $arguments, $data)
120
    {
121
        if (!$this->checkDataType($gridField->getList())) {
122
            return;
123
        }
124
125
        if ($actionName !== 'paginate') {
126
            return;
127
        }
128
        $state = $this->getGridPagerState($gridField);
129
        $state->currentPage = (int)$arguments;
0 ignored issues
show
Documentation introduced by
The property currentPage does not exist on object<SilverStripe\Form...idField\GridState_Data>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
130
    }
131
132
    protected $totalItems = 0;
133
134
    /**
135
     * Retrieves/Sets up the state object used to store and retrieve information
136
     * about the current paging details of this GridField
137
     * @param GridField $gridField
138
     * @return GridState_Data
139
     */
140
    protected function getGridPagerState(GridField $gridField)
141
    {
142
        $state = $gridField->State->GridFieldPaginator;
0 ignored issues
show
Documentation introduced by
The property GridFieldPaginator does not exist on object<SilverStripe\Form...idField\GridState_Data>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
143
144
        // Force the state to the initial page if none is set
145
        $state->currentPage(1);
146
147
        return $state;
148
    }
149
150
    /**
151
     *
152
     * @param GridField $gridField
153
     * @param SS_List $dataList
154
     * @return SS_List
155
     */
156
    public function getManipulatedData(GridField $gridField, SS_List $dataList)
157
    {
158
        if (!$this->checkDataType($dataList)) {
159
            return $dataList;
160
        }
161
162
        $state = $this->getGridPagerState($gridField);
163
164
        // Update item count prior to filter. GridFieldPageCount will rely on this value
165
        $this->totalItems = $dataList->count();
166
167
        $startRow = $this->itemsPerPage * ($state->currentPage - 1);
168
169
        // Prevent visiting a page with an offset higher than the total number of items
170
        if ($startRow >= $this->totalItems) {
171
            $state->currentPage = 1;
0 ignored issues
show
Documentation introduced by
The property currentPage does not exist on object<SilverStripe\Form...idField\GridState_Data>. Since you implemented __set, maybe consider adding a @property annotation.

Since your code implements the magic setter _set, this function will be called for any write access on an undefined variable. You can add the @property annotation to your class or interface to document the existence of this variable.

<?php

/**
 * @property int $x
 * @property int $y
 * @property string $text
 */
class MyLabel
{
    private $properties;

    private $allowedProperties = array('x', 'y', 'text');

    public function __get($name)
    {
        if (isset($properties[$name]) && in_array($name, $this->allowedProperties)) {
            return $properties[$name];
        } else {
            return null;
        }
    }

    public function __set($name, $value)
    {
        if (in_array($name, $this->allowedProperties)) {
            $properties[$name] = $value;
        } else {
            throw new \LogicException("Property $name is not defined.");
        }
    }

}

Since the property has write access only, you can use the @property-write annotation instead.

Of course, you may also just have mistyped another name, in which case you should fix the error.

See also the PhpDoc documentation for @property.

Loading history...
172
            $startRow = 0;
173
        }
174
175
        if (!($dataList instanceof Limitable) || ($dataList instanceof UnsavedRelationList)) {
176
            return $dataList;
177
        }
178
179
        return $dataList->limit((int)$this->itemsPerPage, (int)$startRow);
180
    }
181
182
    /**
183
     * Determines arguments to be passed to the template for building this field
184
     *
185
     * @param GridField $gridField
186
     * @return ArrayData If paging is available this will be an ArrayData
187
     * object of paging details with these parameters:
188
     * <ul>
189
     *  <li>OnlyOnePage:                boolean - Is there only one page?</li>
190
     *  <li>FirstShownRecord:           integer - Number of the first record displayed</li>
191
     *  <li>LastShownRecord:            integer - Number of the last record displayed</li>
192
     *  <li>NumRecords:                 integer - Total number of records</li>
193
     *  <li>NumPages:                   integer - The number of pages</li>
194
     *  <li>CurrentPageNum (optional):  integer - If OnlyOnePage is false, the number of the current page</li>
195
     *  <li>FirstPage (optional):       GridField_FormAction - Button to go to the first page</li>
196
     *  <li>PreviousPage (optional):    GridField_FormAction - Button to go to the previous page</li>
197
     *  <li>NextPage (optional):        GridField_FormAction - Button to go to the next page</li>
198
     *  <li>LastPage (optional):        GridField_FormAction - Button to go to last page</li>
199
     * </ul>
200
     */
201
    public function getTemplateParameters(GridField $gridField)
202
    {
203
        if (!$this->checkDataType($gridField->getList())) {
204
            return null;
205
        }
206
207
        $state = $this->getGridPagerState($gridField);
208
209
        // Figure out which page and record range we're on
210
        $totalRows = $this->totalItems;
211
        if (!$totalRows) {
212
            return null;
213
        }
214
215
        $totalPages = 1;
216
        $firstShownRecord = 1;
217
        $lastShownRecord = $totalRows;
218
        if ($itemsPerPage = $this->getItemsPerPage()) {
219
            $totalPages = (int)ceil($totalRows / $itemsPerPage);
220
            if ($totalPages == 0) {
221
                $totalPages = 1;
222
            }
223
            $firstShownRecord = ($state->currentPage - 1) * $itemsPerPage + 1;
224
            if ($firstShownRecord > $totalRows) {
225
                $firstShownRecord = $totalRows;
226
            }
227
            $lastShownRecord = $state->currentPage * $itemsPerPage;
228
            if ($lastShownRecord > $totalRows) {
229
                $lastShownRecord = $totalRows;
230
            }
231
        }
232
233
        // If there is only 1 page for all the records in list, we don't need to go further
234
        // to sort out those first page, last page, pre and next pages, etc
235
        // we are not render those in to the paginator.
236
        if ($totalPages === 1) {
237
            return new ArrayData(array(
238
                'OnlyOnePage' => true,
239
                'FirstShownRecord' => $firstShownRecord,
240
                'LastShownRecord' => $lastShownRecord,
241
                'NumRecords' => $totalRows,
242
                'NumPages' => $totalPages
243
            ));
244
        } else {
245
            // First page button
246
            $firstPage = new GridField_FormAction($gridField, 'pagination_first', 'First', 'paginate', 1);
247
            $firstPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-double-left ss-gridfield-firstpage');
248
            if ($state->currentPage == 1) {
249
                $firstPage = $firstPage->performDisabledTransformation();
250
            }
251
252
            // Previous page button
253
            $previousPageNum = $state->currentPage <= 1 ? 1 : $state->currentPage - 1;
254
            $previousPage = new GridField_FormAction(
255
                $gridField,
256
                'pagination_prev',
257
                'Previous',
258
                'paginate',
259
                $previousPageNum
260
            );
261
            $previousPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-left ss-gridfield-previouspage');
262
            if ($state->currentPage == 1) {
263
                $previousPage = $previousPage->performDisabledTransformation();
264
            }
265
266
            // Next page button
267
            $nextPageNum = $state->currentPage >= $totalPages ? $totalPages : $state->currentPage + 1;
268
            $nextPage = new GridField_FormAction(
269
                $gridField,
270
                'pagination_next',
271
                'Next',
272
                'paginate',
273
                $nextPageNum
274
            );
275
            $nextPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-right ss-gridfield-nextpage');
276
            if ($state->currentPage == $totalPages) {
277
                $nextPage = $nextPage->performDisabledTransformation();
278
            }
279
280
            // Last page button
281
            $lastPage = new GridField_FormAction($gridField, 'pagination_last', 'Last', 'paginate', $totalPages);
282
            $lastPage->addExtraClass('btn btn-secondary btn--no-text font-icon-angle-double-right ss-gridfield-lastpage');
283
            if ($state->currentPage == $totalPages) {
284
                $lastPage = $lastPage->performDisabledTransformation();
285
            }
286
287
            // Render in template
288
            return new ArrayData(array(
289
                'OnlyOnePage' => false,
290
                'FirstPage' => $firstPage,
291
                'PreviousPage' => $previousPage,
292
                'CurrentPageNum' => $state->currentPage,
293
                'NumPages' => $totalPages,
294
                'NextPage' => $nextPage,
295
                'LastPage' => $lastPage,
296
                'FirstShownRecord' => $firstShownRecord,
297
                'LastShownRecord' => $lastShownRecord,
298
                'NumRecords' => $totalRows
299
            ));
300
        }
301
    }
302
303
    /**
304
     *
305
     * @param GridField $gridField
306
     * @return array
307
     */
308
    public function getHTMLFragments($gridField)
309
    {
310
        $forTemplate = $this->getTemplateParameters($gridField);
311
        if (!$forTemplate) {
312
            return null;
313
        }
314
        $template = SSViewer::get_templates_by_class($this, '_Row', __CLASS__);
315
        return array(
316
            'footer' => $forTemplate->renderWith(
317
                $template,
318
                array('Colspan' => count($gridField->getColumns()))
319
            )
320
        );
321
    }
322
323
    /**
324
     * @param int $num
325
     * @return $this
326
     */
327
    public function setItemsPerPage($num)
328
    {
329
        $this->itemsPerPage = $num;
330
        return $this;
331
    }
332
333
    /**
334
     * @return Int
335
     */
336
    public function getItemsPerPage()
337
    {
338
        return $this->itemsPerPage;
339
    }
340
}
341