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 (#3250)
by Cristian
33:00 queued 16:22
created

Create::getRelationFields()   B

Complexity

Conditions 8
Paths 7

Size

Total Lines 20
Code Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

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

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

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

52
        return $this->/** @scrutinizer ignore-call */ fields();
Loading history...
53
    }
54
55
    /**
56
     * Get all fields with relation set (model key set on field).
57
     *
58
     * @return array The fields with model key set.
59
     */
60
    public function getRelationFields()
61
    {
62
        $fields = $this->fields();
63
        $relationFields = [];
64
65
        foreach ($fields as $field) {
66
            if (isset($field['model']) && $field['model'] !== false) {
67
                array_push($relationFields, $field);
68
            }
69
70
            if (isset($field['subfields']) &&
71
                is_array($field['subfields']) &&
72
                count($field['subfields'])) {
73
                foreach ($field['subfields'] as $subfield) {
74
                    array_push($relationFields, $subfield);
75
                }
76
            }
77
        }
78
79
        return $relationFields;
80
    }
81
82
    /**
83
     * Associate and dissociate.
84
     *
85
     * @param  Model
86
     * @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...
87
     * @return Model Model with relationships set up.
88
     */
89
    public function associateBelongsToRelations($item, array $data)
90
    {
91
        $belongsToFields = $this->getFieldsWithRelationType('BelongsTo');
0 ignored issues
show
Bug introduced by
It seems like getFieldsWithRelationType() 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

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

237
            $attributeKey = $this->/** @scrutinizer ignore-call */ parseRelationFieldNamesFromHtml([$relation_field])[0]['name'];
Loading history...
238
239
            if (! is_null(Arr::get($data, $attributeKey)) && isset($relation_field['pivot']) && $relation_field['pivot'] !== true) {
240
                $key = implode('.relations.', explode('.', $this->getOnlyRelationEntity($relation_field)));
241
                $fieldData = Arr::get($relationData, 'relations.'.$key, []);
242
                if (! array_key_exists('model', $fieldData)) {
243
                    $fieldData['model'] = $relation_field['model'];
244
                }
245
                if (! array_key_exists('parent', $fieldData)) {
246
                    $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

246
                    /** @scrutinizer ignore-call */ 
247
                    $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...
247
                }
248
                $relatedAttribute = Arr::last(explode('.', $attributeKey));
249
                $fieldData['values'][$relatedAttribute] = Arr::get($data, $attributeKey);
250
251
                Arr::set($relationData, 'relations.'.$key, $fieldData);
252
            }
253
        }
254
255
        return $relationData;
256
    }
257
258
    public function getOnlyRelationEntity($relation_field)
259
    {
260
        $entity_array = explode('.', $relation_field['entity']);
261
262
        $relation_model = $this->getRelationModel($relation_field['entity'], -1);
263
264
        $related_method = Arr::last($entity_array);
265
266
        if (! method_exists($relation_model, $related_method)) {
267
            if (count($entity_array) <= 1) {
268
                return $relation_field['entity'];
269
            } else {
270
                array_pop($entity_array);
271
            }
272
273
            return implode('.', $entity_array);
274
        }
275
276
        return $relation_field['entity'];
277
    }
278
}
279