Completed
Push — master ( 0d39ab...d6d294 )
by Eliurkis
01:28 queued 01:25
created

src/CrudController.php (1 issue)

Labels
Severity

Upgrade to new PHP Analysis Engine

These results are based on our legacy PHP analysis, consider migrating to our new PHP analysis engine instead. Learn more

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
        $this->validateRequest($request);
93
94
        DB::beginTransaction();
95
96
        try {
97
            $row = $this->entity->create(array_merge($request->all(), $this->queryFilters));
98
            $this->updateForeignRelations($row, $request);
99
        } catch (QueryException $e) {
100
            return redirect()
101
                ->back()
102
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
103
        }
104
105
        DB::commit();
106
107
        return redirect()
108
            ->route($this->route.'.index')
109
            ->with('success', isset($this->textsGeneral['save_action'])
110
                ? $this->textsGeneral['save_action']
111
                : trans('eliurkis::crud.save_action'));
112
    }
113
114
    public function edit($id)
115
    {
116
        if (! $this->entityInstance) {
117
            $this->entityInstance = $this->entity->findOrFail($id);
118
        }
119
120
        $this->prepareFields();
121
122
        return view('crud::create')
123
            ->with('type', 'edit')
124
            ->with('route', $this->route)
125
            ->with('t', $this->texts)
126
            ->with('fields', $this->fields)
127
            ->with('formColsClasses', $this->formColsClasses)
128
            ->with('links', $this->prepareLinks())
129
            ->with('data', $this->entityInstance);
130
    }
131
132
    public function update(Request $request, $id)
133
    {
134
        $this->validateRequest($request);
135
136
        DB::beginTransaction();
137
138
        try {
139
            $row = $this->entity->findOrFail($id);
140
            $row->update(
141
                array_merge(
142
                    $request->all(),
143
                    $this->queryFilters
144
                )
145
            );
146
            $this->updateForeignRelations($row, $request);
147
        } catch (QueryException $e) {
148
            return redirect()
149
                ->back()
150
                ->with('error', 'Ha ocurrido un error, intente nuevamente');
151
        }
152
153
        DB::commit();
154
155
        return redirect()
156
            ->route($this->route.'.index', $this->getParamsFilters($row))
157
            ->with('success', isset($this->textsGeneral['save_action'])
158
                ? $this->textsGeneral['save_action']
159
                : trans('eliurkis::crud.save_action'));
160
    }
161
162
    public function destroy($id)
163
    {
164
        $this->entity->destroy($id);
165
166
        return redirect()
167
            ->route($this->route.'.index')
168
            ->with('success', isset($this->textsGeneral['delete_action'])
169
                ? $this->textsGeneral['delete_action']
170
                : trans('eliurkis::crud.delete_action'));
171
    }
172
173
    /* Private Actions */
174
175
    /**
176
     * @param $entity
177
     * @param Request $request
178
     *
179
     * @return mixed
180
     */
181
    protected function filters($entity, $request)
182
    {
183
        if ($request->query('filter')) {
184
            foreach ($request->query('filter') as $field => $value) {
0 ignored issues
show
The expression $request->query('filter') of type string|array is not guaranteed to be traversable. How about adding an additional type check?

There are different options of fixing this problem.

  1. If you want to be on the safe side, you can add an additional type-check:

    $collection = json_decode($data, true);
    if ( ! is_array($collection)) {
        throw new \RuntimeException('$collection must be an array.');
    }
    
    foreach ($collection as $item) { /** ... */ }
    
  2. If you are sure that the expression is traversable, you might want to add a doc comment cast to improve IDE auto-completion and static analysis:

    /** @var array $collection */
    $collection = json_decode($data, true);
    
    foreach ($collection as $item) { /** .. */ }
    
  3. Mark the issue as a false-positive: Just hover the remove button, in the top-right corner of this issue for more options.

Loading history...
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])
208
                    ? $urlParams['filter'][$filter]
209
                    : null;
210
211
                // Create URL
212
                if (isset($urlParams['filter'][$filter])) {
213
                    unset($urlParams['filter'][$filter]);
214
                }
215
                $this->fields[$filter]['attributes']['data-filter-url'] = route($this->route.'.index', $urlParams)
216
                    .(count($urlParams) ? '&' : '?');
217
218
                // Create array
219
                $this->action = 'list';
220
                $this->htmlFilters[$filter] = $this->prepareField($filter);
221
            }
222
        }
223
    }
224
225
    /**
226
     * @param $entity
227
     * @param Request $request
228
     *
229
     * @return mixed
230
     */
231
    protected function paginate($entity, $request)
232
    {
233
        $rows = $entity->paginate($this->paginate);
234
235
        if ($request->get('q') != '') {
236
            $rows->appends(['q' => $request->get('q')]);
237
        }
238
239
        if ($request->get('filter')) {
240
            foreach ($request->get('filter') as $field => $value) {
241
                $rows->appends(['filter['.$field.']' => $value]);
242
            }
243
        }
244
245
        return $rows;
246
    }
247
248
    /**
249
     * @param $entity
250
     * @param Request $request
251
     *
252
     * @return mixed
253
     */
254
    protected function search($entity, $request)
255
    {
256
        if ($request->get('q') != '') {
257
            $searchableCols = isset($this->searchable['columns']) ? $this->searchable['columns'] : $this->searchable;
258
259
            $entity = $entity->where(function ($query) use ($request, $searchableCols) {
260
                foreach ($searchableCols as $field) {
261
                    $query->orWhere($field, 'like', '%'.$request->get('q').'%');
262
                }
263
            });
264
265
            if (isset($this->searchable['joins'])) {
266
                foreach ($this->searchable['joins'] as $table => $joinFields) {
267
                    $entity = $entity->join($table, $joinFields[0], '=', $joinFields[1]);
268
                }
269
            }
270
        }
271
272
        return $entity;
273
    }
274
275
    protected function getForeignRelationsFields()
276
    {
277
        $foreignRelations = [];
278
        foreach ($this->fields as $field => $options) {
279
            if ($options['type'] === 'foreign') {
280
                $foreignRelations[] = $field;
281
            }
282
        }
283
284
        return $foreignRelations;
285
    }
286
287
    protected function getBelongToFields()
288
    {
289
        $fields = [];
290
        foreach ($this->fields as $field => $options) {
291
            if ($options['type'] === 'select' && isset($options['config']['rel'])) {
292
                $fields[] = $options['config']['rel'];
293
            }
294
        }
295
296
        return $fields;
297
    }
298
299
    /**
300
     * @param object $row
301
     * @param Request $request
302
     */
303
    protected function updateForeignRelations($row, $request)
304
    {
305
        $foreignRelations = $this->getForeignRelationsFields();
306
307
        foreach ($foreignRelations as $foreignRelation) {
308
            $values = $request->get($foreignRelation);
309
            $row->$foreignRelation()->sync((array) $values);
310
        }
311
    }
312
313
    protected function getParamsFilters($row)
314
    {
315
        $params = [];
316
317
        if (count($this->filterRequire)) {
318
            $params['filter'] = [];
319
320
            foreach ($this->filterRequire as $field) {
321
                $params['filter'][$field] = $row->$field;
322
            }
323
        }
324
325
        return $params;
326
    }
327
328
    protected function prepareLinks()
329
    {
330
        $links = ['index', 'create', 'store'];
331
332
        foreach ($links as $link) {
333
            if (! isset($this->links[$link])) {
334
                $this->links[$link] = route($this->route.'.'.$link);
335
            }
336
        }
337
338
        return $this->links;
339
    }
340
341
    /**
342
     * @param Request $request
343
     */
344
    protected function validateRequest($request)
345
    {
346
        $validations = [
347
            'rules'            => [],
348
            'messages'         => [],
349
            'customAttributes' => [],
350
        ];
351
352
        foreach ($this->fields as $field => $options) {
353
            if (isset($options['validation'])) {
354
                $validations['rules'][$field] = $options['validation'];
355
                $validations['customAttributes'][$field] = $options['label'];
356
            }
357
        }
358
359
        if ($validations['rules']) {
360
            $this->validate(
361
                $request,
362
                $validations['rules'],
363
                $validations['messages'],
364
                $validations['customAttributes']
365
            );
366
        }
367
    }
368
369
    protected function prepareRelationalFields($name)
370
    {
371
        // Default values
372
        $config = isset($this->fields[$name]['config']) ? $this->fields[$name]['config'] : [];
373
        $config['options'] = isset($config['options']) ? $config['options'] : [];
374
        $config['cols'] = isset($config['cols']) ? $config['cols'] : 1;
375
376
        // Get foreign values
377
        if (! count($config['options']) && isset($config['entity'])) {
378
            $config['options'] = $config['entity']::get()
379
                ->lists($config['field_value'], $config['field_key'])
380
                ->toArray();
381
        }
382
383
        // No selection for filters
384
        if ($this->action == 'list' && isset($config['filter_no_selection'])) {
385
            $config['options'] = array_merge([
386
                '-1' => $config['filter_no_selection'],
387
            ], $config['options']);
388
        }
389
390
        $this->fields[$name]['config'] = $config;
391
392
        return $this->fields[$name];
393
    }
394
395
    protected function prepareFields()
396
    {
397
        if ($this->entityInstance) {
398
            \Form::model($this->entityInstance);
399
        }
400
401
        foreach ($this->fields as $name => $properties) {
402
            $this->fields[$name]['html'] = $this->prepareField($name, $properties);
403
        }
404
    }
405
406
    protected function prepareField($name, $properties = [])
407
    {
408
        // Init
409
        if (! $properties) {
410
            $properties = $this->fields[$name];
411
        }
412
413
        $this->fields[$name]['config'] = isset($properties['config']) ? $properties['config'] : [];
414
        $this->fields[$name]['attributes'] = isset($properties['attributes']) ? $properties['attributes'] : [];
415
        $this->fields[$name]['attributes']['class'] = 'form-control';
416
        $this->fields[$name]['html'] = null;
417
418
        $config = $this->fields[$name]['config'];
419
420
        $value = $this->entityInstance
421
            ? $this->entityInstance->$name
422
            : (isset($config['default_value']) ? $config['default_value'] : null);
423
424
        // Define field type class namespace
425
        $className = '\Eliurkis\Crud\FieldTypes\\'.ucfirst($properties['type']);
426
        if (! class_exists($className)) {
427
            return;
428
        }
429
430
        if ($properties['type'] == 'foreign' || $properties['type'] == 'select') {
431
            $properties = $this->prepareRelationalFields($name);
432
433
            if ($properties['type'] == 'foreign' && $this->entityInstance) {
434
                $value = $this->entityInstance->{$config['rel']}->lists('id')->toArray();
435
            }
436
437
            if ($properties['type'] == 'select') {
438
                $properties['attributes']['class'] = 'form-control chosen-select-width';
439
            }
440
441
            return $className::prepare(
442
                $name,
443
                $properties['config']['options'],
444
                $value,
445
                $properties
446
            );
447
        }
448
449
        return $className::prepare(
450
            $name,
451
            $value,
452
            $this->fields[$name]
453
        );
454
    }
455
}
456