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
Push — v6 ( 279770...4aa68f )
by Cristian
26:51 queued 11:50
created

inferFieldTypeFromRelationType()   A

Complexity

Conditions 5
Paths 5

Size

Total Lines 14
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
c 0
b 0
f 0
nc 5
nop 1
dl 0
loc 14
rs 9.6111
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 (empty($field)) {
111
            abort(500, 'Field name can\'t be empty');
112
        }
113
114
        if (is_string($field)) {
115
            return ['name' => $field];
116
        }
117
118
        if (is_array($field) && ! isset($field['name'])) {
119
            abort(500, 'All fields must have their name defined');
120
        }
121
122
        return $field;
123
    }
124
125
    /**
126
     * If entity is not present, but it looks like the field SHOULD be a relationship field,
127
     * try to determine the method on the model that defines the relationship, and pass it to
128
     * the field as 'entity'.
129
     *
130
     * @param  array  $field
131
     * @return array
132
     */
133
    protected function makeSureFieldHasEntity($field)
134
    {
135
        $model = isset($field['baseModel']) ? (new $field['baseModel']) : $this->model;
136
137
        if (isset($field['entity'])) {
138
            return $field;
139
        }
140
141
        // by default, entity is false if we cannot link it with guessing functions to a relation
142
        $field['entity'] = false;
143
144
        // if the name is an array it's definitely not a relationship
145
        if (is_array($field['name'])) {
146
            return $field;
147
        }
148
149
        //if the name is dot notation we are sure it's a relationship
150
        if (strpos($field['name'], '.') !== false) {
151
            $possibleMethodName = Str::of($field['name'])->before('.');
152
            // check model method for possibility of being a relationship
153
            $field['entity'] = $this->modelMethodIsRelationship($model, $possibleMethodName) ? $field['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

153
            $field['entity'] = $this->/** @scrutinizer ignore-call */ modelMethodIsRelationship($model, $possibleMethodName) ? $field['name'] : false;
Loading history...
154
155
            return $field;
156
        }
157
158
        // if there's a method on the model with this name
159
        if (method_exists($model, $field['name'])) {
160
            // check model method for possibility of being a relationship
161
            $field['entity'] = $this->modelMethodIsRelationship($model, $field['name']);
162
163
            return $field;
164
        }
165
166
        // if the name ends with _id and that method exists,
167
        // we can probably use it as an entity
168
        if (Str::endsWith($field['name'], '_id')) {
169
            $possibleMethodName = Str::replaceLast('_id', '', $field['name']);
170
171
            if (method_exists($model, $possibleMethodName)) {
172
                // check model method for possibility of being a relationship
173
                $field['entity'] = $this->modelMethodIsRelationship($model, $possibleMethodName);
174
175
                return $field;
176
            }
177
        }
178
179
        return $field;
180
    }
181
182
    protected function makeSureFieldHasAttribute($field)
183
    {
184
        if (isset($field['entity']) && $field['entity']) {
185
            // if the user setup the attribute in relation string, we are not going to infer that attribute from model
186
            // instead we get the defined attribute by the user.
187
            if ($this->isAttributeInRelationString($field)) {
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

187
            if ($this->/** @scrutinizer ignore-call */ isAttributeInRelationString($field)) {
Loading history...
188
                $field['attribute'] = $field['attribute'] ?? Str::afterLast($field['entity'], '.');
189
190
                return $field;
191
            }
192
        }
193
        // if there's a model defined, but no attribute
194
        // guess an attribute using the identifiableAttribute functionality in CrudTrait
195
        if (isset($field['model']) && ! isset($field['attribute']) && method_exists($field['model'], 'identifiableAttribute')) {
196
            $field['attribute'] = (new $field['model']())->identifiableAttribute();
197
        }
198
199
        return $field;
200
    }
201
202
    /**
203
     * Set the label of a field, if it's missing, by capitalizing the name and replacing
204
     * underscores with spaces.
205
     *
206
     * @param  array  $field  Field definition array.
207
     * @return array Field definition array that contains label too.
208
     */
209
    protected function makeSureFieldHasLabel($field)
210
    {
211
        if (! isset($field['label'])) {
212
            $name = is_array($field['name']) ? $field['name'][0] : $field['name'];
213
            $name = str_replace('_id', '', $name);
214
            $field['label'] = mb_ucfirst(str_replace('_', ' ', $name));
215
        }
216
217
        return $field;
218
    }
219
220
    /**
221
     * Set the type of a field, if it's missing, by inferring it from the
222
     * db column type.
223
     *
224
     * @param  array  $field  Field definition array.
225
     * @return array Field definition array that contains type too.
226
     */
227
    protected function makeSureFieldHasType($field)
228
    {
229
        if (! isset($field['type'])) {
230
            $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

230
            $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...
231
        }
232
233
        return $field;
234
    }
235
236
    protected function inferFieldTypeFromRelationType($relationType)
237
    {
238
        if (backpack_pro()) {
239
            return 'relationship';
240
        }
241
242
        switch ($relationType) {
243
            case 'BelongsTo':
244
                return 'select';
245
            case 'BelongsToMany':
246
            case 'MorphToMany':
247
                return 'select_multiple';
248
            default:
249
                return 'text';
250
        }
251
    }
252
253
    /**
254
     * If a field has subfields, go through each subfield and guess
255
     * its attribute, filling in whatever is missing.
256
     *
257
     * @param  array  $field  Field definition array.
258
     * @return array The improved definition of that field (a better 'subfields' array)
259
     */
260
    protected function makeSureSubfieldsHaveNecessaryAttributes($field)
261
    {
262
        if (! isset($field['subfields'])) {
263
            return $field;
264
        }
265
266
        foreach ($field['subfields'] as $key => $subfield) {
267
            if (empty($field)) {
268
                abort(500, 'Field name can\'t be empty');
269
            }
270
271
            // make sure the field definition is an array
272
            if (is_string($subfield)) {
273
                $subfield = ['name' => $subfield];
274
            }
275
276
            $subfield['parentFieldName'] = is_array($field['name']) ? false : $field['name'];
277
278
            if (! isset($field['model'])) {
279
                // we're inside a simple 'repeatable' with no model/relationship, so
280
                // we assume all subfields are supposed to be text fields
281
                $subfield['type'] = $subfield['type'] ?? 'text';
282
                $subfield['entity'] = $subfield['entity'] ?? false;
283
            } else {
284
                // we should use 'model' as the `baseModel` for all subfields, so that when
285
                // we look if `category()` relationship exists on the model, we look on
286
                // the model this repeatable represents, not the main CRUD model
287
                $currentEntity = $subfield['baseEntity'] ?? $field['entity'];
288
                $subfield['baseModel'] = $subfield['baseModel'] ?? $field['model'];
289
                $subfield['baseEntity'] = isset($field['baseEntity']) ? $field['baseEntity'].'.'.$currentEntity : $currentEntity;
290
                $subfield['baseFieldName'] = is_array($subfield['name']) ? implode(',', $subfield['name']) : $subfield['name'];
291
                $subfield['baseFieldName'] = Str::afterLast($subfield['baseFieldName'], '.');
292
            }
293
294
            $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

294
            /** @scrutinizer ignore-call */ 
295
            $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...
295
        }
296
297
        // when field has any of `many` relations we need to append either the pivot selector for the `ToMany` or the
298
        // local key for the `many` relations. Other relations don't need any special treatment when used as subfields.
299
        if (isset($field['relation_type'])) {
300
            switch ($field['relation_type']) {
301
                case 'MorphToMany':
302
                case 'BelongsToMany':
303
                    $pivotSelectorField = static::getPivotFieldStructure($field);
304
                    $this->setupFieldValidation($pivotSelectorField, $field['name']);
0 ignored issues
show
Bug introduced by
It seems like setupFieldValidation() 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

304
                    $this->/** @scrutinizer ignore-call */ 
305
                           setupFieldValidation($pivotSelectorField, $field['name']);
Loading history...
305
                    $field['subfields'] = Arr::prepend($field['subfields'], $pivotSelectorField);
306
                    break;
307
                case 'MorphMany':
308
                case 'HasMany':
309
                    $entity = isset($field['baseEntity']) ? $field['baseEntity'].'.'.$field['entity'] : $field['entity'];
310
                    $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

310
                    /** @scrutinizer ignore-call */ 
311
                    $relationInstance = $this->getRelationInstance(['entity' => $entity]);
Loading history...
311
                    $field['subfields'] = Arr::prepend($field['subfields'], [
312
                        'name' => $relationInstance->getRelated()->getKeyName(),
313
                        'type' => 'hidden',
314
                    ]);
315
                    break;
316
            }
317
        }
318
319
        return $field;
320
    }
321
322
    /**
323
     * Enable the tabs functionality, if a field has a tab defined.
324
     *
325
     * @param  array  $field  Field definition array.
326
     * @return void
327
     */
328
    protected function enableTabsIfFieldUsesThem($field)
329
    {
330
        // if a tab was mentioned, we should enable it
331
        if (isset($field['tab'])) {
332
            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

332
            if (! $this->/** @scrutinizer ignore-call */ tabsEnabled()) {
Loading history...
333
                $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

333
                $this->/** @scrutinizer ignore-call */ 
334
                       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...
334
            }
335
        }
336
    }
337
338
    /**
339
     * Add a field to the current operation, using the Settings API.
340
     *
341
     * @param  array  $field  Field definition array.
342
     */
343
    protected function addFieldToOperationSettings($field)
344
    {
345
        $fieldKey = $this->getFieldKey($field);
346
347
        $allFields = $this->getOperationSetting('fields');
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

347
        /** @scrutinizer ignore-call */ 
348
        $allFields = $this->getOperationSetting('fields');
Loading history...
Unused Code introduced by
The assignment to $allFields is dead and can be removed.
Loading history...
348
        $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

348
        $allFields = array_merge($this->/** @scrutinizer ignore-call */ getCleanStateFields(), [$fieldKey => $field]);
Loading history...
349
350
        $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

350
        $this->/** @scrutinizer ignore-call */ 
351
               setOperationSetting('fields', $allFields);
Loading history...
351
    }
352
353
    /**
354
     * Get the string that should be used as an array key, for the attributive array
355
     * where the fields are stored for the current operation.
356
     *
357
     * The array key for the field should be:
358
     * - name (if the name is a string)
359
     * - name1_name2_name3 (if the name is an array)
360
     *
361
     * @param  array  $field  Field definition array.
362
     * @return string The string that should be used as array key.
363
     */
364
    protected function getFieldKey($field)
365
    {
366
        if (is_array($field['name'])) {
367
            return implode('_', $field['name']);
368
        }
369
370
        return $field['name'];
371
    }
372
}
373