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 — master (#3606)
by Cristian
10:52
created

ColumnsProtectedMethods   B

Complexity

Total Complexity 43

Size/Duplication

Total Lines 262
Duplicated Lines 0 %

Importance

Changes 6
Bugs 2 Features 0
Metric Value
eloc 77
c 6
b 2
f 0
dl 0
loc 262
rs 8.96
wmc 43

11 Methods

Rating   Name   Duplication   Size   Complexity  
A makeSureColumnHasLabel() 0 7 2
A makeSureColumnHasPriority() 0 8 2
A makeSureColumnHasKey() 0 7 2
A makeSureColumnHasWrapper() 0 7 2
A addColumnToOperationSettings() 0 6 1
A makeSureColumnHasName() 0 11 4
A makeSureColumnHasType() 0 13 5
C makeSureColumnHasEntity() 0 71 14
A hasDatabaseColumn() 0 15 3
A moveColumn() 0 17 4
A makeSureColumnHasModel() 0 9 4

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 Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
8
trait ColumnsProtectedMethods
9
{
10
    /**
11
     * Add a column to the current operation, using the Setting API.
12
     *
13
     * @param  array  $column  Column definition array.
14
     */
15
    protected function addColumnToOperationSettings($column)
16
    {
17
        $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

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

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

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

69
            $column['label'] = mb_ucfirst($this->/** @scrutinizer ignore-call */ makeLabel($column['name']));
Loading history...
70
        }
71
72
        return $column;
73
    }
74
75
    /**
76
     * If a column definition is missing the type, set a default.
77
     *
78
     * @param  array  $column  Column definition array.
79
     * @return array Column definition array with type.
80
     */
81
    protected function makeSureColumnHasType($column)
82
    {
83
        $could_be_relation = isset($column['entity']) && $column['entity'] !== false;
84
85
        if (! isset($column['type']) && $could_be_relation) {
86
            $column['type'] = 'relationship';
87
        }
88
89
        if (! isset($column['type'])) {
90
            $column['type'] = 'text';
91
        }
92
93
        return $column;
94
    }
95
96
    /**
97
     * If a column definition is missing the key, set the default.
98
     * The key is used when storing all columns using the Settings API,
99
     * it is used as the "key" of the associative array that holds all columns.
100
     *
101
     * @param  array  $column  Column definition array.
102
     * @return array Column definition array with key.
103
     */
104
    protected function makeSureColumnHasKey($column)
105
    {
106
        if (! isset($column['key'])) {
107
            $column['key'] = str_replace('.', '__', $column['name']);
108
        }
109
110
        return $column;
111
    }
112
113
    /**
114
     * If a column definition is missing the wrapper element, set the default (empty).
115
     * The wrapper is the HTML element that wrappes around the column text.
116
     * By defining this array a developer can wrap the text into an anchor (link),
117
     * span, div or whatever they want.
118
     *
119
     * @param  array  $column  Column definition array.
120
     * @return array Column definition array with wrapper.
121
     */
122
    protected function makeSureColumnHasWrapper($column)
123
    {
124
        if (! isset($column['wrapper'])) {
125
            $column['wrapper'] = [];
126
        }
127
128
        return $column;
129
    }
130
131
    protected function makeSureColumnHasEntity($column)
132
    {
133
        if (isset($column['entity'])) {
134
            return $column;
135
        }
136
137
        // if the name is an array it's definitely not a relationship
138
        if (is_array($column['name'])) {
139
            return $column;
140
        }
141
142
        // if the name is dot notation it might be a relationship
143
        if (strpos($column['name'], '.') !== false) {
144
            $possibleMethodName = Str::before($column['name'], '.');
145
146
            // if the first part of the string exists as method,
147
            // it is a relationship
148
            if (method_exists($this->model, $possibleMethodName)) {
149
150
                // if it has parameters it's not a relation method.
151
                $column['entity'] = $this->modelMethodHasParameters($this->model, $possibleMethodName) ? false : $column['name'];
0 ignored issues
show
Bug introduced by
It seems like modelMethodHasParameters() 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

151
                $column['entity'] = $this->/** @scrutinizer ignore-call */ modelMethodHasParameters($this->model, $possibleMethodName) ? false : $column['name'];
Loading history...
152
153
                $parts = explode('.', $column['entity']);
154
155
                $attribute_in_relation = false;
156
157
                $model = $this->model;
158
159
                // here we are going to iterate through all relation parts to check
160
                // if the attribute is present in the relation string.
161
                foreach ($parts as $i => $part) {
162
                    try {
163
                        $model = $model->$part()->getRelated();
164
                    } catch (\Exception $e) {
165
                        $attribute_in_relation = true;
166
                    }
167
                }
168
                // if the user setup the attribute in relation string, we are not going to infer that attribute from model
169
                // instead we get the defined attribute by the user.
170
                if ($attribute_in_relation) {
171
                    $column['attribute'] = $column['attribute'] ?? end($parts);
172
                }
173
174
                return $column;
175
            }
176
        }
177
178
        // if there's a method on the model with this name
179
        if (method_exists($this->model, $column['name'])) {
180
181
            // if it has parameters it's not a relation method.
182
            $column['entity'] = $this->modelMethodHasParameters($this->model, $column['name']) ? false : $column['name'];
183
184
            return $column;
185
        }
186
187
        // if the name ends with _id and that method exists,
188
        // we can probably use it as an entity
189
        if (Str::endsWith($column['name'], '_id')) {
190
            $possibleMethodName = Str::replaceLast('_id', '', $column['name']);
191
192
            if (method_exists($this->model, $possibleMethodName)) {
193
194
                // if it has parameters it's not a relation method.
195
                $column['entity'] = $this->modelMethodHasParameters($this->model, $possibleMethodName) ? false : $possibleMethodName;
196
197
                return $column;
198
            }
199
        }
200
201
        return $column;
202
    }
203
204
    /**
205
     * If an entity has been defined for the column, but no model,
206
     * determine the model from that relationship.
207
     *
208
     * @param  array  $column  Column definition array.
209
     * @return array Column definition array with model.
210
     */
211
    protected function makeSureColumnHasModel($column)
212
    {
213
        // if this is a relation type field and no corresponding model was specified,
214
        // get it from the relation method defined in the main model
215
        if (isset($column['entity']) && $column['entity'] !== false && ! isset($column['model'])) {
216
            $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

216
            /** @scrutinizer ignore-call */ 
217
            $column['model'] = $this->getRelationModel($column['entity']);
Loading history...
217
        }
218
219
        return $column;
220
    }
221
222
    /**
223
     * Move the most recently added column before or after the given target column. Default is before.
224
     *
225
     * @param  string|array  $targetColumn  The target column name or array.
226
     * @param  bool  $before  If true, the column will be moved before the target column, otherwise it will be moved after it.
227
     */
228
    protected function moveColumn($targetColumn, $before = true)
229
    {
230
        // TODO: this and the moveField method from the Fields trait should be refactored into a single method and moved
231
        //       into a common class
232
        $targetColumnName = is_array($targetColumn) ? $targetColumn['name'] : $targetColumn;
233
        $columnsArray = $this->columns();
234
235
        if (array_key_exists($targetColumnName, $columnsArray)) {
236
            $targetColumnPosition = $before ? array_search($targetColumnName, array_keys($columnsArray)) :
237
                array_search($targetColumnName, array_keys($columnsArray)) + 1;
238
239
            $element = array_pop($columnsArray);
240
            $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

240
            $beginningPart = array_slice($columnsArray, 0, /** @scrutinizer ignore-type */ $targetColumnPosition, true);
Loading history...
241
            $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

241
            $endingArrayPart = array_slice($columnsArray, /** @scrutinizer ignore-type */ $targetColumnPosition, null, true);
Loading history...
242
243
            $columnsArray = array_merge($beginningPart, [$element['name'] => $element], $endingArrayPart);
244
            $this->setOperationSetting('columns', $columnsArray);
245
        }
246
    }
247
248
    /**
249
     * Check if the column exists in the database, as a DB column.
250
     *
251
     * @param  string  $table
252
     * @param  string  $name
253
     * @return bool
254
     */
255
    protected function hasDatabaseColumn($table, $name)
256
    {
257
        static $cache = [];
258
259
        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

259
        if (! $this->/** @scrutinizer ignore-call */ driverIsSql()) {
Loading history...
260
            return true;
261
        }
262
263
        if (isset($cache[$table])) {
264
            $columns = $cache[$table];
265
        } else {
266
            $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

266
            $columns = $cache[$table] = $this->/** @scrutinizer ignore-call */ getSchema()->getColumnListing($table);
Loading history...
267
        }
268
269
        return in_array($name, $columns);
270
    }
271
}
272