Scrutinizer GitHub App not installed

We could not synchronize checks via GitHub's checks API since Scrutinizer's GitHub App is not installed for this repository.

Install GitHub App

Passed
Push — optimize-queries ( e16a2f...539379 )
by Pedro
15:02
created

Read::buildPageLengthMenuFromArray()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 17
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 8
c 0
b 0
f 0
nc 2
nop 1
dl 0
loc 17
rs 10
1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Traits;
4
5
use Backpack\CRUD\app\Exceptions\BackpackProRequiredException;
6
use Exception;
7
use Illuminate\Support\Str;
8
9
/**
10
 * Properties and methods used by the List operation.
11
 */
12
trait Read
13
{
14
    /**
15
     * Find and retrieve the id of the current entry.
16
     *
17
     * @return int|bool The id in the db or false.
18
     */
19
    public function getCurrentEntryId()
20
    {
21
        if ($this->entry) {
22
            return $this->entry->getKey();
23
        }
24
25
        $params = \Route::current()->parameters();
26
27
        return  // use the entity name to get the current entry
28
                // this makes sure the ID is corrent even for nested resources
29
                $this->getRequest()->input($this->entity_name) ??
0 ignored issues
show
Bug introduced by
It seems like getRequest() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

29
                $this->/** @scrutinizer ignore-call */ 
30
                       getRequest()->input($this->entity_name) ??
Loading history...
30
                // otherwise use the next to last parameter
31
                array_values($params)[count($params) - 1] ??
32
                // otherwise return false
33
                false;
34
    }
35
36
    /**
37
     * Find and retrieve the current entry.
38
     *
39
     * @return \Illuminate\Database\Eloquent\Model|bool The row in the db or false.
40
     */
41
    public function getCurrentEntry()
42
    {
43
        $id = $this->getCurrentEntryId();
44
45
        if ($id === false) {
46
            return false;
47
        }
48
49
        return $this->getEntry($id);
50
    }
51
52
    /**
53
     * Find and retrieve an entry in the database or fail.
54
     *
55
     * @param int The id of the row in the db to fetch.
0 ignored issues
show
Bug introduced by
The type Backpack\CRUD\app\Library\CrudPanel\Traits\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
56
     * @return \Illuminate\Database\Eloquent\Model The row in the db.
57
     */
58
    public function getEntry($id)
59
    {
60
        if (! $this->entry) {
61
            $this->eagerLoadRelationships('fields');
62
            $this->entry = $this->query->findOrFail($id);
0 ignored issues
show
Bug Best Practice introduced by
The property entry does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
63
            $this->entry = $this->entry->withFakes();
64
        }
65
66
        return $this->entry;
67
    }
68
69
    /**
70
     * Find and retrieve an entry in the database or fail.
71
     * When found, make sure we set the Locale on it.
72
     *
73
     * @param int The id of the row in the db to fetch.
74
     * @return \Illuminate\Database\Eloquent\Model The row in the db.
75
     */
76
    public function getEntryWithLocale($id)
77
    {
78
        if (! $this->entry) {
79
            $this->entry = $this->getEntry($id);
0 ignored issues
show
Bug Best Practice introduced by
The property entry does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
80
        }
81
82
        if ($this->entry->translationEnabled()) {
83
            $locale = request('_locale', \App::getLocale());
84
            if (in_array($locale, array_keys($this->entry->getAvailableLocales()))) {
85
                $this->entry->setLocale($locale);
86
            }
87
        }
88
89
        return $this->entry;
90
    }
91
92
    /**
93
     * Make the query JOIN all relationships used in the definitions (fields, columns).
94
     * So there will be less database queries overall.
95
     */
96
    public function eagerLoadRelationships(string $definitions)
97
    {
98
        $definitions = $this->{$definitions}();
99
        $relationStrings = [];
100
        // dd($definitions);
101
        foreach ($definitions as $definitionName => $definition) {
102
            $relationString = isset($definition['entity']) && $definition['entity'] !== false ? $definition['entity'] : '';
103
            if (strpos($definition['entity'], '.') !== false) {
104
                $definitionAttribute = $definition['attribute'] ?? null;
105
                if ($definitionAttribute) {
106
                    $relationString = Str::endsWith($relationString, $definitionAttribute) ? Str::beforeLast($relationString, '.') : $relationString;
107
108
                    $relationStrings[] = $relationString;
109
110
                    continue;
111
                }
112
113
                $parts = explode('.', $relationString);
114
                $model = $this->model;
115
116
                // Iterate over each relation part to find the valid relations without attributes
117
                // We should eager load the relation but not the attribute
118
                foreach ($parts as $i => $part) {
119
                    try {
120
                        $model = $model->$part()->getRelated();
121
                    } catch (Exception $e) {
122
                        $relationString = implode('.', array_slice($parts, 0, $i));
123
                    }
124
                }
125
126
                $relationStrings[] = $relationString;
127
128
                continue;
129
            }
130
            if ($relationString) {
131
                $relationStrings[] = $relationString;
132
            }
133
        }
134
135
        $this->with(array_unique($relationStrings));
0 ignored issues
show
Bug introduced by
It seems like with() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

135
        $this->/** @scrutinizer ignore-call */ 
136
               with(array_unique($relationStrings));
Loading history...
136
    }
137
138
    /**
139
     * Return a Model builder instance with the current crud query applied.
140
     *
141
     * @return \Illuminate\Database\Eloquent\Builder
142
     */
143
    public function getModelWithCrudPanelQuery()
144
    {
145
        return $this->model->setQuery($this->query->getQuery());
146
    }
147
148
    /**
149
     * Find and retrieve an entry in the database or fail.
150
     *
151
     * @param int The id of the row in the db to fetch.
152
     * @return \Illuminate\Database\Eloquent\Model The row in the db.
153
     */
154
    public function getEntryWithoutFakes($id)
155
    {
156
        return $this->getModelWithCrudPanelQuery()->findOrFail($id);
157
    }
158
159
    /**
160
     * Make the query JOIN all relationships used in the columns, too,
161
     * so there will be less database queries overall.
162
     */
163
    public function autoEagerLoadRelationshipColumns()
164
    {
165
        $relationships = $this->getColumnsRelationships();
0 ignored issues
show
Bug introduced by
It seems like getColumnsRelationships() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

165
        /** @scrutinizer ignore-call */ 
166
        $relationships = $this->getColumnsRelationships();
Loading history...
166
167
        foreach ($relationships as $relation) {
168
            if (strpos($relation, '.') !== false) {
169
                $parts = explode('.', $relation);
170
                $model = $this->model;
171
172
                // Iterate over each relation part to find the valid relations without attributes
173
                // We should eager load the relation but not the attribute
174
                foreach ($parts as $i => $part) {
175
                    try {
176
                        $model = $model->$part()->getRelated();
177
                    } catch (Exception $e) {
178
                        $relation = implode('.', array_slice($parts, 0, $i));
179
                    }
180
                }
181
            }
182
            $this->with($relation);
183
        }
184
    }
185
186
    /**
187
     * Get all entries from the database.
188
     *
189
     * @return array|\Illuminate\Database\Eloquent\Collection
190
     */
191
    public function getEntries()
192
    {
193
        $this->autoEagerLoadRelationshipColumns();
194
195
        $entries = $this->query->get();
196
197
        // add the fake columns for each entry
198
        foreach ($entries as $key => $entry) {
199
            $entry->addFakes($this->getFakeColumnsAsArray());
0 ignored issues
show
Bug introduced by
It seems like getFakeColumnsAsArray() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

199
            $entry->addFakes($this->/** @scrutinizer ignore-call */ getFakeColumnsAsArray());
Loading history...
200
        }
201
202
        return $entries;
203
    }
204
205
    /**
206
     * Enable the DETAILS ROW functionality:.
207
     *
208
     * In the table view, show a plus sign next to each entry.
209
     * When clicking that plus sign, an AJAX call will bring whatever content you want from the EntityCrudController::showDetailsRow($id) and show it to the user.
210
     */
211
    public function enableDetailsRow()
212
    {
213
        if (! backpack_pro()) {
214
            throw new BackpackProRequiredException('Details row');
215
        }
216
217
        $this->setOperationSetting('detailsRow', true);
0 ignored issues
show
Bug introduced by
It seems like setOperationSetting() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

217
        $this->/** @scrutinizer ignore-call */ 
218
               setOperationSetting('detailsRow', true);
Loading history...
218
    }
219
220
    /**
221
     * Disable the DETAILS ROW functionality:.
222
     */
223
    public function disableDetailsRow()
224
    {
225
        $this->setOperationSetting('detailsRow', false);
226
    }
227
228
    /**
229
     * Add two more columns at the beginning of the ListEntrie table:
230
     * - one shows the checkboxes needed for bulk actions
231
     * - one is blank, in order for evenual detailsRow or expand buttons
232
     * to be in a separate column.
233
     */
234
    public function enableBulkActions()
235
    {
236
        $this->setOperationSetting('bulkActions', true);
237
    }
238
239
    /**
240
     * Remove the two columns needed for bulk actions.
241
     */
242
    public function disableBulkActions()
243
    {
244
        $this->setOperationSetting('bulkActions', false);
245
246
        $this->removeColumn('bulk_actions');
0 ignored issues
show
Bug introduced by
It seems like removeColumn() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

246
        $this->/** @scrutinizer ignore-call */ 
247
               removeColumn('bulk_actions');
Loading history...
247
    }
248
249
    /**
250
     * Set the number of rows that should be show on the list view.
251
     */
252
    public function setDefaultPageLength($value)
253
    {
254
        $this->abortIfInvalidPageLength($value);
255
256
        $this->setOperationSetting('defaultPageLength', $value);
257
    }
258
259
    /**
260
     * Get the number of rows that should be show on the list view.
261
     *
262
     * @return int
263
     */
264
    public function getDefaultPageLength()
265
    {
266
        return $this->getOperationSetting('defaultPageLength') ?? config('backpack.crud.operations.list.defaultPageLength') ?? 25;
0 ignored issues
show
Bug introduced by
It seems like getOperationSetting() must be provided by classes using this trait. How about adding it as abstract method to this trait? ( Ignorable by Annotation )

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

266
        return $this->/** @scrutinizer ignore-call */ getOperationSetting('defaultPageLength') ?? config('backpack.crud.operations.list.defaultPageLength') ?? 25;
Loading history...
267
    }
268
269
    /**
270
     * If a custom page length was specified as default, make sure it
271
     * also show up in the page length menu.
272
     */
273
    public function addCustomPageLengthToPageLengthMenu()
274
    {
275
        $values = $this->getOperationSetting('pageLengthMenu')[0];
276
        $labels = $this->getOperationSetting('pageLengthMenu')[1];
277
278
        if (array_search($this->getDefaultPageLength(), $values) === false) {
279
            for ($i = 0; $i < count($values); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
280
                if ($values[$i] > $this->getDefaultPageLength() || $values[$i] === -1) {
281
                    array_splice($values, $i, 0, $this->getDefaultPageLength());
282
                    array_splice($labels, $i, 0, $this->getDefaultPageLength());
283
                    break;
284
                }
285
                if ($i === count($values) - 1) {
286
                    $values[] = $this->getDefaultPageLength();
287
                    $labels[] = $this->getDefaultPageLength();
288
                    break;
289
                }
290
            }
291
        }
292
293
        $this->setOperationSetting('pageLengthMenu', [$values, $labels]);
294
    }
295
296
    /**
297
     * Specify array of available page lengths on the list view.
298
     *
299
     * @param  array|int  $menu
300
     *
301
     * https://backpackforlaravel.com/docs/4.1/crud-cheat-sheet#page-length
302
     */
303
    public function setPageLengthMenu($menu)
304
    {
305
        if (is_array($menu)) {
306
            // start checking $menu integrity
307
            if (count($menu) !== count($menu, COUNT_RECURSIVE)) {
308
                // developer defined as setPageLengthMenu([[50, 100, 300]]) or setPageLengthMenu([[50, 100, 300],['f','h','t']])
309
                // we will apply the same labels as the values to the menu if developer didn't
310
                $this->abortIfInvalidPageLength($menu[0]);
311
312
                if (! isset($menu[1]) || ! is_array($menu[1])) {
313
                    $menu[1] = $menu[0];
314
                }
315
            } else {
316
                // developer defined setPageLengthMenu([10 => 'f', 100 => 'h', 300 => 't']) OR setPageLengthMenu([50, 100, 300])
317
                $menu = $this->buildPageLengthMenuFromArray($menu);
318
            }
319
        } else {
320
            // developer added only a single value setPageLengthMenu(10)
321
            $this->abortIfInvalidPageLength($menu);
322
323
            $menu = [[$menu], [$menu]];
324
        }
325
326
        $this->setOperationSetting('pageLengthMenu', $menu);
327
    }
328
329
    /**
330
     * Builds the menu from the given array. It works out with two different types of arrays:
331
     *  [1, 2, 3] AND [1 => 'one', 2 => 'two', 3 => 'three'].
332
     *
333
     * @param  array  $menu
334
     * @return array
335
     */
336
    private function buildPageLengthMenuFromArray($menu)
337
    {
338
        // check if the values of the array are strings, in case developer defined:
339
        // setPageLengthMenu([0 => 'f', 100 => 'h', 300 => 't'])
340
        if (count(array_filter(array_values($menu), 'is_string')) > 0) {
341
            $values = array_keys($menu);
342
            $labels = array_values($menu);
343
344
            $this->abortIfInvalidPageLength($values);
345
346
            return [$values, $labels];
347
        } else {
348
            // developer defined length as setPageLengthMenu([50, 100, 300])
349
            // we will use the same values as labels
350
            $this->abortIfInvalidPageLength($menu);
351
352
            return [$menu, $menu];
353
        }
354
    }
355
356
    /**
357
     * Get page length menu for the list view.
358
     *
359
     * @return array
360
     */
361
    public function getPageLengthMenu()
362
    {
363
        // if we have a 2D array, update all the values in the right hand array to their translated values
364
        if (isset($this->getOperationSetting('pageLengthMenu')[1]) && is_array($this->getOperationSetting('pageLengthMenu')[1])) {
365
            $aux = $this->getOperationSetting('pageLengthMenu');
366
            foreach ($this->getOperationSetting('pageLengthMenu')[1] as $key => $val) {
367
                $aux[1][$key] = trans($val);
368
            }
369
            $this->setOperationSetting('pageLengthMenu', $aux);
370
        }
371
        $this->addCustomPageLengthToPageLengthMenu();
372
373
        return $this->getOperationSetting('pageLengthMenu');
374
    }
375
376
    /**
377
     * Checks if the provided PageLength segment is valid.
378
     *
379
     * @param  array|int  $value
380
     * @return void
381
     */
382
    private function abortIfInvalidPageLength($value)
383
    {
384
        if ($value === 0 || (is_array($value) && in_array(0, $value))) {
385
            abort(500, 'You should not use 0 as a key in paginator. If you are looking for "ALL" option, use -1 instead.');
386
        }
387
    }
388
389
    /*
390
    |--------------------------------------------------------------------------
391
    |                                EXPORT BUTTONS
392
    |--------------------------------------------------------------------------
393
    */
394
395
    /**
396
     * Tell the list view to show the DataTables export buttons.
397
     */
398
    public function enableExportButtons()
399
    {
400
        if (! backpack_pro()) {
401
            throw new BackpackProRequiredException('Export buttons');
402
        }
403
404
        $this->setOperationSetting('exportButtons', true);
405
        $this->setOperationSetting('showTableColumnPicker', true);
406
        $this->setOperationSetting('showExportButton', true);
407
    }
408
409
    /**
410
     * Check if export buttons are enabled for the table view.
411
     *
412
     * @return bool
413
     */
414
    public function exportButtons()
415
    {
416
        return $this->getOperationSetting('exportButtons') ?? false;
417
    }
418
}
419