Passed
Push — master ( 1f307e...d7b3c7 )
by Mario
02:21
created

WorksWithRelations   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 212
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 32
eloc 89
dl 0
loc 212
rs 9.84
c 0
b 0
f 0

7 Methods

Rating   Name   Duplication   Size   Complexity  
A updateOrCreateBelongsToOne() 0 16 3
A updateOrCreateHasMany() 0 25 5
A _checkRelationExists() 0 10 4
A updateOrCreateHasOne() 0 13 3
A updateOrCreateRelations() 0 9 3
B handleRelations() 0 15 8
B updateOrCreateBelongsToMany() 0 40 6
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
        try {
131
            $records = [];
132
133
            if (count($fillable) > 1) {
134
                foreach ($fillable as $fields) {
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
                }
144
            } else {
145
                $record = $this->updateOrCreateHasOne($fillable, $model, $relation);
146
                array_push($records, $record);
147
            }
148
149
            return $records;
150
        } catch (\Exception $e) {
151
            $message = $this->storeFailedMessage($e->getMessage());
152
            throw new ResourceControllerException($message);
153
        }
154
    }
155
156
    /**
157
     * BelongsToMany relation updateOrCreate logic.
158
     *
159
     * @param array    $fillable The relation fillable.
160
     * @param Model    $model    The eloquent model.
161
     * @param Relation $relation The eloquent relation.
162
     *
163
     * @return array
164
     */
165
    protected function updateOrCreateBelongsToMany(array $fillable, Model $model, Relation $relation)
166
    {
167
        try {
168
            $keys = [];
169
            $records = [];
170
171
            $related = $relation->getRelated();
172
173
            if (count($fillable) > 1) {
174
                foreach ($fillable as $fields) {
175
                    if (array_key_exists('id', $fields)) {
176
                        $id = $fields['id'];
177
                    } else {
178
                        $id = '';
179
                    }
180
181
                    $record = $related->updateOrCreate(['id' => $id], array_except($fields, ['id']));
182
183
                    array_push($keys, $record->id);
184
                    array_push($records, $record);
185
                }
186
            } else {
187
                if (array_key_exists('id', $fillable)) {
188
                    $id = $fillable['id'];
189
                } else {
190
                    $id = '';
191
                }
192
193
                $record = $related->updateOrCreate(['id' => $id], array_except($fillable, ['id']));
194
195
                array_push($keys, $record->id);
196
                array_push($records, $record);
197
            }
198
199
            $relation->sync($keys);
200
201
            return $records;
202
        } catch (\Exception $e) {
203
            $message = $this->storeFailedMessage($e->getMessage());
204
            throw new ResourceControllerException($message);
205
        }
206
    }
207
208
    /**
209
     * Throw an exception if array type request data is not named after an existent Eloquent relation.
210
     *
211
     * @param Model  $model        The eloquent model.
212
     * @param string $relationName The eloquent relation name.
213
     *
214
     * @throws MassAssignmentException
215
     *
216
     * @return void
217
     */
218
    private function _checkRelationExists(Model $model, string $relationName)
219
    {
220
        if (!method_exists($model, $relationName) || !$model->{$relationName}() instanceof Relation) {
221
            if (Lang::has('resource-controller.data2relationinexistent')) {
222
                $message = trans('resource-controller.data2relationinexistent', ['relationName' => $relationName]);
223
            } else {
224
                $message = "Array type request data '{$relationName}' must be named after an existent relation.";
225
            }
226
227
            throw new MassAssignmentException($message);
228
        }
229
    }
230
}
231