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 (#3316)
by
unknown
16:59
created

Create::getBelongsToRelationFields()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 4
c 0
b 0
f 0
nc 1
nop 0
dl 0
loc 6
rs 10
1
<?php
2
3
namespace Backpack\CRUD\app\Library\CrudPanel\Traits;
4
5
use Illuminate\Database\Eloquent\Model;
6
use Illuminate\Database\Eloquent\Relations\BelongsTo;
7
use Illuminate\Database\Eloquent\Relations\HasOne;
8
use Illuminate\Support\Arr;
9
10
trait Create
11
{
12
    /*
13
    |--------------------------------------------------------------------------
14
    |                                   CREATE
15
    |--------------------------------------------------------------------------
16
    */
17
18
    /**
19
     * Insert a row in the database.
20
     *
21
     * @param array $data All input values to be inserted.
22
     *
23
     * @return \Illuminate\Database\Eloquent\Model
24
     */
25
    public function create($data)
26
    {
27
        $data = $this->decodeJsonCastedAttributes($data);
0 ignored issues
show
Bug introduced by
It seems like decodeJsonCastedAttributes() 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

27
        /** @scrutinizer ignore-call */ 
28
        $data = $this->decodeJsonCastedAttributes($data);
Loading history...
28
        $data = $this->compactFakeFields($data);
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

28
        /** @scrutinizer ignore-call */ 
29
        $data = $this->compactFakeFields($data);
Loading history...
29
30
        // omit the n-n relationships when updating the eloquent item
31
        $nn_relationships = Arr::pluck($this->getRelationFieldsWithPivot(), 'name');
32
33
        // init and fill model
34
        $item = $this->model->make(Arr::except($data, $nn_relationships));
35
36
        // handle BelongsTo 1:1 relations
37
        $item = $this->handleBelongsToRelations($item, $data);
38
        $item->save();
39
40
        // if there are any relationships available, also sync those
41
        $this->createRelations($item, $data);
42
43
        return $item;
44
    }
45
46
    /**
47
     * Get all fields needed for the ADD NEW ENTRY form.
48
     *
49
     * @return array The fields with attributes and fake attributes.
50
     */
51
    public function getCreateFields()
52
    {
53
        return $this->fields();
0 ignored issues
show
Bug introduced by
It seems like fields() 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

53
        return $this->/** @scrutinizer ignore-call */ fields();
Loading history...
54
    }
55
56
    /**
57
     * Get all fields with relation set (model key set on field).
58
     *
59
     * @return array The fields with model key set.
60
     */
61
    public function getRelationFields()
62
    {
63
        $fields = $this->fields();
64
        $relationFields = [];
65
66
        foreach ($fields as $field) {
67
            if (isset($field['model']) && $field['model'] !== false) {
68
                array_push($relationFields, $field);
69
            }
70
71
            if (isset($field['subfields']) &&
72
                is_array($field['subfields']) &&
73
                count($field['subfields'])) {
74
                foreach ($field['subfields'] as $subfield) {
75
                    array_push($relationFields, $subfield);
76
                }
77
            }
78
        }
79
80
        return $relationFields;
81
    }
82
83
    /**
84
     * Associate and dissociate.
85
     *
86
     * @param  Model
87
     * @param  array The form data.
0 ignored issues
show
Bug introduced by
The type Backpack\CRUD\app\Library\CrudPanel\Traits\The was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
88
     * @return Model Model with relationships set up.
89
     */
90
    public function handleBelongsToRelations($item, array $data)
91
    {
92
        foreach ($this->getBelongsToRelationFields() as $relationField) {
93
            $item->{$this->getOnlyRelationEntity($relationField)}()
94
                ->associate($relationField['model']::find(Arr::get($data, $relationField['name'])));
95
        }
96
97
        return $item;
98
    }
99
100
    /**
101
     * Get all BelongsTo relationship fields.
102
     *
103
     * @return array The fields with 1:1 BelongsTo relationships and with model set.
104
     */
105
    public function getBelongsToRelationFields(): array
106
    {
107
        return collect($this->fields())
108
            ->where('model')
109
            ->where('relation_type', 'BelongsTo')
110
            ->toArray();
111
    }
112
113
    /**
114
     * Get all fields with n-n relation set (pivot table is true).
115
     *
116
     * @return array The fields with n-n relationships.
117
     */
118
    public function getRelationFieldsWithPivot()
119
    {
120
        $all_relation_fields = $this->getRelationFields();
121
122
        return Arr::where($all_relation_fields, function ($value, $key) {
123
            return isset($value['pivot']) && $value['pivot'];
124
        });
125
    }
126
127
    /**
128
     * Create the relations for the current model.
129
     *
130
     * @param \Illuminate\Database\Eloquent\Model $item The current CRUD model.
131
     * @param array                               $data The form data.
132
     */
133
    public function createRelations($item, $data)
134
    {
135
        $this->syncPivot($item, $data);
136
        $this->createOneToOneRelations($item, $data);
137
    }
138
139
    /**
140
     * Sync the declared many-to-many associations through the pivot field.
141
     *
142
     * @param \Illuminate\Database\Eloquent\Model $model The current CRUD model.
143
     * @param array                               $data  The form data.
144
     */
145
    public function syncPivot($model, $data)
146
    {
147
        $fields_with_relationships = $this->getRelationFields();
148
        foreach ($fields_with_relationships as $key => $field) {
149
            if (isset($field['pivot']) && $field['pivot']) {
150
                $values = isset($data[$field['name']]) ? $data[$field['name']] : [];
151
152
                // if a JSON was passed instead of an array, turn it into an array
153
                if (is_string($values)) {
154
                    $values = json_decode($values);
155
                }
156
157
                $relation_data = [];
158
                foreach ($values as $pivot_id) {
159
                    $pivot_data = [];
160
161
                    if (isset($field['pivotFields'])) {
162
                        foreach ($field['pivotFields'] as $pivot_field_name) {
163
                            $pivot_data[$pivot_field_name] = $data[$pivot_field_name][$pivot_id];
164
                        }
165
                    }
166
                    $relation_data[$pivot_id] = $pivot_data;
167
                }
168
169
                $model->{$field['name']}()->sync($relation_data);
170
            }
171
172
            if (isset($field['morph']) && $field['morph'] && isset($data[$field['name']])) {
173
                $values = $data[$field['name']];
174
                $model->{$field['name']}()->sync($values);
175
            }
176
        }
177
    }
178
179
    /**
180
     * Create any existing one to one relations for the current model from the form data.
181
     *
182
     * @param \Illuminate\Database\Eloquent\Model $item The current CRUD model.
183
     * @param array                               $data The form data.
184
     */
185
    private function createOneToOneRelations($item, $data)
186
    {
187
        $relationData = $this->getRelationDataFromFormData($data);
188
        $this->createRelationsForItem($item, $relationData);
189
    }
190
191
    /**
192
     * Create any existing one to one relations for the current model from the relation data.
193
     *
194
     * @param \Illuminate\Database\Eloquent\Model $item          The current CRUD model.
195
     * @param array                               $formattedData The form data.
196
     *
197
     * @return bool|null
198
     */
199
    private function createRelationsForItem($item, $formattedData)
200
    {
201
        if (! isset($formattedData['relations'])) {
202
            return false;
203
        }
204
        foreach ($formattedData['relations'] as $relationMethod => $relationData) {
205
            if (! isset($relationData['model'])) {
206
                continue;
207
            }
208
            $model = $relationData['model'];
209
            $relation = $item->{$relationMethod}();
210
211
            if ($relation instanceof HasOne) {
212
                if ($item->{$relationMethod} != null) {
213
                    $item->{$relationMethod}->update($relationData['values']);
214
                    $modelInstance = $item->{$relationMethod};
215
                } else {
216
                    $modelInstance = new $model($relationData['values']);
217
                    $relation->save($modelInstance);
218
                }
219
            }
220
221
            if (isset($relationData['relations'])) {
222
                $this->createRelationsForItem($modelInstance, ['relations' => $relationData['relations']]);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $modelInstance does not seem to be defined for all execution paths leading up to this point.
Loading history...
223
            }
224
        }
225
    }
226
227
    /**
228
     * Get a relation data array from the form data.
229
     * For each relation defined in the fields through the entity attribute, set the model, the parent model and the
230
     * attribute values.
231
     *
232
     * We traverse this relation array later to create the relations, for example:
233
     *
234
     * Current model HasOne Address, this Address (line_1, country_id) BelongsTo Country through country_id in Address Model.
235
     *
236
     * So when editing current model crud user have two fields address.line_1 and address.country (we infer country_id from relation)
237
     *
238
     * Those will be nested accordingly in this relation array, so address relation will have a nested relation with country.
239
     *
240
     *
241
     * @param array $data The form data.
242
     *
243
     * @return array The formatted relation data.
244
     */
245
    private function getRelationDataFromFormData($data)
246
    {
247
        $relation_fields = $this->getRelationFields();
248
        $relationData = [];
249
        foreach ($relation_fields as $relation_field) {
250
            $attributeKey = $this->parseRelationFieldNamesFromHtml([$relation_field])[0]['name'];
0 ignored issues
show
Bug introduced by
It seems like parseRelationFieldNamesFromHtml() 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

250
            $attributeKey = $this->/** @scrutinizer ignore-call */ parseRelationFieldNamesFromHtml([$relation_field])[0]['name'];
Loading history...
251
252
            if (! is_null(Arr::get($data, $attributeKey)) && isset($relation_field['pivot']) && $relation_field['pivot'] !== true) {
253
                $key = implode('.relations.', explode('.', $this->getOnlyRelationEntity($relation_field)));
254
                $fieldData = Arr::get($relationData, 'relations.'.$key, []);
255
                if (! array_key_exists('model', $fieldData)) {
256
                    $fieldData['model'] = $relation_field['model'];
257
                }
258
                if (! array_key_exists('parent', $fieldData)) {
259
                    $fieldData['parent'] = $this->getRelationModel($attributeKey, -1);
0 ignored issues
show
Bug introduced by
The method getRelationModel() does not exist on Backpack\CRUD\app\Library\CrudPanel\Traits\Create. Did you maybe mean getRelationFields()? ( Ignorable by Annotation )

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

259
                    /** @scrutinizer ignore-call */ 
260
                    $fieldData['parent'] = $this->getRelationModel($attributeKey, -1);

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...
260
                }
261
                $relatedAttribute = Arr::last(explode('.', $attributeKey));
262
                $fieldData['values'][$relatedAttribute] = Arr::get($data, $attributeKey);
263
264
                Arr::set($relationData, 'relations.'.$key, $fieldData);
265
            }
266
        }
267
268
        return $relationData;
269
    }
270
271
    public function getOnlyRelationEntity($relation_field)
272
    {
273
        $entity_array = explode('.', $relation_field['entity']);
274
275
        $relation_model = $this->getRelationModel($relation_field['entity'], -1);
276
277
        $related_method = Arr::last($entity_array);
278
279
        if (! method_exists($relation_model, $related_method)) {
280
            if (count($entity_array) <= 1) {
281
                return $relation_field['entity'];
282
            } else {
283
                array_pop($entity_array);
284
            }
285
286
            return implode('.', $entity_array);
287
        }
288
289
        return $relation_field['entity'];
290
    }
291
}
292