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

Read::getRelationshipsFromCrudObjects()   B
last analyzed

Complexity

Conditions 10
Paths 45

Size

Total Lines 46
Code Lines 23

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 23
nc 45
nop 1
dl 0
loc 46
rs 7.6666
c 0
b 0
f 0

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\Facades\Route;
8
use Illuminate\Support\Str;
9
10
/**
11
 * Properties and methods used by the List operation.
12
 */
13
trait Read
14
{
15
    /**
16
     * Find and retrieve the id of the current entry.
17
     *
18
     * @return int|string|bool The id in the db or false.
19
     */
20
    public function getCurrentEntryId()
21
    {
22
        if ($this->entry) {
23
            return $this->entry->getKey();
24
        }
25
26
        $params = Route::current()?->parameters() ?? [];
27
28
        return  // use the entity name to get the current entry
29
                // this makes sure the ID is current even for nested resources
30
                $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

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

61
        return $this->/** @scrutinizer ignore-call */ setLocaleOnModel($entry);
Loading history...
62
    }
63
64
    /**
65
     * Find and retrieve an entry in the database or fail.
66
     *
67
     * @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...
68
     * @return \Illuminate\Database\Eloquent\Model The row in the db.
69
     */
70
    public function getEntry($id)
71
    {
72
        if (! $this->entry) {
73
            if ($this->getOperationSetting('eagerLoadRelationships')) {
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

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

133
        $this->/** @scrutinizer ignore-call */ 
134
               with($this->getRelationshipsFromCrudObjects('columns'));
Loading history...
134
    }
135
136
    public function eagerLoadRelationshipFields()
137
    {
138
        $this->with($this->getRelationshipsFromCrudObjects('fields'));
139
    }
140
141
    private function getRelationshipsFromCrudObjects(string $crudObjectType): array
142
    {
143
        $crudObjects = $this->{$crudObjectType}();
144
145
        $relationStrings = [];
146
147
        foreach ($crudObjects as $crudObjectName => $attributes) {
148
            $relationString = isset($attributes['entity']) && $attributes['entity'] !== false ? $attributes['entity'] : '';
149
150
            if (! $relationString) {
151
                continue;
152
            }
153
154
            if (strpos($attributes['entity'], '.') === false) {
155
                $relationStrings[] = $relationString;
156
            }
157
158
            $relationAttribute = $attributes['attribute'] ?? null;
159
160
            if ($relationAttribute) {
161
                $relationString = Str::endsWith($relationString, $relationAttribute) ? Str::beforeLast($relationString, '.') : $relationString;
162
163
                $relationStrings[] = $relationString;
164
165
                continue;
166
            }
167
168
            $parts = explode('.', $relationString);
169
            $model = $this->model;
170
171
            // Iterate over each relation part to find the valid relations without attributes
172
            // We should eager load the relation but not the attribute
173
            foreach ($parts as $i => $part) {
174
                try {
175
                    $model = $model->$part()->getRelated();
176
                } catch (Exception $e) {
177
                    $relationString = implode('.', array_slice($parts, 0, $i));
178
                }
179
            }
180
181
            $relationStrings[] = $relationString;
182
183
            continue;
184
        }
185
186
        return array_unique($relationStrings);
187
    }
188
189
    /**
190
     * Get all entries from the database.
191
     *
192
     * @return array|\Illuminate\Database\Eloquent\Collection
193
     */
194
    public function getEntries()
195
    {
196
        $this->autoEagerLoadRelationshipColumns();
197
198
        $entries = $this->query->get();
199
200
        // add the fake columns for each entry
201
        foreach ($entries as $key => $entry) {
202
            $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

202
            $entry->addFakes($this->/** @scrutinizer ignore-call */ getFakeColumnsAsArray());
Loading history...
203
        }
204
205
        return $entries;
206
    }
207
208
    /**
209
     * Enable the DETAILS ROW functionality:.
210
     *
211
     * In the table view, show a plus sign next to each entry.
212
     * 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.
213
     */
214
    public function enableDetailsRow()
215
    {
216
        if (! backpack_pro()) {
217
            throw new BackpackProRequiredException('Details row');
218
        }
219
220
        $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

220
        $this->/** @scrutinizer ignore-call */ 
221
               setOperationSetting('detailsRow', true);
Loading history...
221
    }
222
223
    /**
224
     * Disable the DETAILS ROW functionality:.
225
     */
226
    public function disableDetailsRow()
227
    {
228
        $this->setOperationSetting('detailsRow', false);
229
    }
230
231
    /**
232
     * Add two more columns at the beginning of the ListEntries table:
233
     * - one shows the checkboxes needed for bulk actions
234
     * - one is blank, in order for evenual detailsRow or expand buttons
235
     * to be in a separate column.
236
     */
237
    public function enableBulkActions()
238
    {
239
        $this->setOperationSetting('bulkActions', true);
240
    }
241
242
    /**
243
     * Remove the two columns needed for bulk actions.
244
     */
245
    public function disableBulkActions()
246
    {
247
        $this->setOperationSetting('bulkActions', false);
248
249
        $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

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