Completed
Push — master ( 66783e...72cfb5 )
by Eliurkis
02:12
created

CrudController::destroy()   B

Complexity

Conditions 5
Paths 10

Size

Total Lines 27
Code Lines 21

Duplication

Lines 0
Ratio 0 %

Importance

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