Completed
Push — master ( 7cba09...f5a087 )
by Song
02:15
created

Form::invalidColumn()   A

Complexity

Conditions 6
Paths 3

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 3
nop 2
dl 0
loc 11
rs 9.2222
c 0
b 0
f 0
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\HasHooks;
10
use Encore\Admin\Form\Row;
11
use Encore\Admin\Form\Tab;
12
use Illuminate\Contracts\Support\Renderable;
13
use Illuminate\Database\Eloquent\Model;
14
use Illuminate\Database\Eloquent\Relations;
15
use Illuminate\Database\Eloquent\SoftDeletes;
16
use Illuminate\Http\Request;
17
use Illuminate\Support\Arr;
18
use Illuminate\Support\Facades\DB;
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\File           file($column, $label = '')
43
 * @method Field\Image          image($column, $label = '')
44
 * @method Field\Date           date($column, $label = '')
45
 * @method Field\Datetime       datetime($column, $label = '')
46
 * @method Field\Time           time($column, $label = '')
47
 * @method Field\Year           year($column, $label = '')
48
 * @method Field\Month          month($column, $label = '')
49
 * @method Field\DateRange      dateRange($start, $end, $label = '')
50
 * @method Field\DateTimeRange  datetimeRange($start, $end, $label = '')
51
 * @method Field\TimeRange      timeRange($start, $end, $label = '')
52
 * @method Field\Number         number($column, $label = '')
53
 * @method Field\Currency       currency($column, $label = '')
54
 * @method Field\HasMany        hasMany($relationName, $label = '', $callback)
55
 * @method Field\SwitchField    switch($column, $label = '')
56
 * @method Field\Display        display($column, $label = '')
57
 * @method Field\Rate           rate($column, $label = '')
58
 * @method Field\Divide         divider()
59
 * @method Field\Password       password($column, $label = '')
60
 * @method Field\Decimal        decimal($column, $label = '')
61
 * @method Field\Html           html($html, $label = '')
62
 * @method Field\Tags           tags($column, $label = '')
63
 * @method Field\Icon           icon($column, $label = '')
64
 * @method Field\Embeds         embeds($column, $label = '')
65
 * @method Field\MultipleImage  multipleImage($column, $label = '')
66
 * @method Field\MultipleFile   multipleFile($column, $label = '')
67
 * @method Field\Captcha        captcha($column, $label = '')
68
 * @method Field\Listbox        listbox($column, $label = '')
69
 * @method Field\Table          table($column, $label, $builder)
70
 * @method Field\Timezone       timezone($column, $label = '')
71
 */
72
class Form implements Renderable
73
{
74
    use HasHooks;
75
76
    /**
77
     * Remove flag in `has many` form.
78
     */
79
    const REMOVE_FLAG_NAME = '_remove_';
80
81
    /**
82
     * Eloquent model of the form.
83
     *
84
     * @var Model
85
     */
86
    protected $model;
87
88
    /**
89
     * @var \Illuminate\Validation\Validator
90
     */
91
    protected $validator;
92
93
    /**
94
     * @var Builder
95
     */
96
    protected $builder;
97
98
    /**
99
     * Data for save to current model from input.
100
     *
101
     * @var array
102
     */
103
    protected $updates = [];
104
105
    /**
106
     * Data for save to model's relations from input.
107
     *
108
     * @var array
109
     */
110
    protected $relations = [];
111
112
    /**
113
     * Input data.
114
     *
115
     * @var array
116
     */
117
    protected $inputs = [];
118
119
    /**
120
     * Available fields.
121
     *
122
     * @var array
123
     */
124
    public static $availableFields = [];
125
126
    /**
127
     * Form field alias.
128
     *
129
     * @var array
130
     */
131
    public static $fieldAlias = [];
132
133
    /**
134
     * Ignored saving fields.
135
     *
136
     * @var array
137
     */
138
    protected $ignored = [];
139
140
    /**
141
     * Collected field assets.
142
     *
143
     * @var array
144
     */
145
    protected static $collectedAssets = [];
146
147
    /**
148
     * @var Form\Tab
149
     */
150
    protected $tab = null;
151
152
    /**
153
     * Field rows in form.
154
     *
155
     * @var array
156
     */
157
    public $rows = [];
158
159
    /**
160
     * @var bool
161
     */
162
    protected $isSoftDeletes = false;
163
164
    /**
165
     * Initialization closure array.
166
     *
167
     * @var []Closure
168
     */
169
    protected static $initCallbacks;
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
        $this->isSoftDeletes = in_array(SoftDeletes::class, class_uses_deep($this->model));
188
189
        $this->callInitCallbacks();
190
    }
191
192
    /**
193
     * Initialize with user pre-defined default disables, etc.
194
     *
195
     * @param Closure $callback
196
     */
197
    public static function init(Closure $callback = null)
198
    {
199
        static::$initCallbacks[] = $callback;
200
    }
201
202
    /**
203
     * Call the initialization closure array in sequence.
204
     */
205
    protected function callInitCallbacks()
206
    {
207
        if (empty(static::$initCallbacks)) {
208
            return;
209
        }
210
211
        foreach (static::$initCallbacks as $callback) {
212
            call_user_func($callback, $this);
213
        }
214
    }
215
216
    /**
217
     * @param Field $field
218
     *
219
     * @return $this
220
     */
221
    public function pushField(Field $field)
222
    {
223
        $field->setForm($this);
224
225
        $this->builder->fields()->push($field);
226
227
        return $this;
228
    }
229
230
    /**
231
     * @return Model
232
     */
233
    public function model()
234
    {
235
        return $this->model;
236
    }
237
238
    /**
239
     * @return Builder
240
     */
241
    public function builder()
242
    {
243
        return $this->builder;
244
    }
245
246
    /**
247
     * Generate a edit form.
248
     *
249
     * @param $id
250
     *
251
     * @return $this
252
     */
253
    public function edit($id)
254
    {
255
        $this->builder->setMode(Builder::MODE_EDIT);
256
        $this->builder->setResourceId($id);
257
258
        $this->setFieldValue($id);
259
260
        return $this;
261
    }
262
263
    /**
264
     * Use tab to split form.
265
     *
266
     * @param string  $title
267
     * @param Closure $content
268
     *
269
     * @return $this
270
     */
271
    public function tab($title, Closure $content, $active = false)
272
    {
273
        $this->getTab()->append($title, $content, $active);
274
275
        return $this;
276
    }
277
278
    /**
279
     * Get Tab instance.
280
     *
281
     * @return Tab
282
     */
283
    public function getTab()
284
    {
285
        if (is_null($this->tab)) {
286
            $this->tab = new Tab($this);
287
        }
288
289
        return $this->tab;
290
    }
291
292
    /**
293
     * Destroy data entity and remove files.
294
     *
295
     * @param $id
296
     *
297
     * @return mixed
298
     */
299
    public function destroy($id)
300
    {
301
        try {
302
            if (($ret = $this->callDeleting()) instanceof Response) {
303
                return $ret;
304
            }
305
306
            collect(explode(',', $id))->filter()->each(function ($id) {
307
                $builder = $this->model()->newQuery();
308
309
                if ($this->isSoftDeletes) {
310
                    $builder = $builder->withTrashed();
311
                }
312
313
                $model = $builder->with($this->getRelations())->findOrFail($id);
314
315
                if ($this->isSoftDeletes && $model->trashed()) {
316
                    $this->deleteFiles($model, true);
317
                    $model->forceDelete();
318
319
                    return;
320
                }
321
322
                $this->deleteFiles($model);
323
                $model->delete();
324
            });
325
326
            if (($ret = $this->callDeleted()) instanceof Response) {
327
                return $ret;
328
            }
329
330
            $response = [
331
                'status'  => true,
332
                'message' => trans('admin.delete_succeeded'),
333
            ];
334
        } catch (\Exception $exception) {
335
            $response = [
336
                'status'  => false,
337
                'message' => $exception->getMessage() ?: trans('admin.delete_failed'),
338
            ];
339
        }
340
341
        return response()->json($response);
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...
342
    }
343
344
    /**
345
     * Remove files in record.
346
     *
347
     * @param Model $model
348
     * @param bool  $forceDelete
349
     */
350
    protected function deleteFiles(Model $model, $forceDelete = false)
351
    {
352
        // If it's a soft delete, the files in the data will not be deleted.
353
        if (!$forceDelete && $this->isSoftDeletes) {
354
            return;
355
        }
356
357
        $data = $model->toArray();
358
359
        $this->builder->fields()->filter(function ($field) {
360
            return $field instanceof Field\File;
361
        })->each(function (Field\File $file) use ($data) {
362
            $file->setOriginal($data);
363
364
            $file->destroy();
365
        });
366
    }
367
368
    /**
369
     * Store a new record.
370
     *
371
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
372
     */
373
    public function store()
374
    {
375
        $data = \request()->all();
376
377
        // Handle validation errors.
378
        if ($validationMessages = $this->validationMessages($data)) {
379
            return back()->withInput()->withErrors($validationMessages);
380
        }
381
382
        if (($response = $this->prepare($data)) instanceof Response) {
383
            return $response;
384
        }
385
386 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...
387
            $inserts = $this->prepareInsert($this->updates);
388
389
            foreach ($inserts as $column => $value) {
390
                $this->model->setAttribute($column, $value);
391
            }
392
393
            $this->model->save();
394
395
            $this->updateRelation($this->relations);
396
        });
397
398
        if (($response = $this->callSaved()) instanceof Response) {
399
            return $response;
400
        }
401
402
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
403
            return $response;
404
        }
405
406
        return $this->redirectAfterStore();
407
    }
408
409
    /**
410
     * Get ajax response.
411
     *
412
     * @param string $message
413
     *
414
     * @return bool|\Illuminate\Http\JsonResponse
415
     */
416
    protected function ajaxResponse($message)
417
    {
418
        $request = Request::capture();
419
420
        // ajax but not pjax
421
        if ($request->ajax() && !$request->pjax()) {
422
            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...
423
                'status'  => true,
424
                'message' => $message,
425
            ]);
426
        }
427
428
        return false;
429
    }
430
431
    /**
432
     * Prepare input data for insert or update.
433
     *
434
     * @param array $data
435
     *
436
     * @return mixed
437
     */
438
    protected function prepare($data = [])
439
    {
440
        if (($response = $this->callSubmitted()) instanceof Response) {
441
            return $response;
442
        }
443
444
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
445
446
        if (($response = $this->callSaving()) instanceof Response) {
447
            return $response;
448
        }
449
450
        $this->relations = $this->getRelationInputs($this->inputs);
451
452
        $this->updates = Arr::except($this->inputs, array_keys($this->relations));
453
    }
454
455
    /**
456
     * Remove ignored fields from input.
457
     *
458
     * @param array $input
459
     *
460
     * @return array
461
     */
462
    protected function removeIgnoredFields($input)
463
    {
464
        Arr::forget($input, $this->ignored);
465
466
        return $input;
467
    }
468
469
    /**
470
     * Get inputs for relations.
471
     *
472
     * @param array $inputs
473
     *
474
     * @return array
475
     */
476
    protected function getRelationInputs($inputs = [])
477
    {
478
        $relations = [];
479
480
        foreach ($inputs as $column => $value) {
481
            if (!method_exists($this->model, $column)) {
482
                continue;
483
            }
484
485
            $relation = call_user_func([$this->model, $column]);
486
487
            if ($relation instanceof Relations\Relation) {
488
                $relations[$column] = $value;
489
            }
490
        }
491
492
        return $relations;
493
    }
494
495
    /**
496
     * Handle update.
497
     *
498
     * @param int  $id
499
     * @param null $data
500
     *
501
     * @return bool|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|null|Response
502
     */
503
    public function update($id, $data = null)
504
    {
505
        $data = ($data) ?: request()->all();
506
507
        $isEditable = $this->isEditable($data);
508
509
        if (($data = $this->handleColumnUpdates($id, $data)) instanceof Response) {
510
            return $data;
511
        }
512
513
        /* @var Model $this->model */
514
        $builder = $this->model();
515
516
        if ($this->isSoftDeletes) {
517
            $builder = $builder->withTrashed();
518
        }
519
520
        $this->model = $builder->with($this->getRelations())->findOrFail($id);
521
522
        $this->setFieldOriginalValue();
523
524
        // Handle validation errors.
525
        if ($validationMessages = $this->validationMessages($data)) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 509 can also be of type object<Illuminate\Contra...outing\ResponseFactory>; however, Encore\Admin\Form::validationMessages() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
526
            if (!$isEditable) {
527
                return back()->withInput()->withErrors($validationMessages);
528
            }
529
530
            return response()->json(['errors' => Arr::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...
531
        }
532
533
        if (($response = $this->prepare($data)) instanceof Response) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 509 can also be of type object<Illuminate\Contra...outing\ResponseFactory>; however, Encore\Admin\Form::prepare() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
534
            return $response;
535
        }
536
537 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...
538
            $updates = $this->prepareUpdate($this->updates);
539
540
            foreach ($updates as $column => $value) {
541
                /* @var Model $this->model */
542
                $this->model->setAttribute($column, $value);
543
            }
544
545
            $this->model->save();
546
547
            $this->updateRelation($this->relations);
548
        });
549
550
        if (($result = $this->callSaved()) instanceof Response) {
551
            return $result;
552
        }
553
554
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
555
            return $response;
556
        }
557
558
        return $this->redirectAfterUpdate($id);
559
    }
560
561
    /**
562
     * Get RedirectResponse after store.
563
     *
564
     * @return \Illuminate\Http\RedirectResponse
565
     */
566
    protected function redirectAfterStore()
567
    {
568
        $resourcesPath = $this->resource(0);
569
570
        $key = $this->model->getKey();
571
572
        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 572 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
573
    }
574
575
    /**
576
     * Get RedirectResponse after update.
577
     *
578
     * @param mixed $key
579
     *
580
     * @return \Illuminate\Http\RedirectResponse
581
     */
582
    protected function redirectAfterUpdate($key)
583
    {
584
        $resourcesPath = $this->resource(-1);
585
586
        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 586 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
587
    }
588
589
    /**
590
     * Get RedirectResponse after data saving.
591
     *
592
     * @param string $resourcesPath
593
     * @param string $key
594
     *
595
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
596
     */
597
    protected function redirectAfterSaving($resourcesPath, $key)
598
    {
599
        if (request('after-save') == 1) {
600
            // continue editing
601
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
602
        } elseif (request('after-save') == 2) {
603
            // continue creating
604
            $url = rtrim($resourcesPath, '/').'/create';
605
        } elseif (request('after-save') == 3) {
606
            // view resource
607
            $url = rtrim($resourcesPath, '/')."/{$key}";
608
        } else {
609
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
610
        }
611
612
        admin_toastr(trans('admin.save_succeeded'));
613
614
        return redirect($url);
615
    }
616
617
    /**
618
     * Check if request is from editable.
619
     *
620
     * @param array $input
621
     *
622
     * @return bool
623
     */
624
    protected function isEditable(array $input = [])
625
    {
626
        return array_key_exists('_editable', $input);
627
    }
628
629
    /**
630
     * Handle updates for single column.
631
     *
632
     * @param int   $id
633
     * @param array $data
634
     *
635
     * @return array|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|Response
636
     */
637
    protected function handleColumnUpdates($id, $data)
638
    {
639
        $data = $this->handleEditable($data);
640
641
        $data = $this->handleFileDelete($data);
642
643
        $data = $this->handleFileSort($data);
644
645
        if ($this->handleOrderable($id, $data)) {
646
            return response([
647
                'status'  => true,
648
                'message' => trans('admin.update_succeeded'),
649
            ]);
650
        }
651
652
        return $data;
653
    }
654
655
    /**
656
     * Handle editable update.
657
     *
658
     * @param array $input
659
     *
660
     * @return array
661
     */
662
    protected function handleEditable(array $input = [])
663
    {
664
        if (array_key_exists('_editable', $input)) {
665
            $name = $input['name'];
666
            $value = $input['value'];
667
668
            Arr::forget($input, ['pk', 'value', 'name']);
669
            Arr::set($input, $name, $value);
670
        }
671
672
        return $input;
673
    }
674
675
    /**
676
     * @param array $input
677
     *
678
     * @return array
679
     */
680
    protected function handleFileDelete(array $input = [])
681
    {
682
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
683
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
684
            unset($input['key']);
685
        }
686
687
        request()->replace($input);
688
689
        return $input;
690
    }
691
692
    /**
693
     * @param array $input
694
     *
695
     * @return array
696
     */
697
    protected function handleFileSort(array $input = [])
698
    {
699
        if (!array_key_exists(Field::FILE_SORT_FLAG, $input)) {
700
            return $input;
701
        }
702
703
        $sorts = array_filter($input[Field::FILE_SORT_FLAG]);
704
705
        if (empty($sorts)) {
706
            return $input;
707
        }
708
709
        foreach ($sorts as $column => $order) {
710
            $input[$column] = $order;
711
        }
712
713
        request()->replace($input);
714
715
        return $input;
716
    }
717
718
    /**
719
     * Handle orderable update.
720
     *
721
     * @param int   $id
722
     * @param array $input
723
     *
724
     * @return bool
725
     */
726
    protected function handleOrderable($id, array $input = [])
727
    {
728
        if (array_key_exists('_orderable', $input)) {
729
            $model = $this->model->find($id);
730
731
            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...
732
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
733
734
                return true;
735
            }
736
        }
737
738
        return false;
739
    }
740
741
    /**
742
     * Update relation data.
743
     *
744
     * @param array $relationsData
745
     *
746
     * @return void
747
     */
748
    protected function updateRelation($relationsData)
749
    {
750
        foreach ($relationsData as $name => $values) {
751
            if (!method_exists($this->model, $name)) {
752
                continue;
753
            }
754
755
            $relation = $this->model->$name();
756
757
            $oneToOneRelation = $relation instanceof Relations\HasOne
758
                || $relation instanceof Relations\MorphOne
759
                || $relation instanceof Relations\BelongsTo;
760
761
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
762
763
            if (empty($prepared)) {
764
                continue;
765
            }
766
767
            switch (true) {
768
                case $relation instanceof Relations\BelongsToMany:
769
                case $relation instanceof Relations\MorphToMany:
770
                    if (isset($prepared[$name])) {
771
                        $relation->sync($prepared[$name]);
772
                    }
773
                    break;
774
                case $relation instanceof Relations\HasOne:
775
776
                    $related = $this->model->$name;
777
778
                    // if related is empty
779
                    if (is_null($related)) {
780
                        $related = $relation->getRelated();
781
                        $qualifiedParentKeyName = $relation->getQualifiedParentKeyName();
782
                        $localKey = Arr::last(explode('.', $qualifiedParentKeyName));
783
                        $related->{$relation->getForeignKeyName()} = $this->model->{$localKey};
784
                    }
785
786
                    foreach ($prepared[$name] as $column => $value) {
787
                        $related->setAttribute($column, $value);
788
                    }
789
790
                    $related->save();
791
                    break;
792
                case $relation instanceof Relations\BelongsTo:
793
                case $relation instanceof Relations\MorphTo:
794
795
                    $parent = $this->model->$name;
796
797
                    // if related is empty
798
                    if (is_null($parent)) {
799
                        $parent = $relation->getRelated();
800
                    }
801
802
                    foreach ($prepared[$name] as $column => $value) {
803
                        $parent->setAttribute($column, $value);
804
                    }
805
806
                    $parent->save();
807
808
                    // When in creating, associate two models
809
                    $foreignKeyMethod = (app()->version() < '5.8.0') ? 'getForeignKey' : 'getForeignKeyName';
810
                    if (!$this->model->{$relation->{$foreignKeyMethod}()}) {
811
                        $this->model->{$relation->{$foreignKeyMethod}()} = $parent->getKey();
812
813
                        $this->model->save();
814
                    }
815
816
                    break;
817
                case $relation instanceof Relations\MorphOne:
818
                    $related = $this->model->$name;
819
                    if (is_null($related)) {
820
                        $related = $relation->make();
821
                    }
822
                    foreach ($prepared[$name] as $column => $value) {
823
                        $related->setAttribute($column, $value);
824
                    }
825
                    $related->save();
826
                    break;
827
                case $relation instanceof Relations\HasMany:
828
                case $relation instanceof Relations\MorphMany:
829
830
                    foreach ($prepared[$name] as $related) {
831
                        /** @var Relations\Relation $relation */
832
                        $relation = $this->model()->$name();
833
834
                        $keyName = $relation->getRelated()->getKeyName();
835
836
                        $instance = $relation->findOrNew(Arr::get($related, $keyName));
837
838
                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
839
                            $instance->delete();
840
841
                            continue;
842
                        }
843
844
                        Arr::forget($related, static::REMOVE_FLAG_NAME);
845
846
                        $instance->fill($related);
847
848
                        $instance->save();
849
                    }
850
851
                    break;
852
            }
853
        }
854
    }
855
856
    /**
857
     * Prepare input data for update.
858
     *
859
     * @param array $updates
860
     * @param bool  $oneToOneRelation If column is one-to-one relation.
861
     *
862
     * @return array
863
     */
864
    protected function prepareUpdate(array $updates, $oneToOneRelation = false)
865
    {
866
        $prepared = [];
867
868
        /** @var Field $field */
869
        foreach ($this->builder->fields() as $field) {
870
            $columns = $field->column();
871
872
            // If column not in input array data, then continue.
873
            if (!Arr::has($updates, $columns)) {
874
                continue;
875
            }
876
877
            if ($this->isInvalidColumn($columns, $oneToOneRelation || $field->isJsonType)) {
878
                continue;
879
            }
880
881
            $value = $this->getDataByColumn($updates, $columns);
882
883
            $value = $field->prepare($value);
884
885 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...
886
                foreach ($columns as $name => $column) {
887
                    Arr::set($prepared, $column, $value[$name]);
888
                }
889
            } elseif (is_string($columns)) {
890
                Arr::set($prepared, $columns, $value);
891
            }
892
        }
893
894
        return $prepared;
895
    }
896
897
    /**
898
     * @param string|array $columns
899
     * @param bool         $containsDot
900
     *
901
     * @return bool
902
     */
903
    protected function isInvalidColumn($columns, $containsDot = false)
904
    {
905
        foreach ((array) $columns as $column) {
906
            if ((!$containsDot && Str::contains($column, '.')) ||
907
                ($containsDot && !Str::contains($column, '.'))) {
908
                return true;
909
            }
910
        }
911
912
        return false;
913
    }
914
915
    /**
916
     * Prepare input data for insert.
917
     *
918
     * @param $inserts
919
     *
920
     * @return array
921
     */
922
    protected function prepareInsert($inserts)
923
    {
924
        if ($this->isHasOneRelation($inserts)) {
925
            $inserts = Arr::dot($inserts);
926
        }
927
928
        foreach ($inserts as $column => $value) {
929
            if (is_null($field = $this->getFieldByColumn($column))) {
930
                unset($inserts[$column]);
931
                continue;
932
            }
933
934
            $inserts[$column] = $field->prepare($value);
935
        }
936
937
        $prepared = [];
938
939
        foreach ($inserts as $key => $value) {
940
            Arr::set($prepared, $key, $value);
941
        }
942
943
        return $prepared;
944
    }
945
946
    /**
947
     * Is input data is has-one relation.
948
     *
949
     * @param array $inserts
950
     *
951
     * @return bool
952
     */
953
    protected function isHasOneRelation($inserts)
954
    {
955
        $first = current($inserts);
956
957
        if (!is_array($first)) {
958
            return false;
959
        }
960
961
        if (is_array(current($first))) {
962
            return false;
963
        }
964
965
        return Arr::isAssoc($first);
966
    }
967
968
    /**
969
     * Ignore fields to save.
970
     *
971
     * @param string|array $fields
972
     *
973
     * @return $this
974
     */
975
    public function ignore($fields)
976
    {
977
        $this->ignored = array_merge($this->ignored, (array) $fields);
978
979
        return $this;
980
    }
981
982
    /**
983
     * @param array        $data
984
     * @param string|array $columns
985
     *
986
     * @return array|mixed
987
     */
988 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...
989
    {
990
        if (is_string($columns)) {
991
            return Arr::get($data, $columns);
992
        }
993
994
        if (is_array($columns)) {
995
            $value = [];
996
            foreach ($columns as $name => $column) {
997
                if (!Arr::has($data, $column)) {
998
                    continue;
999
                }
1000
                $value[$name] = Arr::get($data, $column);
1001
            }
1002
1003
            return $value;
1004
        }
1005
    }
1006
1007
    /**
1008
     * Find field object by column.
1009
     *
1010
     * @param $column
1011
     *
1012
     * @return mixed
1013
     */
1014
    protected function getFieldByColumn($column)
1015
    {
1016
        return $this->builder->fields()->first(
1017
            function (Field $field) use ($column) {
1018
                if (is_array($field->column())) {
1019
                    return in_array($column, $field->column());
1020
                }
1021
1022
                return $field->column() == $column;
1023
            }
1024
        );
1025
    }
1026
1027
    /**
1028
     * Set original data for each field.
1029
     *
1030
     * @return void
1031
     */
1032
    protected function setFieldOriginalValue()
1033
    {
1034
//        static::doNotSnakeAttributes($this->model);
1035
1036
        $values = $this->model->toArray();
1037
1038
        $this->builder->fields()->each(function (Field $field) use ($values) {
1039
            $field->setOriginal($values);
1040
        });
1041
    }
1042
1043
    /**
1044
     * Set all fields value in form.
1045
     *
1046
     * @param $id
1047
     *
1048
     * @return void
1049
     */
1050
    protected function setFieldValue($id)
1051
    {
1052
        $relations = $this->getRelations();
1053
1054
        $builder = $this->model();
1055
1056
        if ($this->isSoftDeletes) {
1057
            $builder = $builder->withTrashed();
1058
        }
1059
1060
        $this->model = $builder->with($relations)->findOrFail($id);
1061
1062
        $this->callEditing();
1063
1064
//        static::doNotSnakeAttributes($this->model);
1065
1066
        $data = $this->model->toArray();
1067
1068
        $this->builder->fields()->each(function (Field $field) use ($data) {
1069
            if (!in_array($field->column(), $this->ignored)) {
1070
                $field->fill($data);
1071
            }
1072
        });
1073
    }
1074
1075
    /**
1076
     * Don't snake case attributes.
1077
     *
1078
     * @param Model $model
1079
     *
1080
     * @return void
1081
     */
1082
    protected static function doNotSnakeAttributes(Model $model)
1083
    {
1084
        $class = get_class($model);
1085
1086
        $class::$snakeAttributes = false;
1087
    }
1088
1089
    /**
1090
     * Get validation messages.
1091
     *
1092
     * @param array $input
1093
     *
1094
     * @return MessageBag|bool
1095
     */
1096
    public function validationMessages($input)
1097
    {
1098
        $failedValidators = [];
1099
1100
        /** @var Field $field */
1101
        foreach ($this->builder->fields() as $field) {
1102
            if (!$validator = $field->getValidator($input)) {
1103
                continue;
1104
            }
1105
1106
            if (($validator instanceof Validator) && !$validator->passes()) {
1107
                $failedValidators[] = $validator;
1108
            }
1109
        }
1110
1111
        $message = $this->mergeValidationMessages($failedValidators);
1112
1113
        return $message->any() ? $message : false;
1114
    }
1115
1116
    /**
1117
     * Merge validation messages from input validators.
1118
     *
1119
     * @param \Illuminate\Validation\Validator[] $validators
1120
     *
1121
     * @return MessageBag
1122
     */
1123
    protected function mergeValidationMessages($validators)
1124
    {
1125
        $messageBag = new MessageBag();
1126
1127
        foreach ($validators as $validator) {
1128
            $messageBag = $messageBag->merge($validator->messages());
1129
        }
1130
1131
        return $messageBag;
1132
    }
1133
1134
    /**
1135
     * Get all relations of model from callable.
1136
     *
1137
     * @return array
1138
     */
1139
    public function getRelations()
1140
    {
1141
        $relations = $columns = [];
1142
1143
        /** @var Field $field */
1144
        foreach ($this->builder->fields() as $field) {
1145
            $columns[] = $field->column();
1146
        }
1147
1148
        foreach (Arr::flatten($columns) as $column) {
1149
            if (Str::contains($column, '.')) {
1150
                list($relation) = explode('.', $column);
1151
1152
                if (method_exists($this->model, $relation) &&
1153
                    $this->model->$relation() instanceof Relations\Relation
1154
                ) {
1155
                    $relations[] = $relation;
1156
                }
1157
            } elseif (method_exists($this->model, $column) &&
1158
                !method_exists(Model::class, $column)
1159
            ) {
1160
                $relations[] = $column;
1161
            }
1162
        }
1163
1164
        return array_unique($relations);
1165
    }
1166
1167
    /**
1168
     * Set action for form.
1169
     *
1170
     * @param string $action
1171
     *
1172
     * @return $this
1173
     */
1174
    public function setAction($action)
1175
    {
1176
        $this->builder()->setAction($action);
1177
1178
        return $this;
1179
    }
1180
1181
    /**
1182
     * Set field and label width in current form.
1183
     *
1184
     * @param int $fieldWidth
1185
     * @param int $labelWidth
1186
     *
1187
     * @return $this
1188
     */
1189
    public function setWidth($fieldWidth = 8, $labelWidth = 2)
1190
    {
1191
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1192
            /* @var Field $field  */
1193
            $field->setWidth($fieldWidth, $labelWidth);
1194
        });
1195
1196
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1197
1198
        return $this;
1199
    }
1200
1201
    /**
1202
     * Set view for form.
1203
     *
1204
     * @param string $view
1205
     *
1206
     * @return $this
1207
     */
1208
    public function setView($view)
1209
    {
1210
        $this->builder()->setView($view);
1211
1212
        return $this;
1213
    }
1214
1215
    /**
1216
     * Set title for form.
1217
     *
1218
     * @param string $title
1219
     *
1220
     * @return $this
1221
     */
1222
    public function setTitle($title = '')
1223
    {
1224
        $this->builder()->setTitle($title);
1225
1226
        return $this;
1227
    }
1228
1229
    /**
1230
     * Add a row in form.
1231
     *
1232
     * @param Closure $callback
1233
     *
1234
     * @return $this
1235
     */
1236
    public function row(Closure $callback)
1237
    {
1238
        $this->rows[] = new Row($callback, $this);
1239
1240
        return $this;
1241
    }
1242
1243
    /**
1244
     * Tools setting for form.
1245
     *
1246
     * @param Closure $callback
1247
     */
1248
    public function tools(Closure $callback)
1249
    {
1250
        $callback->call($this, $this->builder->getTools());
1251
    }
1252
1253
    /**
1254
     * @param Closure|null $callback
1255
     *
1256
     * @return Form\Tools
1257
     */
1258
    public function header(Closure $callback = null)
1259
    {
1260
        if (func_num_args() == 0) {
1261
            return $this->builder->getTools();
1262
        }
1263
1264
        $callback->call($this, $this->builder->getTools());
0 ignored issues
show
Bug introduced by
It seems like $callback is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
1265
    }
1266
1267
    /**
1268
     * Disable form submit.
1269
     *
1270
     * @param bool $disable
1271
     *
1272
     * @return $this
1273
     *
1274
     * @deprecated
1275
     */
1276
    public function disableSubmit(bool $disable = true)
1277
    {
1278
        $this->builder()->getFooter()->disableSubmit($disable);
1279
1280
        return $this;
1281
    }
1282
1283
    /**
1284
     * Disable form reset.
1285
     *
1286
     * @param bool $disable
1287
     *
1288
     * @return $this
1289
     *
1290
     * @deprecated
1291
     */
1292
    public function disableReset(bool $disable = true)
1293
    {
1294
        $this->builder()->getFooter()->disableReset($disable);
1295
1296
        return $this;
1297
    }
1298
1299
    /**
1300
     * Disable View Checkbox on footer.
1301
     *
1302
     * @param bool $disable
1303
     *
1304
     * @return $this
1305
     */
1306
    public function disableViewCheck(bool $disable = true)
1307
    {
1308
        $this->builder()->getFooter()->disableViewCheck($disable);
1309
1310
        return $this;
1311
    }
1312
1313
    /**
1314
     * Disable Editing Checkbox on footer.
1315
     *
1316
     * @param bool $disable
1317
     *
1318
     * @return $this
1319
     */
1320
    public function disableEditingCheck(bool $disable = true)
1321
    {
1322
        $this->builder()->getFooter()->disableEditingCheck($disable);
1323
1324
        return $this;
1325
    }
1326
1327
    /**
1328
     * Disable Creating Checkbox on footer.
1329
     *
1330
     * @param bool $disable
1331
     *
1332
     * @return $this
1333
     */
1334
    public function disableCreatingCheck(bool $disable = true)
1335
    {
1336
        $this->builder()->getFooter()->disableCreatingCheck($disable);
1337
1338
        return $this;
1339
    }
1340
1341
    /**
1342
     * Footer setting for form.
1343
     *
1344
     * @param Closure $callback
1345
     */
1346
    public function footer(Closure $callback = null)
1347
    {
1348
        if (func_num_args() == 0) {
1349
            return $this->builder()->getFooter();
1350
        }
1351
1352
        call_user_func($callback, $this->builder()->getFooter());
1353
    }
1354
1355
    /**
1356
     * Get current resource route url.
1357
     *
1358
     * @param int $slice
1359
     *
1360
     * @return string
1361
     */
1362
    public function resource($slice = -2)
1363
    {
1364
        $segments = explode('/', trim(app('request')->getUri(), '/'));
1365
1366
        if ($slice != 0) {
1367
            $segments = array_slice($segments, 0, $slice);
1368
        }
1369
1370
        return implode('/', $segments);
1371
    }
1372
1373
    /**
1374
     * Render the form contents.
1375
     *
1376
     * @return string
1377
     */
1378
    public function render()
1379
    {
1380
        try {
1381
            return $this->builder->render();
1382
        } catch (\Exception $e) {
1383
            return Handler::renderException($e);
1384
        }
1385
    }
1386
1387
    /**
1388
     * Get or set input data.
1389
     *
1390
     * @param string $key
1391
     * @param null   $value
1392
     *
1393
     * @return array|mixed
1394
     */
1395
    public function input($key, $value = null)
1396
    {
1397
        if (is_null($value)) {
1398
            return Arr::get($this->inputs, $key);
1399
        }
1400
1401
        return Arr::set($this->inputs, $key, $value);
1402
    }
1403
1404
    /**
1405
     * Register builtin fields.
1406
     *
1407
     * @return void
1408
     */
1409
    public static function registerBuiltinFields()
1410
    {
1411
        $map = [
1412
            'button'         => Field\Button::class,
1413
            'checkbox'       => Field\Checkbox::class,
1414
            'color'          => Field\Color::class,
1415
            'currency'       => Field\Currency::class,
1416
            'date'           => Field\Date::class,
1417
            'dateRange'      => Field\DateRange::class,
1418
            'datetime'       => Field\Datetime::class,
1419
            'dateTimeRange'  => Field\DatetimeRange::class,
1420
            'datetimeRange'  => Field\DatetimeRange::class,
1421
            'decimal'        => Field\Decimal::class,
1422
            'display'        => Field\Display::class,
1423
            'divider'        => Field\Divide::class,
1424
            'divide'         => Field\Divide::class,
1425
            'embeds'         => Field\Embeds::class,
1426
            'email'          => Field\Email::class,
1427
            'file'           => Field\File::class,
1428
            'hasMany'        => Field\HasMany::class,
1429
            'hidden'         => Field\Hidden::class,
1430
            'id'             => Field\Id::class,
1431
            'image'          => Field\Image::class,
1432
            'ip'             => Field\Ip::class,
1433
            'mobile'         => Field\Mobile::class,
1434
            'month'          => Field\Month::class,
1435
            'multipleSelect' => Field\MultipleSelect::class,
1436
            'number'         => Field\Number::class,
1437
            'password'       => Field\Password::class,
1438
            'radio'          => Field\Radio::class,
1439
            'rate'           => Field\Rate::class,
1440
            'select'         => Field\Select::class,
1441
            'slider'         => Field\Slider::class,
1442
            'switch'         => Field\SwitchField::class,
1443
            'text'           => Field\Text::class,
1444
            'textarea'       => Field\Textarea::class,
1445
            'time'           => Field\Time::class,
1446
            'timeRange'      => Field\TimeRange::class,
1447
            'url'            => Field\Url::class,
1448
            'year'           => Field\Year::class,
1449
            'html'           => Field\Html::class,
1450
            'tags'           => Field\Tags::class,
1451
            'icon'           => Field\Icon::class,
1452
            'multipleFile'   => Field\MultipleFile::class,
1453
            'multipleImage'  => Field\MultipleImage::class,
1454
            'captcha'        => Field\Captcha::class,
1455
            'listbox'        => Field\Listbox::class,
1456
            'table'          => Field\Table::class,
1457
            'timezone'       => Field\Timezone::class,
1458
        ];
1459
1460
        foreach ($map as $abstract => $class) {
1461
            static::extend($abstract, $class);
1462
        }
1463
    }
1464
1465
    /**
1466
     * Register custom field.
1467
     *
1468
     * @param string $abstract
1469
     * @param string $class
1470
     *
1471
     * @return void
1472
     */
1473
    public static function extend($abstract, $class)
1474
    {
1475
        static::$availableFields[$abstract] = $class;
1476
    }
1477
1478
    /**
1479
     * Set form field alias.
1480
     *
1481
     * @param string $field
1482
     * @param string $alias
1483
     *
1484
     * @return void
1485
     */
1486
    public static function alias($field, $alias)
1487
    {
1488
        static::$fieldAlias[$alias] = $field;
1489
    }
1490
1491
    /**
1492
     * Remove registered field.
1493
     *
1494
     * @param array|string $abstract
1495
     */
1496
    public static function forget($abstract)
1497
    {
1498
        Arr::forget(static::$availableFields, $abstract);
1499
    }
1500
1501
    /**
1502
     * Find field class.
1503
     *
1504
     * @param string $method
1505
     *
1506
     * @return bool|mixed
1507
     */
1508
    public static function findFieldClass($method)
1509
    {
1510
        // If alias exists.
1511
        if (isset(static::$fieldAlias[$method])) {
1512
            $method = static::$fieldAlias[$method];
1513
        }
1514
1515
        $class = Arr::get(static::$availableFields, $method);
1516
1517
        if (class_exists($class)) {
1518
            return $class;
1519
        }
1520
1521
        return false;
1522
    }
1523
1524
    /**
1525
     * Collect assets required by registered field.
1526
     *
1527
     * @return array
1528
     */
1529
    public static function collectFieldAssets()
1530
    {
1531
        if (!empty(static::$collectedAssets)) {
1532
            return static::$collectedAssets;
1533
        }
1534
1535
        $css = collect();
1536
        $js = collect();
1537
1538
        foreach (static::$availableFields as $field) {
1539
            if (!method_exists($field, 'getAssets')) {
1540
                continue;
1541
            }
1542
1543
            $assets = call_user_func([$field, 'getAssets']);
1544
1545
            $css->push(Arr::get($assets, 'css'));
1546
            $js->push(Arr::get($assets, 'js'));
1547
        }
1548
1549
        return static::$collectedAssets = [
1550
            'css' => $css->flatten()->unique()->filter()->toArray(),
1551
            'js'  => $js->flatten()->unique()->filter()->toArray(),
1552
        ];
1553
    }
1554
1555
    /**
1556
     * Getter.
1557
     *
1558
     * @param string $name
1559
     *
1560
     * @return array|mixed
1561
     */
1562
    public function __get($name)
1563
    {
1564
        return $this->input($name);
1565
    }
1566
1567
    /**
1568
     * Setter.
1569
     *
1570
     * @param string $name
1571
     * @param mixed  $value
1572
     *
1573
     * @return array
1574
     */
1575
    public function __set($name, $value)
1576
    {
1577
        return Arr::set($this->inputs, $name, $value);
1578
    }
1579
1580
    /**
1581
     * Generate a Field object and add to form builder if Field exists.
1582
     *
1583
     * @param string $method
1584
     * @param array  $arguments
1585
     *
1586
     * @return Field
1587
     */
1588
    public function __call($method, $arguments)
1589
    {
1590
        if ($className = static::findFieldClass($method)) {
1591
            $column = Arr::get($arguments, 0, ''); //[0];
1592
1593
            $element = new $className($column, array_slice($arguments, 1));
1594
1595
            $this->pushField($element);
1596
1597
            return $element;
1598
        }
1599
1600
        admin_error('Error', "Field type [$method] does not exist.");
1601
1602
        return new Field\Nullable();
1603
    }
1604
}
1605