Completed
Push — master ( e71055...b0f979 )
by Eliurkis
04:46
created

CrudController::validateRequest()   C

Complexity

Conditions 7
Paths 14

Size

Total Lines 31
Code Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 2
Bugs 0 Features 1
Metric Value
c 2
b 0
f 1
dl 0
loc 31
rs 6.7272
cc 7
eloc 20
nc 14
nop 2
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 $links = [];
48
    protected $listDisplay = [
49
        'action-buttons' => true,
50
    ];
51
52
    public function __construct($entity, $config = [])
53
    {
54
        $this->entity = $entity;
55
56
        $config = count($config) ? $config : config('crud.'.$this->route);
57
58
        if (is_array($config)) {
59
            foreach ($config as $key => $value) {
60
                $this->$key = $value;
61
            }
62
        }
63
    }
64
65
    public function index(Request $request)
66
    {
67
        // If DataTable is activated
68
        if (isset($this->dataTableActivated)) {
69
            return $this->indexDataTable($request);
70
        }
71
72
        $entity = $this->entity;
73
74
        // Relation Fields
75
        if ($belongToFields = $this->getBelongToFields()) {
76
            $entity = $this->entity->with($belongToFields);
77
        }
78
79
        // Filters
80
        $entity = $this->filters($entity, $request);
81
82
        // Search
83
        $entity = $this->search($entity, $request);
84
85
        // Order By
86
        if (!empty($this->orderBy)) {
87
            foreach ($this->orderBy as $column => $direction) {
88
                $entity = $entity->orderBy($column, $direction);
89
            }
90
        }
91
92
        if ($this->orderByRaw) {
93
            $entity = $entity->orderByRaw($this->orderByRaw);
94
        }
95
96
        // Pagination
97
        $rows = $this->paginate > 0 ? $this->paginate($entity, $request) : $entity->get();
98
99
        // Sort By Rows
100
        if (!empty($this->sortBy)) {
101
            foreach ($this->sortBy as $column => $direction) {
102
                $rows = strtolower($direction) == 'desc'
103
                    ? $rows->sortByDesc($column)
104
                    : $rows->sortBy($column);
105
            }
106
        }
107
108
        // HTML Filters
109
        $this->htmlFilters();
110
111
        return view('crud::list', compact('rows'))
112
            ->with('fields', $this->fields)
113
            ->with('columns', $this->columns)
114
            ->with('searchable', $this->searchable)
115
            ->with('buttons', $this->buttons)
116
            ->with('paginate', $this->paginate)
117
            ->with('t', $this->texts)
118
            ->with('htmlFilters', $this->htmlFilters)
119
            ->with('listDisplay', $this->listDisplay)
120
            ->with('links', $this->prepareLinks())
121
            ->with('request', $request)
122
            ->with('route', $this->route);
123
    }
124
125 View Code Duplication
    public function show($id)
0 ignored issues
show
Duplication introduced by
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...
126
    {
127
        if (!$this->entityInstance) {
128
            $this->entityInstance = $this->entity->findOrFail($id);
129
        }
130
131
        $this->prepareFields();
132
133
        return view('crud::show')
134
            ->with('type', 'show')
135
            ->with('route', $this->route)
136
            ->with('t', $this->texts)
137
            ->with('fields', $this->fields)
138
            ->with('formColsClasses', $this->formColsClasses)
139
            ->with('data', $this->entityInstance);
140
    }
141
142
    public function create()
143
    {
144
        $this->prepareFields();
145
146
        return view('crud::create')
147
            ->with('type', 'create')
148
            ->with('route', $this->route)
149
            ->with('t', $this->texts)
150
            ->with('formColsClasses', $this->formColsClasses)
151
            ->with('links', $this->prepareLinks())
152
            ->with('fields', $this->fields);
153
    }
154
155
    protected function manageFiles($row, $request)
156
    {
157
        $mediaFiles = [];
158
159
        foreach ($this->fields as $fieldName => $field) {
160
            if ($field['type'] === 'file' && $request->file($fieldName)) {
161
                $customProperties = ['route' => $this->route, 'field' => $fieldName];
162
                if (isset($field['storage_path'])) {
163
                    $customProperties['storage_path'] = $field['storage_path'];
164
                }
165
                $mediaFiles[] = $row->addMedia($request->file($fieldName))
166
                    ->withCustomProperties($customProperties)
167
                    ->toMediaCollection($fieldName);
168
            }
169
        }
170
171
        return $mediaFiles;
172
    }
173
174
    public function store(Request $request)
175
    {
176
        $this->validateRequest($request, 'store');
177
178
        DB::beginTransaction();
179
180
        try {
181
            $row = $this->entity->create(array_merge($request->all(), $this->queryFilters));
182
            $this->updateForeignRelations($row, $request);
183
            $mediaFiles = $this->manageFiles($row, $request);
184
        } catch (QueryException $e) {
185
            \Log::error($e);
186
            if (config('app.debug')) {
187
                throw new \Exception($e);
188
            }
189
            return redirect()
190
                ->back()
191
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
192
        }
193
194
        DB::commit();
195
196
        event($this->route.'.store', [$row, $mediaFiles]);
197
198
        return redirect()
199
            ->route($this->route.'.index')
200
            ->with('success', isset($this->textsGeneral['save_action'])
201
                ? $this->textsGeneral['save_action']
202
                : trans('eliurkis::crud.save_action'));
203
    }
204
205 View Code Duplication
    public function edit($id)
0 ignored issues
show
Duplication introduced by
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...
206
    {
207
        if (!$this->entityInstance) {
208
            $this->entityInstance = $this->entity->findOrFail($id);
209
        }
210
211
        $this->prepareFields();
212
213
        return view('crud::create')
214
            ->with('type', 'edit')
215
            ->with('route', $this->route)
216
            ->with('t', $this->texts)
217
            ->with('fields', $this->fields)
218
            ->with('formColsClasses', $this->formColsClasses)
219
            ->with('links', $this->prepareLinks())
220
            ->with('data', $this->entityInstance);
221
    }
222
223
    public function update(Request $request, $id)
224
    {
225
        $this->validateRequest($request, 'update');
226
227
        DB::beginTransaction();
228
229
        try {
230
            $row = $this->entity->findOrFail($id);
231
            $row->update(
232
                array_merge(
233
                    $request->all(),
234
                    $this->queryFilters
235
                )
236
            );
237
            $this->updateForeignRelations($row, $request);
238
            $mediaFiles = $this->manageFiles($row, $request);
239
        } catch (QueryException $e) {
240
            \Log::error($e);
241
            if (config('app.debug')) {
242
                throw new \Exception($e);
243
            }
244
            return redirect()
245
                ->back()
246
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
247
        }
248
249
        DB::commit();
250
251
        event($this->route.'.update', [$row, $mediaFiles]);
252
253
        return redirect()
254
            ->route($this->route.'.index', $this->getParamsFilters($row))
255
            ->with('success', isset($this->textsGeneral['save_action'])
256
                ? $this->textsGeneral['save_action']
257
                : trans('eliurkis::crud.save_action'));
258
    }
259
260
    public function destroy($id)
261
    {
262
        try {
263
            $row = $this->entity->findOrFail($id);
264
            $row->delete();
265
266
            event($this->route.'.destroy', [$row]);
267
        } catch (ModelNotFoundException $e) {
268
            return redirect()
269
                ->route($this->route.'.index')
270
                ->with('error', __('The element that you are trying to delete does not exist'));
271
        } catch (\Exception $e) {
272
            \Log::error($e);
273
            if (config('app.debug')) {
274
                throw new \Exception($e);
275
            }
276
            return redirect()
277
                ->route($this->route.'.index')
278
                ->with('error', __('An error occurred, try again'));
279
        }
280
281
        return redirect()
282
            ->route($this->route.'.index')
283
            ->with('success', isset($this->textsGeneral['delete_action'])
284
                ? $this->textsGeneral['delete_action']
285
                : trans('eliurkis::crud.delete_action'));
286
    }
287
288
    public function download($id, $fieldName)
289
    {
290
        if (!$this->entityInstance) {
291
            $this->entityInstance = $this->entity->findOrFail($id);
292
        }
293
294
        $media = $this->entityInstance->getMedia($fieldName)->last();
295
296
        if ($media && $media->disk === 's3') {
297
            $tempImage = tempnam(sys_get_temp_dir(), $media->file_name);
298
            copy($media->getTemporaryUrl(\Carbon::now()->addMinutes(5)), $tempImage);
299
            return response()->file($tempImage, ['Content-Type' => $media->mime_type]);
300
        }
301
302
        return $media;
303
    }
304
305
    /* Private Actions */
306
307
    /**
308
     * @param         $entity
309
     * @param Request $request
310
     *
311
     * @return mixed
312
     */
313
    protected function filters($entity, $request)
314
    {
315
        if ($request->query('filter')) {
316
            $filters = is_array($request->query('filter')) ? $request->query('filter') : [];
317
            foreach ($filters as $field => $value) {
318
                $entity = $entity->where($field, $value);
319
            }
320
        }
321
322
        if (count($this->queryFilters)) {
323
            foreach ($this->queryFilters as $field => $value) {
324
                if (is_array($value)) {
325
                    $entity = $entity->whereIn($field, $value);
326
                } else {
327
                    $entity = $entity->where($field, $value);
328
                }
329
            }
330
        }
331
332
        return $entity;
333
    }
334
335
    protected function htmlFilters()
336
    {
337
        $this->htmlFilters = [];
338
        if (count($this->filters)) {
339
            foreach ($this->filters as $filter) {
340
                // Build params
341
                $urlParams = \Input::query();
342
343
                // Default Value
344
                $this->fields[$filter]['config']['default_value'] = isset($urlParams['filter'][$filter])
345
                    ? $urlParams['filter'][$filter]
346
                    : null;
347
348
                // Create URL
349
                if (isset($urlParams['filter'][$filter])) {
350
                    unset($urlParams['filter'][$filter]);
351
                }
352
                $this->fields[$filter]['attributes']['data-filter-url'] = route($this->route.'.index', $urlParams)
353
                    .(count($urlParams) ? '&' : '?');
354
355
                // Create array
356
                $this->action = 'list';
357
                $this->htmlFilters[$filter] = $this->prepareField($filter);
358
            }
359
        }
360
    }
361
362
    /**
363
     * @param         $entity
364
     * @param Request $request
365
     *
366
     * @return mixed
367
     */
368
    protected function paginate($entity, $request)
369
    {
370
        $rows = $entity->paginate($this->paginate);
371
372
        if ($request->get('q') != '') {
373
            $rows->appends(['q' => $request->get('q')]);
374
        }
375
376
        if ($request->get('filter')) {
377
            foreach ($request->get('filter') as $field => $value) {
378
                $rows->appends(['filter['.$field.']' => $value]);
379
            }
380
        }
381
382
        return $rows;
383
    }
384
385
    /**
386
     * @param         $entity
387
     * @param Request $request
388
     *
389
     * @return mixed
390
     */
391
    protected function search($entity, $request)
392
    {
393
        if ($request->get('q') != '') {
394
            $searchableCols = isset($this->searchable['columns']) ? $this->searchable['columns'] : $this->searchable;
395
396
            $entity = $entity->where(function (Builder $query) use ($request, $searchableCols) {
397
                foreach ($searchableCols as $field) {
398
                    $query->orWhere($field, 'like', '%'.$request->get('q').'%');
399
                }
400
            });
401
402
            if (isset($this->searchable['joins'])) {
403
                foreach ($this->searchable['joins'] as $table => $joinFields) {
404
                    $entity = $entity->join($table, $joinFields[0], '=', $joinFields[1]);
405
                }
406
            }
407
        }
408
409
        return $entity;
410
    }
411
412
    protected function getForeignRelationsFields()
413
    {
414
        $foreignRelations = [];
415
        foreach ($this->fields as $field => $options) {
416
            if ($options['type'] === 'foreign') {
417
                $foreignRelations[] = $field;
418
            }
419
        }
420
421
        return $foreignRelations;
422
    }
423
424
    protected function getBelongToFields()
425
    {
426
        $fields = [];
427
        foreach ($this->fields as $field => $options) {
428
            if ($options['type'] === 'select' && isset($options['config']['rel'])) {
429
                $fields[] = $options['config']['rel'];
430
            }
431
        }
432
433
        return $fields;
434
    }
435
436
    /**
437
     * @param object  $row
438
     * @param Request $request
439
     */
440
    protected function updateForeignRelations($row, $request)
441
    {
442
        $foreignRelations = $this->getForeignRelationsFields();
443
444
        foreach ($foreignRelations as $foreignRelation) {
445
            $values = $request->get($foreignRelation);
446
            $row->$foreignRelation()->sync((array) $values);
447
        }
448
    }
449
450
    protected function getParamsFilters($row)
451
    {
452
        $params = [];
453
454
        if (count($this->filterRequire)) {
455
            $params['filter'] = [];
456
457
            foreach ($this->filterRequire as $field) {
458
                $params['filter'][$field] = $row->$field;
459
            }
460
        }
461
462
        return $params;
463
    }
464
465
    protected function prepareLinks()
466
    {
467
        $links = ['index', 'create', 'store'];
468
469
        foreach ($links as $link) {
470
            if (!isset($this->links[$link]) && Route::has($this->route.'.'.$link)) {
471
                $this->links[$link] = route($this->route.'.'.$link);
472
            }
473
        }
474
475
        return $this->links;
476
    }
477
478
    /**
479
     * @param Request $request
480
     * @param         $type
481
     */
482
    protected function validateRequest($request, $type)
483
    {
484
        $validations = [
485
            'rules'            => [],
486
            'messages'         => [],
487
            'customAttributes' => [],
488
        ];
489
490
        foreach ($this->fields as $field => $options) {
491
            $validation = null;
492
            if (isset($options['validation'][$type])) {
493
                $validation = $options['validation'][$type];
494
            } elseif (isset($options['validation']) && is_string($options['validation'])) {
495
                $validation = $options['validation'];
496
            }
497
498
            if ($validation != '') {
499
                $validations['rules'][$field] = $validation;
500
                $validations['customAttributes'][$field] = $options['label'];
501
            }
502
        }
503
504
        if ($validations['rules']) {
505
            $this->validate(
506
                $request,
507
                $validations['rules'],
508
                $validations['messages'],
509
                $validations['customAttributes']
510
            );
511
        }
512
    }
513
514
    protected function prepareRelationalFields($name)
515
    {
516
        // Default values
517
        $config = isset($this->fields[$name]['config']) ? $this->fields[$name]['config'] : [];
518
        $config['options'] = isset($config['options']) ? $config['options'] : [];
519
        $config['cols'] = isset($config['cols']) ? $config['cols'] : 1;
520
521
        // Get foreign values
522
        if (!count($config['options']) && isset($config['entity'])) {
523
            $config['options'] = $config['entity']::get()
524
                ->pluck($config['field_value'], $config['field_key'])
525
                ->toArray();
526
        }
527
528
        // No selection for filters
529
        if ($this->action == 'list' && isset($config['filter_no_selection'])) {
530
            $config['options'] = array_merge([
531
                '-1' => $config['filter_no_selection'],
532
            ], $config['options']);
533
        }
534
535
        if (isset($config['pre_options'])) {
536
            $config['options'] = $config['pre_options'] + $config['options'];
537
        }
538
539
        $this->fields[$name]['config'] = $config;
540
541
        return $this->fields[$name];
542
    }
543
544
    protected function prepareFields()
545
    {
546
        if ($this->entityInstance) {
547
            \Form::model($this->entityInstance);
548
        }
549
550
        foreach ($this->fields as $name => $properties) {
551
            $this->fields[$name]['html'] = $this->prepareField($name, $properties);
552
            $this->fields[$name]['value'] = $this->entityInstance->$name ?? null;
553
            $this->fields[$name]['value_text'] = $this->prepareFieldShow($name, $properties);
554
        }
555
556
        return $this->fields;
557
    }
558
559
    protected function prepareFieldShow($name, $properties = [])
560
    {
561
        // Init
562
        if (empty($properties)) {
563
            $properties = $this->fields[$name];
564
        }
565
566
        $this->fields[$name]['config'] = isset($properties['config']) ? $properties['config'] : [];
567
        $this->fields[$name]['attributes'] = isset($properties['attributes']) ? $properties['attributes'] : [];
568
        $config = $this->fields[$name]['config'];
569
570
        $value = $this->entityInstance
571
            ? ($this->entityInstance->$name ?? null)
572
            : (isset($config['default_value']) ? $config['default_value'] : null);
573
574
        if ($this->entityInstance) {
575
            if (($properties['type'] === 'date' || $properties['type'] === 'datetime') &&
576
                $this->entityInstance->$name != '') {
577
                $fieldValue = $this->entityInstance->$name;
578
579
                if (!is_object($fieldValue)) {
580
                    $fieldValue = Carbon::parse($this->entityInstance->$name);
581
                }
582
583
                $value = $fieldValue->diff(Carbon::now())->format('%y') != date('Y')
584
                    ? $fieldValue->format($properties['type'] === 'date' ? 'm/d/Y' : 'm/d/Y h:ia')
585
                    : null;
586
            }
587
588
            if ($properties['type'] === 'file' && $this->entityInstance->getMedia($name)->last()) {
589
                $value = '<a href="'.route($this->route.'.download', [$this->entityInstance->id, $name]).
590
                    '" target="_blank">'.(
591
                    isset($this->fields[$name]['link_name'])
592
                        ? $this->fields[$name]['link_name']
593
                        : 'download'
594
                    ).'</a>';
595
            }
596
597
            if (isset($config['entity'])) {
598
                $value = isset($this->entityInstance->{$config['rel']}->{$config['field_value']})
599
                    ? $this->entityInstance->{$config['rel']}->{$config['field_value']}
600
                    : null;
601
            } elseif (isset($config['options']) && count($config['options'])) {
602
                $value = $config['options'][$value] ?? null;
603
            }
604
        }
605
606
        return empty($value) ? 'N/A' : $value;
607
    }
608
609
    protected function prepareField($name, $properties = [])
610
    {
611
        // Init
612
        if (empty($properties)) {
613
            $properties = $this->fields[$name];
614
        }
615
616
        $this->fields[$name]['config'] = isset($properties['config']) ? $properties['config'] : [];
617
        $this->fields[$name]['attributes'] = isset($properties['attributes']) ? $properties['attributes'] : [];
618
        $this->fields[$name]['attributes']['class'] = 'form-control';
619
        $this->fields[$name]['html'] = null;
620
621
        $config = $this->fields[$name]['config'];
622
623
        $value = $this->entityInstance
624
            ? isset($properties['value_alias'])
625
                ? $this->entityInstance->{$properties['value_alias']}
626
                : $this->entityInstance->$name
627
            : (isset($config['default_value']) ? $config['default_value'] : null);
628
629
        // Define field type class namespace
630
        $className = '\Eliurkis\Crud\FieldTypes\\'.ucfirst($properties['type']);
631
        if (!class_exists($className)) {
632
            return;
633
        }
634
635
        if ($properties['type'] == 'foreign' || $properties['type'] == 'select') {
636
            $properties = $this->prepareRelationalFields($name);
637
638
            if ($properties['type'] == 'foreign' && $this->entityInstance) {
639
                $value = $this->entityInstance->{$config['rel']}->pluck($config['field_key'])->toArray();
640
            }
641
642
            if ($properties['type'] == 'select') {
643
                $properties['attributes']['class'] = 'form-control chosen-select-width';
644
            }
645
646
            return $className::prepare(
647
                $name,
648
                $properties['config']['options'],
649
                $value,
650
                $properties
651
            );
652
        }
653
654
        return $className::prepare(
655
            $name,
656
            $value,
657
            $this->fields[$name]
658
        );
659
    }
660
}
661