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 — main ( b581e3...df6622 )
by Pedro
45:14 queued 09:34
created

Input::getDirectInputsFromInput()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 11
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 3
eloc 7
nc 4
nop 4
dl 0
loc 11
rs 10
c 0
b 0
f 0
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
                    if (! isset($field['parentFieldName']) && isset($field['entity'])) {
131
                        $mainField = $field;
132
                        $mainField['entity'] = Str::beforeLast($field['entity'], '.');
133
134
                        $inferredRelation = $this->inferRelationTypeFromRelationship($mainField);
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

134
                        /** @scrutinizer ignore-call */ 
135
                        $inferredRelation = $this->inferRelationTypeFromRelationship($mainField);
Loading history...
135
                    }
136
137
                    break;
138
            }
139
140
            // we don't need to re-setup this relation method values, we just want the relations
141
            if ($key === $relationMethod) {
142
                unset($inferredRelation);
143
                continue;
144
            }
145
146
            $fieldDetails = Arr::get($relationDetails, $key, []);
147
            $fieldDetails['values'][$attributeName] = Arr::get($input, $fieldName);
148
            $fieldDetails['model'] = $fieldDetails['model'] ?? $field['model'];
149
            $fieldDetails['relation_type'] = $fieldDetails['relation_type'] ?? $inferredRelation ?? $field['relation_type'];
150
            $fieldDetails['crudFields'][] = $field;
151
            $fieldDetails['entity'] = $this->getOnlyRelationEntity($field);
152
153
            if (isset($field['fallback_id'])) {
154
                $fieldDetails['fallback_id'] = $field['fallback_id'];
155
            }
156
            if (isset($field['force_delete'])) {
157
                $fieldDetails['force_delete'] = $field['force_delete'];
158
            }
159
160
            Arr::set($relationDetails, $key, $fieldDetails);
161
            unset($inferredRelation);
162
        }
163
164
        return $relationDetails;
165
    }
166
167
    /**
168
     * Return the input without relations except BelongsTo that we are going to properly match
169
     * with the relation foreign_key in a later stage of the saving process.
170
     *
171
     * @param  array  $fields
172
     * @param  mixed  $relationMethod
173
     * @return array
174
     */
175
    private function excludeRelationFieldsExceptBelongsTo($input, $fields, $relationMethod)
176
    {
177
        // when fields are empty we are in the main entity, we get the regular crud relation fields
178
        if (empty($fields)) {
179
            $fields = $this->getRelationFields();
180
        }
181
182
        $excludedFields = [];
183
        foreach ($fields as $field) {
184
            $nameToExclude = $relationMethod ? Str::after($field['name'], $relationMethod.'.') : $field['name'];
185
186
            // when using dot notation if relationMethod is not set we are sure we want to exclude those relations.
187
            if ($this->getOnlyRelationEntity($field) !== $field['entity']) {
188
                if (! $relationMethod) {
189
                    $excludedFields[] = $nameToExclude;
190
                }
191
192
                continue;
193
            }
194
195
            if (isset($field['relation_type']) && $field['relation_type'] !== 'BelongsTo') {
196
                $excludedFields[] = $nameToExclude;
197
198
                continue;
199
            }
200
        }
201
202
        return Arr::where($input, function ($item, $key) use ($excludedFields) {
203
            return ! in_array($key, $excludedFields);
204
        });
205
    }
206
207
    /**
208
     * Decode attributes that are casted as array/object/json in the model.
209
     * So that they are not json_encoded twice before they are stored in the db
210
     * (once by Backpack in front-end, once by Laravel Attribute Casting).
211
     *
212
     * @param  array  $input
213
     * @param  mixed  $model
214
     * @return array
215
     */
216
    public function decodeJsonCastedAttributes($input, $model = false)
217
    {
218
        $model = $model ? $model : $this->model;
219
        $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

219
        /** @scrutinizer ignore-call */ 
220
        $fields = $this->getCleanStateFields();
Loading history...
220
        $casted_attributes = $model->getCastedAttributes();
221
        $translatableAttributes = $model->translationEnabled() ? $model->getTranslatableAttributes() : [];
222
223
        foreach ($fields as $field) {
224
            // Test the field is castable
225
            if (isset($field['name']) && is_string($field['name']) && array_key_exists($field['name'], $casted_attributes)) {
226
                // Handle JSON field types
227
                $jsonCastables = ['array', 'object', 'json'];
228
                $fieldCasting = $casted_attributes[$field['name']];
229
230
                if (in_array($fieldCasting, $jsonCastables) && isset($input[$field['name']]) && ! empty($input[$field['name']]) && ! is_array($input[$field['name']]) && ! in_array($field['name'], $translatableAttributes)) {
231
                    try {
232
                        $input[$field['name']] = json_decode($input[$field['name']]);
233
                    } catch (\Exception $e) {
234
                        $input[$field['name']] = [];
235
                    }
236
                }
237
            }
238
        }
239
240
        return $input;
241
    }
242
}
243