Completed
Push — master ( fbfcb9...4fd533 )
by Song
02:36
created

Form::header()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
nc 2
nop 1
dl 0
loc 8
rs 10
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
 */
71
class Form implements Renderable
72
{
73
    use HasHooks;
74
75
    /**
76
     * Remove flag in `has many` form.
77
     */
78
    const REMOVE_FLAG_NAME = '_remove_';
79
80
    /**
81
     * Eloquent model of the form.
82
     *
83
     * @var Model
84
     */
85
    protected $model;
86
87
    /**
88
     * @var \Illuminate\Validation\Validator
89
     */
90
    protected $validator;
91
92
    /**
93
     * @var Builder
94
     */
95
    protected $builder;
96
97
    /**
98
     * Data for save to current model from input.
99
     *
100
     * @var array
101
     */
102
    protected $updates = [];
103
104
    /**
105
     * Data for save to model's relations from input.
106
     *
107
     * @var array
108
     */
109
    protected $relations = [];
110
111
    /**
112
     * Input data.
113
     *
114
     * @var array
115
     */
116
    protected $inputs = [];
117
118
    /**
119
     * Available fields.
120
     *
121
     * @var array
122
     */
123
    public static $availableFields = [];
124
125
    /**
126
     * Form field alias.
127
     *
128
     * @var array
129
     */
130
    public static $fieldAlias = [];
131
132
    /**
133
     * Ignored saving fields.
134
     *
135
     * @var array
136
     */
137
    protected $ignored = [];
138
139
    /**
140
     * Collected field assets.
141
     *
142
     * @var array
143
     */
144
    protected static $collectedAssets = [];
145
146
    /**
147
     * @var Form\Tab
148
     */
149
    protected $tab = null;
150
151
    /**
152
     * Field rows in form.
153
     *
154
     * @var array
155
     */
156
    public $rows = [];
157
158
    /**
159
     * @var bool
160
     */
161
    protected $isSoftDeletes = false;
162
163
    /**
164
     * Initialization closure array.
165
     *
166
     * @var []Closure
167
     */
168
    protected static $initCallbacks;
169
170
    /**
171
     * Create a new form instance.
172
     *
173
     * @param $model
174
     * @param \Closure $callback
175
     */
176
    public function __construct($model, Closure $callback = null)
177
    {
178
        $this->model = $model;
179
180
        $this->builder = new Builder($this);
181
182
        if ($callback instanceof Closure) {
183
            $callback($this);
184
        }
185
186
        $this->isSoftDeletes = in_array(SoftDeletes::class, class_uses_deep($this->model));
187
188
        $this->callInitCallbacks();
189
    }
190
191
    /**
192
     * Initialize with user pre-defined default disables, etc.
193
     *
194
     * @param Closure $callback
195
     */
196
    public static function init(Closure $callback = null)
197
    {
198
        static::$initCallbacks[] = $callback;
199
    }
200
201
    /**
202
     * Call the initialization closure array in sequence.
203
     */
204
    protected function callInitCallbacks()
205
    {
206
        if (empty(static::$initCallbacks)) {
207
            return;
208
        }
209
210
        foreach (static::$initCallbacks as $callback) {
211
            call_user_func($callback, $this);
212
        }
213
    }
214
215
    /**
216
     * @param Field $field
217
     *
218
     * @return $this
219
     */
220
    public function pushField(Field $field)
221
    {
222
        $field->setForm($this);
223
224
        $this->builder->fields()->push($field);
225
226
        return $this;
227
    }
228
229
    /**
230
     * @return Model
231
     */
232
    public function model()
233
    {
234
        return $this->model;
235
    }
236
237
    /**
238
     * @return Builder
239
     */
240
    public function builder()
241
    {
242
        return $this->builder;
243
    }
244
245
    /**
246
     * Generate a edit form.
247
     *
248
     * @param $id
249
     *
250
     * @return $this
251
     */
252
    public function edit($id)
253
    {
254
        $this->builder->setMode(Builder::MODE_EDIT);
255
        $this->builder->setResourceId($id);
256
257
        $this->setFieldValue($id);
258
259
        return $this;
260
    }
261
262
    /**
263
     * Use tab to split form.
264
     *
265
     * @param string  $title
266
     * @param Closure $content
267
     *
268
     * @return $this
269
     */
270
    public function tab($title, Closure $content, $active = false)
271
    {
272
        $this->getTab()->append($title, $content, $active);
273
274
        return $this;
275
    }
276
277
    /**
278
     * Get Tab instance.
279
     *
280
     * @return Tab
281
     */
282
    public function getTab()
283
    {
284
        if (is_null($this->tab)) {
285
            $this->tab = new Tab($this);
286
        }
287
288
        return $this->tab;
289
    }
290
291
    /**
292
     * Destroy data entity and remove files.
293
     *
294
     * @param $id
295
     *
296
     * @return mixed
297
     */
298
    public function destroy($id)
299
    {
300
        try {
301
            if (($ret = $this->callDeleting()) instanceof Response) {
302
                return $ret;
303
            }
304
305
            collect(explode(',', $id))->filter()->each(function ($id) {
306
                $builder = $this->model()->newQuery();
307
308
                if ($this->isSoftDeletes) {
309
                    $builder = $builder->withTrashed();
310
                }
311
312
                $model = $builder->with($this->getRelations())->findOrFail($id);
313
314
                if ($this->isSoftDeletes && $model->trashed()) {
315
                    $this->deleteFiles($model, true);
316
                    $model->forceDelete();
317
318
                    return;
319
                }
320
321
                $this->deleteFiles($model);
322
                $model->delete();
323
            });
324
325
            if (($ret = $this->callDeleted()) instanceof Response) {
326
                return $ret;
327
            }
328
329
            $response = [
330
                'status'  => true,
331
                'message' => trans('admin.delete_succeeded'),
332
            ];
333
        } catch (\Exception $exception) {
334
            $response = [
335
                'status'  => false,
336
                'message' => $exception->getMessage() ?: trans('admin.delete_failed'),
337
            ];
338
        }
339
340
        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...
341
    }
342
343
    /**
344
     * Remove files in record.
345
     *
346
     * @param Model $model
347
     * @param bool  $forceDelete
348
     */
349
    protected function deleteFiles(Model $model, $forceDelete = false)
350
    {
351
        // If it's a soft delete, the files in the data will not be deleted.
352
        if (!$forceDelete && $this->isSoftDeletes) {
353
            return;
354
        }
355
356
        $data = $model->toArray();
357
358
        $this->builder->fields()->filter(function ($field) {
359
            return $field instanceof Field\File;
360
        })->each(function (Field\File $file) use ($data) {
361
            $file->setOriginal($data);
362
363
            $file->destroy();
364
        });
365
    }
366
367
    /**
368
     * Store a new record.
369
     *
370
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
371
     */
372
    public function store()
373
    {
374
        $data = \request()->all();
375
376
        // Handle validation errors.
377
        if ($validationMessages = $this->validationMessages($data)) {
378
            return back()->withInput()->withErrors($validationMessages);
379
        }
380
381
        if (($response = $this->prepare($data)) instanceof Response) {
382
            return $response;
383
        }
384
385 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...
386
            $inserts = $this->prepareInsert($this->updates);
387
388
            foreach ($inserts as $column => $value) {
389
                $this->model->setAttribute($column, $value);
390
            }
391
392
            $this->model->save();
393
394
            $this->updateRelation($this->relations);
395
        });
396
397
        if (($response = $this->callSaved()) instanceof Response) {
398
            return $response;
399
        }
400
401
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
402
            return $response;
403
        }
404
405
        return $this->redirectAfterStore();
406
    }
407
408
    /**
409
     * Get ajax response.
410
     *
411
     * @param string $message
412
     *
413
     * @return bool|\Illuminate\Http\JsonResponse
414
     */
415
    protected function ajaxResponse($message)
416
    {
417
        $request = Request::capture();
418
419
        // ajax but not pjax
420
        if ($request->ajax() && !$request->pjax()) {
421
            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...
422
                'status'  => true,
423
                'message' => $message,
424
            ]);
425
        }
426
427
        return false;
428
    }
429
430
    /**
431
     * Prepare input data for insert or update.
432
     *
433
     * @param array $data
434
     *
435
     * @return mixed
436
     */
437
    protected function prepare($data = [])
438
    {
439
        if (($response = $this->callSubmitted()) instanceof Response) {
440
            return $response;
441
        }
442
443
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
444
445
        if (($response = $this->callSaving()) instanceof Response) {
446
            return $response;
447
        }
448
449
        $this->relations = $this->getRelationInputs($this->inputs);
450
451
        $this->updates = Arr::except($this->inputs, array_keys($this->relations));
452
    }
453
454
    /**
455
     * Remove ignored fields from input.
456
     *
457
     * @param array $input
458
     *
459
     * @return array
460
     */
461
    protected function removeIgnoredFields($input)
462
    {
463
        Arr::forget($input, $this->ignored);
464
465
        return $input;
466
    }
467
468
    /**
469
     * Get inputs for relations.
470
     *
471
     * @param array $inputs
472
     *
473
     * @return array
474
     */
475
    protected function getRelationInputs($inputs = [])
476
    {
477
        $relations = [];
478
479
        foreach ($inputs as $column => $value) {
480
            if (!method_exists($this->model, $column)) {
481
                continue;
482
            }
483
484
            $relation = call_user_func([$this->model, $column]);
485
486
            if ($relation instanceof Relations\Relation) {
487
                $relations[$column] = $value;
488
            }
489
        }
490
491
        return $relations;
492
    }
493
494
    /**
495
     * Handle update.
496
     *
497
     * @param int  $id
498
     * @param null $data
499
     *
500
     * @return bool|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|null|Response
501
     */
502
    public function update($id, $data = null)
503
    {
504
        $data = ($data) ?: request()->all();
505
506
        $isEditable = $this->isEditable($data);
507
508
        if (($data = $this->handleColumnUpdates($id, $data)) instanceof Response) {
509
            return $data;
510
        }
511
512
        /* @var Model $this->model */
513
        $builder = $this->model();
514
515
        if ($this->isSoftDeletes) {
516
            $builder = $builder->withTrashed();
517
        }
518
519
        $this->model = $builder->with($this->getRelations())->findOrFail($id);
520
521
        $this->setFieldOriginalValue();
522
523
        // Handle validation errors.
524
        if ($validationMessages = $this->validationMessages($data)) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 508 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...
525
            if (!$isEditable) {
526
                return back()->withInput()->withErrors($validationMessages);
527
            }
528
529
            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...
530
        }
531
532
        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 508 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...
533
            return $response;
534
        }
535
536 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...
537
            $updates = $this->prepareUpdate($this->updates);
538
539
            foreach ($updates as $column => $value) {
540
                /* @var Model $this->model */
541
                $this->model->setAttribute($column, $value);
542
            }
543
544
            $this->model->save();
545
546
            $this->updateRelation($this->relations);
547
        });
548
549
        if (($result = $this->callSaved()) instanceof Response) {
550
            return $result;
551
        }
552
553
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
554
            return $response;
555
        }
556
557
        return $this->redirectAfterUpdate($id);
558
    }
559
560
    /**
561
     * Get RedirectResponse after store.
562
     *
563
     * @return \Illuminate\Http\RedirectResponse
564
     */
565
    protected function redirectAfterStore()
566
    {
567
        $resourcesPath = $this->resource(0);
568
569
        $key = $this->model->getKey();
570
571
        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 571 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
572
    }
573
574
    /**
575
     * Get RedirectResponse after update.
576
     *
577
     * @param mixed $key
578
     *
579
     * @return \Illuminate\Http\RedirectResponse
580
     */
581
    protected function redirectAfterUpdate($key)
582
    {
583
        $resourcesPath = $this->resource(-1);
584
585
        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 585 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
586
    }
587
588
    /**
589
     * Get RedirectResponse after data saving.
590
     *
591
     * @param string $resourcesPath
592
     * @param string $key
593
     *
594
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
595
     */
596
    protected function redirectAfterSaving($resourcesPath, $key)
597
    {
598
        if (request('after-save') == 1) {
599
            // continue editing
600
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
601
        } elseif (request('after-save') == 2) {
602
            // continue creating
603
            $url = rtrim($resourcesPath, '/').'/create';
604
        } elseif (request('after-save') == 3) {
605
            // view resource
606
            $url = rtrim($resourcesPath, '/')."/{$key}";
607
        } else {
608
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
609
        }
610
611
        admin_toastr(trans('admin.save_succeeded'));
612
613
        return redirect($url);
614
    }
615
616
    /**
617
     * Check if request is from editable.
618
     *
619
     * @param array $input
620
     *
621
     * @return bool
622
     */
623
    protected function isEditable(array $input = [])
624
    {
625
        return array_key_exists('_editable', $input);
626
    }
627
628
    /**
629
     * Handle updates for single column.
630
     *
631
     * @param int   $id
632
     * @param array $data
633
     *
634
     * @return array|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|Response
635
     */
636
    protected function handleColumnUpdates($id, $data)
637
    {
638
        $data = $this->handleEditable($data);
639
640
        $data = $this->handleFileDelete($data);
641
642
        $data = $this->handleFileSort($data);
643
644
        if ($this->handleOrderable($id, $data)) {
645
            return response([
646
                'status'  => true,
647
                'message' => trans('admin.update_succeeded'),
648
            ]);
649
        }
650
651
        return $data;
652
    }
653
654
    /**
655
     * Handle editable update.
656
     *
657
     * @param array $input
658
     *
659
     * @return array
660
     */
661
    protected function handleEditable(array $input = [])
662
    {
663
        if (array_key_exists('_editable', $input)) {
664
            $name = $input['name'];
665
            $value = $input['value'];
666
667
            Arr::forget($input, ['pk', 'value', 'name']);
668
            Arr::set($input, $name, $value);
669
        }
670
671
        return $input;
672
    }
673
674
    /**
675
     * @param array $input
676
     *
677
     * @return array
678
     */
679
    protected function handleFileDelete(array $input = [])
680
    {
681
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
682
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
683
            unset($input['key']);
684
        }
685
686
        request()->replace($input);
687
688
        return $input;
689
    }
690
691
    /**
692
     * @param array $input
693
     *
694
     * @return array
695
     */
696
    protected function handleFileSort(array $input = [])
697
    {
698
        if (!array_key_exists(Field::FILE_SORT_FLAG, $input)) {
699
            return $input;
700
        }
701
702
        $sorts = array_filter($input[Field::FILE_SORT_FLAG]);
703
704
        if (empty($sorts)) {
705
            return $input;
706
        }
707
708
        foreach ($sorts as $column => $order) {
709
            $input[$column] = $order;
710
        }
711
712
        request()->replace($input);
713
714
        return $input;
715
    }
716
717
    /**
718
     * Handle orderable update.
719
     *
720
     * @param int   $id
721
     * @param array $input
722
     *
723
     * @return bool
724
     */
725
    protected function handleOrderable($id, array $input = [])
726
    {
727
        if (array_key_exists('_orderable', $input)) {
728
            $model = $this->model->find($id);
729
730
            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...
731
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
732
733
                return true;
734
            }
735
        }
736
737
        return false;
738
    }
739
740
    /**
741
     * Update relation data.
742
     *
743
     * @param array $relationsData
744
     *
745
     * @return void
746
     */
747
    protected function updateRelation($relationsData)
748
    {
749
        foreach ($relationsData as $name => $values) {
750
            if (!method_exists($this->model, $name)) {
751
                continue;
752
            }
753
754
            $relation = $this->model->$name();
755
756
            $oneToOneRelation = $relation instanceof Relations\HasOne
757
                || $relation instanceof Relations\MorphOne
758
                || $relation instanceof Relations\BelongsTo;
759
760
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
761
762
            if (empty($prepared)) {
763
                continue;
764
            }
765
766
            switch (true) {
767
                case $relation instanceof Relations\BelongsToMany:
768
                case $relation instanceof Relations\MorphToMany:
769
                    if (isset($prepared[$name])) {
770
                        $relation->sync($prepared[$name]);
771
                    }
772
                    break;
773
                case $relation instanceof Relations\HasOne:
774
775
                    $related = $this->model->$name;
776
777
                    // if related is empty
778
                    if (is_null($related)) {
779
                        $related = $relation->getRelated();
780
                        $qualifiedParentKeyName = $relation->getQualifiedParentKeyName();
781
                        $localKey = Arr::last(explode('.', $qualifiedParentKeyName));
782
                        $related->{$relation->getForeignKeyName()} = $this->model->{$localKey};
783
                    }
784
785
                    foreach ($prepared[$name] as $column => $value) {
786
                        $related->setAttribute($column, $value);
787
                    }
788
789
                    $related->save();
790
                    break;
791
                case $relation instanceof Relations\BelongsTo:
792
                case $relation instanceof Relations\MorphTo:
793
794
                    $parent = $this->model->$name;
795
796
                    // if related is empty
797
                    if (is_null($parent)) {
798
                        $parent = $relation->getRelated();
799
                    }
800
801
                    foreach ($prepared[$name] as $column => $value) {
802
                        $parent->setAttribute($column, $value);
803
                    }
804
805
                    $parent->save();
806
807
                    // When in creating, associate two models
808
                    $foreignKeyMethod = (app()->version() < '5.8.0') ? 'getForeignKey' : 'getForeignKeyName';
809
                    if (!$this->model->{$relation->{$foreignKeyMethod}()}) {
810
                        $this->model->{$relation->{$foreignKeyMethod}()} = $parent->getKey();
811
812
                        $this->model->save();
813
                    }
814
815
                    break;
816
                case $relation instanceof Relations\MorphOne:
817
                    $related = $this->model->$name;
818
                    if (is_null($related)) {
819
                        $related = $relation->make();
820
                    }
821
                    foreach ($prepared[$name] as $column => $value) {
822
                        $related->setAttribute($column, $value);
823
                    }
824
                    $related->save();
825
                    break;
826
                case $relation instanceof Relations\HasMany:
827
                case $relation instanceof Relations\MorphMany:
828
829
                    foreach ($prepared[$name] as $related) {
830
                        /** @var Relations\Relation $relation */
831
                        $relation = $this->model()->$name();
832
833
                        $keyName = $relation->getRelated()->getKeyName();
834
835
                        $instance = $relation->findOrNew(Arr::get($related, $keyName));
836
837
                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
838
                            $instance->delete();
839
840
                            continue;
841
                        }
842
843
                        Arr::forget($related, static::REMOVE_FLAG_NAME);
844
845
                        $instance->fill($related);
846
847
                        $instance->save();
848
                    }
849
850
                    break;
851
            }
852
        }
853
    }
854
855
    /**
856
     * Prepare input data for update.
857
     *
858
     * @param array $updates
859
     * @param bool  $oneToOneRelation If column is one-to-one relation.
860
     *
861
     * @return array
862
     */
863
    protected function prepareUpdate(array $updates, $oneToOneRelation = false)
864
    {
865
        $prepared = [];
866
867
        /** @var Field $field */
868
        foreach ($this->builder->fields() as $field) {
869
            $columns = $field->column();
870
871
            // If column not in input array data, then continue.
872
            if (!Arr::has($updates, $columns)) {
873
                continue;
874
            }
875
876
            if ($this->invalidColumn($columns, $oneToOneRelation)) {
877
                continue;
878
            }
879
880
            $value = $this->getDataByColumn($updates, $columns);
881
882
            $value = $field->prepare($value);
883
884 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...
885
                foreach ($columns as $name => $column) {
886
                    Arr::set($prepared, $column, $value[$name]);
887
                }
888
            } elseif (is_string($columns)) {
889
                Arr::set($prepared, $columns, $value);
890
            }
891
        }
892
893
        return $prepared;
894
    }
895
896
    /**
897
     * @param string|array $columns
898
     * @param bool         $oneToOneRelation
899
     *
900
     * @return bool
901
     */
902
    protected function invalidColumn($columns, $oneToOneRelation = false)
903
    {
904
        foreach ((array) $columns as $column) {
905
            if ((!$oneToOneRelation && Str::contains($column, '.')) ||
906
                ($oneToOneRelation && !Str::contains($column, '.'))) {
907
                return true;
908
            }
909
        }
910
911
        return false;
912
    }
913
914
    /**
915
     * Prepare input data for insert.
916
     *
917
     * @param $inserts
918
     *
919
     * @return array
920
     */
921
    protected function prepareInsert($inserts)
922
    {
923
        if ($this->isHasOneRelation($inserts)) {
924
            $inserts = Arr::dot($inserts);
925
        }
926
927
        foreach ($inserts as $column => $value) {
928
            if (is_null($field = $this->getFieldByColumn($column))) {
929
                unset($inserts[$column]);
930
                continue;
931
            }
932
933
            $inserts[$column] = $field->prepare($value);
934
        }
935
936
        $prepared = [];
937
938
        foreach ($inserts as $key => $value) {
939
            Arr::set($prepared, $key, $value);
940
        }
941
942
        return $prepared;
943
    }
944
945
    /**
946
     * Is input data is has-one relation.
947
     *
948
     * @param array $inserts
949
     *
950
     * @return bool
951
     */
952
    protected function isHasOneRelation($inserts)
953
    {
954
        $first = current($inserts);
955
956
        if (!is_array($first)) {
957
            return false;
958
        }
959
960
        if (is_array(current($first))) {
961
            return false;
962
        }
963
964
        return Arr::isAssoc($first);
965
    }
966
967
    /**
968
     * Ignore fields to save.
969
     *
970
     * @param string|array $fields
971
     *
972
     * @return $this
973
     */
974
    public function ignore($fields)
975
    {
976
        $this->ignored = array_merge($this->ignored, (array) $fields);
977
978
        return $this;
979
    }
980
981
    /**
982
     * @param array        $data
983
     * @param string|array $columns
984
     *
985
     * @return array|mixed
986
     */
987 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...
988
    {
989
        if (is_string($columns)) {
990
            return Arr::get($data, $columns);
991
        }
992
993
        if (is_array($columns)) {
994
            $value = [];
995
            foreach ($columns as $name => $column) {
996
                if (!Arr::has($data, $column)) {
997
                    continue;
998
                }
999
                $value[$name] = Arr::get($data, $column);
1000
            }
1001
1002
            return $value;
1003
        }
1004
    }
1005
1006
    /**
1007
     * Find field object by column.
1008
     *
1009
     * @param $column
1010
     *
1011
     * @return mixed
1012
     */
1013
    protected function getFieldByColumn($column)
1014
    {
1015
        return $this->builder->fields()->first(
1016
            function (Field $field) use ($column) {
1017
                if (is_array($field->column())) {
1018
                    return in_array($column, $field->column());
1019
                }
1020
1021
                return $field->column() == $column;
1022
            }
1023
        );
1024
    }
1025
1026
    /**
1027
     * Set original data for each field.
1028
     *
1029
     * @return void
1030
     */
1031
    protected function setFieldOriginalValue()
1032
    {
1033
//        static::doNotSnakeAttributes($this->model);
1034
1035
        $values = $this->model->toArray();
1036
1037
        $this->builder->fields()->each(function (Field $field) use ($values) {
1038
            $field->setOriginal($values);
1039
        });
1040
    }
1041
1042
    /**
1043
     * Set all fields value in form.
1044
     *
1045
     * @param $id
1046
     *
1047
     * @return void
1048
     */
1049
    protected function setFieldValue($id)
1050
    {
1051
        $relations = $this->getRelations();
1052
1053
        $builder = $this->model();
1054
1055
        if ($this->isSoftDeletes) {
1056
            $builder = $builder->withTrashed();
1057
        }
1058
1059
        $this->model = $builder->with($relations)->findOrFail($id);
1060
1061
        $this->callEditing();
1062
1063
//        static::doNotSnakeAttributes($this->model);
1064
1065
        $data = $this->model->toArray();
1066
1067
        $this->builder->fields()->each(function (Field $field) use ($data) {
1068
            if (!in_array($field->column(), $this->ignored)) {
1069
                $field->fill($data);
1070
            }
1071
        });
1072
    }
1073
1074
    /**
1075
     * Don't snake case attributes.
1076
     *
1077
     * @param Model $model
1078
     *
1079
     * @return void
1080
     */
1081
    protected static function doNotSnakeAttributes(Model $model)
1082
    {
1083
        $class = get_class($model);
1084
1085
        $class::$snakeAttributes = false;
1086
    }
1087
1088
    /**
1089
     * Get validation messages.
1090
     *
1091
     * @param array $input
1092
     *
1093
     * @return MessageBag|bool
1094
     */
1095
    public function validationMessages($input)
1096
    {
1097
        $failedValidators = [];
1098
1099
        /** @var Field $field */
1100
        foreach ($this->builder->fields() as $field) {
1101
            if (!$validator = $field->getValidator($input)) {
1102
                continue;
1103
            }
1104
1105
            if (($validator instanceof Validator) && !$validator->passes()) {
1106
                $failedValidators[] = $validator;
1107
            }
1108
        }
1109
1110
        $message = $this->mergeValidationMessages($failedValidators);
1111
1112
        return $message->any() ? $message : false;
1113
    }
1114
1115
    /**
1116
     * Merge validation messages from input validators.
1117
     *
1118
     * @param \Illuminate\Validation\Validator[] $validators
1119
     *
1120
     * @return MessageBag
1121
     */
1122
    protected function mergeValidationMessages($validators)
1123
    {
1124
        $messageBag = new MessageBag();
1125
1126
        foreach ($validators as $validator) {
1127
            $messageBag = $messageBag->merge($validator->messages());
1128
        }
1129
1130
        return $messageBag;
1131
    }
1132
1133
    /**
1134
     * Get all relations of model from callable.
1135
     *
1136
     * @return array
1137
     */
1138
    public function getRelations()
1139
    {
1140
        $relations = $columns = [];
1141
1142
        /** @var Field $field */
1143
        foreach ($this->builder->fields() as $field) {
1144
            $columns[] = $field->column();
1145
        }
1146
1147
        foreach (Arr::flatten($columns) as $column) {
1148
            if (Str::contains($column, '.')) {
1149
                list($relation) = explode('.', $column);
1150
1151
                if (method_exists($this->model, $relation) &&
1152
                    $this->model->$relation() instanceof Relations\Relation
1153
                ) {
1154
                    $relations[] = $relation;
1155
                }
1156
            } elseif (method_exists($this->model, $column) &&
1157
                !method_exists(Model::class, $column)
1158
            ) {
1159
                $relations[] = $column;
1160
            }
1161
        }
1162
1163
        return array_unique($relations);
1164
    }
1165
1166
    /**
1167
     * Set action for form.
1168
     *
1169
     * @param string $action
1170
     *
1171
     * @return $this
1172
     */
1173
    public function setAction($action)
1174
    {
1175
        $this->builder()->setAction($action);
1176
1177
        return $this;
1178
    }
1179
1180
    /**
1181
     * Set field and label width in current form.
1182
     *
1183
     * @param int $fieldWidth
1184
     * @param int $labelWidth
1185
     *
1186
     * @return $this
1187
     */
1188
    public function setWidth($fieldWidth = 8, $labelWidth = 2)
1189
    {
1190
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1191
            /* @var Field $field  */
1192
            $field->setWidth($fieldWidth, $labelWidth);
1193
        });
1194
1195
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1196
1197
        return $this;
1198
    }
1199
1200
    /**
1201
     * Set view for form.
1202
     *
1203
     * @param string $view
1204
     *
1205
     * @return $this
1206
     */
1207
    public function setView($view)
1208
    {
1209
        $this->builder()->setView($view);
1210
1211
        return $this;
1212
    }
1213
1214
    /**
1215
     * Set title for form.
1216
     *
1217
     * @param string $title
1218
     *
1219
     * @return $this
1220
     */
1221
    public function setTitle($title = '')
1222
    {
1223
        $this->builder()->setTitle($title);
1224
1225
        return $this;
1226
    }
1227
1228
    /**
1229
     * Add a row in form.
1230
     *
1231
     * @param Closure $callback
1232
     *
1233
     * @return $this
1234
     */
1235
    public function row(Closure $callback)
1236
    {
1237
        $this->rows[] = new Row($callback, $this);
1238
1239
        return $this;
1240
    }
1241
1242
    /**
1243
     * Tools setting for form.
1244
     *
1245
     * @param Closure $callback
1246
     */
1247
    public function tools(Closure $callback)
1248
    {
1249
        $callback->call($this, $this->builder->getTools());
1250
    }
1251
1252
    /**
1253
     * @param Closure|null $callback
1254
     *
1255
     * @return Form\Tools
1256
     */
1257
    public function header(Closure $callback = null)
1258
    {
1259
        if (func_num_args() == 0) {
1260
            return $this->builder->getTools();
1261
        }
1262
1263
        $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...
1264
    }
1265
1266
    /**
1267
     * Disable form submit.
1268
     *
1269
     * @param bool $disable
1270
     *
1271
     * @return $this
1272
     *
1273
     * @deprecated
1274
     */
1275
    public function disableSubmit(bool $disable = true)
1276
    {
1277
        $this->builder()->getFooter()->disableSubmit($disable);
1278
1279
        return $this;
1280
    }
1281
1282
    /**
1283
     * Disable form reset.
1284
     *
1285
     * @param bool $disable
1286
     *
1287
     * @return $this
1288
     *
1289
     * @deprecated
1290
     */
1291
    public function disableReset(bool $disable = true)
1292
    {
1293
        $this->builder()->getFooter()->disableReset($disable);
1294
1295
        return $this;
1296
    }
1297
1298
    /**
1299
     * Disable View Checkbox on footer.
1300
     *
1301
     * @param bool $disable
1302
     *
1303
     * @return $this
1304
     */
1305
    public function disableViewCheck(bool $disable = true)
1306
    {
1307
        $this->builder()->getFooter()->disableViewCheck($disable);
1308
1309
        return $this;
1310
    }
1311
1312
    /**
1313
     * Disable Editing Checkbox on footer.
1314
     *
1315
     * @param bool $disable
1316
     *
1317
     * @return $this
1318
     */
1319
    public function disableEditingCheck(bool $disable = true)
1320
    {
1321
        $this->builder()->getFooter()->disableEditingCheck($disable);
1322
1323
        return $this;
1324
    }
1325
1326
    /**
1327
     * Disable Creating Checkbox on footer.
1328
     *
1329
     * @param bool $disable
1330
     *
1331
     * @return $this
1332
     */
1333
    public function disableCreatingCheck(bool $disable = true)
1334
    {
1335
        $this->builder()->getFooter()->disableCreatingCheck($disable);
1336
1337
        return $this;
1338
    }
1339
1340
    /**
1341
     * Footer setting for form.
1342
     *
1343
     * @param Closure $callback
1344
     */
1345
    public function footer(Closure $callback = null)
1346
    {
1347
        if (func_num_args() == 0) {
1348
            return $this->builder()->getFooter();
1349
        }
1350
1351
        call_user_func($callback, $this->builder()->getFooter());
1352
    }
1353
1354
    /**
1355
     * Get current resource route url.
1356
     *
1357
     * @param int $slice
1358
     *
1359
     * @return string
1360
     */
1361
    public function resource($slice = -2)
1362
    {
1363
        $segments = explode('/', trim(app('request')->getUri(), '/'));
1364
1365
        if ($slice != 0) {
1366
            $segments = array_slice($segments, 0, $slice);
1367
        }
1368
1369
        return implode('/', $segments);
1370
    }
1371
1372
    /**
1373
     * Render the form contents.
1374
     *
1375
     * @return string
1376
     */
1377
    public function render()
1378
    {
1379
        try {
1380
            return $this->builder->render();
1381
        } catch (\Exception $e) {
1382
            return Handler::renderException($e);
1383
        }
1384
    }
1385
1386
    /**
1387
     * Get or set input data.
1388
     *
1389
     * @param string $key
1390
     * @param null   $value
1391
     *
1392
     * @return array|mixed
1393
     */
1394
    public function input($key, $value = null)
1395
    {
1396
        if (is_null($value)) {
1397
            return Arr::get($this->inputs, $key);
1398
        }
1399
1400
        return Arr::set($this->inputs, $key, $value);
1401
    }
1402
1403
    /**
1404
     * Register builtin fields.
1405
     *
1406
     * @return void
1407
     */
1408
    public static function registerBuiltinFields()
1409
    {
1410
        $map = [
1411
            'button'         => Field\Button::class,
1412
            'checkbox'       => Field\Checkbox::class,
1413
            'color'          => Field\Color::class,
1414
            'currency'       => Field\Currency::class,
1415
            'date'           => Field\Date::class,
1416
            'dateRange'      => Field\DateRange::class,
1417
            'datetime'       => Field\Datetime::class,
1418
            'dateTimeRange'  => Field\DatetimeRange::class,
1419
            'datetimeRange'  => Field\DatetimeRange::class,
1420
            'decimal'        => Field\Decimal::class,
1421
            'display'        => Field\Display::class,
1422
            'divider'        => Field\Divide::class,
1423
            'divide'         => Field\Divide::class,
1424
            'embeds'         => Field\Embeds::class,
1425
            'email'          => Field\Email::class,
1426
            'file'           => Field\File::class,
1427
            'hasMany'        => Field\HasMany::class,
1428
            'hidden'         => Field\Hidden::class,
1429
            'id'             => Field\Id::class,
1430
            'image'          => Field\Image::class,
1431
            'ip'             => Field\Ip::class,
1432
            'mobile'         => Field\Mobile::class,
1433
            'month'          => Field\Month::class,
1434
            'multipleSelect' => Field\MultipleSelect::class,
1435
            'number'         => Field\Number::class,
1436
            'password'       => Field\Password::class,
1437
            'radio'          => Field\Radio::class,
1438
            'rate'           => Field\Rate::class,
1439
            'select'         => Field\Select::class,
1440
            'slider'         => Field\Slider::class,
1441
            'switch'         => Field\SwitchField::class,
1442
            'text'           => Field\Text::class,
1443
            'textarea'       => Field\Textarea::class,
1444
            'time'           => Field\Time::class,
1445
            'timeRange'      => Field\TimeRange::class,
1446
            'url'            => Field\Url::class,
1447
            'year'           => Field\Year::class,
1448
            'html'           => Field\Html::class,
1449
            'tags'           => Field\Tags::class,
1450
            'icon'           => Field\Icon::class,
1451
            'multipleFile'   => Field\MultipleFile::class,
1452
            'multipleImage'  => Field\MultipleImage::class,
1453
            'captcha'        => Field\Captcha::class,
1454
            'listbox'        => Field\Listbox::class,
1455
            'table'          => Field\Table::class,
1456
        ];
1457
1458
        foreach ($map as $abstract => $class) {
1459
            static::extend($abstract, $class);
1460
        }
1461
    }
1462
1463
    /**
1464
     * Register custom field.
1465
     *
1466
     * @param string $abstract
1467
     * @param string $class
1468
     *
1469
     * @return void
1470
     */
1471
    public static function extend($abstract, $class)
1472
    {
1473
        static::$availableFields[$abstract] = $class;
1474
    }
1475
1476
    /**
1477
     * Set form field alias.
1478
     *
1479
     * @param string $field
1480
     * @param string $alias
1481
     *
1482
     * @return void
1483
     */
1484
    public static function alias($field, $alias)
1485
    {
1486
        static::$fieldAlias[$alias] = $field;
1487
    }
1488
1489
    /**
1490
     * Remove registered field.
1491
     *
1492
     * @param array|string $abstract
1493
     */
1494
    public static function forget($abstract)
1495
    {
1496
        Arr::forget(static::$availableFields, $abstract);
1497
    }
1498
1499
    /**
1500
     * Find field class.
1501
     *
1502
     * @param string $method
1503
     *
1504
     * @return bool|mixed
1505
     */
1506
    public static function findFieldClass($method)
1507
    {
1508
        // If alias exists.
1509
        if (isset(static::$fieldAlias[$method])) {
1510
            $method = static::$fieldAlias[$method];
1511
        }
1512
1513
        $class = Arr::get(static::$availableFields, $method);
1514
1515
        if (class_exists($class)) {
1516
            return $class;
1517
        }
1518
1519
        return false;
1520
    }
1521
1522
    /**
1523
     * Collect assets required by registered field.
1524
     *
1525
     * @return array
1526
     */
1527
    public static function collectFieldAssets()
1528
    {
1529
        if (!empty(static::$collectedAssets)) {
1530
            return static::$collectedAssets;
1531
        }
1532
1533
        $css = collect();
1534
        $js = collect();
1535
1536
        foreach (static::$availableFields as $field) {
1537
            if (!method_exists($field, 'getAssets')) {
1538
                continue;
1539
            }
1540
1541
            $assets = call_user_func([$field, 'getAssets']);
1542
1543
            $css->push(Arr::get($assets, 'css'));
1544
            $js->push(Arr::get($assets, 'js'));
1545
        }
1546
1547
        return static::$collectedAssets = [
1548
            'css' => $css->flatten()->unique()->filter()->toArray(),
1549
            'js'  => $js->flatten()->unique()->filter()->toArray(),
1550
        ];
1551
    }
1552
1553
    /**
1554
     * Getter.
1555
     *
1556
     * @param string $name
1557
     *
1558
     * @return array|mixed
1559
     */
1560
    public function __get($name)
1561
    {
1562
        return $this->input($name);
1563
    }
1564
1565
    /**
1566
     * Setter.
1567
     *
1568
     * @param string $name
1569
     * @param mixed  $value
1570
     *
1571
     * @return array
1572
     */
1573
    public function __set($name, $value)
1574
    {
1575
        return Arr::set($this->inputs, $name, $value);
1576
    }
1577
1578
    /**
1579
     * Generate a Field object and add to form builder if Field exists.
1580
     *
1581
     * @param string $method
1582
     * @param array  $arguments
1583
     *
1584
     * @return Field
1585
     */
1586
    public function __call($method, $arguments)
1587
    {
1588
        if ($className = static::findFieldClass($method)) {
1589
            $column = Arr::get($arguments, 0, ''); //[0];
1590
1591
            $element = new $className($column, array_slice($arguments, 1));
1592
1593
            $this->pushField($element);
1594
1595
            return $element;
1596
        }
1597
1598
        admin_error('Error', "Field type [$method] does not exist.");
1599
1600
        return new Field\Nullable();
1601
    }
1602
}
1603