HandleRepeaters::getFormFieldsForRepeater()   F
last analyzed

Complexity

Conditions 18
Paths 2312

Size

Total Lines 104
Code Lines 65

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 289.0894

Importance

Changes 0
Metric Value
cc 18
eloc 65
nc 2312
nop 5
dl 0
loc 104
ccs 3
cts 52
cp 0.0577
crap 289.0894
rs 0.7
c 0
b 0
f 0

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 A17\Twill\Repositories\Behaviors;
4
5
use A17\Twill\Services\Blocks\BlockCollection;
6
use Carbon\Carbon;
7
use Illuminate\Support\Arr;
8
use Illuminate\Support\Collection;
9
use Illuminate\Support\Str;
10
11
trait HandleRepeaters
12
{
13
    /**
14
     * All repeaters used in the model, as an array of repeater names:
15
     * [
16
     *     'article_repeater',
17
     *     'page_repeater'
18
     * ].
19
     *
20
     * When only the repeater name is given, the model and relation are inferred from the name.
21
     * The parameters can also be overridden with an array:
22
     * [
23
     *     'article_repeater',
24
     *     'page_repeater' => [
25
     *         'model' => 'Page',
26
     *         'relation' => 'pages'
27
     *     ]
28
     * ]
29
     *
30
     * @var array
31
     */
32
    protected $repeaters = [];
33
34
    /**
35
     * @param \A17\Twill\Models\Model $object
36
     * @param array $fields
37
     * @return void
38
     */
39 31
    public function afterSaveHandleRepeaters($object, $fields)
40
    {
41 31
        foreach ($this->getRepeaters() as $repeater) {
42
            $this->updateRepeater($object, $fields, $repeater['relation'], $repeater['model'], $repeater['repeaterName']);
43
        }
44 31
    }
45
46
    /**
47
     * @param \A17\Twill\Models\Model $object
48
     * @param array $fields
49
     * @return array
50
     */
51 6
    public function getFormFieldsHandleRepeaters($object, $fields)
52
    {
53 6
        foreach ($this->getRepeaters() as $repeater) {
54
            $fields = $this->getFormFieldsForRepeater($object, $fields, $repeater['relation'], $repeater['model'], $repeater['repeaterName']);
55
        }
56
57 6
        return $fields;
58
    }
59
60
    /**
61
     * @param \A17\Twill\Models\Model $object
62
     * @param array $fields
63
     * @param string $relation
64
     * @param bool $keepExisting
65
     * @param \A17\Twill\Models\Model|null $model
66
     * @return void
67
     */
68
    public function updateRepeaterMany($object, $fields, $relation, $keepExisting = true, $model = null)
69
    {
70
        $relationFields = $fields['repeaters'][$relation] ?? [];
71
        $relationRepository = $this->getModelRepository($relation, $model);
0 ignored issues
show
Bug introduced by
It seems like getModelRepository() 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

71
        /** @scrutinizer ignore-call */ 
72
        $relationRepository = $this->getModelRepository($relation, $model);
Loading history...
72
73
        if (!$keepExisting) {
74
            $object->$relation()->each(function ($repeaterElement) {
75
                $repeaterElement->forceDelete();
76
            });
77
        }
78
79
        foreach ($relationFields as $relationField) {
80
            $newRelation = $relationRepository->create($relationField);
81
            $object->$relation()->attach($newRelation->id);
82
        }
83
    }
84
85
    /**
86
     * @param \A17\Twill\Models\Model $object
87
     * @param array $fields
88
     * @param string $relation
89
     * @param string|null $morph
90
     * @param \A17\Twill\Models\Model|null $model
91
     * @param string|null $repeaterName
92
     * @return void
93
     */
94
    public function updateRepeaterMorphMany($object, $fields, $relation, $morph = null, $model = null, $repeaterName = null)
95
    {
96
        if (!$repeaterName) {
97
            $repeaterName = $relation;
98
        }
99
100
        $relationFields = $fields['repeaters'][$repeaterName] ?? [];
101
        $relationRepository = $this->getModelRepository($relation, $model);
102
103
        $morph = $morph ?: $relation;
104
105
        $morphFieldType = $morph . '_type';
106
        $morphFieldId = $morph . '_id';
107
108
        // if no relation field submitted, soft deletes all associated rows
109
        if (!$relationFields) {
110
            $relationRepository->updateBasic(null, [
111
                'deleted_at' => Carbon::now(),
112
            ], [
113
                $morphFieldType => $object->getMorphClass(),
114
                $morphFieldId => $object->id,
115
            ]);
116
        }
117
118
        // keep a list of updated and new rows to delete (soft delete?) old rows that were deleted from the frontend
119
        $currentIdList = [];
120
121
        foreach ($relationFields as $index => $relationField) {
122
            $relationField['position'] = $index + 1;
123
            if (isset($relationField['id']) && Str::startsWith($relationField['id'], $relation)) {
124
                // row already exists, let's update
125
                $id = str_replace($relation . '-', '', $relationField['id']);
126
                $relationRepository->update($id, $relationField);
127
                $currentIdList[] = $id;
128
            } else {
129
                // new row, let's attach to our object and create
130
                unset($relationField['id']);
131
                $newRelation = $relationRepository->create($relationField);
132
                $object->$relation()->save($newRelation);
133
                $currentIdList[] = $newRelation['id'];
134
            }
135
        }
136
137
        foreach ($object->$relation->pluck('id') as $id) {
138
            if (!in_array($id, $currentIdList)) {
139
                $relationRepository->updateBasic(null, [
140
                    'deleted_at' => Carbon::now(),
141
                ], [
142
                    'id' => $id,
143
                ]);
144
            }
145
        }
146
    }
147
148
    /**
149
     * Given relation, model and repeaterName, retrieve the repeater data from request and update the database record.
150
     *
151
     * @param \A17\Twill\Models\Model $object
152
     * @param array $fields
153
     * @param string $relation
154
     * @param \A17\Twill\Models\Model|\A17\Twill\Repositories\ModuleRepository|null $modelOrRepository
155
     * @param string|null $repeaterName
156
     * @return void
157
     */
158
    public function updateRepeater($object, $fields, $relation, $modelOrRepository = null, $repeaterName = null)
159
    {
160
        if (!$repeaterName) {
161
            $repeaterName = $relation;
162
        }
163
164
        $relationFields = $fields['repeaters'][$repeaterName] ?? [];
165
166
        $relationRepository = $this->getModelRepository($relation, $modelOrRepository);
167
168
        // if no relation field submitted, soft deletes all associated rows
169
        if (!$relationFields) {
170
            $relationRepository->updateBasic(null, [
171
                'deleted_at' => Carbon::now(),
172
            ], [
173
                $this->model->getForeignKey() => $object->id,
174
            ]);
175
        }
176
177
        // keep a list of updated and new rows to delete (soft delete?) old rows that were deleted from the frontend
178
        $currentIdList = [];
179
180
        foreach ($relationFields as $index => $relationField) {
181
            $relationField['position'] = $index + 1;
182
            if (isset($relationField['id']) && Str::startsWith($relationField['id'], $relation)) {
183
                // row already exists, let's update
184
                $id = str_replace($relation . '-', '', $relationField['id']);
185
                $relationRepository->update($id, $relationField);
186
                $currentIdList[] = $id;
187
            } else {
188
                // new row, let's attach to our object and create
189
                $relationField[$this->model->getForeignKey()] = $object->id;
190
                unset($relationField['id']);
191
                $newRelation = $relationRepository->create($relationField);
192
                $currentIdList[] = $newRelation['id'];
193
            }
194
        }
195
196
        foreach ($object->$relation->pluck('id') as $id) {
197
            if (!in_array($id, $currentIdList)) {
198
                $relationRepository->updateBasic(null, [
199
                    'deleted_at' => Carbon::now(),
200
                ], [
201
                    'id' => $id,
202
                ]);
203
            }
204
        }
205
    }
206
207
    /**
208
     * Given relation, model and repeaterName, get the necessary fields for rendering a repeater
209
     *
210
     * @param \A17\Twill\Models\Model $object
211
     * @param array $fields
212
     * @param string $relation
213
     * @param \A17\Twill\Models\Model|\A17\Twill\Repositories\ModuleRepository|null $modelOrRepository
214
     * @param string|null $repeaterName
215
     * @return array
216
     */
217
    public function getFormFieldsForRepeater($object, $fields, $relation, $modelOrRepository = null, $repeaterName = null)
218
    {
219
        if (!$repeaterName) {
220
            $repeaterName = $relation;
221
        }
222
223
        $repeaters = [];
224
        $repeatersFields = [];
225
        $repeatersBrowsers = [];
226
        $repeatersMedias = [];
227
        $repeatersFiles = [];
228
        $relationRepository = $this->getModelRepository($relation, $modelOrRepository);
229
        $repeatersList = app(BlockCollection::class)->getRepeaterList()->keyBy('name');
230
231
        foreach ($object->$relation as $relationItem) {
232
            $repeaters[] = [
233
                'id' => $relation . '-' . $relationItem->id,
234
                'type' => $repeatersList[$repeaterName]['component'],
235
                'title' => $repeatersList[$repeaterName]['title'],
236
                'titleField' => $repeatersList[$repeaterName]['titleField'],
237
                'hideTitlePrefix' => $repeatersList[$repeaterName]['hideTitlePrefix'],
238
            ];
239
240
            $relatedItemFormFields = $relationRepository->getFormFields($relationItem);
241
            $translatedFields = [];
242
243
            if (isset($relatedItemFormFields['translations'])) {
244
                foreach ($relatedItemFormFields['translations'] as $key => $values) {
245
                    $repeatersFields[] = [
246
                        'name' => "blocks[$relation-$relationItem->id][$key]",
247
                        'value' => $values,
248
                    ];
249
250
                    $translatedFields[] = $key;
251
                }
252
            }
253
254
            if (isset($relatedItemFormFields['medias'])) {
255
                if (config('twill.media_library.translated_form_fields', false)) {
256
                    Collection::make($relatedItemFormFields['medias'])->each(function ($rolesWithMedias, $locale) use (&$repeatersMedias, $relation, $relationItem) {
257
                        $repeatersMedias[] = Collection::make($rolesWithMedias)->mapWithKeys(function ($medias, $role) use ($locale, $relation, $relationItem) {
258
                            return [
259
                                "blocks[$relation-$relationItem->id][$role][$locale]" => $medias,
260
                            ];
261
                        })->toArray();
262
                    });
263
                } else {
264
                    foreach ($relatedItemFormFields['medias'] as $key => $values) {
265
                        $repeatersMedias["blocks[$relation-$relationItem->id][$key]"] = $values;
266
                    }
267
                }
268
            }
269
270
            if (isset($relatedItemFormFields['files'])) {
271
                Collection::make($relatedItemFormFields['files'])->each(function ($rolesWithFiles, $locale) use (&$repeatersFiles, $relation, $relationItem) {
272
                    $repeatersFiles[] = Collection::make($rolesWithFiles)->mapWithKeys(function ($files, $role) use ($locale, $relation, $relationItem) {
273
                        return [
274
                            "blocks[$relation-$relationItem->id][$role][$locale]" => $files,
275
                        ];
276
                    })->toArray();
277
                });
278
            }
279
280
            if (isset($relatedItemFormFields['browsers'])) {
281
                foreach ($relatedItemFormFields['browsers'] as $key => $values) {
282
                    $repeatersBrowsers["blocks[$relation-$relationItem->id][$key]"] = $values;
283
                }
284
            }
285
286
            $itemFields = method_exists($relationItem, 'toRepeaterArray') ? $relationItem->toRepeaterArray() : Arr::except($relationItem->attributesToArray(), $translatedFields);
287
288
            foreach ($itemFields as $key => $value) {
289
                $repeatersFields[] = [
290
                    'name' => "blocks[$relation-$relationItem->id][$key]",
291
                    'value' => $value,
292
                ];
293
            }
294
295
            if (isset($relatedItemFormFields['repeaters'])) {
296
                foreach ($relatedItemFormFields['repeaters'] as $childRepeaterName => $childRepeaterItems) {
297
                    $fields['repeaters']["blocks-$relation-{$relationItem->id}_$childRepeaterName"] = $childRepeaterItems;
298
                    $repeatersFields = array_merge($repeatersFields, $relatedItemFormFields['repeaterFields'][$childRepeaterName]);
299
                    $repeatersMedias = array_merge($repeatersMedias, $relatedItemFormFields['repeaterMedias'][$childRepeaterName]);
300
                    $repeatersFiles = array_merge($repeatersFiles, $relatedItemFormFields['repeaterFiles'][$childRepeaterName]);
301
                    $repeatersBrowsers = array_merge($repeatersBrowsers, $relatedItemFormFields['repeaterBrowsers'][$childRepeaterName]);
302
                }
303 32
            }
304
        }
305 32
306
        if (!empty($repeatersMedias) && config('twill.media_library.translated_form_fields', false)) {
307
            $repeatersMedias = call_user_func_array('array_merge', $repeatersMedias);
308
        }
309
310
        if (!empty($repeatersFiles)) {
311
            $repeatersFiles = call_user_func_array('array_merge', $repeatersFiles);
312 32
        }
313
314
        $fields['repeaters'][$repeaterName] = $repeaters;
315
        $fields['repeaterFields'][$repeaterName] = $repeatersFields;
316
        $fields['repeaterMedias'][$repeaterName] = $repeatersMedias;
317
        $fields['repeaterFiles'][$repeaterName] = $repeatersFiles;
318
        $fields['repeaterBrowsers'][$repeaterName] = $repeatersBrowsers;
319
320
        return $fields;
321
    }
322
323
    /**
324
     * Get all repeaters' model and relation from the $repeaters attribute.
325
     * The missing information will be inferred by convention of Twill.
326
     *
327
     * @return \Illuminate\Support\Collection
328
     */
329
    protected function getRepeaters()
330
    {
331
        return collect($this->repeaters)->map(function ($repeater, $key) {
332
            $repeaterName = is_string($repeater) ? $repeater : $key;
333
            return [
334
                'relation' => !empty($repeater['relation']) ? $repeater['relation'] : $this->inferRelationFromRepeaterName($repeaterName),
335
                'model' => !empty($repeater['model']) ? $repeater['model'] : $this->inferModelFromRepeaterName($repeaterName),
336
                'repeaterName' => $repeaterName,
337
            ];
338
        })->values();
339
    }
340
341
    /**
342
     * Guess the relation name (shoud be lower camel case, ex. userGroup, contactOffice).
343
     *
344
     * @param string $repeaterName
345
     * @return string
346
     */
347
    protected function inferRelationFromRepeaterName(string $repeaterName): string
348
    {
349
        return Str::camel($repeaterName);
350
    }
351
352
    /**
353
     * Guess the model name (should be singular upper camel case, ex. User, ArticleType).
354
     *
355
     * @param string $repeaterName
356
     * @return string
357
     */
358
    protected function inferModelFromRepeaterName(string $repeaterName): string
359
    {
360
        return Str::studly(Str::singular($repeaterName));
361
    }
362
}
363