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 Setup Failed
Pull Request — master (#4161)
by
unknown
15:22
created

FieldsProtectedMethods::makeSureFieldHasEntity()   B

Complexity

Conditions 11
Paths 20

Size

Total Lines 44
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

Changes 4
Bugs 1 Features 1
Metric Value
cc 11
eloc 18
c 4
b 1
f 1
nc 20
nop 1
dl 0
loc 44
rs 7.3166

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 Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
8
trait FieldsProtectedMethods
9
{
10
    /**
11
     * If field has entity we want to get the relation type from it.
12
     *
13
     * @param  array  $field
14
     * @return array
15
     */
16
    public function makeSureFieldHasRelationType($field)
17
    {
18
        $field['relation_type'] = $field['relation_type'] ?? $this->inferRelationTypeFromRelationship($field);
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

18
        $field['relation_type'] = $field['relation_type'] ?? $this->/** @scrutinizer ignore-call */ inferRelationTypeFromRelationship($field);
Loading history...
19
20
        return $field;
21
    }
22
23
    /**
24
     * If field has entity we want to make sure it also has a model for that relation.
25
     *
26
     * @param  array  $field
27
     * @return array
28
     */
29
    public function makeSureFieldHasModel($field)
30
    {
31
        $field['model'] = $field['model'] ?? $this->inferFieldModelFromRelationship($field);
0 ignored issues
show
Bug introduced by
It seems like inferFieldModelFromRelationship() 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
        $field['model'] = $field['model'] ?? $this->/** @scrutinizer ignore-call */ inferFieldModelFromRelationship($field);
Loading history...
32
33
        return $field;
34
    }
35
36
    /**
37
     * Based on relation type we can guess if pivot is set.
38
     *
39
     * @param  array  $field
40
     * @return array
41
     */
42
    public function makeSureFieldHasPivot($field)
43
    {
44
        $field['pivot'] = $field['pivot'] ?? $this->guessIfFieldHasPivotFromRelationType($field['relation_type']);
0 ignored issues
show
Bug introduced by
It seems like guessIfFieldHasPivotFromRelationType() 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

44
        $field['pivot'] = $field['pivot'] ?? $this->/** @scrutinizer ignore-call */ guessIfFieldHasPivotFromRelationType($field['relation_type']);
Loading history...
45
46
        return $field;
47
    }
48
49
    /**
50
     * Based on relation type we can try to guess if it is a multiple field.
51
     *
52
     * @param  array  $field
53
     * @return array
54
     */
55
    public function makeSureFieldHasMultiple($field)
56
    {
57
        if (isset($field['relation_type'])) {
58
            $field['multiple'] = $field['multiple'] ?? $this->guessIfFieldHasMultipleFromRelationType($field['relation_type']);
0 ignored issues
show
Bug introduced by
It seems like guessIfFieldHasMultipleFromRelationType() 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

58
            $field['multiple'] = $field['multiple'] ?? $this->/** @scrutinizer ignore-call */ guessIfFieldHasMultipleFromRelationType($field['relation_type']);
Loading history...
59
        }
60
61
        return $field;
62
    }
63
64
    /**
65
     * In case field name is dot notation we want to convert it to a valid HTML array field name for validation purposes.
66
     *
67
     * @param  array  $field
68
     * @return array
69
     */
70
    public function overwriteFieldNameFromDotNotationToArray($field)
71
    {
72
        if (! is_array($field['name']) && strpos($field['name'], '.') !== false) {
73
            $entity_array = explode('.', $field['name']);
74
            $name_string = '';
75
76
            foreach ($entity_array as $key => $array_entity) {
77
                $name_string .= ($key == 0) ? $array_entity : '['.$array_entity.']';
78
            }
79
80
            $field['name'] = $name_string;
81
        }
82
83
        return $field;
84
    }
85
86
    /**
87
     * Run the field name overwrite in multiple fields.
88
     *
89
     * @param  array  $fields
90
     * @return array
91
     */
92
    public function overwriteFieldNamesFromDotNotationToArray($fields)
93
    {
94
        foreach ($fields as $key => $field) {
95
            $fields[$key] = $this->overwriteFieldNameFromDotNotationToArray($field);
96
        }
97
98
        return $fields;
99
    }
100
101
    /**
102
     * If the field_definition_array array is a string, it means the programmer was lazy
103
     * and has only passed the name of the field. Turn that into a proper array.
104
     *
105
     * @param  string|array  $field  The field definition array (or string).
106
     * @return array
107
     */
108
    protected function makeSureFieldHasName($field)
109
    {
110
        if (is_string($field)) {
111
            return ['name' => $field];
112
        }
113
114
        if (is_array($field) && ! isset($field['name'])) {
115
            abort(500, 'All fields must have their name defined');
116
        }
117
118
        return $field;
119
    }
120
121
    /**
122
     * If entity is not present, but it looks like the field SHOULD be a relationship field,
123
     * try to determine the method on the model that defines the relationship, and pass it to
124
     * the field as 'entity'.
125
     *
126
     * @param  array  $field
127
     * @return array
128
     */
129
    protected function makeSureFieldHasEntity($field)
130
    {
131
        $model = isset($field['baseModel']) ? app($field['baseModel']) : $this->model;
132
133
        if (isset($field['entity'])) {
134
            return $field;
135
        }
136
137
        // if the name is an array it's definitely not a relationship
138
        if (is_array($field['name'])) {
139
            return $field;
140
        }
141
142
        //if the name is dot notation we are sure it's a relationship
143
        if (strpos($field['name'], '.') !== false) {
144
            $possibleMethodName = Str::of($field['name'])->before('.');
145
            // if it has parameters it's not a relation method.
146
            $field['entity'] = $this->modelMethodHasParameters($model, $possibleMethodName) ? false : $field['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

146
            $field['entity'] = $this->/** @scrutinizer ignore-call */ modelMethodHasParameters($model, $possibleMethodName) ? false : $field['name'];
Loading history...
147
148
            return $field;
149
        }
150
151
        // if there's a method on the model with this name
152
        if (method_exists($model, $field['name'])) {
153
            // if it has parameters it's not a relation method.
154
            $field['entity'] = $this->modelMethodHasParameters($model, $field['name']) ? false : $field['name'];
155
156
            return $field;
157
        }
158
159
        // if the name ends with _id and that method exists,
160
        // we can probably use it as an entity
161
        if (Str::endsWith($field['name'], '_id')) {
162
            $possibleMethodName = Str::replaceLast('_id', '', $field['name']);
163
164
            if (method_exists($model, $possibleMethodName)) {
165
                // if it has parameters it's not a relation method.
166
                $field['entity'] = $this->modelMethodHasParameters($model, $possibleMethodName) ? false : $possibleMethodName;
167
168
                return $field;
169
            }
170
        }
171
172
        return $field;
173
    }
174
175
    protected function makeSureFieldHasAttribute($field)
176
    {
177
        // if there's a model defined, but no attribute
178
        // guess an attribute using the identifiableAttribute functionality in CrudTrait
179
        if (isset($field['model']) && ! isset($field['attribute']) && method_exists($field['model'], 'identifiableAttribute')) {
180
            $field['attribute'] = call_user_func([(new $field['model']()), 'identifiableAttribute']);
181
        }
182
183
        return $field;
184
    }
185
186
    /**
187
     * Set the label of a field, if it's missing, by capitalizing the name and replacing
188
     * underscores with spaces.
189
     *
190
     * @param  array  $field  Field definition array.
191
     * @return array Field definition array that contains label too.
192
     */
193
    protected function makeSureFieldHasLabel($field)
194
    {
195
        if (! isset($field['label'])) {
196
            $name = is_array($field['name']) ? $field['name'][0] : $field['name'];
197
            $name = str_replace('_id', '', $name);
198
            $field['label'] = mb_ucfirst(str_replace('_', ' ', $name));
199
        }
200
201
        return $field;
202
    }
203
204
    /**
205
     * Set the type of a field, if it's missing, by inferring it from the
206
     * db column type.
207
     *
208
     * @param  array  $field  Field definition array.
209
     * @return array Field definition array that contains type too.
210
     */
211
    protected function makeSureFieldHasType($field)
212
    {
213
        if (! isset($field['type'])) {
214
            $field['type'] = isset($field['relation_type']) ? $this->inferFieldTypeFromRelationType($field['relation_type']) : $this->inferFieldTypeFromDbColumnType($field['name']);
0 ignored issues
show
Bug introduced by
The method inferFieldTypeFromDbColumnType() does not exist on Backpack\CRUD\app\Librar...\FieldsProtectedMethods. Did you maybe mean inferFieldTypeFromRelationType()? ( Ignorable by Annotation )

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

214
            $field['type'] = isset($field['relation_type']) ? $this->inferFieldTypeFromRelationType($field['relation_type']) : $this->/** @scrutinizer ignore-call */ inferFieldTypeFromDbColumnType($field['name']);

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...
215
        }
216
217
        return $field;
218
    }
219
220
    protected function inferFieldTypeFromRelationType($relationType)
221
    {
222
        if (backpack_pro()) {
223
            return 'relationship';
224
        }
225
226
        switch ($relationType) {
227
            case 'BelongsTo':
228
                return 'select';
229
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
230
231
            case 'BelongsToMany':
232
            case 'MorphToMany':
233
                return 'select_multiple';
234
235
            default:
236
                return 'text';
237
                break;
238
        }
239
    }
240
241
    /**
242
     * If a field has subfields, go through each subfield and guess
243
     * its attribute, filling in whatever is missing.
244
     *
245
     * @param  array  $field  Field definition array.
246
     * @return array The improved definition of that field (a better 'subfields' array)
247
     */
248
    protected function makeSureSubfieldsHaveNecessaryAttributes($field)
249
    {
250
        if (! isset($field['subfields'])) {
251
            return $field;
252
        }
253
254
        foreach ($field['subfields'] as $key => $subfield) {
255
            // make sure the field definition is an array
256
            if (is_string($subfield)) {
257
                $subfield = ['name' => $subfield];
258
            }
259
260
            if (! isset($field['model'])) {
261
                // we're inside a simple 'repeatable' with no model/relationship, so
262
                // we assume all subfields are supposed to be text fields
263
                $subfield['type'] = $subfield['type'] ?? 'text';
264
                $subfield['entity'] = $subfield['entity'] ?? false;
265
            } else {
266
                // we should use 'model' as the `baseModel` for all subfields, so that when
267
                // we look if `category()` relationship exists on the model, we look on
268
                // the model this repeatable represents, not the main CRUD model
269
                $currentEntity = $subfield['baseEntity'] ?? $field['entity'];
270
                $subfield['baseModel'] = $subfield['baseModel'] ?? $field['model'];
271
                $subfield['baseEntity'] = isset($field['baseEntity']) ? $field['baseEntity'].'.'.$currentEntity : $currentEntity;
272
            }
273
274
            $field['subfields'][$key] = $this->makeSureFieldHasNecessaryAttributes($subfield);
0 ignored issues
show
Bug introduced by
The method makeSureFieldHasNecessaryAttributes() does not exist on Backpack\CRUD\app\Librar...\FieldsProtectedMethods. Did you maybe mean makeSureFieldHasAttribute()? ( Ignorable by Annotation )

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

274
            /** @scrutinizer ignore-call */ 
275
            $field['subfields'][$key] = $this->makeSureFieldHasNecessaryAttributes($subfield);

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...
275
        }
276
277
        // when field has any of `many` relations we need to append either the pivot selector for the `ToMany` or the
278
        // local key for the `many` relations. Other relations don't need any special treatment when used as subfields.
279
        if (isset($field['relation_type'])) {
280
            switch ($field['relation_type']) {
281
                case 'MorphToMany':
282
                case 'BelongsToMany':
283
                    $pivotSelectorField = static::getPivotFieldStructure($field);
284
                    $field['subfields'] = Arr::prepend($field['subfields'], $pivotSelectorField);
285
                    break;
286
                case 'MorphMany':
287
                case 'HasMany':
288
                    $entity = isset($field['baseEntity']) ? $field['baseEntity'].'.'.$field['entity'] : $field['entity'];
289
                    $relationInstance = $this->getRelationInstance(['entity' => $entity]);
0 ignored issues
show
Bug introduced by
It seems like getRelationInstance() 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

289
                    /** @scrutinizer ignore-call */ 
290
                    $relationInstance = $this->getRelationInstance(['entity' => $entity]);
Loading history...
290
                    $field['subfields'] = Arr::prepend($field['subfields'], [
291
                        'name' => $relationInstance->getLocalKeyName(),
292
                        'type' => 'hidden',
293
                    ]);
294
                break;
295
            }
296
        }
297
298
        return $field;
299
    }
300
301
    /**
302
     * Enable the tabs functionality, if a field has a tab defined.
303
     *
304
     * @param  array  $field  Field definition array.
305
     * @return void
306
     */
307
    protected function enableTabsIfFieldUsesThem($field)
308
    {
309
        // if a tab was mentioned, we should enable it
310
        if (isset($field['tab'])) {
311
            if (! $this->tabsEnabled()) {
0 ignored issues
show
Bug introduced by
It seems like tabsEnabled() 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

311
            if (! $this->/** @scrutinizer ignore-call */ tabsEnabled()) {
Loading history...
312
                $this->enableTabs();
0 ignored issues
show
Bug introduced by
The method enableTabs() does not exist on Backpack\CRUD\app\Librar...\FieldsProtectedMethods. Did you maybe mean enableTabsIfFieldUsesThem()? ( Ignorable by Annotation )

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

312
                $this->/** @scrutinizer ignore-call */ 
313
                       enableTabs();

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...
313
            }
314
        }
315
    }
316
317
    /**
318
     * Add a field to the current operation, using the Settings API.
319
     *
320
     * @param  array  $field  Field definition array.
321
     */
322
    protected function addFieldToOperationSettings($field)
323
    {
324
        $fieldKey = $this->getFieldKey($field);
325
326
        $allFields = $this->getOperationSetting('fields');
0 ignored issues
show
Unused Code introduced by
The assignment to $allFields is dead and can be removed.
Loading history...
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

326
        /** @scrutinizer ignore-call */ 
327
        $allFields = $this->getOperationSetting('fields');
Loading history...
327
        $allFields = array_merge($this->getCleanStateFields(), [$fieldKey => $field]);
0 ignored issues
show
Bug introduced by
It seems like getCleanStateFields() 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

327
        $allFields = array_merge($this->/** @scrutinizer ignore-call */ getCleanStateFields(), [$fieldKey => $field]);
Loading history...
328
329
        $this->setOperationSetting('fields', $allFields);
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

329
        $this->/** @scrutinizer ignore-call */ 
330
               setOperationSetting('fields', $allFields);
Loading history...
330
    }
331
332
    /**
333
     * Get the string that should be used as an array key, for the attributive array
334
     * where the fields are stored for the current operation.
335
     *
336
     * The array key for the field should be:
337
     * - name (if the name is a string)
338
     * - name1_name2_name3 (if the name is an array)
339
     *
340
     * @param  array  $field  Field definition array.
341
     * @return string The string that should be used as array key.
342
     */
343
    protected function getFieldKey($field)
344
    {
345
        if (is_array($field['name'])) {
346
            return implode('_', $field['name']);
347
        }
348
349
        return $field['name'];
350
    }
351
}
352