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-delete-button-on-edit-oper... ( 37dd25...b7e06f )
by
unknown
12:02
created

Update::hideDeleteButton()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
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 Update
9
{
10
    /*
11
    |--------------------------------------------------------------------------
12
    |                                   UPDATE
13
    |--------------------------------------------------------------------------
14
    */
15
16
    /**
17
     * Update a row in the database.
18
     *
19
     * @param  int  $id  The entity's id
20
     * @param  array  $input  All inputs to be updated.
21
     * @return object
22
     */
23
    public function update($id, $input)
24
    {
25
        $item = $this->model->findOrFail($id);
26
27
        [$directInputs, $relationInputs] = $this->splitInputIntoDirectAndRelations($input);
0 ignored issues
show
Bug introduced by
It seems like splitInputIntoDirectAndRelations() 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
        [$directInputs, $relationInputs] = $this->splitInputIntoDirectAndRelations($input);
Loading history...
28
        $updated = $item->update($directInputs);
0 ignored issues
show
Unused Code introduced by
The assignment to $updated is dead and can be removed.
Loading history...
29
        $this->createRelationsForItem($item, $relationInputs);
0 ignored issues
show
Bug introduced by
It seems like createRelationsForItem() 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

29
        $this->/** @scrutinizer ignore-call */ 
30
               createRelationsForItem($item, $relationInputs);
Loading history...
30
31
        return $item;
32
    }
33
34
    /**
35
     * Get all fields needed for the EDIT ENTRY form.
36
     *
37
     * @param  int  $id  The id of the entry that is being edited.
38
     * @return array The fields with attributes, fake attributes and values.
39
     */
40
    public function getUpdateFields($id = false)
41
    {
42
        $fields = $this->fields();
0 ignored issues
show
Bug introduced by
The method fields() does not exist on Backpack\CRUD\app\Library\CrudPanel\Traits\Update. Did you maybe mean getSubfieldsValues()? ( Ignorable by Annotation )

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

42
        /** @scrutinizer ignore-call */ 
43
        $fields = $this->fields();

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...
43
        $entry = ($id != false) ? $this->getEntry($id) : $this->getCurrentEntry();
0 ignored issues
show
Bug introduced by
It seems like getCurrentEntry() 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

43
        $entry = ($id != false) ? $this->getEntry($id) : $this->/** @scrutinizer ignore-call */ getCurrentEntry();
Loading history...
Bug introduced by
It seems like getEntry() 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

43
        $entry = ($id != false) ? $this->/** @scrutinizer ignore-call */ getEntry($id) : $this->getCurrentEntry();
Loading history...
Bug Best Practice introduced by
It seems like you are loosely comparing $id of type false|integer against false; this is ambiguous if the integer can be zero. Consider using a strict comparison !== instead.
Loading history...
44
45
        foreach ($fields as &$field) {
46
            $field['value'] = $field['value'] ?? $this->getModelAttributeValue($entry, $field);
47
        }
48
49
        // always have a hidden input for the entry id
50
        if (! array_key_exists('id', $fields)) {
51
            $fields['id'] = [
52
                'name'  => $entry->getKeyName(),
53
                'value' => $entry->getKey(),
54
                'type'  => 'hidden',
55
            ];
56
        }
57
58
        return $fields;
59
    }
60
61
    /**
62
     * Get the value of the 'name' attribute from the declared relation model in the given field.
63
     *
64
     * @param  \Illuminate\Database\Eloquent\Model  $model  The current CRUD model.
65
     * @param  array  $field  The CRUD field array.
66
     * @return mixed The value of the 'name' attribute from the relation model.
67
     */
68
    private function getModelAttributeValue($model, $field)
69
    {
70
        $model = $model->withFakes();
71
72
        $fieldEntity = $field['entity'] ?? false;
73
        $fakeField = $field['fake'] ?? false;
74
75
        if ($fieldEntity && ! $fakeField) {
76
            return $this->getModelAttributeValueFromRelationship($model, $field);
0 ignored issues
show
Bug introduced by
It seems like $model can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $model of Backpack\CRUD\app\Librar...ValueFromRelationship() does only seem to accept Illuminate\Database\Eloquent\Model, maybe add an additional type check? ( Ignorable by Annotation )

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

76
            return $this->getModelAttributeValueFromRelationship(/** @scrutinizer ignore-type */ $model, $field);
Loading history...
77
        }
78
79
        if (is_string($field['name'])) {
80
            return $model->{$field['name']};
81
        }
82
83
        if (is_array($field['name'])) {
84
            $result = [];
85
            foreach ($field['name'] as $name) {
86
                $result[] = $model->{$name};
87
            }
88
89
            return $result;
90
        }
91
    }
92
93
    /**
94
     * Returns the value of the given attribute in the relationship.
95
     * It takes into account nested relationships.
96
     *
97
     * @param  \Illuminate\Database\Eloquent\Model  $model
98
     * @param  array  $field
99
     * @return mixed
100
     */
101
    private function getModelAttributeValueFromRelationship($model, $field)
102
    {
103
        [$relatedModel, $relationMethod] = $this->getModelAndMethodFromEntity($model, $field);
104
105
        if (! method_exists($relatedModel, $relationMethod)) {
106
            return $relatedModel->{$relationMethod};
107
        }
108
109
        $relation = $relatedModel->{$relationMethod}();
110
        $relationType = Str::afterLast(get_class($relation), '\\');
111
112
        switch ($relationType) {
113
            case 'MorphMany':
114
            case 'HasMany':
115
            case 'BelongsToMany':
116
            case 'MorphToMany':
117
                $relationModels = $relatedModel->{$relationMethod};
118
                $result = collect();
119
120
                foreach ($relationModels as $model) {
0 ignored issues
show
introduced by
$model is overwriting one of the parameters of this function.
Loading history...
121
                    $model = $this->setupRelatedModelLocale($model);
122
                    // when subfields are NOT set we don't need to get any more values
123
                    // we just return the plain models as we only need the ids
124
                    if (! isset($field['subfields'])) {
125
                        $result->push($model);
126
                        continue;
127
                    }
128
                    // when subfields are set we need to parse their values so they can be displayed
129
                    switch ($relationType) {
130
                        case 'HasMany':
131
                        case 'MorphMany':
132
                            // we will get model direct attributes and merge with subfields values.
133
                            $directAttributes = $this->getModelWithFakes($model)->getAttributes();
134
                            $result->push(array_merge($directAttributes, $this->getSubfieldsValues($field['subfields'], $model)));
0 ignored issues
show
Bug introduced by
array_merge($directAttri...['subfields'], $model)) of type array is incompatible with the type Illuminate\Support\TValue expected by parameter $values of Illuminate\Support\Collection::push(). ( Ignorable by Annotation )

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

134
                            $result->push(/** @scrutinizer ignore-type */ array_merge($directAttributes, $this->getSubfieldsValues($field['subfields'], $model)));
Loading history...
135
                            break;
136
137
                        case 'BelongsToMany':
138
                        case 'MorphToMany':
139
                            // for any given model, we grab the attributes that belong to our pivot table.
140
                            $item = $model->{$relation->getPivotAccessor()}->getAttributes();
141
                            $item[$relationMethod] = $model->getKey();
142
                            $result->push($item);
143
                            break;
144
                    }
145
                }
146
147
                return $result;
148
                break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
149
            case 'HasOne':
150
            case 'MorphOne':
151
                if (! method_exists($relatedModel, $relationMethod)) {
152
                    return;
153
                }
154
155
                $model = $relatedModel->{$relationMethod};
156
157
                if (! $model) {
158
                    return;
159
                }
160
161
                $model = $this->setupRelatedModelLocale($model);
162
                $model = $this->getModelWithFakes($model);
163
164
                // if `entity` contains a dot here it means developer added a main HasOne/MorphOne relation with dot notation
165
                if (Str::contains($field['entity'], '.')) {
166
                    return $model->{Str::afterLast($field['entity'], '.')};
167
                }
168
169
                // when subfields exists developer used the repeatable interface to manage this relation
170
                if ($field['subfields']) {
171
                    return [$this->getSubfieldsValues($field['subfields'], $model)];
172
                }
173
174
                return $this->getModelWithFakes($model);
175
176
                break;
177
            case 'BelongsTo':
178
                if ($relatedModel->{$relationMethod}) {
179
                    return $relatedModel->{$relationMethod}->getKey();
180
                }
181
182
                return $relatedModel->{$relationMethod};
183
                break;
184
            default:
185
                return $relatedModel->{$relationMethod};
186
        }
187
    }
188
189
    /**
190
     * Set the locale on the related models.
191
     *
192
     * @param  \Illuminate\Database\Eloquent\Model  $model
193
     * @return \Illuminate\Database\Eloquent\Model
194
     */
195
    private function setupRelatedModelLocale($model)
196
    {
197
        if (method_exists($model, 'translationEnabled') && $model->translationEnabled()) {
198
            $locale = request('_locale', \App::getLocale());
199
            if (in_array($locale, array_keys($model->getAvailableLocales()))) {
0 ignored issues
show
Bug introduced by
It seems like $model->getAvailableLocales() can also be of type Illuminate\Database\Eloquent\Builder; however, parameter $array of array_keys() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

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

199
            if (in_array($locale, array_keys(/** @scrutinizer ignore-type */ $model->getAvailableLocales()))) {
Loading history...
200
                $model->setLocale($locale);
201
            }
202
        }
203
204
        return $model;
205
    }
206
207
    /**
208
     * This function checks if the provided model uses the CrudTrait.
209
     * If IT DOES it adds the fakes to the model attributes.
210
     * Otherwise just return the model back.
211
     *
212
     * @param  \Illuminate\Database\Eloquent\Model  $model
213
     * @return \Illuminate\Database\Eloquent\Model
214
     */
215
    private function getModelWithFakes($model)
216
    {
217
        if (in_array(\Backpack\CRUD\app\Models\Traits\CrudTrait::class, class_uses_recursive($model))) {
218
            return $model->withFakes();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $model->withFakes() also could return the type Illuminate\Database\Eloquent\Builder which is incompatible with the documented return type Illuminate\Database\Eloquent\Model.
Loading history...
219
        }
220
221
        return $model;
222
    }
223
224
    /**
225
     * Returns the model and the method from the relation string starting from the provided model.
226
     *
227
     * @param  Illuminate\Database\Eloquent\Model  $model
0 ignored issues
show
Bug introduced by
The type Backpack\CRUD\app\Librar...Database\Eloquent\Model was not found. Did you mean Illuminate\Database\Eloquent\Model? If so, make sure to prefix the type with \.
Loading history...
228
     * @param  array  $field
229
     * @return array
230
     */
231
    private function getModelAndMethodFromEntity($model, $field)
232
    {
233
        // HasOne and MorphOne relations contains the field in the relation string. We want only the relation part.
234
        $relationEntity = $this->getOnlyRelationEntity($field);
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

234
        /** @scrutinizer ignore-call */ 
235
        $relationEntity = $this->getOnlyRelationEntity($field);
Loading history...
235
236
        $relationArray = explode('.', $relationEntity);
237
238
        $relatedModel = array_reduce(array_splice($relationArray, 0, -1), function ($obj, $method) {
239
            // if the string ends with `_id` we strip it out
240
            $method = Str::endsWith($method, '_id') ? Str::replaceLast('_id', '', $method) : $method;
241
242
            return $obj->{$method} ? $obj->{$method} : $obj;
243
        }, $model);
244
245
        $relationMethod = Str::afterLast($relationEntity, '.');
246
247
        return [$relatedModel, $relationMethod];
248
    }
249
250
    /**
251
     * Return the subfields values from the related model.
252
     *
253
     * @param  array  $subfields
254
     * @param  \Illuminate\Database\Eloquent\Model  $relatedModel
255
     * @return array
256
     */
257
    private function getSubfieldsValues($subfields, $relatedModel)
258
    {
259
        $result = [];
260
        foreach ($subfields as $subfield) {
261
            $name = is_string($subfield) ? $subfield : $subfield['name'];
0 ignored issues
show
Unused Code introduced by
The assignment to $name is dead and can be removed.
Loading history...
262
            // if the subfield name does not contain a dot we just need to check
263
            // if it has subfields and return the result accordingly.
264
            foreach ((array) $subfield['name'] as $name) {
265
                if (! Str::contains($name, '.')) {
266
                    // when subfields are present, $relatedModel->{$name} returns a model instance
267
                    // otherwise returns the model attribute.
268
                    if ($relatedModel->{$name}) {
269
                        if (isset($subfield['subfields'])) {
270
                            $result[$name] = [$relatedModel->{$name}->only(array_column($subfield['subfields'], 'name'))];
271
                        } else {
272
                            $result[$name] = $relatedModel->{$name};
273
                        }
274
                    }
275
                } else {
276
                    // if the subfield name contains a dot, we are going to iterate through
277
                    // those parts to get the last connected part and parse it for returning.
278
                    // $iterator would be either a string (the attribute in model, eg: street)
279
                    // or a model instance (eg: AddressModel)
280
                    $iterator = $relatedModel;
281
282
                    foreach (explode('.', $name) as $part) {
283
                        $iterator = $iterator->$part;
284
                    }
285
286
                    Arr::set($result, $name, (is_a($iterator, 'Illuminate\Database\Eloquent\Model', true) ? $this->getModelWithFakes($iterator)->getAttributes() : $iterator));
287
                }
288
            }
289
        }
290
291
        return $result;
292
    }
293
}
294