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

Test Failed
Pull Request — main (#4267)
by Cristian
28:44 queued 14:24
created

Read   C

Complexity

Total Complexity 53

Size/Duplication

Total Lines 369
Duplicated Lines 0 %

Importance

Changes 4
Bugs 1 Features 2
Metric Value
eloc 100
dl 0
loc 369
rs 6.96
c 4
b 1
f 2
wmc 53

23 Methods

Rating   Name   Duplication   Size   Complexity  
A getCurrentEntry() 0 9 2
A getCurrentEntryId() 0 15 2
A getCurrentEntryWithLocale() 0 9 2
A getEntry() 0 8 2
A getEntryWithoutFakes() 0 3 1
A enableBulkActions() 0 3 1
A disableDetailsRow() 0 3 1
A shouldUseFallbackLocale() 0 5 3
A disableBulkActions() 0 5 1
A setPageLengthMenu() 0 24 5
A addCustomPageLengthToPageLengthMenu() 0 21 6
A getDefaultPageLength() 0 3 1
A enableExportButtons() 0 9 2
A buildPageLengthMenuFromArray() 0 17 2
A getModelWithCrudPanelQuery() 0 3 1
A autoEagerLoadRelationshipColumns() 0 20 5
A getPageLengthMenu() 0 13 4
A getEntries() 0 12 2
A getEntryWithLocale() 0 7 2
A setDefaultPageLength() 0 5 1
A exportButtons() 0 3 1
A abortIfInvalidPageLength() 0 4 4
A enableDetailsRow() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like Read often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Read, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Traits;
4
5
use Backpack\CRUD\app\Exceptions\BackpackProRequiredException;
6
use Exception;
7
8
/**
9
 * Properties and methods used by the List operation.
10
 */
11
trait Read
12
{
13
    /**
14
     * Find and retrieve the id of the current entry.
15
     *
16
     * @return int|bool The id in the db or false.
17
     */
18
    public function getCurrentEntryId()
19
    {
20
        if ($this->entry) {
21
            return $this->entry->getKey();
22
        }
23
24
        $params = \Route::current()->parameters();
25
26
        return  // use the entity name to get the current entry
27
                // this makes sure the ID is corrent even for nested resources
28
                $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

28
                $this->/** @scrutinizer ignore-call */ 
29
                       getRequest()->input($this->entity_name) ??
Loading history...
29
                // otherwise use the next to last parameter
30
                array_values($params)[count($params) - 1] ??
31
                // otherwise return false
32
                false;
33
    }
34
35
    /**
36
     * Find and retrieve the current entry.
37
     *
38
     * @return \Illuminate\Database\Eloquent\Model|bool The row in the db or false.
39
     */
40
    public function getCurrentEntry()
41
    {
42
        $id = $this->getCurrentEntryId();
43
44
        if ($id === false) {
45
            return false;
46
        }
47
48
        return $this->getEntry($id);
49
    }
50
51
    public function getCurrentEntryWithLocale()
52
    {
53
        $entry = $this->getCurrentEntry();
54
55
        if (! $entry) {
56
            return false;
57
        }
58
59
        return $this->setLocaleOnModel($entry);
0 ignored issues
show
Bug introduced by
It seems like setLocaleOnModel() 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

59
        return $this->/** @scrutinizer ignore-call */ setLocaleOnModel($entry);
Loading history...
60
    }
61
62
    /**
63
     * Find and retrieve an entry in the database or fail.
64
     *
65
     * @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...
66
     * @return \Illuminate\Database\Eloquent\Model The row in the db.
67
     */
68
    public function getEntry($id)
69
    {
70
        if (! $this->entry) {
71
            $this->entry = $this->getModelWithCrudPanelQuery()->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...
72
            $this->entry = $this->entry->withFakes();
73
        }
74
75
        return $this->entry;
76
    }
77
78
    private function shouldUseFallbackLocale()
79
    {
80
        $fallbackRequestValue = $this->getRequest()->get('_use_fallback');
81
82
        return $fallbackRequestValue === 'true' ? true : (in_array($fallbackRequestValue, array_keys($this->model->getAvailableLocales())) ? $fallbackRequestValue : false);
83
    }
84
85
    /**
86
     * Find and retrieve an entry in the database or fail.
87
     * When found, make sure we set the Locale on it.
88
     *
89
     * @param int The id of the row in the db to fetch.
90
     * @return \Illuminate\Database\Eloquent\Model The row in the db.
91
     */
92
    public function getEntryWithLocale($id)
93
    {
94
        if (! $this->entry) {
95
            $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...
96
        }
97
98
        return $this->setLocaleOnModel($this->entry);
99
    }
100
101
    /**
102
     * Return a Model builder instance with the current crud query applied.
103
     *
104
     * @return \Illuminate\Database\Eloquent\Builder
105
     */
106
    public function getModelWithCrudPanelQuery()
107
    {
108
        return $this->model->setQuery($this->query->getQuery());
109
    }
110
111
    /**
112
     * Find and retrieve an entry in the database or fail.
113
     *
114
     * @param int The id of the row in the db to fetch.
115
     * @return \Illuminate\Database\Eloquent\Model The row in the db.
116
     */
117
    public function getEntryWithoutFakes($id)
118
    {
119
        return $this->getModelWithCrudPanelQuery()->findOrFail($id);
120
    }
121
122
    /**
123
     * Make the query JOIN all relationships used in the columns, too,
124
     * so there will be less database queries overall.
125
     */
126
    public function autoEagerLoadRelationshipColumns()
127
    {
128
        $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

128
        /** @scrutinizer ignore-call */ 
129
        $relationships = $this->getColumnsRelationships();
Loading history...
129
130
        foreach ($relationships as $relation) {
131
            if (strpos($relation, '.') !== false) {
132
                $parts = explode('.', $relation);
133
                $model = $this->model;
134
135
                // Iterate over each relation part to find the valid relations without attributes
136
                // We should eager load the relation but not the attribute
137
                foreach ($parts as $i => $part) {
138
                    try {
139
                        $model = $model->$part()->getRelated();
140
                    } catch (Exception $e) {
141
                        $relation = implode('.', array_slice($parts, 0, $i));
142
                    }
143
                }
144
            }
145
            $this->with($relation);
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

145
            $this->/** @scrutinizer ignore-call */ 
146
                   with($relation);
Loading history...
146
        }
147
    }
148
149
    /**
150
     * Get all entries from the database.
151
     *
152
     * @return array|\Illuminate\Database\Eloquent\Collection
153
     */
154
    public function getEntries()
155
    {
156
        $this->autoEagerLoadRelationshipColumns();
157
158
        $entries = $this->query->get();
159
160
        // add the fake columns for each entry
161
        foreach ($entries as $key => $entry) {
162
            $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

162
            $entry->addFakes($this->/** @scrutinizer ignore-call */ getFakeColumnsAsArray());
Loading history...
163
        }
164
165
        return $entries;
166
    }
167
168
    /**
169
     * Enable the DETAILS ROW functionality:.
170
     *
171
     * In the table view, show a plus sign next to each entry.
172
     * 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.
173
     */
174
    public function enableDetailsRow()
175
    {
176
        if (! backpack_pro()) {
177
            throw new BackpackProRequiredException('Details row');
178
        }
179
180
        $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

180
        $this->/** @scrutinizer ignore-call */ 
181
               setOperationSetting('detailsRow', true);
Loading history...
181
    }
182
183
    /**
184
     * Disable the DETAILS ROW functionality:.
185
     */
186
    public function disableDetailsRow()
187
    {
188
        $this->setOperationSetting('detailsRow', false);
189
    }
190
191
    /**
192
     * Add two more columns at the beginning of the ListEntrie table:
193
     * - one shows the checkboxes needed for bulk actions
194
     * - one is blank, in order for evenual detailsRow or expand buttons
195
     * to be in a separate column.
196
     */
197
    public function enableBulkActions()
198
    {
199
        $this->setOperationSetting('bulkActions', true);
200
    }
201
202
    /**
203
     * Remove the two columns needed for bulk actions.
204
     */
205
    public function disableBulkActions()
206
    {
207
        $this->setOperationSetting('bulkActions', false);
208
209
        $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

209
        $this->/** @scrutinizer ignore-call */ 
210
               removeColumn('bulk_actions');
Loading history...
210
    }
211
212
    /**
213
     * Set the number of rows that should be show on the list view.
214
     */
215
    public function setDefaultPageLength($value)
216
    {
217
        $this->abortIfInvalidPageLength($value);
218
219
        $this->setOperationSetting('defaultPageLength', $value);
220
    }
221
222
    /**
223
     * Get the number of rows that should be show on the list view.
224
     *
225
     * @return int
226
     */
227
    public function getDefaultPageLength()
228
    {
229
        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

229
        return $this->/** @scrutinizer ignore-call */ getOperationSetting('defaultPageLength') ?? config('backpack.crud.operations.list.defaultPageLength') ?? 25;
Loading history...
230
    }
231
232
    /**
233
     * If a custom page length was specified as default, make sure it
234
     * also show up in the page length menu.
235
     */
236
    public function addCustomPageLengthToPageLengthMenu()
237
    {
238
        $values = $this->getOperationSetting('pageLengthMenu')[0];
239
        $labels = $this->getOperationSetting('pageLengthMenu')[1];
240
241
        if (array_search($this->getDefaultPageLength(), $values) === false) {
242
            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...
243
                if ($values[$i] > $this->getDefaultPageLength() || $values[$i] === -1) {
244
                    array_splice($values, $i, 0, $this->getDefaultPageLength());
245
                    array_splice($labels, $i, 0, $this->getDefaultPageLength());
246
                    break;
247
                }
248
                if ($i === count($values) - 1) {
249
                    $values[] = $this->getDefaultPageLength();
250
                    $labels[] = $this->getDefaultPageLength();
251
                    break;
252
                }
253
            }
254
        }
255
256
        $this->setOperationSetting('pageLengthMenu', [$values, $labels]);
257
    }
258
259
    /**
260
     * Specify array of available page lengths on the list view.
261
     *
262
     * @param  array|int  $menu
263
     *
264
     * https://backpackforlaravel.com/docs/4.1/crud-cheat-sheet#page-length
265
     */
266
    public function setPageLengthMenu($menu)
267
    {
268
        if (is_array($menu)) {
269
            // start checking $menu integrity
270
            if (count($menu) !== count($menu, COUNT_RECURSIVE)) {
271
                // developer defined as setPageLengthMenu([[50, 100, 300]]) or setPageLengthMenu([[50, 100, 300],['f','h','t']])
272
                // we will apply the same labels as the values to the menu if developer didn't
273
                $this->abortIfInvalidPageLength($menu[0]);
274
275
                if (! isset($menu[1]) || ! is_array($menu[1])) {
276
                    $menu[1] = $menu[0];
277
                }
278
            } else {
279
                // developer defined setPageLengthMenu([10 => 'f', 100 => 'h', 300 => 't']) OR setPageLengthMenu([50, 100, 300])
280
                $menu = $this->buildPageLengthMenuFromArray($menu);
281
            }
282
        } else {
283
            // developer added only a single value setPageLengthMenu(10)
284
            $this->abortIfInvalidPageLength($menu);
285
286
            $menu = [[$menu], [$menu]];
287
        }
288
289
        $this->setOperationSetting('pageLengthMenu', $menu);
290
    }
291
292
    /**
293
     * Builds the menu from the given array. It works out with two different types of arrays:
294
     *  [1, 2, 3] AND [1 => 'one', 2 => 'two', 3 => 'three'].
295
     *
296
     * @param  array  $menu
297
     * @return array
298
     */
299
    private function buildPageLengthMenuFromArray($menu)
300
    {
301
        // check if the values of the array are strings, in case developer defined:
302
        // setPageLengthMenu([0 => 'f', 100 => 'h', 300 => 't'])
303
        if (count(array_filter(array_values($menu), 'is_string')) > 0) {
304
            $values = array_keys($menu);
305
            $labels = array_values($menu);
306
307
            $this->abortIfInvalidPageLength($values);
308
309
            return [$values, $labels];
310
        } else {
311
            // developer defined length as setPageLengthMenu([50, 100, 300])
312
            // we will use the same values as labels
313
            $this->abortIfInvalidPageLength($menu);
314
315
            return [$menu, $menu];
316
        }
317
    }
318
319
    /**
320
     * Get page length menu for the list view.
321
     *
322
     * @return array
323
     */
324
    public function getPageLengthMenu()
325
    {
326
        // if we have a 2D array, update all the values in the right hand array to their translated values
327
        if (isset($this->getOperationSetting('pageLengthMenu')[1]) && is_array($this->getOperationSetting('pageLengthMenu')[1])) {
328
            $aux = $this->getOperationSetting('pageLengthMenu');
329
            foreach ($this->getOperationSetting('pageLengthMenu')[1] as $key => $val) {
330
                $aux[1][$key] = trans($val);
331
            }
332
            $this->setOperationSetting('pageLengthMenu', $aux);
333
        }
334
        $this->addCustomPageLengthToPageLengthMenu();
335
336
        return $this->getOperationSetting('pageLengthMenu');
337
    }
338
339
    /**
340
     * Checks if the provided PageLength segment is valid.
341
     *
342
     * @param  array|int  $value
343
     * @return void
344
     */
345
    private function abortIfInvalidPageLength($value)
346
    {
347
        if ($value === 0 || (is_array($value) && in_array(0, $value))) {
348
            abort(500, 'You should not use 0 as a key in paginator. If you are looking for "ALL" option, use -1 instead.');
349
        }
350
    }
351
352
    /*
353
    |--------------------------------------------------------------------------
354
    |                                EXPORT BUTTONS
355
    |--------------------------------------------------------------------------
356
    */
357
358
    /**
359
     * Tell the list view to show the DataTables export buttons.
360
     */
361
    public function enableExportButtons()
362
    {
363
        if (! backpack_pro()) {
364
            throw new BackpackProRequiredException('Export buttons');
365
        }
366
367
        $this->setOperationSetting('exportButtons', true);
368
        $this->setOperationSetting('showTableColumnPicker', true);
369
        $this->setOperationSetting('showExportButton', true);
370
    }
371
372
    /**
373
     * Check if export buttons are enabled for the table view.
374
     *
375
     * @return bool
376
     */
377
    public function exportButtons()
378
    {
379
        return $this->getOperationSetting('exportButtons') ?? false;
380
    }
381
}
382