CrudController::prepareFieldShow()   F
last analyzed

Complexity

Conditions 21
Paths 3904

Size

Total Lines 49

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 49
rs 0
c 0
b 0
f 0
cc 21
nc 3904
nop 2

How to fix   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 Eliurkis\Crud;
4
5
use App\Http\Controllers\Controller;
6
use DB;
7
use Illuminate\Database\Eloquent\Builder;
8
use Illuminate\Database\Eloquent\ModelNotFoundException;
9
use Illuminate\Database\QueryException;
10
use Illuminate\Http\Request;
11
use Illuminate\Support\Carbon;
12
use Illuminate\Support\Facades\Route;
13
14
class CrudController extends Controller
15
{
16
    protected $route;
17
    protected $entity;
18
    protected $entityInstance = null;
19
    protected $fields = [];
20
    protected $columns = [];
21
    protected $buttons = [
22
        'show',
23
        'create',
24
        'edit',
25
        'delete',
26
    ];
27
    protected $paginate = null;
28
    protected $searchable = [];
29
    protected $filters = [];
30
    protected $queryFilters = [];
31
    protected $orderBy = [];
32
    protected $orderByRaw = null;
33
    protected $filterRequire = [];
34
    protected $textsGeneral = [
35
        'list_title'   => 'Contents',
36
        'create_title' => '',
37
        'edit_title'   => '',
38
    ];
39
    protected $texts = [];
40
    protected $htmlFilters = [];
41
    protected $action = null;
42
    protected $formColsClasses = [
43
        'col-md-10 col-md-offset-1',
44
        'col-md-2',
45
        'col-md-10',
46
    ];
47
    protected $formCols = [
48
        'show'   => 2,
49
        'create' => 2,
50
        'edit'   => 2,
51
    ];
52
    protected $links = [];
53
    protected $listDisplay = [
54
        'action-buttons' => true,
55
    ];
56
    protected $actions = [];
57
58
    public function __construct($entity, $config = [])
59
    {
60
        $this->entity = $entity;
61
62
        $config = count($config) ? $config : config('crud.'.$this->route);
63
64
        if (is_array($config)) {
65
            foreach ($config as $key => $value) {
66
                $this->$key = $value;
67
            }
68
        }
69
70
        $this->actions = [
71
            'details' => function ($row, $route) {
72
                return '<a href="'.route($route.'.show', $row->{$row->getKeyName()}).'" class="btn-default btn btn-xs">
73
                            <i class="fas fa-eye"></i>
74
                        </a>';
75
            },
76
            'edit'    => function ($row, $route) {
77
                return '<a href="'.route($route.'.edit', $row->{$row->getKeyName()}).'" class="btn-primary btn btn-xs edit_element">
78
                            <i class="far fa-edit"></i>
79
                        </a>';
80
            },
81
            'delete'  => function ($row, $route) {
82
                return '<a href="'.route($route.'.destroy', $row->{$row->getKeyName()}).'"
83
                           class="btn-danger btn btn-xs delete_element"
84
                           onclick="return confirm(\''.trans('eliurkis::crud.confirmation_delete').'\');">
85
                            <i class="far fa-trash-alt"></i>
86
                        </a>';
87
            },
88
        ];
89
    }
90
91
    public function index(Request $request)
92
    {
93
        // If DataTable is activated
94
        if (isset($this->dataTableActivated)) {
95
            return $this->indexDataTable($request);
96
        }
97
98
        $entity = $this->entity;
99
100
        // Relation Fields
101
        if ($belongToFields = $this->getBelongToFields()) {
102
            $entity = $this->entity->with($belongToFields);
103
        }
104
105
        // Filters
106
        $entity = $this->filters($entity, $request);
107
108
        // Search
109
        $entity = $this->search($entity, $request);
110
111
        // Order By
112
        if (!empty($this->orderBy)) {
113
            foreach ($this->orderBy as $column => $direction) {
114
                $entity = $entity->orderBy($column, $direction);
115
            }
116
        }
117
118
        if ($this->orderByRaw) {
119
            $entity = $entity->orderByRaw($this->orderByRaw);
120
        }
121
122
        // Pagination
123
        $rows = $this->paginate > 0 ? $this->paginate($entity, $request) : $entity->get();
124
125
        // Sort By Rows
126
        if (!empty($this->sortBy)) {
127
            foreach ($this->sortBy as $column => $direction) {
128
                $rows = strtolower($direction) == 'desc'
129
                    ? $rows->sortByDesc($column)
130
                    : $rows->sortBy($column);
131
            }
132
        }
133
134
        // HTML Filters
135
        $this->htmlFilters();
136
137
        return view('crud::list', compact('rows'))
138
            ->with('fields', $this->fields)
139
            ->with('columns', $this->columns)
140
            ->with('searchable', $this->searchable)
141
            ->with('buttons', $this->buttons)
142
            ->with('paginate', $this->paginate)
143
            ->with('t', $this->texts)
144
            ->with('htmlFilters', $this->htmlFilters)
145
            ->with('listDisplay', $this->listDisplay)
146
            ->with('links', $this->prepareLinks())
147
            ->with('request', $request)
148
            ->with('actions', $this->actions)
149
            ->with('route', $this->route);
150
    }
151
152 View Code Duplication
    public function show($id)
0 ignored issues
show
Duplication introduced by Eliurkis Diaz
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
153
    {
154
        if (!$this->entityInstance) {
155
            $this->entityInstance = $this->entity->findOrFail($id);
156
        }
157
158
        $this->prepareFields();
159
160
        return view('crud::show')
161
            ->with('type', 'show')
162
            ->with('route', $this->route)
163
            ->with('t', $this->texts)
164
            ->with('formColsClasses', $this->formColsClasses)
165
            ->with('colsNumber', $this->formCols['show'])
166
            ->with('fieldsGroup', collect($this->fields)->split($this->formCols['show']))
167
            ->with('fields', $this->fields)
168
            ->with('data', $this->entityInstance);
169
    }
170
171
    public function create()
172
    {
173
        $this->prepareFields();
174
175
        return view('crud::create')
176
            ->with('type', 'create')
177
            ->with('route', $this->route)
178
            ->with('t', $this->texts)
179
            ->with('formColsClasses', $this->formColsClasses)
180
            ->with('links', $this->prepareLinks())
181
            ->with('colsNumber', $this->formCols['create'])
182
            ->with('fieldsGroup', collect($this->fields)->split($this->formCols['create']))
183
            ->with('fields', $this->fields);
184
    }
185
186
    protected function manageFiles($row, $request)
187
    {
188
        $mediaFiles = [];
189
190
        foreach ($this->fields as $fieldName => $field) {
191
            if (substr($field['type'], 0, 4) === 'file' && $request->file($fieldName)) {
192
                $customProperties = ['route' => $this->route, 'field' => $fieldName];
193
                if (isset($field['storage_path'])) {
194
                    $customProperties['storage_path'] = $field['storage_path'];
195
                }
196
                $mediaFiles[] = $row->addMedia($request->file($fieldName))
197
                    ->withCustomProperties($customProperties)
198
                    ->toMediaCollection($fieldName);
199
            }
200
        }
201
202
        return $mediaFiles;
203
    }
204
205
    public function store(Request $request)
206
    {
207
        $this->validateRequest($request, 'store');
208
209
        DB::beginTransaction();
210
211
        try {
212
            $row = $this->entity->create(array_merge($request->all(), $this->queryFilters));
213
            $this->updateForeignRelations($row, $request);
214
            $mediaFiles = $this->manageFiles($row, $request);
215
        } catch (QueryException $e) {
216
            \Log::error($e);
217
            if (config('app.debug')) {
218
                throw new \Exception($e);
219
            }
220
221
            return redirect()
222
                ->back()
223
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
224
        }
225
226
        DB::commit();
227
228
        event($this->route.'.store', [$row, $mediaFiles]);
229
230
        return redirect()
231
            ->route($this->route.'.index')
232
            ->with('success', isset($this->textsGeneral['save_action'])
233
                ? $this->textsGeneral['save_action']
234
                : trans('eliurkis::crud.save_action'));
235
    }
236
237 View Code Duplication
    public function edit($id)
0 ignored issues
show
Duplication introduced by Eliurkis Diaz
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
238
    {
239
        if (!$this->entityInstance) {
240
            $this->entityInstance = $this->entity->findOrFail($id);
241
        }
242
243
        $this->prepareFields();
244
245
        return view('crud::create')
246
            ->with('type', 'edit')
247
            ->with('route', $this->route)
248
            ->with('t', $this->texts)
249
            ->with('formColsClasses', $this->formColsClasses)
250
            ->with('links', $this->prepareLinks())
251
            ->with('colsNumber', $this->formCols['edit'])
252
            ->with('fieldsGroup', collect($this->fields)->split($this->formCols['edit']))
253
            ->with('fields', $this->fields)
254
            ->with('data', $this->entityInstance);
255
    }
256
257
    public function update(Request $request, $id)
258
    {
259
        $this->validateRequest($request, 'update');
260
261
        DB::beginTransaction();
262
263
        try {
264
            $row = $this->entity->findOrFail($id);
265
            $row->update(
266
                array_merge(
267
                    $request->all(),
268
                    $this->queryFilters
269
                )
270
            );
271
            $this->updateForeignRelations($row, $request);
272
            $mediaFiles = $this->manageFiles($row, $request);
273
        } catch (QueryException $e) {
274
            \Log::error($e);
275
            if (config('app.debug')) {
276
                throw new \Exception($e);
277
            }
278
279
            return redirect()
280
                ->back()
281
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
282
        }
283
284
        DB::commit();
285
286
        event($this->route.'.update', [$row, $mediaFiles]);
287
288
        return redirect()
289
            ->route($this->route.'.index', $this->getParamsFilters($row))
290
            ->with('success', isset($this->textsGeneral['save_action'])
291
                ? $this->textsGeneral['save_action']
292
                : trans('eliurkis::crud.save_action'));
293
    }
294
295
    public function destroy($id)
296
    {
297
        try {
298
            $row = $this->entity->findOrFail($id);
299
            $row->delete();
300
301
            event($this->route.'.destroy', [$row]);
302
        } catch (ModelNotFoundException $e) {
303
            return redirect()
304
                ->route($this->route.'.index')
305
                ->with('error', __('The element that you are trying to delete does not exist'));
306
        } catch (\Exception $e) {
307
            \Log::error($e);
308
            if (config('app.debug')) {
309
                throw new \Exception($e);
310
            }
311
312
            return redirect()
313
                ->route($this->route.'.index')
314
                ->with('error', __('An error occurred, try again'));
315
        }
316
317
        return redirect()
318
            ->route($this->route.'.index')
319
            ->with('success', isset($this->textsGeneral['delete_action'])
320
                ? $this->textsGeneral['delete_action']
321
                : trans('eliurkis::crud.delete_action'));
322
    }
323
324
    public function download($id, $fieldName)
325
    {
326
        if (!$this->entityInstance) {
327
            $this->entityInstance = $this->entity->findOrFail($id);
328
        }
329
330
        $media = $this->entityInstance->getMedia($fieldName)->last();
331
332
        if ($media && $media->disk === 's3') {
333
            $tempImage = tempnam(sys_get_temp_dir(), $media->file_name);
334
            copy($media->getTemporaryUrl(\Carbon::now()->addMinutes(5)), $tempImage);
335
336
            return response()->file($tempImage, ['Content-Type' => $media->mime_type]);
337
        }
338
339
        return $media;
340
    }
341
342
    /* Private Actions */
343
344
    /**
345
     * @param         $entity
346
     * @param Request $request
347
     *
348
     * @return mixed
349
     */
350
    protected function filters($entity, $request)
351
    {
352
        if ($request->query('filter')) {
353
            $filters = is_array($request->query('filter')) ? $request->query('filter') : [];
354
            foreach ($filters as $field => $value) {
355
                $entity = $entity->where($field, $value);
356
            }
357
        }
358
359
        if (count($this->queryFilters)) {
360
            foreach ($this->queryFilters as $field => $value) {
361
                if (is_array($value)) {
362
                    $entity = $entity->whereIn($field, $value);
363
                } else {
364
                    $entity = $entity->where($field, $value);
365
                }
366
            }
367
        }
368
369
        return $entity;
370
    }
371
372
    protected function htmlFilters()
373
    {
374
        $this->htmlFilters = [];
375
        if (count($this->filters)) {
376
            foreach ($this->filters as $filter) {
377
                // Build params
378
                $urlParams = \Input::query();
379
380
                // Default Value
381
                $this->fields[$filter]['config']['default_value'] = isset($urlParams['filter'][$filter])
382
                    ? $urlParams['filter'][$filter]
383
                    : null;
384
385
                // Create URL
386
                if (isset($urlParams['filter'][$filter])) {
387
                    unset($urlParams['filter'][$filter]);
388
                }
389
                $this->fields[$filter]['attributes']['data-filter-url'] = route($this->route.'.index', $urlParams)
390
                    .(count($urlParams) ? '&' : '?');
391
392
                // Create array
393
                $this->action = 'list';
394
                $this->htmlFilters[$filter] = $this->prepareField($filter);
395
            }
396
        }
397
    }
398
399
    /**
400
     * @param         $entity
401
     * @param Request $request
402
     *
403
     * @return mixed
404
     */
405
    protected function paginate($entity, $request)
406
    {
407
        $rows = $entity->paginate($this->paginate);
408
409
        if ($request->get('q') != '') {
410
            $rows->appends(['q' => $request->get('q')]);
411
        }
412
413
        if ($request->get('filter')) {
414
            foreach ($request->get('filter') as $field => $value) {
415
                $rows->appends(['filter['.$field.']' => $value]);
416
            }
417
        }
418
419
        return $rows;
420
    }
421
422
    /**
423
     * @param         $entity
424
     * @param Request $request
425
     *
426
     * @return mixed
427
     */
428
    protected function search($entity, $request)
429
    {
430
        if ($request->get('q') != '') {
431
            $searchableCols = isset($this->searchable['columns']) ? $this->searchable['columns'] : $this->searchable;
432
433
            $entity = $entity->where(function (Builder $query) use ($request, $searchableCols) {
434
                foreach ($searchableCols as $field) {
435
                    $query->orWhere($field, 'like', '%'.$request->get('q').'%');
436
                }
437
            });
438
439
            if (isset($this->searchable['joins'])) {
440
                foreach ($this->searchable['joins'] as $table => $joinFields) {
441
                    $entity = $entity->join($table, $joinFields[0], '=', $joinFields[1]);
442
                }
443
            }
444
        }
445
446
        return $entity;
447
    }
448
449
    protected function getForeignRelationsFields()
450
    {
451
        $foreignRelations = [];
452
        foreach ($this->fields as $field => $options) {
453
            if ($options['type'] === 'foreign') {
454
                $foreignRelations[] = $field;
455
            }
456
        }
457
458
        return $foreignRelations;
459
    }
460
461
    protected function getBelongToFields()
462
    {
463
        $fields = [];
464
        foreach ($this->fields as $field => $options) {
465
            if ($options['type'] === 'select' && isset($options['config']['rel'])) {
466
                $fields[] = $options['config']['rel'];
467
            }
468
        }
469
470
        return $fields;
471
    }
472
473
    /**
474
     * @param object  $row
475
     * @param Request $request
476
     */
477
    protected function updateForeignRelations($row, $request)
478
    {
479
        $foreignRelations = $this->getForeignRelationsFields();
480
481
        foreach ($foreignRelations as $foreignRelation) {
482
            $values = $request->get($foreignRelation);
483
            $row->$foreignRelation()->sync((array) $values);
484
        }
485
    }
486
487
    protected function getParamsFilters($row)
488
    {
489
        $params = [];
490
491
        if (count($this->filterRequire)) {
492
            $params['filter'] = [];
493
494
            foreach ($this->filterRequire as $field) {
495
                $params['filter'][$field] = $row->$field;
496
            }
497
        }
498
499
        return $params;
500
    }
501
502
    protected function prepareLinks()
503
    {
504
        $links = ['index', 'create', 'store'];
505
506
        foreach ($links as $link) {
507
            if (!isset($this->links[$link]) && Route::has($this->route.'.'.$link)) {
508
                $this->links[$link] = route($this->route.'.'.$link);
509
            }
510
        }
511
512
        return $this->links;
513
    }
514
515
    /**
516
     * @param Request $request
517
     * @param         $type
518
     */
519
    protected function validateRequest($request, $type)
520
    {
521
        $validations = [
522
            'rules'            => [],
523
            'messages'         => [],
524
            'customAttributes' => [],
525
        ];
526
527
        foreach ($this->fields as $field => $options) {
528
            $validation = null;
529
            if (isset($options['validation'][$type])) {
530
                $validation = $options['validation'][$type];
531
            } elseif (isset($options['validation']) && is_string($options['validation'])) {
532
                $validation = $options['validation'];
533
            }
534
535
            if ($validation != '') {
536
                $validations['rules'][$field] = $validation;
537
                $validations['customAttributes'][$field] = $options['label'];
538
            }
539
        }
540
541
        if ($validations['rules']) {
542
            $this->validate(
543
                $request,
544
                $validations['rules'],
545
                $validations['messages'],
546
                $validations['customAttributes']
547
            );
548
        }
549
    }
550
551
    protected function prepareRelationalFields($name)
552
    {
553
        // Default values
554
        $config = isset($this->fields[$name]['config']) ? $this->fields[$name]['config'] : [];
555
        $config['options'] = isset($config['options']) ? $config['options'] : [];
556
        $config['cols'] = isset($config['cols']) ? $config['cols'] : 1;
557
558
        // Get foreign values
559
        if (!count($config['options']) && isset($config['entity'])) {
560
            $config['options'] = $config['entity']::get()
561
                ->pluck($config['field_value'], $config['field_key'])
562
                ->toArray();
563
        }
564
565
        // No selection for filters
566
        if ($this->action == 'list' && isset($config['filter_no_selection'])) {
567
            $config['options'] = array_merge([
568
                '-1' => $config['filter_no_selection'],
569
            ], $config['options']);
570
        }
571
572
        if (isset($config['pre_options'])) {
573
            $config['options'] = $config['pre_options'] + $config['options'];
574
        }
575
576
        $this->fields[$name]['config'] = $config;
577
578
        return $this->fields[$name];
579
    }
580
581
    protected function prepareFields()
582
    {
583
        if ($this->entityInstance) {
584
            \Form::model($this->entityInstance);
585
        }
586
587
        foreach ($this->fields as $name => $properties) {
588
            $this->fields[$name]['html'] = $this->prepareField($name, $properties);
589
            $this->fields[$name]['value'] = $this->entityInstance->$name ?? null;
590
            $this->fields[$name]['value_text'] = $this->prepareFieldShow($name, $properties);
591
        }
592
593
        return $this->fields;
594
    }
595
596
    protected function prepareFieldShow($name, $properties = [])
597
    {
598
        // Init
599
        if (empty($properties)) {
600
            $properties = $this->fields[$name];
601
        }
602
603
        $this->fields[$name]['config'] = isset($properties['config']) ? $properties['config'] : [];
604
        $this->fields[$name]['attributes'] = isset($properties['attributes']) ? $properties['attributes'] : [];
605
        $config = $this->fields[$name]['config'];
606
607
        $value = $this->entityInstance
608
            ? ($this->entityInstance->$name ?? null)
609
            : (isset($config['default_value']) ? $config['default_value'] : null);
610
611
        if ($this->entityInstance) {
612
            if (($properties['type'] === 'date' || $properties['type'] === 'datetime') &&
613
                $this->entityInstance->$name != '') {
614
                $fieldValue = $this->entityInstance->$name;
615
616
                if (!is_object($fieldValue)) {
617
                    $fieldValue = Carbon::parse($this->entityInstance->$name);
618
                }
619
620
                $value = $fieldValue->diff(Carbon::now())->format('%y') != date('Y')
621
                    ? $fieldValue->format($properties['type'] === 'date' ? 'm/d/Y' : 'm/d/Y h:ia')
622
                    : null;
623
            }
624
625
            if (substr($properties['type'], 0, 4) === 'file' && $this->entityInstance->getMedia($name)->last()) {
626
                $value = '<a href="'.route($this->route.'.download', [$this->entityInstance->id, $name]).
627
                    '" target="_blank">'.(
628
                    isset($this->fields[$name]['link_name'])
629
                        ? $this->fields[$name]['link_name']
630
                        : 'download'
631
                    ).'</a>';
632
            }
633
634
            if (isset($config['entity'])) {
635
                $value = isset($this->entityInstance->{$config['rel']}->{$config['field_value']})
636
                    ? $this->entityInstance->{$config['rel']}->{$config['field_value']}
637
                    : null;
638
            } elseif (isset($config['options']) && count($config['options'])) {
639
                $value = $config['options'][$value] ?? null;
640
            }
641
        }
642
643
        return empty($value) ? 'N/A' : $value;
644
    }
645
646
    protected function prepareField($name, $properties = [])
647
    {
648
        // Init
649
        if (empty($properties)) {
650
            $properties = $this->fields[$name];
651
        }
652
653
        $this->fields[$name]['config'] = isset($properties['config']) ? $properties['config'] : [];
654
        $this->fields[$name]['attributes'] = isset($properties['attributes']) ? $properties['attributes'] : [];
655
        $this->fields[$name]['attributes']['class'] = 'form-control';
656
        $this->fields[$name]['html'] = null;
657
658
        $config = $this->fields[$name]['config'];
659
660
        $value = $this->entityInstance
661
            ? isset($properties['value_alias'])
662
                ? $this->entityInstance->{$properties['value_alias']}
663
                : $this->entityInstance->$name
664
            : ($this->fields[$name]['default_value'] ?? null);
665
666
        // Define field type class namespace
667
        $className = '\Eliurkis\Crud\FieldTypes\\'.studly_case($properties['type']);
668
        if (!class_exists($className)) {
669
            return;
670
        }
671
672
        if ($properties['type'] == 'foreign' || $properties['type'] == 'select') {
673
            $properties = $this->prepareRelationalFields($name);
674
675
            if ($properties['type'] == 'foreign' && $this->entityInstance) {
676
                $value = $this->entityInstance->{$config['rel']}->pluck($config['field_key'])->toArray();
677
            }
678
679
            if ($properties['type'] == 'select') {
680
                $properties['attributes']['class'] = 'form-control chosen-select-width';
681
            }
682
683
            return $className::prepare(
684
                $name,
685
                $properties['config']['options'],
686
                $value,
687
                $properties
688
            );
689
        }
690
691
        return $className::prepare(
692
            $name,
693
            $value,
694
            $this->fields[$name]
695
        );
696
    }
697
}
698