Completed
Pull Request — master (#4)
by Eliurkis
01:41
created

CrudController   C

Complexity

Total Complexity 71

Size/Duplication

Total Lines 377
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 1

Importance

Changes 1
Bugs 1 Features 0
Metric Value
wmc 71
c 1
b 1
f 0
lcom 1
cbo 1
dl 0
loc 377
rs 5.5904

18 Methods

Rating   Name   Duplication   Size   Complexity  
B index() 0 34 3
A create() 0 12 1
B store() 0 26 4
A edit() 0 17 2
B update() 0 32 4
A destroy() 0 10 2
B filters() 0 16 5
B htmlFilters() 0 23 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 3
C prepareMultipleFields() 0 23 8
A prepareValidation() 0 17 3
C prepareFields() 0 22 8

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 DB;
6
use Illuminate\Http\Request;
7
use App\Http\Controllers\Controller;
8
use Illuminate\Database\QueryException;
9
10
class CrudController extends Controller
11
{
12
    protected $route;
13
    protected $entity;
14
    protected $entityInstance = null;
15
    protected $fields = [];
16
    protected $columns = [];
17
    protected $buttons = [
18
        'create',
19
        'edit',
20
        'delete',
21
    ];
22
    protected $paginate = null;
23
    protected $searchable = [];
24
    protected $filters = [];
25
    protected $queryFilters = [];
26
    protected $filterRequire = [];
27
    protected $textsGeneral = [
28
        'list_title'   => 'Contents',
29
        'create_title' => '',
30
        'edit_title'   => '',
31
    ];
32
    protected $texts = [];
33
    protected $htmlFilters = [];
34
    protected $action = null;
35
    protected $formColsClasses = [
36
        'col-md-10 col-md-offset-1',
37
        'col-md-2',
38
        'col-md-10',
39
    ];
40
    protected $links = [];
41
42
    public function index(Request $request)
43
    {
44
        $entity = $this->entity;
45
46
        // Relation Fields
47
        $belongToFields = $this->getBelongToFields();
48
        if (count($belongToFields)) {
49
            $entity = $this->entity->with($belongToFields);
50
        }
51
52
        // Filters
53
        $entity = $this->filters($entity, $request);
54
55
        // Search
56
        $entity = $this->search($entity, $request);
57
58
        // Pagination
59
        $rows = $this->paginate > 0 ? $this->paginate($entity, $request) : $entity->get();
60
61
        // HTML Filters
62
        $this->htmlFilters();
63
64
        return view('crud::list', compact('rows'))
65
            ->with('fields', $this->fields)
66
            ->with('columns', $this->columns)
67
            ->with('searchable', $this->searchable)
68
            ->with('buttons', $this->buttons)
69
            ->with('paginate', $this->paginate)
70
            ->with('t', $this->texts)
71
            ->with('htmlFilters', $this->htmlFilters)
72
            ->with('links', $this->prepareLinks())
73
            ->with('request', $request)
74
            ->with('route', $this->route);
75
    }
76
77
    public function create()
78
    {
79
        $this->prepareFields();
80
81
        return view('crud::create')
82
            ->with('type', 'create')
83
            ->with('route', $this->route)
84
            ->with('t', $this->texts)
85
            ->with('formColsClasses', $this->formColsClasses)
86
            ->with('links', $this->prepareLinks())
87
            ->with('fields', $this->fields);
88
    }
89
90
    public function store(Request $request)
91
    {
92
        $validate = $this->prepareValidation();
93
        if ($validate['rules']) {
94
            $this->validate($request, $validate['rules'], $validate['messages'], $validate['customAttributes']);
95
        }
96
97
        DB::beginTransaction();
98
99
        try {
100
            $row = $this->entity->create(array_merge($request->all(), $this->queryFilters));
101
            $this->updateForeignRelations($row, $request);
102
        } catch (QueryException $e) {
0 ignored issues
show
Bug introduced by
The class Illuminate\Database\QueryException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
103
            return redirect()
104
                ->back()
105
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
106
        }
107
108
        DB::commit();
109
110
        return redirect()
111
            ->route($this->route.'.index')
112
            ->with('success', isset($this->textsGeneral['save_action'])
113
                ? $this->textsGeneral['save_action']
114
                : trans('eliurkis::crud.save_action'));
115
    }
116
117
    public function edit($id)
118
    {
119
        if (! $this->entityInstance) {
120
            $this->entityInstance = $this->entity->findOrFail($id);
121
        }
122
123
        $this->prepareFields();
124
125
        return view('crud::create')
126
            ->with('type', 'edit')
127
            ->with('route', $this->route)
128
            ->with('t', $this->texts)
129
            ->with('fields', $this->fields)
130
            ->with('formColsClasses', $this->formColsClasses)
131
            ->with('links', $this->prepareLinks())
132
            ->with('data', $this->entityInstance);
133
    }
134
135
    public function update(Request $request, $id)
136
    {
137
        $validations = $this->prepareValidation();
138
        if ($validations) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $validations of type array<string,array> is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
139
            $this->validate($request, $validations);
140
        }
141
142
        DB::beginTransaction();
143
144
        try {
145
            $row = $this->entity->findOrFail($id);
146
            $row->update(
147
                array_merge(
148
                    $request->all(),
149
                    $this->queryFilters
150
                )
151
            );
152
            $this->updateForeignRelations($row, $request);
153
        } catch (QueryException $e) {
0 ignored issues
show
Bug introduced by
The class Illuminate\Database\QueryException does not exist. Did you forget a USE statement, or did you not list all dependencies?

Scrutinizer analyzes your composer.json/composer.lock file if available to determine the classes, and functions that are defined by your dependencies.

It seems like the listed class was neither found in your dependencies, nor was it found in the analyzed files in your repository. If you are using some other form of dependency management, you might want to disable this analysis.

Loading history...
154
            return redirect()
155
                ->back()
156
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
157
        }
158
159
        DB::commit();
160
161
        return redirect()
162
            ->route($this->route.'.index', $this->getParamsFilters())
163
            ->with('success', isset($this->textsGeneral['save_action'])
164
                ? $this->textsGeneral['save_action']
165
                : trans('eliurkis::crud.save_action'));
166
    }
167
168
    public function destroy($id)
169
    {
170
        $this->entity->destroy($id);
171
172
        return redirect()
173
            ->route($this->route.'.index')
174
            ->with('success', isset($this->textsGeneral['delete_action'])
175
                ? $this->textsGeneral['delete_action']
176
                : trans('eliurkis::crud.delete_action'));
177
    }
178
179
    /* Private Actions */
180
181
    protected function filters($entity, $request)
182
    {
183
        if ($request->query('filter')) {
184
            foreach ($request->query('filter') as $field => $value) {
185
                $entity = $entity->where($field, $value);
186
            }
187
        }
188
189
        if (count($this->queryFilters)) {
190
            foreach ($this->queryFilters as $field => $value) {
191
                $entity = $entity->where($field, $value);
192
            }
193
        }
194
195
        return $entity;
196
    }
197
198
    protected function htmlFilters()
199
    {
200
        $this->htmlFilters = [];
201
        if (count($this->filters)) {
202
            foreach ($this->filters as $filter) {
203
                // Build params
204
                $urlParams = \Input::query();
205
206
                // Default Value
207
                $this->fields[$filter]['config']['default_value'] = isset($urlParams['filter'][$filter]) ? $urlParams['filter'][$filter] : null;
208
209
                // Create URL
210
                if (isset($urlParams['filter'][$filter])) {
211
                    unset($urlParams['filter'][$filter]);
212
                }
213
                $this->fields[$filter]['attributes']['data-filter-url'] = route($this->route.'.index', $urlParams).(count($urlParams) ? '&' : '?');
214
215
                // Create array
216
                $this->action = 'list';
217
                $this->htmlFilters[$filter] = $this->fieldtype_select($filter);
218
            }
219
        }
220
    }
221
222
    protected function paginate($entity, $request)
223
    {
224
        $rows = $entity->paginate($this->paginate);
225
226
        if ($request->get('q') != '') {
227
            $rows->appends(['q' => $request->get('q')]);
228
        }
229
230
        if ($request->get('filter')) {
231
            foreach ($request->get('filter') as $field => $value) {
232
                $rows->appends(['filter['.$field.']' => $value]);
233
            }
234
        }
235
236
        return $rows;
237
    }
238
239
    protected function search($entity, $request)
240
    {
241
        if ($request->get('q') != '') {
242
            $searchableCols = isset($this->searchable['columns']) ? $this->searchable['columns'] : $this->searchable;
243
244
            $entity = $entity->where(function ($query) use ($request, $searchableCols) {
245
                foreach ($searchableCols as $field) {
246
                    $query->orWhere($field, 'like', '%'.$request->get('q').'%');
247
                }
248
            });
249
250
            if (isset($this->searchable['joins'])) {
251
                foreach ($this->searchable['joins'] as $table => $joinFields) {
252
                    $entity = $entity->join($table, $joinFields[0], '=', $joinFields[1]);
253
                }
254
            }
255
        }
256
257
        return $entity;
258
    }
259
260
    protected function getForeignRelationsFields()
261
    {
262
        $foreignRelations = [];
263
        foreach ($this->fields as $field => $options) {
264
            if ($options['type'] === 'foreign') {
265
                $foreignRelations[] = $field;
266
            }
267
        }
268
269
        return $foreignRelations;
270
    }
271
272
    protected function getBelongToFields()
273
    {
274
        $fields = [];
275
        foreach ($this->fields as $field => $options) {
276
            if ($options['type'] === 'select' && isset($options['config']['rel'])) {
277
                $fields[] = $options['config']['rel'];
278
            }
279
        }
280
281
        return $fields;
282
    }
283
284
    protected function updateForeignRelations($row, $request)
285
    {
286
        $foreignRelations = $this->getForeignRelationsFields();
287
288
        foreach ($foreignRelations as $foreignRelation) {
289
            $values = $request->get($foreignRelation);
290
            $row->$foreignRelation()->sync((array) $values);
291
        }
292
    }
293
294
    protected function prepareMultipleFields($name)
295
    {
296
        $properties = $this->fields[$name];
297
        $config = isset($properties['config']) ? $properties['config'] : [];
298
        $config['options'] = isset($config['options']) ? $config['options'] : [];
299
        $config['cols'] = isset($config['cols']) ? $config['cols'] : 1;
300
301
        if (! count($config['options']) && isset($config['entity'])) {
302
            $config['options'] = $config['entity']::get()
303
                ->lists($config['field_value'], $config['field_key'])
304
                ->toArray();
305
        }
306
307
        if ($this->action == 'list' && isset($this->fields[$name]['config']['filter_no_selection'])) {
308
            $config['options'] = array_merge([
309
                '-1' => $this->fields[$name]['config']['filter_no_selection'], ],
310
                $config['options']
311
            );
312
        }
313
314
        $properties['config'] = $config;
315
        $this->fields[$name] = $properties;
316
    }
317
318
    protected function getParamsFilters()
319
    {
320
        $params = [];
321
322
        if (count($this->filterRequire)) {
323
            $params['filter'] = [];
324
325
            foreach ($this->filterRequire as $field) {
326
                $params['filter'][$field] = $row->$field;
0 ignored issues
show
Bug introduced by
The variable $row does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
327
            }
328
        }
329
330
        return $params;
331
    }
332
333
    protected function prepareLinks()
334
    {
335
        $links = ['index', 'create', 'store'];
336
337
        foreach ($links as $link) {
338
            if (! isset($this->links[$link])) {
339
                $this->links[$link] = route($this->route.'.'.$link);
340
            }
341
        }
342
343
        return $this->links;
344
    }
345
346
    protected function prepareValidation()
347
    {
348
        $validations = [
349
            'rules'            => [],
350
            'messages'         => [],
351
            'customAttributes' => [],
352
        ];
353
354
        foreach ($this->fields as $field => $options) {
355
            if (isset($options['validation'])) {
356
                $validations['rules'][$field] = $options['validation'];
357
                $validations['customAttributes'][$field] = $options['label'];
358
            }
359
        }
360
361
        return $validations;
362
    }
363
364
    protected function prepareFields()
365
    {
366
        if ($this->entityInstance) {
367
            \Form::model($this->entityInstance);
368
        }
369
370
        foreach ($this->fields as $name => $properties) {
371
            $this->fields[$name]['config'] = isset($properties['config']) ? $properties['config'] : [];
372
            $this->fields[$name]['attributes'] = isset($properties['attributes']) ? $properties['attributes'] : [];
373
            $this->fields[$name]['attributes']['class'] = 'form-control';
374
375
            $value = $this->entityInstance
376
                ? $this->entityInstance->$name
377
                : isset($config['default_value']) ? $config['default_value'] : null;
0 ignored issues
show
Bug introduced by
The variable $config seems to never exist, and therefore isset should always return false. Did you maybe rename this variable?

This check looks for calls to isset(...) or empty() on variables that are yet undefined. These calls will always produce the same result and can be removed.

This is most likely caused by the renaming of a variable or the removal of a function/method parameter.

Loading history...
Bug introduced by
The variable $config does not exist. Did you forget to declare it?

This check marks access to variables or properties that have not been declared yet. While PHP has no explicit notion of declaring a variable, accessing it before a value is assigned to it is most likely a bug.

Loading history...
378
379
            $className = '\Eliurkis\Crud\FieldTypes\\'.ucfirst($properties['type']);
380
381
            $this->fields[$name]['html'] = class_exists($className)
382
                ? $className::prepare($name, $value, $this->fields[$name])
383
                : null;
384
        }
385
    }
386
}
387