Completed
Push — master ( a12e8c...a61567 )
by Song
03:22
created

Form   F

Complexity

Total Complexity 170

Size/Duplication

Total Lines 1401
Duplicated Lines 4.14 %

Coupling/Cohesion

Components 1
Dependencies 18

Importance

Changes 0
Metric Value
dl 58
loc 1401
rs 0.8
c 0
b 0
f 0
wmc 170
lcom 1
cbo 18

64 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 10 2
A pushField() 0 8 1
A model() 0 4 1
A builder() 0 4 1
A edit() 0 9 1
A tab() 0 6 1
A getTab() 0 8 2
A destroy() 0 11 1
A deleteFiles() 0 20 2
B store() 11 35 6
A ajaxResponse() 0 14 3
A prepare() 0 16 3
A removeIgnoredFields() 0 6 1
A getRelationInputs() 0 16 4
A callSubmitted() 0 8 4
A callSaving() 0 8 4
A callSaved() 0 8 4
B update() 12 58 9
A redirectAfterStore() 0 8 1
A redirectAfterUpdate() 0 6 1
A redirectAfterSaving() 0 16 4
A isEditable() 0 4 1
A handleEditable() 0 12 2
A handleFileDelete() 0 11 2
A handleOrderable() 0 14 4
D updateRelation() 0 81 20
B prepareUpdate() 7 32 7
A invalidColumn() 0 11 6
A prepareInsert() 0 23 5
A isHasOneRelation() 0 14 3
A submitted() 0 4 1
A saving() 0 4 1
A saved() 0 4 1
A ignore() 0 6 1
A getDataByColumn() 18 18 5
A getFieldByColumn() 0 12 2
A setFieldOriginalValue() 0 10 1
A setFieldValue() 0 16 2
A doNotSnakeAttributes() 0 6 1
A validationMessages() 0 19 6
A mergeValidationMessages() 0 10 2
B getRelations() 0 27 8
A setAction() 0 6 1
A setWidth() 0 11 1
A setView() 0 6 1
A setTitle() 0 6 1
A row() 0 6 1
A tools() 0 4 1
A disableSubmit() 0 6 1
A disableReset() 0 6 1
A disableViewCheck() 0 6 1
A disableEditingCheck() 0 6 1
A footer() 0 4 1
A resource() 0 14 4
A render() 0 8 2
A input() 0 8 2
B registerBuiltinFields() 0 55 2
A extend() 0 4 1
A forget() 0 4 1
A findFieldClass() 10 10 2
A collectFieldAssets() 0 25 4
A __get() 0 4 1
A __set() 0 4 1
A __call() 0 12 2

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Form 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 Form, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Encore\Admin;
4
5
use Closure;
6
use Encore\Admin\Exception\Handler;
7
use Encore\Admin\Form\Builder;
8
use Encore\Admin\Form\Field;
9
use Encore\Admin\Form\Row;
10
use Encore\Admin\Form\Tab;
11
use Illuminate\Contracts\Support\Renderable;
12
use Illuminate\Database\Eloquent\Model;
13
use Illuminate\Database\Eloquent\Relations;
14
use Illuminate\Database\Eloquent\SoftDeletes;
15
use Illuminate\Http\Request;
16
use Illuminate\Support\Arr;
17
use Illuminate\Support\Facades\DB;
18
use Illuminate\Support\Facades\Input;
19
use Illuminate\Support\MessageBag;
20
use Illuminate\Support\Str;
21
use Illuminate\Validation\Validator;
22
use Spatie\EloquentSortable\Sortable;
23
use Symfony\Component\HttpFoundation\Response;
24
25
/**
26
 * Class Form.
27
 *
28
 * @method Field\Text           text($column, $label = '')
29
 * @method Field\Checkbox       checkbox($column, $label = '')
30
 * @method Field\Radio          radio($column, $label = '')
31
 * @method Field\Select         select($column, $label = '')
32
 * @method Field\MultipleSelect multipleSelect($column, $label = '')
33
 * @method Field\Textarea       textarea($column, $label = '')
34
 * @method Field\Hidden         hidden($column, $label = '')
35
 * @method Field\Id             id($column, $label = '')
36
 * @method Field\Ip             ip($column, $label = '')
37
 * @method Field\Url            url($column, $label = '')
38
 * @method Field\Color          color($column, $label = '')
39
 * @method Field\Email          email($column, $label = '')
40
 * @method Field\Mobile         mobile($column, $label = '')
41
 * @method Field\Slider         slider($column, $label = '')
42
 * @method Field\Map            map($latitude, $longitude, $label = '')
43
 * @method Field\Editor         editor($column, $label = '')
44
 * @method Field\File           file($column, $label = '')
45
 * @method Field\Image          image($column, $label = '')
46
 * @method Field\Date           date($column, $label = '')
47
 * @method Field\Datetime       datetime($column, $label = '')
48
 * @method Field\Time           time($column, $label = '')
49
 * @method Field\Year           year($column, $label = '')
50
 * @method Field\Month          month($column, $label = '')
51
 * @method Field\DateRange      dateRange($start, $end, $label = '')
52
 * @method Field\DateTimeRange  datetimeRange($start, $end, $label = '')
53
 * @method Field\TimeRange      timeRange($start, $end, $label = '')
54
 * @method Field\Number         number($column, $label = '')
55
 * @method Field\Currency       currency($column, $label = '')
56
 * @method Field\HasMany        hasMany($relationName, $callback)
57
 * @method Field\SwitchField    switch($column, $label = '')
58
 * @method Field\Display        display($column, $label = '')
59
 * @method Field\Rate           rate($column, $label = '')
60
 * @method Field\Divide         divider()
61
 * @method Field\Password       password($column, $label = '')
62
 * @method Field\Decimal        decimal($column, $label = '')
63
 * @method Field\Html           html($html, $label = '')
64
 * @method Field\Tags           tags($column, $label = '')
65
 * @method Field\Icon           icon($column, $label = '')
66
 * @method Field\Embeds         embeds($column, $label = '')
67
 * @method Field\MultipleImage  multipleImage($column, $label = '')
68
 * @method Field\MultipleFile   multipleFile($column, $label = '')
69
 * @method Field\Captcha        captcha($column, $label = '')
70
 * @method Field\Listbox        listbox($column, $label = '')
71
 */
72
class Form implements Renderable
73
{
74
    /**
75
     * Eloquent model of the form.
76
     *
77
     * @var Model
78
     */
79
    protected $model;
80
81
    /**
82
     * @var \Illuminate\Validation\Validator
83
     */
84
    protected $validator;
85
86
    /**
87
     * @var Builder
88
     */
89
    protected $builder;
90
91
    /**
92
     * Submitted callback.
93
     *
94
     * @var Closure[]
95
     */
96
    protected $submitted = [];
97
98
    /**
99
     * Saving callback.
100
     *
101
     * @var Closure[]
102
     */
103
    protected $saving = [];
104
105
    /**
106
     * Saved callback.
107
     *
108
     * @var Closure[]
109
     */
110
    protected $saved = [];
111
112
    /**
113
     * Data for save to current model from input.
114
     *
115
     * @var array
116
     */
117
    protected $updates = [];
118
119
    /**
120
     * Data for save to model's relations from input.
121
     *
122
     * @var array
123
     */
124
    protected $relations = [];
125
126
    /**
127
     * Input data.
128
     *
129
     * @var array
130
     */
131
    protected $inputs = [];
132
133
    /**
134
     * Available fields.
135
     *
136
     * @var array
137
     */
138
    public static $availableFields = [];
139
140
    /**
141
     * Ignored saving fields.
142
     *
143
     * @var array
144
     */
145
    protected $ignored = [];
146
147
    /**
148
     * Collected field assets.
149
     *
150
     * @var array
151
     */
152
    protected static $collectedAssets = [];
153
154
    /**
155
     * @var Form\Tab
156
     */
157
    protected $tab = null;
158
159
    /**
160
     * Remove flag in `has many` form.
161
     */
162
    const REMOVE_FLAG_NAME = '_remove_';
163
164
    /**
165
     * Field rows in form.
166
     *
167
     * @var array
168
     */
169
    public $rows = [];
170
171
    /**
172
     * Create a new form instance.
173
     *
174
     * @param $model
175
     * @param \Closure $callback
176
     */
177
    public function __construct($model, Closure $callback = null)
178
    {
179
        $this->model = $model;
180
181
        $this->builder = new Builder($this);
182
183
        if ($callback instanceof Closure) {
184
            $callback($this);
185
        }
186
    }
187
188
    /**
189
     * @param Field $field
190
     *
191
     * @return $this
192
     */
193
    public function pushField(Field $field)
194
    {
195
        $field->setForm($this);
196
197
        $this->builder->fields()->push($field);
198
199
        return $this;
200
    }
201
202
    /**
203
     * @return Model
204
     */
205
    public function model()
206
    {
207
        return $this->model;
208
    }
209
210
    /**
211
     * @return Builder
212
     */
213
    public function builder()
214
    {
215
        return $this->builder;
216
    }
217
218
    /**
219
     * Generate a edit form.
220
     *
221
     * @param $id
222
     *
223
     * @return $this
224
     */
225
    public function edit($id)
226
    {
227
        $this->builder->setMode(Builder::MODE_EDIT);
228
        $this->builder->setResourceId($id);
229
230
        $this->setFieldValue($id);
231
232
        return $this;
233
    }
234
235
    /**
236
     * Use tab to split form.
237
     *
238
     * @param string  $title
239
     * @param Closure $content
240
     *
241
     * @return $this
242
     */
243
    public function tab($title, Closure $content, $active = false)
244
    {
245
        $this->getTab()->append($title, $content, $active);
246
247
        return $this;
248
    }
249
250
    /**
251
     * Get Tab instance.
252
     *
253
     * @return Tab
254
     */
255
    public function getTab()
256
    {
257
        if (is_null($this->tab)) {
258
            $this->tab = new Tab($this);
259
        }
260
261
        return $this->tab;
262
    }
263
264
    /**
265
     * Destroy data entity and remove files.
266
     *
267
     * @param $id
268
     *
269
     * @return mixed
270
     */
271
    public function destroy($id)
272
    {
273
        $ids = explode(',', $id);
274
275
        collect($ids)->filter()->each(function ($id) {
276
            $this->deleteFiles($id);
277
            $this->model()->find($id)->delete();
278
        });
279
280
        return true;
281
    }
282
283
    /**
284
     * Remove files in record.
285
     *
286
     * @param $id
287
     */
288
    protected function deleteFiles($id)
289
    {
290
        // If it's a soft delete, the files in the data will not be deleted.
291
        if (in_array(SoftDeletes::class, class_uses($this->model))) {
292
            return;
293
        }
294
295
        $data = $this
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
296
            ->model()
297
            ->with($this->getRelations())
298
            ->findOrFail($id)->toArray();
299
300
        $this->builder->fields()->filter(function ($field) {
301
            return $field instanceof Field\File;
302
        })->each(function (Field\File $file) use ($data) {
303
            $file->setOriginal($data);
304
305
            $file->destroy();
306
        });
307
    }
308
309
    /**
310
     * Store a new record.
311
     *
312
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
313
     */
314
    public function store()
315
    {
316
        $data = Input::all();
317
318
        // Handle validation errors.
319
        if ($validationMessages = $this->validationMessages($data)) {
320
            return back()->withInput()->withErrors($validationMessages);
321
        }
322
323
        if (($response = $this->prepare($data)) instanceof Response) {
324
            return $response;
325
        }
326
327 View Code Duplication
        DB::transaction(function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
328
            $inserts = $this->prepareInsert($this->updates);
329
330
            foreach ($inserts as $column => $value) {
331
                $this->model->setAttribute($column, $value);
332
            }
333
334
            $this->model->save();
335
336
            $this->updateRelation($this->relations);
337
        });
338
339
        if (($response = $this->callSaved()) instanceof Response) {
340
            return $response;
341
        }
342
343
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
344
            return $response;
345
        }
346
347
        return $this->redirectAfterStore();
348
    }
349
350
    /**
351
     * Get ajax response.
352
     *
353
     * @param string $message
354
     *
355
     * @return bool|\Illuminate\Http\JsonResponse
356
     */
357
    protected function ajaxResponse($message)
358
    {
359
        $request = Request::capture();
360
361
        // ajax but not pjax
362
        if ($request->ajax() && !$request->pjax()) {
363
            return response()->json([
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
364
                'status'  => true,
365
                'message' => $message,
366
            ]);
367
        }
368
369
        return false;
370
    }
371
372
    /**
373
     * Prepare input data for insert or update.
374
     *
375
     * @param array $data
376
     *
377
     * @return mixed
378
     */
379
    protected function prepare($data = [])
380
    {
381
        if (($response = $this->callSubmitted()) instanceof Response) {
382
            return $response;
383
        }
384
385
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
386
387
        if (($response = $this->callSaving()) instanceof Response) {
388
            return $response;
389
        }
390
391
        $this->relations = $this->getRelationInputs($this->inputs);
392
393
        $this->updates = array_except($this->inputs, array_keys($this->relations));
394
    }
395
396
    /**
397
     * Remove ignored fields from input.
398
     *
399
     * @param array $input
400
     *
401
     * @return array
402
     */
403
    protected function removeIgnoredFields($input)
404
    {
405
        array_forget($input, $this->ignored);
406
407
        return $input;
408
    }
409
410
    /**
411
     * Get inputs for relations.
412
     *
413
     * @param array $inputs
414
     *
415
     * @return array
416
     */
417
    protected function getRelationInputs($inputs = [])
418
    {
419
        $relations = [];
420
421
        foreach ($inputs as $column => $value) {
422
            if (method_exists($this->model, $column)) {
423
                $relation = call_user_func([$this->model, $column]);
424
425
                if ($relation instanceof Relations\Relation) {
426
                    $relations[$column] = $value;
427
                }
428
            }
429
        }
430
431
        return $relations;
432
    }
433
434
    /**
435
     * Call submitted callback.
436
     *
437
     * @return mixed
438
     */
439
    protected function callSubmitted()
440
    {
441
        foreach ($this->submitted as $func) {
442
            if ($func instanceof Closure && ($ret = call_user_func($func, $this)) instanceof Response) {
443
                return $ret;
444
            }
445
        }
446
    }
447
448
    /**
449
     * Call saving callback.
450
     *
451
     * @return mixed
452
     */
453
    protected function callSaving()
454
    {
455
        foreach ($this->saving as $func) {
456
            if ($func instanceof Closure && ($ret = call_user_func($func, $this)) instanceof Response) {
457
                return $ret;
458
            }
459
        }
460
    }
461
462
    /**
463
     * Callback after saving a Model.
464
     *
465
     * @return mixed|null
466
     */
467
    protected function callSaved()
468
    {
469
        foreach ($this->saved as $func) {
470
            if ($func instanceof Closure && ($ret = call_user_func($func, $this)) instanceof Response) {
471
                return $ret;
472
            }
473
        }
474
    }
475
476
    /**
477
     * Handle update.
478
     *
479
     * @param int $id
480
     *
481
     * @return \Symfony\Component\HttpFoundation\Response
482
     */
483
    public function update($id, $data = null)
484
    {
485
        $data = ($data) ?: Input::all();
486
487
        $isEditable = $this->isEditable($data);
488
489
        $data = $this->handleEditable($data);
490
491
        $data = $this->handleFileDelete($data);
492
493
        if ($this->handleOrderable($id, $data)) {
494
            return response([
0 ignored issues
show
Bug Compatibility introduced by
The expression response(array('status' ...n.update_succeeded'))); of type Illuminate\Http\Response...Routing\ResponseFactory adds the type Illuminate\Contracts\Routing\ResponseFactory to the return on line 494 which is incompatible with the return type documented by Encore\Admin\Form::update of type Symfony\Component\HttpFoundation\Response.
Loading history...
495
                'status'  => true,
496
                'message' => trans('admin.update_succeeded'),
497
            ]);
498
        }
499
500
        /* @var Model $this->model */
501
        $this->model = $this->model->with($this->getRelations())->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
502
503
        $this->setFieldOriginalValue();
504
505
        // Handle validation errors.
506
        if ($validationMessages = $this->validationMessages($data)) {
507
            if (!$isEditable) {
508
                return back()->withInput()->withErrors($validationMessages);
509
            } else {
510
                return response()->json(['errors' => array_dot($validationMessages->getMessages())], 422);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
511
            }
512
        }
513
514
        if (($response = $this->prepare($data)) instanceof Response) {
515
            return $response;
516
        }
517
518 View Code Duplication
        DB::transaction(function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
519
            $updates = $this->prepareUpdate($this->updates);
520
521
            foreach ($updates as $column => $value) {
522
                /* @var Model $this->model */
523
                $this->model->setAttribute($column, $value);
524
            }
525
526
            $this->model->save();
527
528
            $this->updateRelation($this->relations);
529
        });
530
531
        if (($result = $this->callSaved()) instanceof Response) {
532
            return $result;
533
        }
534
535
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->ajaxResponse(tran...in.update_succeeded')); of type boolean|Illuminate\Http\JsonResponse adds the type boolean to the return on line 536 which is incompatible with the return type documented by Encore\Admin\Form::update of type Symfony\Component\HttpFoundation\Response.
Loading history...
536
            return $response;
537
        }
538
539
        return $this->redirectAfterUpdate($id);
540
    }
541
542
    /**
543
     * Get RedirectResponse after store.
544
     *
545
     * @return \Illuminate\Http\RedirectResponse
546
     */
547
    protected function redirectAfterStore()
548
    {
549
        $resourcesPath = $this->resource(0);
550
551
        $key = $this->model->getKey();
552
553
        return $this->redirectAfterSaving($resourcesPath, $key);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->redirectAfterSaving($resourcesPath, $key); of type Illuminate\Http\Redirect...nate\Routing\Redirector adds the type Illuminate\Routing\Redirector to the return on line 553 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
554
    }
555
556
    /**
557
     * Get RedirectResponse after update.
558
     *
559
     * @param mixed $key
560
     *
561
     * @return \Illuminate\Http\RedirectResponse
562
     */
563
    protected function redirectAfterUpdate($key)
564
    {
565
        $resourcesPath = $this->resource(-1);
566
567
        return $this->redirectAfterSaving($resourcesPath, $key);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->redirectAfterSaving($resourcesPath, $key); of type Illuminate\Http\Redirect...nate\Routing\Redirector adds the type Illuminate\Routing\Redirector to the return on line 567 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
568
    }
569
570
    /**
571
     * Get RedirectResponse after data saving.
572
     *
573
     * @param string $resourcesPath
574
     * @param string $key
575
     *
576
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
577
     */
578
    protected function redirectAfterSaving($resourcesPath, $key)
579
    {
580
        if (request('after-save') == 1) {
581
            // continue editing
582
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
583
        } elseif (request('after-save') == 2) {
584
            // view resource
585
            $url = rtrim($resourcesPath, '/')."/{$key}";
586
        } else {
587
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
588
        }
589
590
        admin_toastr(trans('admin.save_succeeded'));
591
592
        return redirect($url);
593
    }
594
595
    /**
596
     * Check if request is from editable.
597
     *
598
     * @param array $input
599
     *
600
     * @return bool
601
     */
602
    protected function isEditable(array $input = [])
603
    {
604
        return array_key_exists('_editable', $input);
605
    }
606
607
    /**
608
     * Handle editable update.
609
     *
610
     * @param array $input
611
     *
612
     * @return array
613
     */
614
    protected function handleEditable(array $input = [])
615
    {
616
        if (array_key_exists('_editable', $input)) {
617
            $name = $input['name'];
618
            $value = $input['value'];
619
620
            array_forget($input, ['pk', 'value', 'name']);
621
            array_set($input, $name, $value);
622
        }
623
624
        return $input;
625
    }
626
627
    /**
628
     * @param array $input
629
     *
630
     * @return array
631
     */
632
    protected function handleFileDelete(array $input = [])
633
    {
634
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
635
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
636
            unset($input['key']);
637
        }
638
639
        Input::replace($input);
640
641
        return $input;
642
    }
643
644
    /**
645
     * Handle orderable update.
646
     *
647
     * @param int   $id
648
     * @param array $input
649
     *
650
     * @return bool
651
     */
652
    protected function handleOrderable($id, array $input = [])
653
    {
654
        if (array_key_exists('_orderable', $input)) {
655
            $model = $this->model->find($id);
656
657
            if ($model instanceof Sortable) {
0 ignored issues
show
Bug introduced by
The class Spatie\EloquentSortable\Sortable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
658
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
659
660
                return true;
661
            }
662
        }
663
664
        return false;
665
    }
666
667
    /**
668
     * Update relation data.
669
     *
670
     * @param array $relationsData
671
     *
672
     * @return void
673
     */
674
    protected function updateRelation($relationsData)
675
    {
676
        foreach ($relationsData as $name => $values) {
677
            if (!method_exists($this->model, $name)) {
678
                continue;
679
            }
680
681
            $relation = $this->model->$name();
682
683
            $oneToOneRelation = $relation instanceof Relations\HasOne
684
                || $relation instanceof Relations\MorphOne
685
                || $relation instanceof Relations\BelongsTo;
686
687
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
688
689
            if (empty($prepared)) {
690
                continue;
691
            }
692
693
            switch (get_class($relation)) {
694
                case Relations\BelongsToMany::class:
695
                case Relations\MorphToMany::class:
696
                    if (isset($prepared[$name])) {
697
                        $relation->sync($prepared[$name]);
698
                    }
699
                    break;
700
                case Relations\HasOne::class:
701
                case Relations\BelongsTo::class:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
702
703
                    $related = $this->model->$name;
704
705
                    // if related is empty
706
                    if (is_null($related)) {
707
                        $related = $relation->getRelated();
708
                        $related->{$relation->getForeignKeyName()} = $this->model->{$this->model->getKeyName()};
709
                    }
710
711
                    foreach ($prepared[$name] as $column => $value) {
712
                        $related->setAttribute($column, $value);
713
                    }
714
715
                    $related->save();
716
                    break;
717
                case Relations\MorphOne::class:
718
                    $related = $this->model->$name;
719
                    if (is_null($related)) {
720
                        $related = $relation->make();
721
                    }
722
                    foreach ($prepared[$name] as $column => $value) {
723
                        $related->setAttribute($column, $value);
724
                    }
725
                    $related->save();
726
                    break;
727
                case Relations\HasMany::class:
728
                case Relations\MorphMany::class:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

According to the PSR-2, the body of a case statement must start on the line immediately following the case statement.

switch ($expr) {
case "A":
    doSomething(); //right
    break;
case "B":

    doSomethingElse(); //wrong
    break;

}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
729
730
                    foreach ($prepared[$name] as $related) {
731
                        /** @var Relations\Relation $relation */
732
                        $relation = $this->model()->$name();
733
734
                        $keyName = $relation->getRelated()->getKeyName();
735
736
                        $instance = $relation->findOrNew(array_get($related, $keyName));
737
738
                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
739
                            $instance->delete();
740
741
                            continue;
742
                        }
743
744
                        array_forget($related, static::REMOVE_FLAG_NAME);
745
746
                        $instance->fill($related);
747
748
                        $instance->save();
749
                    }
750
751
                    break;
752
            }
753
        }
754
    }
755
756
    /**
757
     * Prepare input data for update.
758
     *
759
     * @param array $updates
760
     * @param bool  $oneToOneRelation If column is one-to-one relation.
761
     *
762
     * @return array
763
     */
764
    protected function prepareUpdate(array $updates, $oneToOneRelation = false)
765
    {
766
        $prepared = [];
767
768
        /** @var Field $field */
769
        foreach ($this->builder->fields() as $field) {
770
            $columns = $field->column();
771
772
            // If column not in input array data, then continue.
773
            if (!array_has($updates, $columns)) {
774
                continue;
775
            }
776
777
            if ($this->invalidColumn($columns, $oneToOneRelation)) {
778
                continue;
779
            }
780
781
            $value = $this->getDataByColumn($updates, $columns);
782
783
            $value = $field->prepare($value);
784
785 View Code Duplication
            if (is_array($columns)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across 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...
786
                foreach ($columns as $name => $column) {
787
                    array_set($prepared, $column, $value[$name]);
788
                }
789
            } elseif (is_string($columns)) {
790
                array_set($prepared, $columns, $value);
791
            }
792
        }
793
794
        return $prepared;
795
    }
796
797
    /**
798
     * @param string|array $columns
799
     * @param bool         $oneToOneRelation
800
     *
801
     * @return bool
802
     */
803
    protected function invalidColumn($columns, $oneToOneRelation = false)
804
    {
805
        foreach ((array) $columns as $column) {
806
            if ((!$oneToOneRelation && Str::contains($column, '.')) ||
807
                ($oneToOneRelation && !Str::contains($column, '.'))) {
808
                return true;
809
            }
810
        }
811
812
        return false;
813
    }
814
815
    /**
816
     * Prepare input data for insert.
817
     *
818
     * @param $inserts
819
     *
820
     * @return array
821
     */
822
    protected function prepareInsert($inserts)
823
    {
824
        if ($this->isHasOneRelation($inserts)) {
825
            $inserts = array_dot($inserts);
826
        }
827
828
        foreach ($inserts as $column => $value) {
829
            if (is_null($field = $this->getFieldByColumn($column))) {
830
                unset($inserts[$column]);
831
                continue;
832
            }
833
834
            $inserts[$column] = $field->prepare($value);
835
        }
836
837
        $prepared = [];
838
839
        foreach ($inserts as $key => $value) {
840
            array_set($prepared, $key, $value);
841
        }
842
843
        return $prepared;
844
    }
845
846
    /**
847
     * Is input data is has-one relation.
848
     *
849
     * @param array $inserts
850
     *
851
     * @return bool
852
     */
853
    protected function isHasOneRelation($inserts)
854
    {
855
        $first = current($inserts);
856
857
        if (!is_array($first)) {
858
            return false;
859
        }
860
861
        if (is_array(current($first))) {
862
            return false;
863
        }
864
865
        return Arr::isAssoc($first);
866
    }
867
868
    /**
869
     * Set submitted callback.
870
     *
871
     * @param Closure $callback
872
     *
873
     * @return void
874
     */
875
    public function submitted(Closure $callback)
876
    {
877
        $this->submitted[] = $callback;
878
    }
879
880
    /**
881
     * Set saving callback.
882
     *
883
     * @param Closure $callback
884
     *
885
     * @return void
886
     */
887
    public function saving(Closure $callback)
888
    {
889
        $this->saving[] = $callback;
890
    }
891
892
    /**
893
     * Set saved callback.
894
     *
895
     * @param Closure $callback
896
     *
897
     * @return void
898
     */
899
    public function saved(Closure $callback)
900
    {
901
        $this->saved[] = $callback;
902
    }
903
904
    /**
905
     * Ignore fields to save.
906
     *
907
     * @param string|array $fields
908
     *
909
     * @return $this
910
     */
911
    public function ignore($fields)
912
    {
913
        $this->ignored = array_merge($this->ignored, (array) $fields);
914
915
        return $this;
916
    }
917
918
    /**
919
     * @param array        $data
920
     * @param string|array $columns
921
     *
922
     * @return array|mixed
923
     */
924 View Code Duplication
    protected function getDataByColumn($data, $columns)
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...
925
    {
926
        if (is_string($columns)) {
927
            return array_get($data, $columns);
928
        }
929
930
        if (is_array($columns)) {
931
            $value = [];
932
            foreach ($columns as $name => $column) {
933
                if (!array_has($data, $column)) {
934
                    continue;
935
                }
936
                $value[$name] = array_get($data, $column);
937
            }
938
939
            return $value;
940
        }
941
    }
942
943
    /**
944
     * Find field object by column.
945
     *
946
     * @param $column
947
     *
948
     * @return mixed
949
     */
950
    protected function getFieldByColumn($column)
951
    {
952
        return $this->builder->fields()->first(
953
            function (Field $field) use ($column) {
954
                if (is_array($field->column())) {
955
                    return in_array($column, $field->column());
956
                }
957
958
                return $field->column() == $column;
959
            }
960
        );
961
    }
962
963
    /**
964
     * Set original data for each field.
965
     *
966
     * @return void
967
     */
968
    protected function setFieldOriginalValue()
969
    {
970
//        static::doNotSnakeAttributes($this->model);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
971
972
        $values = $this->model->toArray();
973
974
        $this->builder->fields()->each(function (Field $field) use ($values) {
975
            $field->setOriginal($values);
976
        });
977
    }
978
979
    /**
980
     * Set all fields value in form.
981
     *
982
     * @param $id
983
     *
984
     * @return void
985
     */
986
    protected function setFieldValue($id)
987
    {
988
        $relations = $this->getRelations();
989
990
        $this->model = $this->model->with($relations)->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

It seems like the method you are trying to call exists only in some of the possible types.

Let’s take a look at an example:

class A
{
    public function foo() { }
}

class B extends A
{
    public function bar() { }
}

/**
 * @param A|B $x
 */
function someFunction($x)
{
    $x->foo(); // This call is fine as the method exists in A and B.
    $x->bar(); // This method only exists in B and might cause an error.
}

Available Fixes

  1. Add an additional type-check:

    /**
     * @param A|B $x
     */
    function someFunction($x)
    {
        $x->foo();
    
        if ($x instanceof B) {
            $x->bar();
        }
    }
    
  2. Only allow a single type to be passed if the variable comes from a parameter:

    function someFunction(B $x) { /** ... */ }
    
Loading history...
991
992
//        static::doNotSnakeAttributes($this->model);
0 ignored issues
show
Unused Code Comprehensibility introduced by
70% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
993
994
        $data = $this->model->toArray();
995
996
        $this->builder->fields()->each(function (Field $field) use ($data) {
997
            if (!in_array($field->column(), $this->ignored)) {
998
                $field->fill($data);
999
            }
1000
        });
1001
    }
1002
1003
    /**
1004
     * Don't snake case attributes.
1005
     *
1006
     * @param Model $model
1007
     *
1008
     * @return void
1009
     */
1010
    protected static function doNotSnakeAttributes(Model $model)
1011
    {
1012
        $class = get_class($model);
1013
1014
        $class::$snakeAttributes = false;
1015
    }
1016
1017
    /**
1018
     * Get validation messages.
1019
     *
1020
     * @param array $input
1021
     *
1022
     * @return MessageBag|bool
1023
     */
1024
    protected function validationMessages($input)
1025
    {
1026
        $failedValidators = [];
1027
1028
        /** @var Field $field */
1029
        foreach ($this->builder->fields() as $field) {
1030
            if (!$validator = $field->getValidator($input)) {
1031
                continue;
1032
            }
1033
1034
            if (($validator instanceof Validator) && !$validator->passes()) {
1035
                $failedValidators[] = $validator;
1036
            }
1037
        }
1038
1039
        $message = $this->mergeValidationMessages($failedValidators);
1040
1041
        return $message->any() ? $message : false;
1042
    }
1043
1044
    /**
1045
     * Merge validation messages from input validators.
1046
     *
1047
     * @param \Illuminate\Validation\Validator[] $validators
1048
     *
1049
     * @return MessageBag
1050
     */
1051
    protected function mergeValidationMessages($validators)
1052
    {
1053
        $messageBag = new MessageBag();
1054
1055
        foreach ($validators as $validator) {
1056
            $messageBag = $messageBag->merge($validator->messages());
1057
        }
1058
1059
        return $messageBag;
1060
    }
1061
1062
    /**
1063
     * Get all relations of model from callable.
1064
     *
1065
     * @return array
1066
     */
1067
    public function getRelations()
1068
    {
1069
        $relations = $columns = [];
1070
1071
        /** @var Field $field */
1072
        foreach ($this->builder->fields() as $field) {
1073
            $columns[] = $field->column();
1074
        }
1075
1076
        foreach (array_flatten($columns) as $column) {
1077
            if (str_contains($column, '.')) {
1078
                list($relation) = explode('.', $column);
1079
1080
                if (method_exists($this->model, $relation) &&
1081
                    $this->model->$relation() instanceof Relations\Relation
1082
                ) {
1083
                    $relations[] = $relation;
1084
                }
1085
            } elseif (method_exists($this->model, $column) &&
1086
                !method_exists(Model::class, $column)
1087
            ) {
1088
                $relations[] = $column;
1089
            }
1090
        }
1091
1092
        return array_unique($relations);
1093
    }
1094
1095
    /**
1096
     * Set action for form.
1097
     *
1098
     * @param string $action
1099
     *
1100
     * @return $this
1101
     */
1102
    public function setAction($action)
1103
    {
1104
        $this->builder()->setAction($action);
1105
1106
        return $this;
1107
    }
1108
1109
    /**
1110
     * Set field and label width in current form.
1111
     *
1112
     * @param int $fieldWidth
1113
     * @param int $labelWidth
1114
     *
1115
     * @return $this
1116
     */
1117
    public function setWidth($fieldWidth = 8, $labelWidth = 2)
1118
    {
1119
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1120
            /* @var Field $field  */
1121
            $field->setWidth($fieldWidth, $labelWidth);
1122
        });
1123
1124
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1125
1126
        return $this;
1127
    }
1128
1129
    /**
1130
     * Set view for form.
1131
     *
1132
     * @param string $view
1133
     *
1134
     * @return $this
1135
     */
1136
    public function setView($view)
1137
    {
1138
        $this->builder()->setView($view);
1139
1140
        return $this;
1141
    }
1142
1143
    /**
1144
     * Set title for form.
1145
     *
1146
     * @param string $title
1147
     *
1148
     * @return $this
1149
     */
1150
    public function setTitle($title = '')
1151
    {
1152
        $this->builder()->setTitle($title);
1153
1154
        return $this;
1155
    }
1156
1157
    /**
1158
     * Add a row in form.
1159
     *
1160
     * @param Closure $callback
1161
     *
1162
     * @return $this
1163
     */
1164
    public function row(Closure $callback)
1165
    {
1166
        $this->rows[] = new Row($callback, $this);
1167
1168
        return $this;
1169
    }
1170
1171
    /**
1172
     * Tools setting for form.
1173
     *
1174
     * @param Closure $callback
1175
     */
1176
    public function tools(Closure $callback)
1177
    {
1178
        $callback->call($this, $this->builder->getTools());
1179
    }
1180
1181
    /**
1182
     * Disable form submit.
1183
     *
1184
     * @return $this
1185
     *
1186
     * @deprecated
1187
     */
1188
    public function disableSubmit()
1189
    {
1190
        $this->builder()->getFooter()->disableSubmit();
1191
1192
        return $this;
1193
    }
1194
1195
    /**
1196
     * Disable form reset.
1197
     *
1198
     * @return $this
1199
     *
1200
     * @deprecated
1201
     */
1202
    public function disableReset()
1203
    {
1204
        $this->builder()->getFooter()->disableReset();
1205
1206
        return $this;
1207
    }
1208
1209
    /**
1210
     * Disable View Checkbox on footer.
1211
     *
1212
     * @return $this
1213
     */
1214
    public function disableViewCheck()
1215
    {
1216
        $this->builder()->getFooter()->disableViewCheck();
1217
1218
        return $this;
1219
    }
1220
1221
    /**
1222
     * Disable Editing Checkbox on footer.
1223
     *
1224
     * @return $this
1225
     */
1226
    public function disableEditingCheck()
1227
    {
1228
        $this->builder()->getFooter()->disableEditingCheck();
1229
1230
        return $this;
1231
    }
1232
1233
    /**
1234
     * Footer setting for form.
1235
     *
1236
     * @param Closure $callback
1237
     */
1238
    public function footer(Closure $callback)
1239
    {
1240
        call_user_func($callback, $this->builder()->getFooter());
1241
    }
1242
1243
    /**
1244
     * Get current resource route url.
1245
     *
1246
     * @param int $slice
1247
     *
1248
     * @return string
1249
     */
1250
    public function resource($slice = -2)
1251
    {
1252
        $segments = explode('/', trim(app('request')->getUri(), '/'));
1253
1254
        if ($slice != 0) {
1255
            $segments = array_slice($segments, 0, $slice);
1256
        }
1257
        // # fix #1768
1258
        if ($segments[0] == 'http:' && config('admin.secure') == true) {
1259
            $segments[0] = 'https:';
1260
        }
1261
1262
        return implode('/', $segments);
1263
    }
1264
1265
    /**
1266
     * Render the form contents.
1267
     *
1268
     * @return string
1269
     */
1270
    public function render()
1271
    {
1272
        try {
1273
            return $this->builder->render();
1274
        } catch (\Exception $e) {
1275
            return Handler::renderException($e);
1276
        }
1277
    }
1278
1279
    /**
1280
     * Get or set input data.
1281
     *
1282
     * @param string $key
1283
     * @param null   $value
1284
     *
1285
     * @return array|mixed
1286
     */
1287
    public function input($key, $value = null)
1288
    {
1289
        if (is_null($value)) {
1290
            return array_get($this->inputs, $key);
1291
        }
1292
1293
        return array_set($this->inputs, $key, $value);
1294
    }
1295
1296
    /**
1297
     * Register builtin fields.
1298
     *
1299
     * @return void
1300
     */
1301
    public static function registerBuiltinFields()
1302
    {
1303
        $map = [
1304
            'button'         => Field\Button::class,
1305
            'checkbox'       => Field\Checkbox::class,
1306
            'color'          => Field\Color::class,
1307
            'currency'       => Field\Currency::class,
1308
            'date'           => Field\Date::class,
1309
            'dateRange'      => Field\DateRange::class,
1310
            'datetime'       => Field\Datetime::class,
1311
            'dateTimeRange'  => Field\DatetimeRange::class,
1312
            'datetimeRange'  => Field\DatetimeRange::class,
1313
            'decimal'        => Field\Decimal::class,
1314
            'display'        => Field\Display::class,
1315
            'divider'        => Field\Divide::class,
1316
            'divide'         => Field\Divide::class,
1317
            'embeds'         => Field\Embeds::class,
1318
            'editor'         => Field\Editor::class,
1319
            'email'          => Field\Email::class,
1320
            'file'           => Field\File::class,
1321
            'hasMany'        => Field\HasMany::class,
1322
            'hidden'         => Field\Hidden::class,
1323
            'id'             => Field\Id::class,
1324
            'image'          => Field\Image::class,
1325
            'ip'             => Field\Ip::class,
1326
            'map'            => Field\Map::class,
1327
            'mobile'         => Field\Mobile::class,
1328
            'month'          => Field\Month::class,
1329
            'multipleSelect' => Field\MultipleSelect::class,
1330
            'number'         => Field\Number::class,
1331
            'password'       => Field\Password::class,
1332
            'radio'          => Field\Radio::class,
1333
            'rate'           => Field\Rate::class,
1334
            'select'         => Field\Select::class,
1335
            'slider'         => Field\Slider::class,
1336
            'switch'         => Field\SwitchField::class,
1337
            'text'           => Field\Text::class,
1338
            'textarea'       => Field\Textarea::class,
1339
            'time'           => Field\Time::class,
1340
            'timeRange'      => Field\TimeRange::class,
1341
            'url'            => Field\Url::class,
1342
            'year'           => Field\Year::class,
1343
            'html'           => Field\Html::class,
1344
            'tags'           => Field\Tags::class,
1345
            'icon'           => Field\Icon::class,
1346
            'multipleFile'   => Field\MultipleFile::class,
1347
            'multipleImage'  => Field\MultipleImage::class,
1348
            'captcha'        => Field\Captcha::class,
1349
            'listbox'        => Field\Listbox::class,
1350
        ];
1351
1352
        foreach ($map as $abstract => $class) {
1353
            static::extend($abstract, $class);
1354
        }
1355
    }
1356
1357
    /**
1358
     * Register custom field.
1359
     *
1360
     * @param string $abstract
1361
     * @param string $class
1362
     *
1363
     * @return void
1364
     */
1365
    public static function extend($abstract, $class)
1366
    {
1367
        static::$availableFields[$abstract] = $class;
1368
    }
1369
1370
    /**
1371
     * Remove registered field.
1372
     *
1373
     * @param array|string $abstract
1374
     */
1375
    public static function forget($abstract)
1376
    {
1377
        array_forget(static::$availableFields, $abstract);
1378
    }
1379
1380
    /**
1381
     * Find field class.
1382
     *
1383
     * @param string $method
1384
     *
1385
     * @return bool|mixed
1386
     */
1387 View Code Duplication
    public static function findFieldClass($method)
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...
1388
    {
1389
        $class = array_get(static::$availableFields, $method);
1390
1391
        if (class_exists($class)) {
1392
            return $class;
1393
        }
1394
1395
        return false;
1396
    }
1397
1398
    /**
1399
     * Collect assets required by registered field.
1400
     *
1401
     * @return array
1402
     */
1403
    public static function collectFieldAssets()
1404
    {
1405
        if (!empty(static::$collectedAssets)) {
1406
            return static::$collectedAssets;
1407
        }
1408
1409
        $css = collect();
1410
        $js = collect();
1411
1412
        foreach (static::$availableFields as $field) {
1413
            if (!method_exists($field, 'getAssets')) {
1414
                continue;
1415
            }
1416
1417
            $assets = call_user_func([$field, 'getAssets']);
1418
1419
            $css->push(array_get($assets, 'css'));
1420
            $js->push(array_get($assets, 'js'));
1421
        }
1422
1423
        return static::$collectedAssets = [
1424
            'css' => $css->flatten()->unique()->filter()->toArray(),
1425
            'js'  => $js->flatten()->unique()->filter()->toArray(),
1426
        ];
1427
    }
1428
1429
    /**
1430
     * Getter.
1431
     *
1432
     * @param string $name
1433
     *
1434
     * @return array|mixed
1435
     */
1436
    public function __get($name)
1437
    {
1438
        return $this->input($name);
1439
    }
1440
1441
    /**
1442
     * Setter.
1443
     *
1444
     * @param string $name
1445
     * @param $value
1446
     */
1447
    public function __set($name, $value)
1448
    {
1449
        $this->input($name, $value);
1450
    }
1451
1452
    /**
1453
     * Generate a Field object and add to form builder if Field exists.
1454
     *
1455
     * @param string $method
1456
     * @param array  $arguments
1457
     *
1458
     * @return Field|void
1459
     */
1460
    public function __call($method, $arguments)
1461
    {
1462
        if ($className = static::findFieldClass($method)) {
1463
            $column = array_get($arguments, 0, ''); //[0];
1464
1465
            $element = new $className($column, array_slice($arguments, 1));
1466
1467
            $this->pushField($element);
1468
1469
            return $element;
1470
        }
1471
    }
1472
}
1473