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
Pull Request — master (#3196)
by
unknown
15:40
created

Create::createRelationsForItem()   C

Complexity

Conditions 13
Paths 21

Size

Total Lines 57
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 13
eloc 41
c 1
b 0
f 0
nc 21
nop 2
dl 0
loc 57
rs 6.6166

How to fix   Long Method    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\Database\Eloquent\Relations\BelongsTo;
6
use Illuminate\Database\Eloquent\Relations\HasOne;
7
use Illuminate\Database\Eloquent\Relations\HasMany;
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
        $item = $this->model->create(Arr::except($data, $nn_relationships));
33
34
        // if there are any relationships available, also sync those
35
        $this->createRelations($item, $data);
36
37
        return $item;
38
    }
39
40
    /**
41
     * Get all fields needed for the ADD NEW ENTRY form.
42
     *
43
     * @return array The fields with attributes and fake attributes.
44
     */
45
    public function getCreateFields()
46
    {
47
        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

47
        return $this->/** @scrutinizer ignore-call */ fields();
Loading history...
48
    }
49
50
    /**
51
     * Get all fields with relation set (model key set on field).
52
     *
53
     * @return array The fields with model key set.
54
     */
55
    public function getRelationFields()
56
    {
57
        $fields = $this->fields();
58
        $relationFields = [];
59
60
        foreach ($fields as $field) {
61
            if (isset($field['model']) && $field['model'] !== false) {
62
                array_push($relationFields, $field);
63
            }
64
65
            if (isset($field['subfields']) &&
66
                is_array($field['subfields']) &&
67
                count($field['subfields'])) {
68
                foreach ($field['subfields'] as $subfield) {
69
                    array_push($relationFields, $subfield);
70
                }
71
            }
72
        }
73
74
        return $relationFields;
75
    }
76
77
    /**
78
     * Get all fields with n-n relation set (pivot table is true).
79
     *
80
     * @return array The fields with n-n relationships.
81
     */
82
    public function getRelationFieldsWithPivot()
83
    {
84
        $all_relation_fields = $this->getRelationFields();
85
86
        return Arr::where($all_relation_fields, function ($value, $key) {
87
            return isset($value['pivot']) && $value['pivot'];
88
        });
89
    }
90
91
    /**
92
     * Create the relations for the current model.
93
     *
94
     * @param \Illuminate\Database\Eloquent\Model $item The current CRUD model.
95
     * @param array                               $data The form data.
96
     */
97
    public function createRelations($item, $data)
98
    {
99
        $this->syncPivot($item, $data);
100
        $this->createOneToOneRelations($item, $data);
101
    }
102
103
    /**
104
     * Sync the declared many-to-many associations through the pivot field.
105
     *
106
     * @param \Illuminate\Database\Eloquent\Model $model The current CRUD model.
107
     * @param array                               $data  The form data.
108
     */
109
    public function syncPivot($model, $data)
110
    {
111
        $fields_with_relationships = $this->getRelationFields();
112
113
        foreach ($fields_with_relationships as $key => $field) {
114
            if (isset($field['pivot']) && $field['pivot']) {
115
                $values = isset($data[$field['name']]) ? $data[$field['name']] : [];
116
117
                // if a JSON was passed instead of an array, turn it into an array
118
                if (is_string($values)) {
119
                    $values = json_decode($values);
120
                }
121
122
                $relation_data = [];
123
                foreach ($values as $pivot_id) {
124
                    $pivot_data = [];
125
126
                    if (isset($field['pivotFields'])) {
127
                        foreach ($field['pivotFields'] as $pivot_field_name) {
128
                            $pivot_data[$pivot_field_name] = $data[$pivot_field_name][$pivot_id];
129
                        }
130
                    }
131
                    $relation_data[$pivot_id] = $pivot_data;
132
                }
133
134
                $model->{$field['name']}()->sync($relation_data);
135
            }
136
137
            if (isset($field['morph']) && $field['morph'] && isset($data[$field['name']])) {
138
                $values = $data[$field['name']];
139
                $model->{$field['name']}()->sync($values);
140
            }
141
        }
142
    }
143
144
    /**
145
     * Create any existing one to one relations for the current model from the form data.
146
     *
147
     * @param \Illuminate\Database\Eloquent\Model $item The current CRUD model.
148
     * @param array                               $data The form data.
149
     */
150
    private function createOneToOneRelations($item, $data)
151
    {
152
        $relationData = $this->getRelationDataFromFormData($data);
153
        $this->createRelationsForItem($item, $relationData);
154
    }
155
156
    /**
157
     * Create any existing one to one or one to many relations for the current model from the relation data.
158
     *
159
     * @param \Illuminate\Database\Eloquent\Model $item          The current CRUD model.
160
     * @param array                               $formattedData The form data.
161
     *
162
     * @return bool|null
163
     */
164
    private function createRelationsForItem($item, $formattedData)
165
    {
166
        if (! isset($formattedData['relations'])) {
167
            return false;
168
        }
169
        foreach ($formattedData['relations'] as $relationMethod => $relationData) {
170
            if (! isset($relationData['model'])) {
171
                continue;
172
            }
173
            $model = $relationData['model'];
174
            $relation = $item->{$relationMethod}();
175
176
            if ($relation instanceof BelongsTo) {
177
                $modelInstance = $model::find($relationData['values'])->first();
178
                if ($modelInstance != null) {
179
                    $relation->associate($modelInstance)->save();
180
                } else {
181
                    $relation->dissociate()->save();
182
                }
183
            } elseif ($relation instanceof HasOne) {
184
                if ($item->{$relationMethod} != null) {
185
                    $item->{$relationMethod}->update($relationData['values']);
186
                    $modelInstance = $item->{$relationMethod};
187
                } else {
188
                    $modelInstance = new $model($relationData['values']);
189
                    $relation->save($modelInstance);
190
                }
191
            } elseif ($relation instanceof HasMany) {
192
                $modelInstance = $relation->getRelated();
193
194
                if ($relationData['values'][$relationMethod] !== null) {
195
                    $modelInstance->whereIn($modelInstance->getKeyName(), $relationData['values'][$relationMethod])
196
                            ->update([$relation->getForeignKeyName() => $item->{$relation->getLocalKeyName()}]);
197
198
                    //we clear up any values that were removed from model relation.
199
                    if(!$modelInstance->isColumnNullable($relation->getForeignKeyName())) {
200
                        $modelInstance->whereNotIn($modelInstance->getKeyName(), $relationData['values'][$relationMethod])
201
                                ->where($relation->getForeignKeyName(), $item->{$relation->getLocalKeyName()})
202
                                ->delete();
203
                    }else{
204
                        $modelInstance->whereNotIn($modelInstance->getKeyName(), $relationData['values'][$relationMethod])
205
                                ->where($relation->getForeignKeyName(), $item->{$relation->getLocalKeyName()})
206
                                ->update([$relation->getForeignKeyName() => null]);
207
                    }
208
                }else{
209
                    //if the foreign key is not nullable we delete the record from the table.
210
                    if(!$modelInstance->isColumnNullable($relation->getForeignKeyName())) {
211
                        $modelInstance->where($relation->getForeignKeyName(), $item->{$relation->getLocalKeyName()})->delete();
212
                    }else{
213
                        $modelInstance->where($relation->getForeignKeyName(), $item->{$relation->getLocalKeyName()})
214
                                ->update([$relation->getForeignKeyName() => null]);
215
                    }
216
                }
217
            }
218
219
            if (isset($relationData['relations'])) {
220
                $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...
221
            }
222
        }
223
    }
224
225
    /**
226
     * Get a relation data array from the form data.
227
     * For each relation defined in the fields through the entity attribute, set the model, the parent model and the
228
     * attribute values.
229
     *
230
     * We traverse this relation array later to create the relations, for example:
231
     *
232
     * Current model HasOne Address, this Address (line_1, country_id) BelongsTo Country through country_id in Address Model.
233
     *
234
     * So when editing current model crud user have two fields address.line_1 and address.country (we infer country_id from relation)
235
     *
236
     * Those will be nested accordingly in this relation array, so address relation will have a nested relation with country.
237
     *
238
     *
239
     * @param array $data The form data.
240
     *
241
     * @return array The formatted relation data.
242
     */
243
    private function getRelationDataFromFormData($data)
244
    {
245
        $relation_fields = $this->getRelationFields();
246
        $relationData = [];
247
        foreach ($relation_fields as $relation_field) {
248
            $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

248
            $attributeKey = $this->/** @scrutinizer ignore-call */ parseRelationFieldNamesFromHtml([$relation_field])[0]['name'];
Loading history...
249
250
251
            if (isset($relation_field['pivot']) && $relation_field['pivot'] !== true) {
252
                $key = implode('.relations.', explode('.', $this->getOnlyRelationEntity($relation_field)));
253
254
                $fieldData = Arr::get($relationData, 'relations.'.$key, []);
255
256
                $fieldData['model'] = $relation_field['model'];
257
258
                $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

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