Passed
Push — 2.x ( d49df2...2d1b2e )
by Quentin
07:05
created

ModuleRepository   F

Complexity

Total Complexity 130

Size/Duplication

Total Lines 898
Duplicated Lines 0 %

Test Coverage

Coverage 62.54%

Importance

Changes 16
Bugs 0 Features 0
Metric Value
eloc 286
dl 0
loc 898
ccs 207
cts 331
cp 0.6254
rs 2
c 16
b 0
f 0
wmc 130

50 Methods

Rating   Name   Duplication   Size   Complexity  
A get() 0 16 4
B getCountByStatusSlug() 0 22 7
A listAll() 0 19 5
A updateOrCreate() 0 9 2
A getCountForAll() 0 4 1
A update() 0 15 1
A getCountForDraft() 0 4 1
A create() 0 19 1
A getById() 0 3 1
A getCountForTrash() 0 4 1
A createForPreview() 0 7 1
A setNewOrder() 0 5 1
A updateBasic() 0 40 5
A getCountForPublished() 0 4 1
A firstOrCreate() 0 3 1
A cmsSearch() 0 17 3
A afterRestore() 0 4 2
C cleanupFields() 0 34 14
A bulkDelete() 0 14 2
A shouldIgnoreFieldBeforeSave() 0 3 1
A bulkForceDelete() 0 19 2
A addIgnoreFieldsBeforeSave() 0 5 2
A bulkRestore() 0 19 2
A prepareFieldsBeforeCreate() 0 9 2
A getLikeOperator() 0 7 2
A isUniqueFeature() 0 3 1
A __call() 0 3 1
A updateOneToMany() 0 14 5
A duplicate() 0 23 3
A getFormFields() 0 9 2
A hasBehavior() 0 9 3
A afterUpdateBasic() 0 4 2
A beforeSave() 0 4 2
A updateMultiSelect() 0 3 1
A delete() 0 14 4
A isTranslatable() 0 3 1
A addLikeFilterScope() 0 5 3
A forceDelete() 0 11 2
A getReservedFields() 0 7 1
A afterDelete() 0 4 2
A prepareFieldsBeforeSave() 0 9 2
A getModelRepository() 0 7 2
A afterSave() 0 4 2
A traitsMethods() 0 14 1
A addRelationFilterScope() 0 8 2
C filter() 0 32 12
A restore() 0 11 2
A searchIn() 0 8 4
A hydrate() 0 7 2
A order() 0 11 3

How to fix   Complexity   

Complex Class

Complex classes like ModuleRepository often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use ModuleRepository, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace A17\Twill\Repositories;
4
5
use A17\Twill\Models\Behaviors\Sortable;
6
use A17\Twill\Repositories\Behaviors\HandleBrowsers;
7
use A17\Twill\Repositories\Behaviors\HandleDates;
8
use A17\Twill\Repositories\Behaviors\HandleFieldsGroups;
9
use A17\Twill\Repositories\Behaviors\HandleRepeaters;
10
use Illuminate\Support\Arr;
11
use Illuminate\Support\Collection;
12
use Illuminate\Support\Facades\App;
13
use Illuminate\Support\Facades\Config;
14
use Illuminate\Support\Facades\DB;
15
use Illuminate\Support\Facades\Log;
16
use Illuminate\Support\Str;
17
use PDO;
18
19
abstract class ModuleRepository
20
{
21
    use HandleDates, HandleBrowsers, HandleRepeaters, HandleFieldsGroups;
0 ignored issues
show
introduced by
The trait A17\Twill\Repositories\Behaviors\HandleBrowsers requires some properties which are not provided by A17\Twill\Repositories\ModuleRepository: $titleInBrowser, $id, $adminEditUrl, $title
Loading history...
Bug introduced by
The trait A17\Twill\Repositories\Behaviors\HandleRepeaters requires the property $id which is not provided by A17\Twill\Repositories\ModuleRepository.
Loading history...
22
23
    /**
24
     * @var \A17\Twill\Models\Model
25
     */
26
    protected $model;
27
28
    /**
29
     * @var string[]
30
     */
31
    protected $ignoreFieldsBeforeSave = [];
32
33
    /**
34
     * @var array
35
     */
36
    protected $countScope = [];
37
38
    /**
39
     * @var array
40
     */
41
    protected $fieldsGroups = [];
42
43
    /**
44
     * @param array $with
45
     * @param array $scopes
46
     * @param array $orders
47
     * @param int $perPage
48
     * @param bool $forcePagination
49
     * @return \Illuminate\Database\Eloquent\Collection
50
     */
51 12
    public function get($with = [], $scopes = [], $orders = [], $perPage = 20, $forcePagination = false)
52
    {
53 12
        $query = $this->model->with($with);
54
55 12
        $query = $this->filter($query, $scopes);
0 ignored issues
show
Bug introduced by
$query of type Illuminate\Database\Eloquent\Builder is incompatible with the type Illuminate\Database\Query\Builder expected by parameter $query of A17\Twill\Repositories\ModuleRepository::filter(). ( Ignorable by Annotation )

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

55
        $query = $this->filter(/** @scrutinizer ignore-type */ $query, $scopes);
Loading history...
56 12
        $query = $this->order($query, $orders);
57
58 12
        if (!$forcePagination && $this->model instanceof Sortable) {
59 3
            return $query->ordered()->get();
60
        }
61
62 10
        if ($perPage == -1) {
63
            return $query->get();
64
        }
65
66 10
        return $query->paginate($perPage);
67
    }
68
69
    /**
70
     * @param string $slug
71
     * @param array $scope
72
     * @return int
73
     */
74 6
    public function getCountByStatusSlug($slug, $scope = [])
75
    {
76 6
        $this->countScope = $scope;
77
78 6
        switch ($slug) {
79 6
            case 'all':
80 5
                return $this->getCountForAll();
81 6
            case 'published':
82 6
                return $this->getCountForPublished();
83 6
            case 'draft':
84 6
                return $this->getCountForDraft();
85 6
            case 'trash':
86 6
                return $this->getCountForTrash();
87
        }
88
89 5
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
90 5
            if (($count = $this->$method($slug)) !== false) {
91 5
                return $count;
92
            }
93
        }
94
95
        return 0;
96
    }
97
98
    /**
99
     * @return int
100
     */
101 5
    public function getCountForAll()
102
    {
103 5
        $query = $this->model->newQuery();
104 5
        return $this->filter($query, $this->countScope)->count();
0 ignored issues
show
Bug introduced by
$query of type Illuminate\Database\Eloquent\Builder is incompatible with the type Illuminate\Database\Query\Builder expected by parameter $query of A17\Twill\Repositories\ModuleRepository::filter(). ( Ignorable by Annotation )

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

104
        return $this->filter(/** @scrutinizer ignore-type */ $query, $this->countScope)->count();
Loading history...
105
    }
106
107
    /**
108
     * @return int
109
     */
110 5
    public function getCountForPublished()
111
    {
112 5
        $query = $this->model->newQuery();
113 5
        return $this->filter($query, $this->countScope)->published()->count();
0 ignored issues
show
Bug introduced by
$query of type Illuminate\Database\Eloquent\Builder is incompatible with the type Illuminate\Database\Query\Builder expected by parameter $query of A17\Twill\Repositories\ModuleRepository::filter(). ( Ignorable by Annotation )

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

113
        return $this->filter(/** @scrutinizer ignore-type */ $query, $this->countScope)->published()->count();
Loading history...
114
    }
115
116
    /**
117
     * @return int
118
     */
119 5
    public function getCountForDraft()
120
    {
121 5
        $query = $this->model->newQuery();
122 5
        return $this->filter($query, $this->countScope)->draft()->count();
0 ignored issues
show
Bug introduced by
$query of type Illuminate\Database\Eloquent\Builder is incompatible with the type Illuminate\Database\Query\Builder expected by parameter $query of A17\Twill\Repositories\ModuleRepository::filter(). ( Ignorable by Annotation )

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

122
        return $this->filter(/** @scrutinizer ignore-type */ $query, $this->countScope)->draft()->count();
Loading history...
123
    }
124
125
    /**
126
     * @return int
127
     */
128 5
    public function getCountForTrash()
129
    {
130 5
        $query = $this->model->newQuery();
131 5
        return $this->filter($query, $this->countScope)->onlyTrashed()->count();
0 ignored issues
show
Bug introduced by
$query of type Illuminate\Database\Eloquent\Builder is incompatible with the type Illuminate\Database\Query\Builder expected by parameter $query of A17\Twill\Repositories\ModuleRepository::filter(). ( Ignorable by Annotation )

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

131
        return $this->filter(/** @scrutinizer ignore-type */ $query, $this->countScope)->onlyTrashed()->count();
Loading history...
132
    }
133
134
    /**
135
     * @param $id
136
     * @param array $with
137
     * @param array $withCount
138
     * @return \A17\Twill\Models\Model
139
     * @throws \Illuminate\Database\Eloquent\ModelNotFoundException
140
     */
141 17
    public function getById($id, $with = [], $withCount = [])
142
    {
143 17
        return $this->model->with($with)->withCount($withCount)->findOrFail($id);
144
    }
145
146
    /**
147
     * @param string $column
148
     * @param array $orders
149
     * @param null $exceptId
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $exceptId is correct as it would always require null to be passed?
Loading history...
150
     * @return \Illuminate\Support\Collection
151
     */
152
    public function listAll($column = 'title', $orders = [], $exceptId = null)
153
    {
154
        $query = $this->model->newQuery();
155
156
        if ($exceptId) {
0 ignored issues
show
introduced by
$exceptId is of type null, thus it always evaluated to false.
Loading history...
157
            $query = $query->where($this->model->getTable() . '.id', '<>', $exceptId);
158
        }
159
160
        if ($this->model instanceof Sortable) {
161
            $query = $query->ordered();
162
        } elseif (!empty($orders)) {
163
            $query = $this->order($query, $orders);
0 ignored issues
show
Bug introduced by
$query of type Illuminate\Database\Eloquent\Builder is incompatible with the type Illuminate\Database\Query\Builder expected by parameter $query of A17\Twill\Repositories\ModuleRepository::order(). ( Ignorable by Annotation )

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

163
            $query = $this->order(/** @scrutinizer ignore-type */ $query, $orders);
Loading history...
164
        }
165
166
        if ($this->model->isTranslatable()) {
167
            $query = $query->withTranslation();
168
        }
169
170
        return $query->get()->pluck($column, 'id');
171
    }
172
173
    /**
174
     * @param $search
175
     * @param array $fields
176
     * @return \Illuminate\Database\Eloquent\Collection
177
     */
178
    public function cmsSearch($search, $fields = [])
179
    {
180
        $query = $this->model->latest();
181
182
        $translatedAttributes = $this->model->translatedAttributes ?? [];
0 ignored issues
show
Bug introduced by
The property translatedAttributes does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
183
184
        foreach ($fields as $field) {
185
            if (in_array($field, $translatedAttributes)) {
186
                $query->orWhereHas('translations', function ($q) use ($field, $search) {
187
                    $q->where($field, $this->getLikeOperator(), "%{$search}%");
188
                });
189
            } else {
190
                $query->orWhere($field, $this->getLikeOperator(), "%{$search}%");
191
            }
192
        }
193
194
        return $query->get();
195
    }
196
197
    /**
198
     * @param $attributes
199
     * @param $fields
200
     * @return \A17\Twill\Models\Model
201
     */
202
    public function firstOrCreate($attributes, $fields = [])
203
    {
204
        return $this->model->where($attributes)->first() ?? $this->create($attributes + $fields);
205
    }
206
207
    /**
208
     * @param string[] $fields
209
     * @return \A17\Twill\Models\Model
210
     */
211 27
    public function create($fields)
212
    {
213
        return DB::transaction(function () use ($fields) {
214 27
            $original_fields = $fields;
215
216 27
            $fields = $this->prepareFieldsBeforeCreate($fields);
217
218 27
            $object = $this->model->create(Arr::except($fields, $this->getReservedFields()));
219
220 27
            $this->beforeSave($object, $original_fields);
221
222 27
            $fields = $this->prepareFieldsBeforeSave($object, $fields);
223
224 27
            $object->save();
225
226 27
            $this->afterSave($object, $fields);
227
228 27
            return $object;
229 27
        }, 3);
230
    }
231
232
    /**
233
     * @param array $fields
234
     * @return \A17\Twill\Models\Model
235
     */
236 2
    public function createForPreview($fields)
237
    {
238 2
        $fields = $this->prepareFieldsBeforeCreate($fields);
239
240 2
        $object = $this->model->newInstance(Arr::except($fields, $this->getReservedFields()));
241
242 2
        return $this->hydrate($object, $fields);
243
    }
244
245
    /**
246
     * @param array $attributes
247
     * @param array $fields
248
     * @return \A17\Twill\Models\Model
249
     */
250
    public function updateOrCreate($attributes, $fields)
251
    {
252
        $object = $this->model->where($attributes)->first();
253
254
        if (!$object) {
255
            return $this->create($fields);
256
        }
257
258
        $this->update($object->id, $fields);
259
    }
260
261
    /**
262
     * @param mixed $id
263
     * @param array $fields
264
     * @return void
265
     */
266 11
    public function update($id, $fields)
267
    {
268
        DB::transaction(function () use ($id, $fields) {
269 11
            $object = $this->model->findOrFail($id);
270
271 11
            $this->beforeSave($object, $fields);
272
273 11
            $fields = $this->prepareFieldsBeforeSave($object, $fields);
274
275 11
            $object->fill(Arr::except($fields, $this->getReservedFields()));
276
277 11
            $object->save();
278
279 11
            $this->afterSave($object, $fields);
280 11
        }, 3);
281 11
    }
282
283
    /**
284
     * @param mixed $id
285
     * @param array $values
286
     * @param array $scopes
287
     * @return mixed
288
     */
289 4
    public function updateBasic($id, $values, $scopes = [])
290
    {
291
        return DB::transaction(function () use ($id, $values, $scopes) {
292
            // apply scopes if no id provided
293 4
            if (is_null($id)) {
294 1
                $query = $this->model->query();
295
296 1
                foreach ($scopes as $column => $value) {
297
                    $query->where($column, $value);
298
                }
299
300 1
                $query->update($values);
301
302
                $query->get()->each(function ($object) use ($values) {
303
                    $this->afterUpdateBasic($object, $values);
304 1
                });
305
306 1
                return true;
307
            }
308
309
            // apply to all ids if array of ids provided
310 3
            if (is_array($id)) {
311
                $query = $this->model->whereIn('id', $id);
312
                $query->update($values);
313
314
                $query->get()->each(function ($object) use ($values) {
315
                    $this->afterUpdateBasic($object, $values);
316
                });
317
318
                return true;
319
            }
320
321 3
            if (($object = $this->model->find($id)) != null) {
322 2
                $object->update($values);
323 2
                $this->afterUpdateBasic($object, $values);
324 2
                return true;
325
            }
326
327 1
            return false;
328 4
        }, 3);
329
    }
330
331
    /**
332
     * @param array $ids
333
     * @return void
334
     */
335 2
    public function setNewOrder($ids)
336
    {
337
        DB::transaction(function () use ($ids) {
338 2
            $this->model->setNewOrder($ids);
339 2
        }, 3);
340 1
    }
341
342
    /**
343
     * @param mixed $id
344
     * @return mixed
345
     */
346
    public function duplicate($id, $titleColumnKey = 'title')
347
    {
348
349
        if (($object = $this->model->find($id)) === null) {
350
            return false;
351
        }
352
353
        if (($revision = $object->revisions()->orderBy('created_at', 'desc')->first()) === null) {
354
            return false;
355
        }
356
357
        $revisionInput = json_decode($revision->payload, true);
0 ignored issues
show
Bug introduced by
The property payload does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
358
        $baseInput = collect($revisionInput)->only([
359
            $titleColumnKey,
360
            'slug',
361
            'languages'
362
        ])->filter()->toArray();
363
364
        $newObject = $this->create($baseInput);
365
366
        $this->update($newObject->id, $revisionInput);
367
368
        return $newObject;
369
    }
370
371
    /**
372
     * @param mixed $id
373
     * @return mixed
374
     */
375 2
    public function delete($id)
376
    {
377
        return DB::transaction(function () use ($id) {
378 2
            if (($object = $this->model->find($id)) === null) {
379
                return false;
380
            }
381
382 2
            if (!method_exists($object, 'canDeleteSafely') || $object->canDeleteSafely()) {
383 2
                $object->delete();
384 2
                $this->afterDelete($object);
385 2
                return true;
386
            }
387
            return false;
388 2
        }, 3);
389
    }
390
391
    /**
392
     * @param array $ids
393
     * @return mixed
394
     */
395 17
    public function bulkDelete($ids)
396
    {
397
        return DB::transaction(function () use ($ids) {
398
            try {
399
                Collection::make($ids)->each(function ($id) {
400
                    $this->delete($id);
401 17
                });
402
            } catch (\Exception $e) {
403
                Log::error($e);
404
                return false;
405
            }
406
407 17
            return true;
408 17
        }, 3);
409
    }
410
411
    /**
412
     * @param mixed $id
413
     * @return mixed
414
     */
415
    public function forceDelete($id)
416
    {
417
        return DB::transaction(function () use ($id) {
418
            if (($object = $this->model->onlyTrashed()->find($id)) === null) {
419
                return false;
420
            } else {
421
                $object->forceDelete();
422
                $this->afterDelete($object);
423
                return true;
424
            }
425
        }, 3);
426
    }
427
428
    /**
429
     * @param mixed $id
430
     * @return mixed
431
     */
432
    public function bulkForceDelete($ids)
433
    {
434
        return DB::transaction(function () use ($ids) {
435
            try {
436
                $query = $this->model->onlyTrashed()->whereIn('id', $ids);
437
                $objects = $query->get();
438
439
                $query->forceDelete();
440
441
                $objects->each(function ($object) {
442
                    $this->afterDelete($object);
443
                });
444
            } catch (\Exception $e) {
445
                Log::error($e);
446
                return false;
447
            }
448
449
            return true;
450
        }, 3);
451
    }
452
453
    /**
454
     * @param mixed $id
455
     * @return mixed
456
     */
457 2
    public function restore($id)
458
    {
459
        return DB::transaction(function () use ($id) {
460 2
            if (($object = $this->model->withTrashed()->find($id)) != null) {
461 1
                $object->restore();
462 1
                $this->afterRestore($object);
463 1
                return true;
464
            }
465
466 1
            return false;
467 2
        }, 3);
468
    }
469
470
    /**
471
     * @param array $ids
472
     * @return mixed
473
     */
474
    public function bulkRestore($ids)
475
    {
476
        return DB::transaction(function () use ($ids) {
477
            try {
478
                $query = $this->model->withTrashed()->whereIn('id', $ids);
479
                $objects = $query->get();
480
481
                $query->restore();
482
483
                $objects->each(function ($object) {
484
                    $this->afterRestore($object);
485
                });
486
            } catch (\Exception $e) {
487
                Log::error($e);
488
                return false;
489
            }
490
491
            return true;
492
        }, 3);
493
    }
494
495
    /**
496
     * @param \A17\Twill\Models\Model $object
497
     * @param array $fields
498
     * @return array
499
     */
500 28
    public function cleanupFields($object, $fields)
501
    {
502 28
        if (property_exists($this->model, 'checkboxes')) {
503 21
            foreach ($this->model->checkboxes as $field) {
0 ignored issues
show
Bug introduced by
The property checkboxes does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
504 21
                if (!$this->shouldIgnoreFieldBeforeSave($field)) {
505 21
                    if (!isset($fields[$field])) {
506 5
                        $fields[$field] = false;
507
                    } else {
508 21
                        $fields[$field] = !empty($fields[$field]);
509
                    }
510
                }
511
            }
512
        }
513
514 28
        if (property_exists($this->model, 'nullable')) {
515
            foreach ($this->model->nullable as $field) {
0 ignored issues
show
Bug introduced by
The property nullable does not seem to exist on A17\Twill\Models\Model. Are you sure there is no database migration missing?

Checks if undeclared accessed properties appear in database migrations and if the creating migration is correct.

Loading history...
516
                if (!isset($fields[$field]) && !$this->shouldIgnoreFieldBeforeSave($field)) {
517
                    $fields[$field] = null;
518
                }
519
            }
520
        }
521
522 28
        foreach ($fields as $key => $value) {
523 28
            if (!$this->shouldIgnoreFieldBeforeSave($key)) {
524 28
                if (is_array($value) && empty($value)) {
525 7
                    $fields[$key] = null;
526
                }
527 28
                if ($value === '') {
528
                    $fields[$key] = null;
529
                }
530
            }
531
        }
532
533 28
        return $fields;
534
    }
535
536
    /**
537
     * @param array $fields
538
     * @return array
539
     */
540 22
    public function prepareFieldsBeforeCreate($fields)
541
    {
542 22
        $fields = $this->cleanupFields(null, $fields);
543
544 22
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
545 22
            $fields = $this->$method($fields);
546
        }
547
548 22
        return $fields;
549
    }
550
551
    /**
552
     * @param \A17\Twill\Models\Model $object
553
     * @param array $fields
554
     * @return string[]
555
     */
556 27
    public function prepareFieldsBeforeSave($object, $fields)
557
    {
558 27
        $fields = $this->cleanupFields($object, $fields);
559
560 27
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
561 27
            $fields = $this->$method($object, $fields);
562
        }
563
564 27
        return $fields;
565
    }
566
567
    /**
568
     * @param \A17\Twill\Models\Model $object
569
     * @param array $fields
570
     * @return void
571
     */
572 2
    public function afterUpdateBasic($object, $fields)
573
    {
574 2
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
575
            $this->$method($object, $fields);
576
        }
577 2
    }
578
579
    /**
580
     * @param \A17\Twill\Models\Model $object
581
     * @param array $fields
582
     * @return void
583
     */
584 27
    public function beforeSave($object, $fields)
585
    {
586 27
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
587 17
            $this->$method($object, $fields);
588
        }
589 27
    }
590
591
    /**
592
     * @param \A17\Twill\Models\Model $object
593
     * @param array $fields
594
     * @return void
595
     */
596 27
    public function afterSave($object, $fields)
597
    {
598 27
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
599 27
            $this->$method($object, $fields);
600
        }
601 27
    }
602
603
    /**
604
     * @param \A17\Twill\Models\Model $object
605
     * @return void
606
     */
607 2
    public function afterDelete($object)
608
    {
609 2
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
610 2
            $this->$method($object);
611
        }
612 2
    }
613
614
    /**
615
     * @param \A17\Twill\Models\Model $object
616
     * @return void
617
     */
618 1
    public function afterRestore($object)
619
    {
620 1
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
621 1
            $this->$method($object);
622
        }
623 1
    }
624
625
    /**
626
     * @param \A17\Twill\Models\Model $object
627
     * @param array $fields
628
     * @return \A17\Twill\Models\Model
629
     */
630 3
    public function hydrate($object, $fields)
631
    {
632 3
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
633 3
            $object = $this->$method($object, $fields);
634
        }
635
636 3
        return $object;
637
    }
638
639
    /**
640
     * @param \A17\Twill\Models\Model $object
641
     * @return array
642
     */
643 5
    public function getFormFields($object)
644
    {
645 5
        $fields = $object->attributesToArray();
646
647 5
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
648 5
            $fields = $this->$method($object, $fields);
649
        }
650
651 5
        return $fields;
652
    }
653
654
    /**
655
     * @param \Illuminate\Database\Query\Builder $query
656
     * @param array $scopes
657
     * @return \Illuminate\Database\Query\Builder
658
     */
659 12
    public function filter($query, array $scopes = [])
660
    {
661 12
        $likeOperator = $this->getLikeOperator();
662
663 12
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
664 11
            $this->$method($query, $scopes);
665
        }
666
667 12
        unset($scopes['search']);
668
669 12
        if (isset($scopes['exceptIds'])) {
670 2
            $query->whereNotIn($this->model->getTable() . '.id', $scopes['exceptIds']);
671 2
            unset($scopes['exceptIds']);
672
        }
673
674 12
        foreach ($scopes as $column => $value) {
675 5
            if (method_exists($this->model, 'scope' . ucfirst($column))) {
676 1
                $query->$column();
677
            } else {
678 4
                if (is_array($value)) {
679 2
                    $query->whereIn($column, $value);
680 2
                } elseif ($column[0] == '%') {
681
                    $value && ($value[0] == '!') ? $query->where(substr($column, 1), "not $likeOperator", '%' . substr($value, 1) . '%') : $query->where(substr($column, 1), $likeOperator, '%' . $value . '%');
682 2
                } elseif (isset($value[0]) && $value[0] == '!') {
683
                    $query->where($column, '<>', substr($value, 1));
684 2
                } elseif ($value !== '') {
685 2
                    $query->where($column, $value);
686
                }
687
            }
688
        }
689
690 12
        return $query;
691
    }
692
693
    /**
694
     * @param \Illuminate\Database\Query\Builder $query
695
     * @param array $orders
696
     * @return \Illuminate\Database\Query\Builder
697
     */
698 12
    public function order($query, array $orders = [])
699
    {
700 12
        foreach ($this->traitsMethods(__FUNCTION__) as $method) {
701 7
            $this->$method($query, $orders);
702
        }
703
704 12
        foreach ($orders as $column => $direction) {
705 5
            $query->orderBy($column, $direction);
706
        }
707
708 12
        return $query;
709
    }
710
711
    /**
712
     * @param \A17\Twill\Models\Model $object
713
     * @param array $fields
714
     * @param string $relationship
715
     * @param string $formField
716
     * @param string $attribute
717
     * @return void
718
     */
719
    public function updateOneToMany($object, $fields, $relationship, $formField, $attribute)
720
    {
721
        if (isset($fields[$formField])) {
722
            foreach ($fields[$formField] as $id) {
723
                $object->$relationship()->updateOrCreate([$attribute => $id]);
724
            }
725
726
            foreach ($object->$relationship as $relationshipObject) {
727
                if (!in_array($relationshipObject->$attribute, $fields[$formField])) {
728
                    $relationshipObject->delete();
729
                }
730
            }
731
        } else {
732
            $object->$relationship()->delete();
733
        }
734
    }
735
736
    /**
737
     * @param \A17\Twill\Models\Model $object
738
     * @param array $fields
739
     * @param string $relationship
740
     * @return void
741
     */
742
    public function updateMultiSelect($object, $fields, $relationship)
743
    {
744
        $object->$relationship()->sync($fields[$relationship] ?? []);
745
    }
746
747
    /**
748
     * @param \Illuminate\Database\Query\Builder $query
749
     * @param array $scopes
750
     * @param string $scopeField
751
     * @param string $scopeRelation
752
     * @return void
753
     */
754 9
    public function addRelationFilterScope($query, &$scopes, $scopeField, $scopeRelation)
755
    {
756 9
        if (isset($scopes[$scopeField])) {
757
            $id = $scopes[$scopeField];
758
            $query->whereHas($scopeRelation, function ($query) use ($id, $scopeField) {
759
                $query->where($scopeField, $id);
760
            });
761
            unset($scopes[$scopeField]);
762
        }
763 9
    }
764
765
    /**
766
     * @param \Illuminate\Database\Query\Builder $query
767
     * @param array $scopes
768
     * @param string $scopeField
769
     * @return void
770
     */
771
    public function addLikeFilterScope($query, &$scopes, $scopeField)
772
    {
773
        if (isset($scopes[$scopeField]) && is_string($scopes[$scopeField])) {
774
            $query->where($scopeField, $this->getLikeOperator(), '%' . $scopes[$scopeField] . '%');
775
            unset($scopes[$scopeField]);
776
        }
777
    }
778
779
    /**
780
     * @param \Illuminate\Database\Query\Builder $query
781
     * @param array $scopes
782
     * @param string $scopeField
783
     * @param string[] $orFields
784
     */
785 5
    public function searchIn($query, &$scopes, $scopeField, $orFields = [])
786
    {
787
788 5
        if (isset($scopes[$scopeField]) && is_string($scopes[$scopeField])) {
789
            $query->where(function ($query) use (&$scopes, $scopeField, $orFields) {
790 2
                foreach ($orFields as $field) {
791 2
                    $query->orWhere($field, $this->getLikeOperator(), '%' . $scopes[$scopeField] . '%');
792 2
                    unset($scopes[$field]);
793
                }
794 2
            });
795
        }
796 5
    }
797
798
    /**
799
     * @return bool
800
     */
801 2
    public function isUniqueFeature()
802
    {
803 2
        return false;
804
    }
805
806
    /**
807
     * @param array $ignore
808
     * @return void
809
     */
810
    public function addIgnoreFieldsBeforeSave($ignore = [])
811
    {
812
        $this->ignoreFieldsBeforeSave = is_array($ignore)
0 ignored issues
show
introduced by
The condition is_array($ignore) is always true.
Loading history...
813
        ? array_merge($this->ignoreFieldsBeforeSave, $ignore)
814
        : array_merge($this->ignoreFieldsBeforeSave, [$ignore]);
815
    }
816
817
    /**
818
     * @param string $ignore
819
     * @return bool
820
     */
821 28
    public function shouldIgnoreFieldBeforeSave($ignore)
822
    {
823 28
        return in_array($ignore, $this->ignoreFieldsBeforeSave);
824
    }
825
826
    /**
827
     * @return string[]
828
     */
829 28
    public function getReservedFields()
830
    {
831
        return [
832 28
            'medias',
833
            'browsers',
834
            'repeaters',
835
            'blocks',
836
        ];
837
    }
838
839
    /**
840
     * @param string $relation
841
     * @param \A17\Twill\Models\Model|null $model
842
     * @return mixed
843
     */
844
    protected function getModelRepository($relation, $model = null)
845
    {
846
        if (!$model) {
847
            $model = ucfirst(Str::singular($relation));
848
        }
849
850
        return App::make(Config::get('twill.namespace') . "\\Repositories\\" . ucfirst($model) . "Repository");
851
    }
852
853
    /**
854
     * @param string|null $method
855
     * @return array
856
     */
857 34
    protected function traitsMethods(string $method = null)
858
    {
859 34
        $method = $method ?? debug_backtrace()[1]['function'];
860
861 34
        $traits = array_values(class_uses_recursive(get_called_class()));
862
863 34
        $uniqueTraits = array_unique(array_map('class_basename', $traits));
864
865
        $methods = array_map(function (string $trait) use ($method) {
866 34
            return $method . $trait;
867 34
        }, $uniqueTraits);
868
869
        return array_filter($methods, function (string $method) {
870 34
            return method_exists(get_called_class(), $method);
871 34
        });
872
    }
873
874
    /**
875
     * @return string
876
     */
877 12
    private function getLikeOperator()
878
    {
879 12
        if (DB::connection()->getPDO()->getAttribute(PDO::ATTR_DRIVER_NAME) === 'pgsql') {
880
            return 'ILIKE';
881
        }
882
883 12
        return 'LIKE';
884
    }
885
886
    /**
887
     * @param string $method
888
     * @param array $parameters
889
     * @return mixed
890
     */
891 2
    public function __call($method, $parameters)
892
    {
893 2
        return $this->model->$method(...$parameters);
894
    }
895
896
    /**
897
     * @param string $behavior
898
     * @return boolean
899
     */
900 29
    public function hasBehavior($behavior)
901
    {
902 29
        $hasBehavior = classHasTrait($this, 'A17\Twill\Repositories\Behaviors\Handle' . ucfirst($behavior));
903
904 29
        if (Str::startsWith($behavior, 'translation')) {
905 28
            $hasBehavior = $hasBehavior && $this->model->isTranslatable();
906
        }
907
908 29
        return $hasBehavior;
909
    }
910
911
    /**
912
     * @return boolean
913
     */
914 6
    public function isTranslatable($column)
915
    {
916 6
        return $this->model->isTranslatable($column);
917
    }
918
}
919