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
Pull Request — main (#5270)
by Cristian
27:17 queued 12:20
created

ColumnsProtectedMethods   F

Complexity

Total Complexity 64

Size/Duplication

Total Lines 332
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 114
dl 0
loc 332
rs 3.28
c 0
b 0
f 0
wmc 64

14 Methods

Rating   Name   Duplication   Size   Complexity  
A makeSureColumnHasLabel() 0 7 2
B makeSureColumnHasEntity() 0 54 11
A prepareAttributesAndAddColumn() 0 8 1
A hasDatabaseColumn() 0 15 3
D makeSureColumnHasType() 0 60 24
A moveColumn() 0 17 4
A addColumnToOperationSettings() 0 6 1
A makeSureColumnHasModel() 0 9 4
A makeSureColumnHasRelationType() 0 7 3
A makeSureColumnHasAttribute() 0 3 1
A makeSureColumnHasName() 0 11 4
A makeSureColumnHasPriority() 0 8 2
A makeSureColumnHasKey() 0 7 2
A makeSureColumnHasWrapper() 0 7 2

How to fix   Complexity   

Complex Class

Complex classes like ColumnsProtectedMethods 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 ColumnsProtectedMethods, 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\Library\CrudPanel\CrudColumn;
6
use Illuminate\Support\Arr;
7
use Illuminate\Support\Str;
8
9
trait ColumnsProtectedMethods
10
{
11
    /**
12
     * Add a column to the current operation, using the Setting API.
13
     *
14
     * @param  array  $column  Column definition array.
15
     */
16
    protected function addColumnToOperationSettings($column)
17
    {
18
        $allColumns = $this->columns();
0 ignored issues
show
Bug introduced by
It seems like columns() 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

18
        /** @scrutinizer ignore-call */ 
19
        $allColumns = $this->columns();
Loading history...
19
        $allColumns = Arr::add($allColumns, $column['key'], $column);
20
21
        $this->setOperationSetting('columns', $allColumns);
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

21
        $this->/** @scrutinizer ignore-call */ 
22
               setOperationSetting('columns', $allColumns);
Loading history...
22
    }
23
24
    /**
25
     * If a column priority has not been defined, provide a default one.
26
     *
27
     * @param  array  $column  Column definition array.
28
     * @return array Proper array defining the column.
29
     */
30
    protected function makeSureColumnHasPriority($column)
31
    {
32
        $columns_count = $this->countColumnsWithoutActions();
0 ignored issues
show
Bug introduced by
It seems like countColumnsWithoutActions() 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

32
        /** @scrutinizer ignore-call */ 
33
        $columns_count = $this->countColumnsWithoutActions();
Loading history...
33
        $assumed_priority = $columns_count ? $columns_count : 0;
34
35
        $column['priority'] = $column['priority'] ?? $assumed_priority;
36
37
        return $column;
38
    }
39
40
    /**
41
     * If the field definition array is actually a string, it means the programmer was lazy
42
     * and has only passed the name of the column. Turn that into a proper array.
43
     *
44
     * @param  array  $column  Column definition array.
45
     * @return array Proper array defining the column.
46
     */
47
    protected function makeSureColumnHasName($column)
48
    {
49
        if (is_string($column)) {
0 ignored issues
show
introduced by
The condition is_string($column) is always false.
Loading history...
50
            $column = ['name' => $column];
51
        }
52
53
        if (is_array($column) && ! isset($column['name'])) {
54
            $column['name'] = 'anonymous_column_'.Str::random(5);
55
        }
56
57
        return $column;
58
    }
59
60
    /**
61
     * If a column array is missing the "label" attribute, an ugly error would be show.
62
     * So we add the field Name as a label - it's better than nothing.
63
     *
64
     * @param  array  $column  Column definition array.
65
     * @return array Proper array defining the column.
66
     */
67
    protected function makeSureColumnHasLabel($column)
68
    {
69
        if (! isset($column['label'])) {
70
            $column['label'] = mb_ucfirst($this->makeLabel($column['name']));
0 ignored issues
show
Bug introduced by
It seems like makeLabel() 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

70
            $column['label'] = mb_ucfirst($this->/** @scrutinizer ignore-call */ makeLabel($column['name']));
Loading history...
71
        }
72
73
        return $column;
74
    }
75
76
    /**
77
     * If a column definition is missing the type, set a default.
78
     *
79
     * @param  array  $column  Column definition array.
80
     * @return array Column definition array with type.
81
     */
82
    protected function makeSureColumnHasType($column)
83
    {
84
        // Do not alter type if it has been set by developer
85
        if (isset($column['type'])) {
86
            return $column;
87
        }
88
89
        // Set text as default column type
90
        $column['type'] = 'text';
91
92
        if (method_exists($this->model, 'translationEnabledForModel') && $this->model->translationEnabledForModel() && array_key_exists($column['name'], $this->model->getTranslations())) {
93
            return $column;
94
        }
95
96
        $could_be_relation = Arr::get($column, 'entity', false) !== false;
97
98
        if ($could_be_relation) {
99
            $column['type'] = $this->inferFieldTypeFromRelationType($column['relation_type']);
0 ignored issues
show
Bug introduced by
It seems like inferFieldTypeFromRelationType() 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

99
            /** @scrutinizer ignore-call */ 
100
            $column['type'] = $this->inferFieldTypeFromRelationType($column['relation_type']);
Loading history...
100
        }
101
102
        if (in_array($column['name'], $this->model->getDates())) {
103
            $column['type'] = 'datetime';
104
        }
105
106
        if ($this->model->hasCast($column['name'])) {
107
            $attributeType = $this->model->getCasts()[$column['name']];
108
109
            switch ($attributeType) {
110
                case 'array':
111
                case 'encrypted:array':
112
                case 'collection':
113
                case 'encrypted:collection':
114
                    $column['type'] = 'array';
115
                    break;
116
                case 'json':
117
                case 'object':
118
                    $column['type'] = 'json';
119
                    break;
120
                case 'bool':
121
                case 'boolean':
122
                    $column['type'] = 'check';
123
                    break;
124
                case 'date':
125
                    $column['type'] = 'date';
126
                    break;
127
                case 'datetime':
128
                    $column['type'] = 'datetime';
129
                    break;
130
                case 'double':
131
                case 'float':
132
                case 'int':
133
                case 'integer':
134
                case 'real':
135
                case 'timestamp':
136
                    $column['type'] = 'number';
137
                    break;
138
            }
139
        }
140
141
        return $column;
142
    }
143
144
    /**
145
     * If a column definition is missing the key, set the default.
146
     * The key is used when storing all columns using the Settings API,
147
     * it is used as the "key" of the associative array that holds all columns.
148
     *
149
     * @param  array  $column  Column definition array.
150
     * @return array Column definition array with key.
151
     */
152
    protected function makeSureColumnHasKey($column)
153
    {
154
        if (! isset($column['key'])) {
155
            $column['key'] = str_replace('.', '__', $column['name']);
156
        }
157
158
        return $column;
159
    }
160
161
    /**
162
     * If a column definition is missing the wrapper element, set the default (empty).
163
     * The wrapper is the HTML element that wrappes around the column text.
164
     * By defining this array a developer can wrap the text into an anchor (link),
165
     * span, div or whatever they want.
166
     *
167
     * @param  array  $column  Column definition array.
168
     * @return array Column definition array with wrapper.
169
     */
170
    protected function makeSureColumnHasWrapper($column)
171
    {
172
        if (! isset($column['wrapper'])) {
173
            $column['wrapper'] = [];
174
        }
175
176
        return $column;
177
    }
178
179
    protected function makeSureColumnHasEntity($column)
180
    {
181
        if (isset($column['entity'])) {
182
            return $column;
183
        }
184
185
        // if the name is an array it's definitely not a relationship
186
        if (is_array($column['name'])) {
187
            return $column;
188
        }
189
190
        // if the name is dot notation it might be a relationship
191
        if (strpos($column['name'], '.') !== false) {
192
            $possibleMethodName = Str::before($column['name'], '.');
193
194
            // if the first part of the string exists as method in the model
195
            if (method_exists($this->model, $possibleMethodName)) {
196
                // check model method for possibility of being a relationship
197
                $column['entity'] = $this->modelMethodIsRelationship($this->model, $possibleMethodName) ? $column['name'] : false;
0 ignored issues
show
Bug introduced by
It seems like modelMethodIsRelationship() 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

197
                $column['entity'] = $this->/** @scrutinizer ignore-call */ modelMethodIsRelationship($this->model, $possibleMethodName) ? $column['name'] : false;
Loading history...
198
199
                if ($column['entity']) {
200
                    // if the user setup the attribute in relation string, we are not going to infer that attribute from model
201
                    // instead we get the defined attribute by the user.
202
                    if ($this->isAttributeInRelationString($column)) {
0 ignored issues
show
Bug introduced by
It seems like isAttributeInRelationString() 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
                    if ($this->/** @scrutinizer ignore-call */ isAttributeInRelationString($column)) {
Loading history...
203
                        $column['attribute'] = $column['attribute'] ?? Str::afterLast($column['entity'], '.');
204
                    }
205
                }
206
207
                return $column;
208
            }
209
        }
210
211
        // if there's a method on the model with this name
212
        if (method_exists($this->model, $column['name'])) {
213
            // check model method for possibility of being a relationship
214
            $column['entity'] = $this->modelMethodIsRelationship($this->model, $column['name']);
215
216
            return $column;
217
        }
218
219
        // if the name ends with _id and that method exists,
220
        // we can probably use it as an entity
221
        if (Str::endsWith($column['name'], '_id')) {
222
            $possibleMethodName = Str::replaceLast('_id', '', $column['name']);
223
224
            if (method_exists($this->model, $possibleMethodName)) {
225
                // check model method for possibility of being a relationship
226
                $column['entity'] = $this->modelMethodIsRelationship($this->model, $possibleMethodName);
227
228
                return $column;
229
            }
230
        }
231
232
        return $column;
233
    }
234
235
    /**
236
     * Infer the attribute for the column when needed.
237
     *
238
     * @param  array  $column
239
     * @return void
240
     */
241
    protected function makeSureColumnHasAttribute(array $column)
242
    {
243
        return $this->makeSureFieldHasAttribute($column);
0 ignored issues
show
Bug introduced by
It seems like makeSureFieldHasAttribute() 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

243
        return $this->/** @scrutinizer ignore-call */ makeSureFieldHasAttribute($column);
Loading history...
244
    }
245
246
    /**
247
     * If an entity has been defined for the column, but no model,
248
     * determine the model from that relationship.
249
     *
250
     * @param  array  $column  Column definition array.
251
     * @return array Column definition array with model.
252
     */
253
    protected function makeSureColumnHasModel($column)
254
    {
255
        // if this is a relation type field and no corresponding model was specified,
256
        // get it from the relation method defined in the main model
257
        if (isset($column['entity']) && $column['entity'] !== false && ! isset($column['model'])) {
258
            $column['model'] = $this->getRelationModel($column['entity']);
0 ignored issues
show
Bug introduced by
It seems like getRelationModel() 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

258
            /** @scrutinizer ignore-call */ 
259
            $column['model'] = $this->getRelationModel($column['entity']);
Loading history...
259
        }
260
261
        return $column;
262
    }
263
264
    /**
265
     * If an entity has been defined for the column, but no relation type,
266
     * determine the relation type from that relationship.
267
     *
268
     * @param  array  $column  Column definition array.
269
     * @return array Column definition array with model.
270
     */
271
    protected function makeSureColumnHasRelationType($column)
272
    {
273
        if (isset($column['entity']) && $column['entity'] !== false) {
274
            $column['relation_type'] = $column['relation_type'] ?? $this->inferRelationTypeFromRelationship($column);
0 ignored issues
show
Bug introduced by
It seems like inferRelationTypeFromRelationship() 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

274
            $column['relation_type'] = $column['relation_type'] ?? $this->/** @scrutinizer ignore-call */ inferRelationTypeFromRelationship($column);
Loading history...
275
        }
276
277
        return $column;
278
    }
279
280
    /**
281
     * Move the most recently added column before or after the given target column. Default is before.
282
     *
283
     * @param  string|array  $targetColumn  The target column name or array.
284
     * @param  bool  $before  If true, the column will be moved before the target column, otherwise it will be moved after it.
285
     */
286
    protected function moveColumn($targetColumn, $before = true)
287
    {
288
        // TODO: this and the moveField method from the Fields trait should be refactored into a single method and moved
289
        //       into a common class
290
        $targetColumnName = is_array($targetColumn) ? $targetColumn['name'] : $targetColumn;
291
        $columnsArray = $this->columns();
292
293
        if (array_key_exists($targetColumnName, $columnsArray)) {
294
            $targetColumnPosition = $before ? array_search($targetColumnName, array_keys($columnsArray)) :
295
                array_search($targetColumnName, array_keys($columnsArray)) + 1;
296
297
            $element = array_pop($columnsArray);
298
            $beginningPart = array_slice($columnsArray, 0, $targetColumnPosition, true);
0 ignored issues
show
Bug introduced by
It seems like $targetColumnPosition can also be of type string; however, parameter $length of array_slice() does only seem to accept integer|null, maybe add an additional type check? ( Ignorable by Annotation )

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

298
            $beginningPart = array_slice($columnsArray, 0, /** @scrutinizer ignore-type */ $targetColumnPosition, true);
Loading history...
299
            $endingArrayPart = array_slice($columnsArray, $targetColumnPosition, null, true);
0 ignored issues
show
Bug introduced by
It seems like $targetColumnPosition can also be of type string; however, parameter $offset of array_slice() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

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

299
            $endingArrayPart = array_slice($columnsArray, /** @scrutinizer ignore-type */ $targetColumnPosition, null, true);
Loading history...
300
301
            $columnsArray = array_merge($beginningPart, [$element['name'] => $element], $endingArrayPart);
302
            $this->setOperationSetting('columns', $columnsArray);
303
        }
304
    }
305
306
    /**
307
     * Check if the column exists in the database, as a DB column.
308
     *
309
     * @param  string  $table
310
     * @param  string  $name
311
     * @return bool
312
     */
313
    protected function hasDatabaseColumn($table, $name)
314
    {
315
        static $cache = [];
316
317
        if (! $this->driverIsSql()) {
0 ignored issues
show
Bug introduced by
It seems like driverIsSql() 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

317
        if (! $this->/** @scrutinizer ignore-call */ driverIsSql()) {
Loading history...
318
            return true;
319
        }
320
321
        if (isset($cache[$table])) {
322
            $columns = $cache[$table];
323
        } else {
324
            $columns = $cache[$table] = $this->getSchema()->getColumnListing($table);
0 ignored issues
show
Bug introduced by
It seems like getSchema() 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

324
            $columns = $cache[$table] = $this->/** @scrutinizer ignore-call */ getSchema()->getColumnListing($table);
Loading history...
325
        }
326
327
        return in_array($name, $columns);
328
    }
329
330
    /**
331
     * Prepare the column attributes and add it to operation settings.
332
     */
333
    private function prepareAttributesAndAddColumn(array|string $column): CrudColumn
334
    {
335
        $column = $this->makeSureColumnHasNeededAttributes($column);
0 ignored issues
show
Bug introduced by
The method makeSureColumnHasNeededAttributes() does not exist on Backpack\CRUD\app\Librar...ColumnsProtectedMethods. Did you maybe mean makeSureColumnHasAttribute()? ( Ignorable by Annotation )

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

335
        /** @scrutinizer ignore-call */ 
336
        $column = $this->makeSureColumnHasNeededAttributes($column);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
336
        $this->addColumnToOperationSettings($column);
337
338
        $column = (new CrudColumn($column['name']))->callRegisteredAttributeMacros();
339
340
        return $column;
341
    }
342
}
343