Completed
Push — master ( 0222da...59a7ac )
by Eliurkis
02:18
created

CrudController   D

Complexity

Total Complexity 95

Size/Duplication

Total Lines 513
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 14
Bugs 2 Features 1
Metric Value
wmc 95
c 14
b 2
f 1
lcom 1
cbo 2
dl 0
loc 513
rs 4.8717

21 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 12 4
C index() 0 50 7
A create() 0 12 1
A manageFiles() 0 10 4
B store() 0 28 4
A edit() 0 17 2
B update() 0 34 4
A destroy() 0 10 2
B filters() 0 21 7
B htmlFilters() 0 26 6
A paginate() 0 16 4
B search() 0 20 6
A getForeignRelationsFields() 0 11 3
A getBelongToFields() 0 11 4
A updateForeignRelations() 0 9 2
A getParamsFilters() 0 14 3
A prepareLinks() 0 12 4
B validateRequest() 0 24 4
D prepareRelationalFields() 0 29 9
A prepareFields() 0 13 3
D prepareField() 0 49 12

How to fix   Complexity   

Complex Class

Complex classes like CrudController 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 CrudController, and based on these observations, apply Extract Interface, too.

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\QueryException;
9
use Illuminate\Http\Request;
10
use Illuminate\Support\Facades\Route;
11
12
class CrudController extends Controller
13
{
14
    protected $route;
15
    protected $entity;
16
    protected $entityInstance = null;
17
    protected $fields = [];
18
    protected $columns = [];
19
    protected $buttons = [
20
        'create',
21
        'edit',
22
        'delete',
23
    ];
24
    protected $paginate = null;
25
    protected $searchable = [];
26
    protected $filters = [];
27
    protected $queryFilters = [];
28
    protected $orderBy = [];
29
    protected $orderByRaw = null;
30
    protected $filterRequire = [];
31
    protected $textsGeneral = [
32
        'list_title'   => 'Contents',
33
        'create_title' => '',
34
        'edit_title'   => '',
35
    ];
36
    protected $texts = [];
37
    protected $htmlFilters = [];
38
    protected $action = null;
39
    protected $formColsClasses = [
40
        'col-md-10 col-md-offset-1',
41
        'col-md-2',
42
        'col-md-10',
43
    ];
44
    protected $links = [];
45
    protected $listDisplay = [
46
        'action-buttons' => true,
47
    ];
48
49
    public function __construct($entity, $config = [])
50
    {
51
        $this->entity = $entity;
52
53
        $config = count($config) ? $config : config('crud.'.$this->route);
54
55
        if (is_array($config)) {
56
            foreach ($config as $key => $value) {
57
                $this->$key = $value;
58
            }
59
        }
60
    }
61
62
    public function index(Request $request)
63
    {
64
        // If DataTable is activated
65
        if (isset($this->dataTableActivated)) {
66
            return $this->indexDataTable($request);
67
        }
68
69
        $entity = $this->entity;
70
71
        // Relation Fields
72
        if ($belongToFields = $this->getBelongToFields()) {
73
            $entity = $this->entity->with($belongToFields);
74
        }
75
76
        // Filters
77
        $entity = $this->filters($entity, $request);
78
79
        // Search
80
        $entity = $this->search($entity, $request);
81
82
        // Order By
83
        if (!empty($this->orderBy)) {
84
            foreach ($this->orderBy as $column => $direction) {
85
                $entity = $entity->orderBy($column, $direction);
86
            }
87
        }
88
89
        if ($this->orderByRaw) {
90
            $entity = $entity->orderByRaw($this->orderByRaw);
91
        }
92
93
        // Pagination
94
        $rows = $this->paginate > 0 ? $this->paginate($entity, $request) : $entity->get();
95
96
        // HTML Filters
97
        $this->htmlFilters();
98
99
        return view('crud::list', compact('rows'))
100
            ->with('fields', $this->fields)
101
            ->with('columns', $this->columns)
102
            ->with('searchable', $this->searchable)
103
            ->with('buttons', $this->buttons)
104
            ->with('paginate', $this->paginate)
105
            ->with('t', $this->texts)
106
            ->with('htmlFilters', $this->htmlFilters)
107
            ->with('listDisplay', $this->listDisplay)
108
            ->with('links', $this->prepareLinks())
109
            ->with('request', $request)
110
            ->with('route', $this->route);
111
    }
112
113
    public function create()
114
    {
115
        $this->prepareFields();
116
117
        return view('crud::create')
118
            ->with('type', 'create')
119
            ->with('route', $this->route)
120
            ->with('t', $this->texts)
121
            ->with('formColsClasses', $this->formColsClasses)
122
            ->with('links', $this->prepareLinks())
123
            ->with('fields', $this->fields);
124
    }
125
126
    protected function manageFiles($row, $request)
127
    {
128
        foreach ($this->fields as $fieldName => $field) {
129
            if ($field['type'] === 'file' && $request->file($fieldName)) {
130
                $row->addMedia($request->file($fieldName))
131
                    ->withCustomProperties(['route' => $this->route])
132
                    ->toMediaCollection($field['media_collection']);
133
            }
134
        }
135
    }
136
137
    public function store(Request $request)
138
    {
139
        $this->validateRequest($request);
140
141
        DB::beginTransaction();
142
143
        try {
144
            $row = $this->entity->create(array_merge($request->all(), $this->queryFilters));
145
            $this->updateForeignRelations($row, $request);
146
            $this->manageFiles($row, $request);
147
        } catch (QueryException $e) {
148
            \Log::error($e);
149
            if (config('app.debug')) {
150
                throw new \Exception($e);
151
            }
152
            return redirect()
153
                ->back()
154
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
155
        }
156
157
        DB::commit();
158
159
        return redirect()
160
            ->route($this->route.'.index')
161
            ->with('success', isset($this->textsGeneral['save_action'])
162
                ? $this->textsGeneral['save_action']
163
                : trans('eliurkis::crud.save_action'));
164
    }
165
166
    public function edit($id)
167
    {
168
        if (!$this->entityInstance) {
169
            $this->entityInstance = $this->entity->findOrFail($id);
170
        }
171
172
        $this->prepareFields();
173
174
        return view('crud::create')
175
            ->with('type', 'edit')
176
            ->with('route', $this->route)
177
            ->with('t', $this->texts)
178
            ->with('fields', $this->fields)
179
            ->with('formColsClasses', $this->formColsClasses)
180
            ->with('links', $this->prepareLinks())
181
            ->with('data', $this->entityInstance);
182
    }
183
184
    public function update(Request $request, $id)
185
    {
186
        $this->validateRequest($request);
187
188
        DB::beginTransaction();
189
190
        try {
191
            $row = $this->entity->findOrFail($id);
192
            $row->update(
193
                array_merge(
194
                    $request->all(),
195
                    $this->queryFilters
196
                )
197
            );
198
            $this->updateForeignRelations($row, $request);
199
            $this->manageFiles($row, $request);
200
        } catch (QueryException $e) {
201
            \Log::error($e);
202
            if (config('app.debug')) {
203
                throw new \Exception($e);
204
            }
205
            return redirect()
206
                ->back()
207
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
208
        }
209
210
        DB::commit();
211
212
        return redirect()
213
            ->route($this->route.'.index', $this->getParamsFilters($row))
214
            ->with('success', isset($this->textsGeneral['save_action'])
215
                ? $this->textsGeneral['save_action']
216
                : trans('eliurkis::crud.save_action'));
217
    }
218
219
    public function destroy($id)
220
    {
221
        $this->entity->destroy($id);
222
223
        return redirect()
224
            ->route($this->route.'.index')
225
            ->with('success', isset($this->textsGeneral['delete_action'])
226
                ? $this->textsGeneral['delete_action']
227
                : trans('eliurkis::crud.delete_action'));
228
    }
229
230
    /* Private Actions */
231
232
    /**
233
     * @param         $entity
234
     * @param Request $request
235
     *
236
     * @return mixed
237
     */
238
    protected function filters($entity, $request)
239
    {
240
        if ($request->query('filter')) {
241
            $filters = is_array($request->query('filter')) ? $request->query('filter') : [];
242
            foreach ($filters as $field => $value) {
243
                $entity = $entity->where($field, $value);
244
            }
245
        }
246
247
        if (count($this->queryFilters)) {
248
            foreach ($this->queryFilters as $field => $value) {
249
                if (is_array($value)) {
250
                    $entity = $entity->whereIn($field, $value);
251
                } else {
252
                    $entity = $entity->where($field, $value);
253
                }
254
            }
255
        }
256
257
        return $entity;
258
    }
259
260
    protected function htmlFilters()
261
    {
262
        $this->htmlFilters = [];
263
        if (count($this->filters)) {
264
            foreach ($this->filters as $filter) {
265
                // Build params
266
                $urlParams = \Input::query();
267
268
                // Default Value
269
                $this->fields[$filter]['config']['default_value'] = isset($urlParams['filter'][$filter])
270
                    ? $urlParams['filter'][$filter]
271
                    : null;
272
273
                // Create URL
274
                if (isset($urlParams['filter'][$filter])) {
275
                    unset($urlParams['filter'][$filter]);
276
                }
277
                $this->fields[$filter]['attributes']['data-filter-url'] = route($this->route.'.index', $urlParams)
278
                    .(count($urlParams) ? '&' : '?');
279
280
                // Create array
281
                $this->action = 'list';
282
                $this->htmlFilters[$filter] = $this->prepareField($filter);
283
            }
284
        }
285
    }
286
287
    /**
288
     * @param         $entity
289
     * @param Request $request
290
     *
291
     * @return mixed
292
     */
293
    protected function paginate($entity, $request)
294
    {
295
        $rows = $entity->paginate($this->paginate);
296
297
        if ($request->get('q') != '') {
298
            $rows->appends(['q' => $request->get('q')]);
299
        }
300
301
        if ($request->get('filter')) {
302
            foreach ($request->get('filter') as $field => $value) {
303
                $rows->appends(['filter['.$field.']' => $value]);
304
            }
305
        }
306
307
        return $rows;
308
    }
309
310
    /**
311
     * @param         $entity
312
     * @param Request $request
313
     *
314
     * @return mixed
315
     */
316
    protected function search($entity, $request)
317
    {
318
        if ($request->get('q') != '') {
319
            $searchableCols = isset($this->searchable['columns']) ? $this->searchable['columns'] : $this->searchable;
320
321
            $entity = $entity->where(function (Builder $query) use ($request, $searchableCols) {
322
                foreach ($searchableCols as $field) {
323
                    $query->orWhere($field, 'like', '%'.$request->get('q').'%');
324
                }
325
            });
326
327
            if (isset($this->searchable['joins'])) {
328
                foreach ($this->searchable['joins'] as $table => $joinFields) {
329
                    $entity = $entity->join($table, $joinFields[0], '=', $joinFields[1]);
330
                }
331
            }
332
        }
333
334
        return $entity;
335
    }
336
337
    protected function getForeignRelationsFields()
338
    {
339
        $foreignRelations = [];
340
        foreach ($this->fields as $field => $options) {
341
            if ($options['type'] === 'foreign') {
342
                $foreignRelations[] = $field;
343
            }
344
        }
345
346
        return $foreignRelations;
347
    }
348
349
    protected function getBelongToFields()
350
    {
351
        $fields = [];
352
        foreach ($this->fields as $field => $options) {
353
            if ($options['type'] === 'select' && isset($options['config']['rel'])) {
354
                $fields[] = $options['config']['rel'];
355
            }
356
        }
357
358
        return $fields;
359
    }
360
361
    /**
362
     * @param object  $row
363
     * @param Request $request
364
     */
365
    protected function updateForeignRelations($row, $request)
366
    {
367
        $foreignRelations = $this->getForeignRelationsFields();
368
369
        foreach ($foreignRelations as $foreignRelation) {
370
            $values = $request->get($foreignRelation);
371
            $row->$foreignRelation()->sync((array) $values);
372
        }
373
    }
374
375
    protected function getParamsFilters($row)
376
    {
377
        $params = [];
378
379
        if (count($this->filterRequire)) {
380
            $params['filter'] = [];
381
382
            foreach ($this->filterRequire as $field) {
383
                $params['filter'][$field] = $row->$field;
384
            }
385
        }
386
387
        return $params;
388
    }
389
390
    protected function prepareLinks()
391
    {
392
        $links = ['index', 'create', 'store'];
393
394
        foreach ($links as $link) {
395
            if (!isset($this->links[$link]) && Route::has($this->route.'.'.$link)) {
396
                $this->links[$link] = route($this->route.'.'.$link);
397
            }
398
        }
399
400
        return $this->links;
401
    }
402
403
    /**
404
     * @param Request $request
405
     */
406
    protected function validateRequest($request)
407
    {
408
        $validations = [
409
            'rules'            => [],
410
            'messages'         => [],
411
            'customAttributes' => [],
412
        ];
413
414
        foreach ($this->fields as $field => $options) {
415
            if (isset($options['validation'])) {
416
                $validations['rules'][$field] = $options['validation'];
417
                $validations['customAttributes'][$field] = $options['label'];
418
            }
419
        }
420
421
        if ($validations['rules']) {
422
            $this->validate(
423
                $request,
424
                $validations['rules'],
425
                $validations['messages'],
426
                $validations['customAttributes']
427
            );
428
        }
429
    }
430
431
    protected function prepareRelationalFields($name)
432
    {
433
        // Default values
434
        $config = isset($this->fields[$name]['config']) ? $this->fields[$name]['config'] : [];
435
        $config['options'] = isset($config['options']) ? $config['options'] : [];
436
        $config['cols'] = isset($config['cols']) ? $config['cols'] : 1;
437
438
        // Get foreign values
439
        if (!count($config['options']) && isset($config['entity'])) {
440
            $config['options'] = $config['entity']::get()
441
                ->pluck($config['field_value'], $config['field_key'])
442
                ->toArray();
443
        }
444
445
        // No selection for filters
446
        if ($this->action == 'list' && isset($config['filter_no_selection'])) {
447
            $config['options'] = array_merge([
448
                '-1' => $config['filter_no_selection'],
449
            ], $config['options']);
450
        }
451
452
        if (isset($config['pre_options'])) {
453
            $config['options'] = $config['pre_options'] + $config['options'];
454
        }
455
456
        $this->fields[$name]['config'] = $config;
457
458
        return $this->fields[$name];
459
    }
460
461
    protected function prepareFields()
462
    {
463
        if ($this->entityInstance) {
464
            \Form::model($this->entityInstance);
465
        }
466
467
        foreach ($this->fields as $name => $properties) {
468
            $this->fields[$name]['html'] = $this->prepareField($name, $properties);
469
            $this->fields[$name]['value'] = $this->entityInstance->$name ?? null;
470
        }
471
472
        return $this->fields;
473
    }
474
475
    protected function prepareField($name, $properties = [])
476
    {
477
        // Init
478
        if (empty($properties)) {
479
            $properties = $this->fields[$name];
480
        }
481
482
        $this->fields[$name]['config'] = isset($properties['config']) ? $properties['config'] : [];
483
        $this->fields[$name]['attributes'] = isset($properties['attributes']) ? $properties['attributes'] : [];
484
        $this->fields[$name]['attributes']['class'] = 'form-control';
485
        $this->fields[$name]['html'] = null;
486
487
        $config = $this->fields[$name]['config'];
488
489
        $value = $this->entityInstance
490
            ? $this->entityInstance->$name
491
            : (isset($config['default_value']) ? $config['default_value'] : null);
492
493
        // Define field type class namespace
494
        $className = '\Eliurkis\Crud\FieldTypes\\'.ucfirst($properties['type']);
495
        if (!class_exists($className)) {
496
            return;
497
        }
498
499
        if ($properties['type'] == 'foreign' || $properties['type'] == 'select') {
500
            $properties = $this->prepareRelationalFields($name);
501
502
            if ($properties['type'] == 'foreign' && $this->entityInstance) {
503
                $value = $this->entityInstance->{$config['rel']}->pluck('id')->toArray();
504
            }
505
506
            if ($properties['type'] == 'select') {
507
                $properties['attributes']['class'] = 'form-control chosen-select-width';
508
            }
509
510
            return $className::prepare(
511
                $name,
512
                $properties['config']['options'],
513
                $value,
514
                $properties
515
            );
516
        }
517
518
        return $className::prepare(
519
            $name,
520
            $value,
521
            $this->fields[$name]
522
        );
523
    }
524
}
525