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 — add-tests ( 6a69a3...c242f5 )
by Pedro
43:15 queued 28:32
created

Input::decodeJsonCastedAttributes()   B

Complexity

Conditions 11
Paths 10

Size

Total Lines 24
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 13
nc 10
nop 2
dl 0
loc 24
rs 7.3166
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 Illuminate\Support\Arr;
6
use Illuminate\Support\Str;
7
8
trait Input
9
{
10
    /**
11
     * ---------------
12
     * PRIVATE METHODS
13
     * ---------------.
14
     */
15
16
    /**
17
     * Returns the direct inputs parsed for model and relationship creation.
18
     *
19
     * @param  array  $inputs
20
     * @param  null|array  $relationDetails
21
     * @param  bool|string  $relationMethod
22
     * @return array
23
     */
24
    private function splitInputIntoDirectAndRelations($inputs, $relationDetails = null, $relationMethod = false)
25
    {
26
        $crudFields = $relationDetails['crudFields'] ?? [];
27
        $model = $relationDetails['model'] ?? false;
28
29
        $directInputs = $this->getDirectInputsFromInput($inputs, $model, $crudFields, $relationMethod);
30
        $relationInputs = $this->getRelationDetailsFromInput($inputs, $crudFields, $relationMethod);
31
32
        return [$directInputs, $relationInputs];
33
    }
34
35
    /**
36
     * Returns the attributes with relationships stripped out from the input.
37
     * BelongsTo relations are ensured to have the correct foreign key set.
38
     * ALL other relations are stripped from the input.
39
     *
40
     * @param  array  $input  - the input array
41
     * @param  mixed  $model  - the model of what we want to get the attributtes for
42
     * @param  array  $fields  - the fields used in this relation
43
     * @param  mixed  $relationMethod  - the relation method
44
     * @return array
45
     */
46
    private function getDirectInputsFromInput($input, $model = false, $fields = [], $relationMethod = false)
47
    {
48
        $model = $model ? (is_string($model) ? app($model) : $model) : $this->model;
49
50
        $input = $this->decodeJsonCastedAttributes($input, $model);
51
        $input = $this->compactFakeFields($input, $model, $fields);
0 ignored issues
show
Bug introduced by
It seems like compactFakeFields() 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

51
        /** @scrutinizer ignore-call */ 
52
        $input = $this->compactFakeFields($input, $model, $fields);
Loading history...
52
        $input = $this->includeMorphToInputsFromRelationship($input);
0 ignored issues
show
Bug introduced by
It seems like includeMorphToInputsFromRelationship() 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

52
        /** @scrutinizer ignore-call */ 
53
        $input = $this->includeMorphToInputsFromRelationship($input);
Loading history...
53
        $input = $this->excludeRelationFieldsExceptBelongsTo($input, $fields, $relationMethod);
54
        $input = $this->changeBelongsToNamesFromRelationshipToForeignKey($input, $fields);
0 ignored issues
show
Bug introduced by
It seems like changeBelongsToNamesFromRelationshipToForeignKey() 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

54
        /** @scrutinizer ignore-call */ 
55
        $input = $this->changeBelongsToNamesFromRelationshipToForeignKey($input, $fields);
Loading history...
55
56
        return $input;
57
    }
58
59
    /**
60
     * Get a relation data array from the form data. For each relation defined in the fields
61
     * through the entity attribute, and set some relation details.
62
     *
63
     * We traverse this relation array later to create the relations, for example:
64
     * - Current model HasOne Address
65
     * - Address (line_1, country_id) BelongsTo Country through country_id in Address Model
66
     *
67
     * So when editing current model crud user have two fields
68
     * - address.line_1
69
     * - address.country
70
     * (we infer country_id from relation)
71
     *
72
     * Those will be nested accordingly in this relation array, so address relation
73
     * will have a nested relation with country.
74
     *
75
     * @param  array  $input  The form input.
76
     * @param  array  $crudFields  - present when getting the relation details for other relations.
77
     * @param  mixed  $relationMethod
78
     * @return array The formatted relation details.
79
     */
80
    private function getRelationDetailsFromInput($input, $crudFields = [], $relationMethod = false)
81
    {
82
        // main entity
83
        if (empty($crudFields)) {
84
            $relationFields = $this->getRelationFields();
0 ignored issues
show
Bug introduced by
It seems like getRelationFields() 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

84
            /** @scrutinizer ignore-call */ 
85
            $relationFields = $this->getRelationFields();
Loading history...
85
        } else {
86
            // relations sends the fields that represent them so we can parse the input accordingly.
87
            $relationFields = $crudFields;
88
89
            foreach ($crudFields as $key => $crudField) {
90
                if (isset($crudField['subfields'])) {
91
                    foreach ($crudField['subfields'] as $crudSubField) {
92
                        if (isset($crudSubField['relation_type'])) {
93
                            $relationFields[] = $crudSubField;
94
                        }
95
                    }
96
                }
97
            }
98
        }
99
100
        //remove fields that are not in the submitted form input
101
        $relationFields = array_filter($relationFields, function ($field) use ($input) {
102
            return Arr::has($input, $field['name']) || isset($input[$field['name']]) || Arr::has($input, Str::afterLast($field['name'], '.'));
103
        });
104
105
        $relationDetails = [];
106
107
        foreach ($relationFields as $field) {
108
            // if relationMethod is set we strip it out of the fieldName that we use to create the relations array
109
            $fieldName = $relationMethod ? Str::after($field['name'], $relationMethod.'.') : $field['name'];
110
111
            $key = Str::before($this->getOnlyRelationEntity(['entity' => $fieldName]), '.');
0 ignored issues
show
Bug introduced by
It seems like getOnlyRelationEntity() 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

111
            $key = Str::before($this->/** @scrutinizer ignore-call */ getOnlyRelationEntity(['entity' => $fieldName]), '.');
Loading history...
112
113
            // if the field entity contains the attribute we want to add that attribute in the correct relation key.
114
            // eg: address.street, we want to add `street` as an attribute in `address` relation, `street` is not
115
            // a relation of `address`
116
            if ($this->getOnlyRelationEntity($field) !== $field['entity']) {
117
                if (Str::before($field['entity'], '.') === $relationMethod) {
118
                    $key = Str::before($this->getOnlyRelationEntity($field), '.');
119
                }
120
            }
121
122
            $attributeName = (string) Str::of($field['name'])->afterLast('.');
123
124
            switch ($field['relation_type']) {
125
                case 'BelongsTo':
126
                    // when it's a nested belongsTo relation we want to make sure
127
                    // the key used to store the values is the main relation key
128
                    $key = Str::beforeLast($this->getOnlyRelationEntity($field), '.');
129
130
                    break;
131
            }
132
133
            // we don't need to re-setup this relation method values, we just want the relations
134
            if ($key === $relationMethod) {
135
                continue;
136
            }
137
138
            $fieldDetails = Arr::get($relationDetails, $key, []);
139
140
            $fieldDetails['values'][$attributeName] = Arr::get($input, $fieldName);
141
            $fieldDetails['model'] = $fieldDetails['model'] ?? $field['model'];
142
            $fieldDetails['relation_type'] = $fieldDetails['relation_type'] ?? $field['relation_type'];
143
            $fieldDetails['crudFields'][] = $field;
144
            $fieldDetails['entity'] = $this->getOnlyRelationEntity($field);
145
146
            if (isset($field['fallback_id'])) {
147
                $fieldDetails['fallback_id'] = $field['fallback_id'];
148
            }
149
            if (isset($field['force_delete'])) {
150
                $fieldDetails['force_delete'] = $field['force_delete'];
151
            }
152
153
            Arr::set($relationDetails, $key, $fieldDetails);
154
        }
155
156
        return $relationDetails;
157
    }
158
159
    /**
160
     * Return the input without relations except BelongsTo that we are going to properly match
161
     * with the relation foreign_key in a later stage of the saving process.
162
     *
163
     * @param  array  $fields
164
     * @param  mixed  $relationMethod
165
     * @return array
166
     */
167
    private function excludeRelationFieldsExceptBelongsTo($input, $fields, $relationMethod)
168
    {
169
        // when fields are empty we are in the main entity, we get the regular crud relation fields
170
        if (empty($fields)) {
171
            $fields = $this->getRelationFields();
172
        }
173
174
        $excludedFields = [];
175
        foreach ($fields as $field) {
176
            $nameToExclude = $relationMethod ? Str::after($field['name'], $relationMethod.'.') : $field['name'];
177
178
            // when using dot notation if relationMethod is not set we are sure we want to exclude those relations.
179
            if ($this->getOnlyRelationEntity($field) !== $field['entity']) {
180
                if (! $relationMethod) {
181
                    $excludedFields[] = $nameToExclude;
182
                }
183
184
                continue;
185
            }
186
187
            if (isset($field['relation_type']) && $field['relation_type'] !== 'BelongsTo') {
188
                $excludedFields[] = $nameToExclude;
189
190
                continue;
191
            }
192
        }
193
194
        return Arr::where($input, function ($item, $key) use ($excludedFields) {
195
            return ! in_array($key, $excludedFields);
196
        });
197
    }
198
199
    /**
200
     * Decode attributes that are casted as array/object/json in the model.
201
     * So that they are not json_encoded twice before they are stored in the db
202
     * (once by Backpack in front-end, once by Laravel Attribute Casting).
203
     *
204
     * @param  array  $input
205
     * @param  mixed  $model
206
     * @return array
207
     */
208
    public function decodeJsonCastedAttributes($input, $model = false)
209
    {
210
        $model = $model ? $model : $this->model;
211
        $fields = $this->getCleanStateFields();
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

211
        /** @scrutinizer ignore-call */ 
212
        $fields = $this->getCleanStateFields();
Loading history...
212
        $casted_attributes = $model->getCastedAttributes();
213
214
        foreach ($fields as $field) {
215
            // Test the field is castable
216
            if (isset($field['name']) && is_string($field['name']) && array_key_exists($field['name'], $casted_attributes)) {
217
                // Handle JSON field types
218
                $jsonCastables = ['array', 'object', 'json'];
219
                $fieldCasting = $casted_attributes[$field['name']];
220
221
                if (in_array($fieldCasting, $jsonCastables) && isset($input[$field['name']]) && ! empty($input[$field['name']]) && ! is_array($input[$field['name']])) {
222
                    try {
223
                        $input[$field['name']] = json_decode($input[$field['name']]);
224
                    } catch (\Exception $e) {
225
                        $input[$field['name']] = [];
226
                    }
227
                }
228
            }
229
        }
230
231
        return $input;
232
    }
233
}
234