Passed
Push — master ( d7b3c7...b5c04f )
by Mario
02:17
created

WorksWithRelations::updateOrCreateHasMany()   B

Complexity

Conditions 6
Paths 10

Size

Total Lines 31
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 21
dl 0
loc 31
rs 8.9617
c 0
b 0
f 0
cc 6
nc 10
nop 3
1
<?php
2
3
namespace RafflesArgentina\ResourceController\Traits;
4
5
use Lang;
6
7
use Illuminate\Http\Request;
8
use Illuminate\Database\Eloquent\Model;
9
use Illuminate\Database\Eloquent\Relations\Relation;
10
use Illuminate\Database\Eloquent\Relations\{HasOne, MorphOne, BelongsTo};
11
use Illuminate\Database\Eloquent\Relations\{HasMany, MorphMany, MorphToMany, BelongsToMany};
12
use Illuminate\Database\Eloquent\MassAssignmentException;
13
14
use RafflesArgentina\ResourceController\Exceptions\ResourceControllerException;
15
16
trait WorksWithRelations
17
{
18
    /**
19
     * Update or create relations handling array type request data.
20
     *
21
     * @param Request $request The Request object.
22
     * @param Model   $model   The eloquent model.
23
     *
24
     * @return void
25
     */
26
    public function updateOrCreateRelations(Request $request, Model $model)
27
    {
28
        $parameterBag = $request->request;
29
        foreach ($parameterBag->all() as $name => $attributes) {
30
            if (is_array($request->{$name})) {
31
                $this->_checkRelationExists($model, $name);
32
33
                $relation = $model->{$name}();
34
                $this->handleRelations($attributes, $model, $relation);
35
            }
36
        }
37
    }
38
39
    /**
40
     * Handle relations.
41
     *
42
     * @param array    $fillable The relation fillable.
43
     * @param Model    $model    The eloquent model.
44
     * @param Relation $relation The eloquent relation.
45
     *
46
     * @return void
47
     */
48
    protected function handleRelations(array $fillable, Model $model, Relation $relation)
49
    {
50
        switch (true) {
51
        case $relation instanceof HasOne || $relation instanceof MorphOne:
52
            $this->updateOrCreateHasOne($fillable, $model, $relation);
53
            break;
54
        case $relation instanceof BelongsTo:
55
            $this->updateOrCreateBelongsToOne($fillable, $model, $relation);
56
            break;
57
        case $relation instanceof HasMany || $relation instanceof MorphMany:
58
            $this->updateOrCreateHasMany($fillable, $model, $relation);
59
            break;
60
        case $relation instanceof BelongsToMany || $relation instanceof MorphToMany:
61
            $this->updateOrCreateBelongsToMany($fillable, $model, $relation);
62
            break;
63
        }
64
    }
65
66
    /**
67
     * HasOne relation updateOrCreate logic.
68
     *
69
     * @param array    $fillable The relation fillable.
70
     * @param Model    $model    The eloquent model.
71
     * @param Relation $relation The eloquent relation.
72
     *
73
     * @return Model
74
     */
75
    protected function updateOrCreateHasOne(array $fillable, Model $model, Relation $relation)
76
    {
77
        try {
78
            if (array_key_exists('id', $fillable)) {
79
                $id = $fillable['id'];
80
            } else {
81
                $id = '';
82
            }
83
84
            return $relation->updateOrCreate(['id' => $id], array_except($fillable, ['id']));
85
        } catch (\Exception $e) {
86
            $message = $this->storeFailedMessage($e->getMessage());
0 ignored issues
show
Bug introduced by
It seems like storeFailedMessage() 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

86
            /** @scrutinizer ignore-call */ 
87
            $message = $this->storeFailedMessage($e->getMessage());
Loading history...
87
            throw new ResourceControllerException($message);
88
        }
89
    }
90
91
    /**
92
     * BelongsToOne relation updateOrCreate logic.
93
     *
94
     * @param array    $fillable The relation fillable.
95
     * @param Model    $model    The eloquent model.
96
     * @param Relation $relation The eloquent relation.
97
     *
98
     * @return Model
99
     */
100
    protected function updateOrCreateBelongsToOne(array $fillable, Model $model, Relation $relation)
101
    {
102
        try {
103
            $related = $relation->getRelated();
104
105
            if (!$relation->first()) {
106
                $record = $relation->associate($related->create($fillable));
107
                $model->save();
108
            } else {
109
                $record = $relation->update($fillable);
110
            }
111
112
            return $record;
113
        } catch (\Exception $e) {
114
            $message = $this->storeFailedMessage($e->getMessage());
115
            throw new ResourceControllerException($message);
116
        }
117
    }
118
119
    /**
120
     * HasMany relation updateOrCreate logic.
121
     *
122
     * @param array    $fillable The relation fillable.
123
     * @param Model    $model    The eloquent model.
124
     * @param Relation $relation The eloquent relation.
125
     *
126
     * @return array
127
     */
128
    protected function updateOrCreateHasMany(array $fillable, Model $model, Relation $relation)
129
    {
130
        $records = [];
131
132
        if (count($fillable) > 1) {
133
            foreach ($fillable as $fields) {
134
                try {
135
                    if (array_key_exists('id', $fields)) {
136
                        $id = $fields['id'];
137
                    } else {
138
                        $id = '';
139
                    }
140
141
                    $record = $relation->updateOrCreate(['id' => $id], array_except($fields, ['id']));
142
                    array_push($records, $record);
143
                } catch (\Exception $e) {
144
                    $message = $this->storeFailedMessage($e->getMessage());
145
                    throw new ResourceControllerException($message);
146
                }
147
            }
148
        } else {
149
            try {
150
                $record = $this->updateOrCreateHasOne($fillable, $model, $relation);
151
                array_push($records, $record);
152
            } catch (\Exception $e) {
153
                $message = $this->storeFailedMessage($e->getMessage());
154
                throw new ResourceControllerException($message);
155
            }
156
        }
157
158
        return $records;
159
    }
160
161
    /**
162
     * BelongsToMany relation updateOrCreate logic.
163
     *
164
     * @param array    $fillable The relation fillable.
165
     * @param Model    $model    The eloquent model.
166
     * @param Relation $relation The eloquent relation.
167
     *
168
     * @return array
169
     */
170
    protected function updateOrCreateBelongsToMany(array $fillable, Model $model, Relation $relation)
171
    {
172
        $keys = [];
173
        $records = [];
174
175
        $related = $relation->getRelated();
176
177
        if (count($fillable) > 1) {
178
            foreach ($fillable as $fields) {
179
                try {
180
                    if (array_key_exists('id', $fields)) {
181
                        $id = $fields['id'];
182
                    } else {
183
                        $id = '';
184
                    }
185
186
                    $record = $related->updateOrCreate(['id' => $id], array_except($fields, ['id']));
187
188
                    array_push($keys, $record->id);
189
                    array_push($records, $record);
190
                } catch (\Exception $e) {
191
                    $message = $this->storeFailedMessage($e->getMessage());
192
                    throw new ResourceControllerException($message);
193
                }
194
            }
195
        } else {
196
            try {
197
                if (array_key_exists('id', $fillable)) {
198
                    $id = $fillable['id'];
199
                } else {
200
                    $id = '';
201
                }
202
203
                $record = $related->updateOrCreate(['id' => $id], array_except($fillable, ['id']));
204
205
                array_push($keys, $record->id);
206
                array_push($records, $record);
207
            } catch (\Exception $e) {
208
                $message = $this->storeFailedMessage($e->getMessage());
209
                throw new ResourceControllerException($message);
210
            }
211
        }
212
213
        try {
214
            $relation->sync($keys);
215
        } catch (\Exception $e) {
216
            $message = $this->storeFailedMessage($e->getMessage());
217
            throw new ResourceControllerException($message);
218
        }
219
220
        return $records;
221
    }
222
223
    /**
224
     * Throw an exception if array type request data is not named after an existent Eloquent relation.
225
     *
226
     * @param Model  $model        The eloquent model.
227
     * @param string $relationName The eloquent relation name.
228
     *
229
     * @throws MassAssignmentException
230
     *
231
     * @return void
232
     */
233
    private function _checkRelationExists(Model $model, string $relationName)
234
    {
235
        if (!method_exists($model, $relationName) || !$model->{$relationName}() instanceof Relation) {
236
            if (Lang::has('resource-controller.data2relationinexistent')) {
237
                $message = trans('resource-controller.data2relationinexistent', ['relationName' => $relationName]);
238
            } else {
239
                $message = "Array type request data '{$relationName}' must be named after an existent relation.";
240
            }
241
242
            throw new MassAssignmentException($message);
243
        }
244
    }
245
}
246