Completed
Pull Request — master (#2254)
by
unknown
02:38
created

Form::doNotSnakeAttributes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
308
            ->findOrFail($id)->toArray();
309
310
        $this->builder->fields()->filter(function ($field) {
311
            return $field instanceof Field\File;
312
        })->each(function (Field\File $file) use ($data) {
313
            $file->setOriginal($data);
314
315
            $file->destroy();
316
        });
317
    }
318
319
    /**
320
     * Store a new record.
321
     *
322
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
323
     */
324
    public function store()
325
    {
326
        $data = Input::all();
327
328
        // Handle validation errors.
329
        if ($validationMessages = $this->validationMessages($data)) {
330
            return back()->withInput()->withErrors($validationMessages);
331
        }
332
333
        if (($response = $this->prepare($data)) instanceof Response) {
334
            return $response;
335
        }
336
337 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...
338
            $inserts = $this->prepareInsert($this->updates);
339
340
            foreach ($inserts as $column => $value) {
341
                $this->model->setAttribute($column, $value);
342
            }
343
344
            $this->model->save();
345
346
            $this->updateRelation($this->relations);
347
        });
348
349
        if (($response = $this->complete($this->saved)) instanceof Response) {
350
            return $response;
351
        }
352
353
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
354
            return $response;
355
        }
356
357
        return $this->redirectAfterStore();
358
    }
359
360
    /**
361
     * Get ajax response.
362
     *
363
     * @param string $message
364
     *
365
     * @return bool|\Illuminate\Http\JsonResponse
366
     */
367
    protected function ajaxResponse($message)
368
    {
369
        $request = Request::capture();
370
371
        // ajax but not pjax
372
        if ($request->ajax() && !$request->pjax()) {
373
            return response()->json([
374
                'status'  => true,
375
                'message' => $message,
376
            ]);
377
        }
378
379
        return false;
380
    }
381
382
    /**
383
     * Prepare input data for insert or update.
384
     *
385
     * @param array $data
386
     *
387
     * @return mixed
388
     */
389
    protected function prepare($data = [])
390
    {
391
        if (($response = $this->callSubmitted()) instanceof Response) {
392
            return $response;
393
        }
394
395
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
396
397
        if (($response = $this->callSaving()) instanceof Response) {
398
            return $response;
399
        }
400
401
        $this->relations = $this->getRelationInputs($this->inputs);
402
403
        $this->updates = array_except($this->inputs, array_keys($this->relations));
404
    }
405
406
    /**
407
     * Remove ignored fields from input.
408
     *
409
     * @param array $input
410
     *
411
     * @return array
412
     */
413
    protected function removeIgnoredFields($input)
414
    {
415
        array_forget($input, $this->ignored);
416
417
        return $input;
418
    }
419
420
    /**
421
     * Get inputs for relations.
422
     *
423
     * @param array $inputs
424
     *
425
     * @return array
426
     */
427
    protected function getRelationInputs($inputs = [])
428
    {
429
        $relations = [];
430
431
        foreach ($inputs as $column => $value) {
432
            if (method_exists($this->model, $column)) {
433
                $relation = call_user_func([$this->model, $column]);
434
435
                if ($relation instanceof Relations\Relation) {
0 ignored issues
show
Bug introduced by
The class Encore\Admin\Relations\Relation 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...
436
                    $relations[$column] = $value;
437
                }
438
            }
439
        }
440
441
        return $relations;
442
    }
443
444
    /**
445
     * Call submitted callback.
446
     *
447
     * @return mixed
448
     */
449
    protected function callSubmitted()
450
    {
451
        if ($this->submitted instanceof Closure) {
452
            return call_user_func($this->submitted, $this);
453
        }
454
    }
455
456
    /**
457
     * Call saving callback.
458
     *
459
     * @return mixed
460
     */
461
    protected function callSaving()
462
    {
463
        if ($this->saving instanceof Closure) {
464
            return call_user_func($this->saving, $this);
465
        }
466
    }
467
468
    /**
469
     * Callback after saving a Model.
470
     *
471
     * @param Closure|null $callback
472
     *
473
     * @return mixed|null
474
     */
475
    protected function complete(Closure $callback = null)
476
    {
477
        if ($callback instanceof Closure) {
478
            return $callback($this);
479
        }
480
    }
481
482
    /**
483
     * Handle update.
484
     *
485
     * @param int $id
486
     *
487
     * @return \Symfony\Component\HttpFoundation\Response
488
     */
489
    public function update($id, $data = null)
490
    {
491
        $data = ($data) ?: Input::all();
492
493
        $isEditable = $this->isEditable($data);
494
495
        $data = $this->handleEditable($data);
496
497
        $data = $this->handleFileDelete($data);
498
499
        if ($this->handleOrderable($id, $data)) {
500
            return response([
501
                'status'  => true,
502
                'message' => trans('admin.update_succeeded'),
503
            ]);
504
        }
505
506
        /* @var Model $this->model */
507
        $this->model = $this->model->with($this->getRelations())->findOrFail($id);
0 ignored issues
show
Bug introduced by
The method findOrFail does only exist in Illuminate\Database\Eloquent\Builder, but not in Illuminate\Database\Eloquent\Model.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
508
509
        $this->setFieldOriginalValue();
510
511
        // Handle validation errors.
512
        if ($validationMessages = $this->validationMessages($data)) {
513
            if (!$isEditable) {
514
                return back()->withInput()->withErrors($validationMessages);
515
            } else {
516
                return response()->json(['errors' => array_dot($validationMessages->getMessages())], 422);
517
            }
518
        }
519
520
        if (($response = $this->prepare($data)) instanceof Response) {
521
            return $response;
522
        }
523
524 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...
525
            $updates = $this->prepareUpdate($this->updates);
526
527
            foreach ($updates as $column => $value) {
528
                /* @var Model $this->model */
529
                $this->model->setAttribute($column, $value);
530
            }
531
532
            $this->model->save();
533
534
            $this->updateRelation($this->relations);
535
        });
536
537
        if (($result = $this->complete($this->saved)) instanceof Response) {
538
            return $result;
539
        }
540
541
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
542
            return $response;
543
        }
544
545
        return $this->redirectAfterUpdate($id);
546
    }
547
548
    /**
549
     * Get RedirectResponse after store.
550
     *
551
     * @return \Illuminate\Http\RedirectResponse
552
     */
553
    protected function redirectAfterStore()
554
    {
555
        $resourcesPath = $this->resource(0);
556
557
        $key = $this->model->getKey();
558
559
        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 559 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
560
    }
561
562
    /**
563
     * Get RedirectResponse after update.
564
     *
565
     * @param mixed $key
566
     *
567
     * @return \Illuminate\Http\RedirectResponse
568
     */
569
    protected function redirectAfterUpdate($key)
570
    {
571
        $resourcesPath = $this->resource(-1);
572
573
        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 573 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
574
    }
575
576
    /**
577
     * Get RedirectResponse after data saving.
578
     *
579
     * @param string $resourcesPath
580
     * @param string $key
581
     *
582
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
583
     */
584
    protected function redirectAfterSaving($resourcesPath, $key)
585
    {
586
        if (request('after-save') == 1) {
587
            // continue editing
588
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
589
        } elseif (request('after-save') == 2) {
590
            // view resource
591
            $url = rtrim($resourcesPath, '/')."/{$key}";
592
        } else {
593
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
594
        }
595
596
        admin_toastr(trans('admin.save_succeeded'));
597
598
        return redirect($url);
599
    }
600
601
    /**
602
     * Check if request is from editable.
603
     *
604
     * @param array $input
605
     *
606
     * @return bool
607
     */
608
    protected function isEditable(array $input = [])
609
    {
610
        return array_key_exists('_editable', $input);
611
    }
612
613
    /**
614
     * Handle editable update.
615
     *
616
     * @param array $input
617
     *
618
     * @return array
619
     */
620
    protected function handleEditable(array $input = [])
621
    {
622
        if (array_key_exists('_editable', $input)) {
623
            $name = $input['name'];
624
            $value = $input['value'];
625
626
            array_forget($input, ['pk', 'value', 'name']);
627
            array_set($input, $name, $value);
628
        }
629
630
        return $input;
631
    }
632
633
    /**
634
     * @param array $input
635
     *
636
     * @return array
637
     */
638
    protected function handleFileDelete(array $input = [])
639
    {
640
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
641
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
642
            unset($input['key']);
643
        }
644
645
        Input::replace($input);
646
647
        return $input;
648
    }
649
650
    /**
651
     * Handle orderable update.
652
     *
653
     * @param int   $id
654
     * @param array $input
655
     *
656
     * @return bool
657
     */
658
    protected function handleOrderable($id, array $input = [])
659
    {
660
        if (array_key_exists('_orderable', $input)) {
661
            $model = $this->model->find($id);
662
663
            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...
664
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
665
666
                return true;
667
            }
668
        }
669
670
        return false;
671
    }
672
673
    /**
674
     * Update relation data.
675
     *
676
     * @param array $relationsData
677
     *
678
     * @return void
679
     */
680
    protected function updateRelation($relationsData)
681
    {
682
        foreach ($relationsData as $name => $values) {
683
            if (!method_exists($this->model, $name)) {
684
                continue;
685
            }
686
687
            $relation = $this->model->$name();
688
689
            $oneToOneRelation = $relation instanceof Relations\HasOne
0 ignored issues
show
Bug introduced by
The class Encore\Admin\Relations\HasOne 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...
690
                || $relation instanceof Relations\MorphOne;
0 ignored issues
show
Bug introduced by
The class Encore\Admin\Relations\MorphOne 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...
691
692
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
693
694
            if (empty($prepared)) {
695
                continue;
696
            }
697
698
            switch (get_class($relation)) {
699
                case Relations\BelongsToMany::class:
700
                case Relations\MorphToMany::class:
701
                    if (isset($prepared[$name])) {
702
                        $relation->sync($prepared[$name]);
703
                    }
704
                    break;
705
                case Relations\HasOne::class:
0 ignored issues
show
Coding Style introduced by
The case body in a switch statement must start on the line following the statement.

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

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

    doSomethingElse(); //wrong
    break;

}

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

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

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

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

    doSomethingElse(); //wrong
    break;

}

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

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

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

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

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

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

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

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

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

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

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

Loading history...
997
998
        $data = $this->model->toArray();
999
1000
        $this->builder->fields()->each(function (Field $field) use ($data) {
1001
            if (!in_array($field->column(), $this->ignored)) {
1002
                $field->fill($data);
1003
            }
1004
        });
1005
    }
1006
1007
    /**
1008
     * Don't snake case attributes.
1009
     *
1010
     * @param Model $model
1011
     *
1012
     * @return void
1013
     */
1014
    protected static function doNotSnakeAttributes(Model $model)
1015
    {
1016
        $class = get_class($model);
1017
1018
        $class::$snakeAttributes = false;
1019
    }
1020
1021
    /**
1022
     * Get validation messages.
1023
     *
1024
     * @param array $input
1025
     *
1026
     * @return MessageBag|bool
1027
     */
1028
    protected function validationMessages($input)
1029
    {
1030
        $failedValidators = [];
1031
1032
        /** @var Field $field */
1033
        foreach ($this->builder->fields() as $field) {
1034
            if (!$validator = $field->getValidator($input)) {
1035
                continue;
1036
            }
1037
1038
            if (($validator instanceof Validator) && !$validator->passes()) {
1039
                $failedValidators[] = $validator;
1040
            }
1041
        }
1042
1043
        $message = $this->mergeValidationMessages($failedValidators);
1044
1045
        return $message->any() ? $message : false;
1046
    }
1047
1048
    /**
1049
     * Merge validation messages from input validators.
1050
     *
1051
     * @param \Illuminate\Validation\Validator[] $validators
1052
     *
1053
     * @return MessageBag
1054
     */
1055
    protected function mergeValidationMessages($validators)
1056
    {
1057
        $messageBag = new MessageBag();
1058
1059
        foreach ($validators as $validator) {
1060
            $messageBag = $messageBag->merge($validator->messages());
1061
        }
1062
1063
        return $messageBag;
1064
    }
1065
1066
    /**
1067
     * Get all relations of model from callable.
1068
     *
1069
     * @return array
1070
     */
1071
    public function getRelations()
1072
    {
1073
        $relations = $columns = [];
1074
1075
        /** @var Field $field */
1076
        foreach ($this->builder->fields() as $field) {
1077
            $columns[] = $field->column();
1078
        }
1079
1080
        foreach (array_flatten($columns) as $column) {
1081
            if (str_contains($column, '.')) {
1082
                list($relation) = explode('.', $column);
1083
1084
                if (method_exists($this->model, $relation) &&
1085
                    $this->model->$relation() instanceof Relations\Relation
0 ignored issues
show
Bug introduced by
The class Encore\Admin\Relations\Relation 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...
1086
                ) {
1087
                    $relations[] = $relation;
1088
                }
1089
            } elseif (method_exists($this->model, $column) &&
1090
                !method_exists(Model::class, $column)
1091
            ) {
1092
                $relations[] = $column;
1093
            }
1094
        }
1095
1096
        return array_unique($relations);
1097
    }
1098
1099
    /**
1100
     * Set action for form.
1101
     *
1102
     * @param string $action
1103
     *
1104
     * @return $this
1105
     */
1106
    public function setAction($action)
1107
    {
1108
        $this->builder()->setAction($action);
1109
1110
        return $this;
1111
    }
1112
1113
    /**
1114
     * Set field and label width in current form.
1115
     *
1116
     * @param int $fieldWidth
1117
     * @param int $labelWidth
1118
     *
1119
     * @return $this
1120
     */
1121
    public function setWidth($fieldWidth = 8, $labelWidth = 2)
1122
    {
1123
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1124
            /* @var Field $field  */
1125
            $field->setWidth($fieldWidth, $labelWidth);
1126
        });
1127
1128
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1129
1130
        return $this;
1131
    }
1132
1133
    /**
1134
     * Set view for form.
1135
     *
1136
     * @param string $view
1137
     *
1138
     * @return $this
1139
     */
1140
    public function setView($view)
1141
    {
1142
        $this->builder()->setView($view);
1143
1144
        return $this;
1145
    }
1146
1147
    /**
1148
     * Set title for form.
1149
     *
1150
     * @param string $title
1151
     *
1152
     * @return $this
1153
     */
1154
    public function setTitle($title = '')
1155
    {
1156
        $this->builder()->setTitle($title);
1157
1158
        return $this;
1159
    }
1160
1161
    /**
1162
     * Add a row in form.
1163
     *
1164
     * @param Closure $callback
1165
     *
1166
     * @return $this
1167
     */
1168
    public function row(Closure $callback)
1169
    {
1170
        $this->rows[] = new Row($callback, $this);
1171
1172
        return $this;
1173
    }
1174
1175
    /**
1176
     * Tools setting for form.
1177
     *
1178
     * @param Closure $callback
1179
     */
1180
    public function tools(Closure $callback)
1181
    {
1182
        $callback->call($this, $this->builder->getTools());
1183
    }
1184
1185
    /**
1186
     * Disable form submit.
1187
     *
1188
     * @return $this
1189
     *
1190
     * @deprecated
1191
     */
1192
    public function disableSubmit()
1193
    {
1194
        $this->builder()->getFooter()->disableSubmit();
0 ignored issues
show
Bug introduced by
The method getFooter() does not seem to exist on object<Encore\Admin\Form\Builder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1195
1196
        return $this;
1197
    }
1198
1199
    /**
1200
     * Disable form reset.
1201
     *
1202
     * @return $this
1203
     *
1204
     * @deprecated
1205
     */
1206
    public function disableReset()
1207
    {
1208
        $this->builder()->getFooter()->disableReset();
0 ignored issues
show
Bug introduced by
The method getFooter() does not seem to exist on object<Encore\Admin\Form\Builder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1209
1210
        return $this;
1211
    }
1212
1213
    /**
1214
     * Disable form remove reserved fields.
1215
     *
1216
     * @return $this
1217
     */
1218
    public function disableRemoveReservedFields()
1219
    {
1220
        $this->builder()->options(['enableRemoveReservedFields' => false]);
1221
1222
        return $this;
1223
    }
1224
1225
    /**
1226
     * Footer setting for form.
1227
     *
1228
     * @param Closure $callback
1229
     */
1230
    public function footer(Closure $callback)
1231
    {
1232
        call_user_func($callback, $this->builder()->getFooter());
0 ignored issues
show
Bug introduced by
The method getFooter() does not seem to exist on object<Encore\Admin\Form\Builder>.

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1233
    }
1234
1235
    /**
1236
     * Get current resource route url.
1237
     *
1238
     * @param int $slice
1239
     *
1240
     * @return string
1241
     */
1242
    public function resource($slice = -2)
1243
    {
1244
        $segments = explode('/', trim(app('request')->getUri(), '/'));
1245
1246
        if ($slice != 0) {
1247
            $segments = array_slice($segments, 0, $slice);
1248
        }
1249
        // # fix #1768
1250
        if ($segments[0] == 'http:' && config('admin.secure') == true) {
1251
            $segments[0] = 'https:';
1252
        }
1253
1254
        return implode('/', $segments);
1255
    }
1256
1257
    /**
1258
     * Render the form contents.
1259
     *
1260
     * @return string
1261
     */
1262
    public function render()
1263
    {
1264
        try {
1265
            return $this->builder->render();
1266
        } catch (\Exception $e) {
1267
            return Handler::renderException($e);
1268
        }
1269
    }
1270
1271
    /**
1272
     * Get or set input data.
1273
     *
1274
     * @param string $key
1275
     * @param null   $value
1276
     *
1277
     * @return array|mixed
1278
     */
1279
    public function input($key, $value = null)
1280
    {
1281
        if (is_null($value)) {
1282
            return array_get($this->inputs, $key);
1283
        }
1284
1285
        return array_set($this->inputs, $key, $value);
1286
    }
1287
1288
    /**
1289
     * Register builtin fields.
1290
     *
1291
     * @return void
1292
     */
1293
    public static function registerBuiltinFields()
1294
    {
1295
        $map = [
1296
            'button'         => Field\Button::class,
1297
            'checkbox'       => Field\Checkbox::class,
1298
            'color'          => Field\Color::class,
1299
            'currency'       => Field\Currency::class,
1300
            'date'           => Field\Date::class,
1301
            'dateRange'      => Field\DateRange::class,
1302
            'datetime'       => Field\Datetime::class,
1303
            'dateTimeRange'  => Field\DatetimeRange::class,
1304
            'datetimeRange'  => Field\DatetimeRange::class,
1305
            'decimal'        => Field\Decimal::class,
1306
            'display'        => Field\Display::class,
1307
            'divider'        => Field\Divide::class,
1308
            'divide'         => Field\Divide::class,
1309
            'embeds'         => Field\Embeds::class,
1310
            'editor'         => Field\Editor::class,
1311
            'email'          => Field\Email::class,
1312
            'file'           => Field\File::class,
1313
            'hasMany'        => Field\HasMany::class,
1314
            'hidden'         => Field\Hidden::class,
1315
            'id'             => Field\Id::class,
1316
            'image'          => Field\Image::class,
1317
            'ip'             => Field\Ip::class,
1318
            'map'            => Field\Map::class,
1319
            'mobile'         => Field\Mobile::class,
1320
            'month'          => Field\Month::class,
1321
            'multipleSelect' => Field\MultipleSelect::class,
1322
            'number'         => Field\Number::class,
1323
            'password'       => Field\Password::class,
1324
            'radio'          => Field\Radio::class,
1325
            'rate'           => Field\Rate::class,
1326
            'select'         => Field\Select::class,
1327
            'slider'         => Field\Slider::class,
1328
            'switch'         => Field\SwitchField::class,
1329
            'text'           => Field\Text::class,
1330
            'textarea'       => Field\Textarea::class,
1331
            'time'           => Field\Time::class,
1332
            'timeRange'      => Field\TimeRange::class,
1333
            'url'            => Field\Url::class,
1334
            'year'           => Field\Year::class,
1335
            'html'           => Field\Html::class,
1336
            'tags'           => Field\Tags::class,
1337
            'icon'           => Field\Icon::class,
1338
            'multipleFile'   => Field\MultipleFile::class,
1339
            'multipleImage'  => Field\MultipleImage::class,
1340
            'captcha'        => Field\Captcha::class,
1341
            'listbox'        => Field\Listbox::class,
1342
        ];
1343
1344
        foreach ($map as $abstract => $class) {
1345
            static::extend($abstract, $class);
1346
        }
1347
    }
1348
1349
    /**
1350
     * Register custom field.
1351
     *
1352
     * @param string $abstract
1353
     * @param string $class
1354
     *
1355
     * @return void
1356
     */
1357
    public static function extend($abstract, $class)
1358
    {
1359
        static::$availableFields[$abstract] = $class;
1360
    }
1361
1362
    /**
1363
     * Remove registered field.
1364
     *
1365
     * @param array|string $abstract
1366
     */
1367
    public static function forget($abstract)
1368
    {
1369
        array_forget(static::$availableFields, $abstract);
1370
    }
1371
1372
    /**
1373
     * Find field class.
1374
     *
1375
     * @param string $method
1376
     *
1377
     * @return bool|mixed
1378
     */
1379 View Code Duplication
    public static function findFieldClass($method)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1380
    {
1381
        $class = array_get(static::$availableFields, $method);
1382
1383
        if (class_exists($class)) {
1384
            return $class;
1385
        }
1386
1387
        return false;
1388
    }
1389
1390
    /**
1391
     * Collect assets required by registered field.
1392
     *
1393
     * @return array
1394
     */
1395
    public static function collectFieldAssets()
1396
    {
1397
        if (!empty(static::$collectedAssets)) {
1398
            return static::$collectedAssets;
1399
        }
1400
1401
        $css = collect();
1402
        $js = collect();
1403
1404
        foreach (static::$availableFields as $field) {
1405
            if (!method_exists($field, 'getAssets')) {
1406
                continue;
1407
            }
1408
1409
            $assets = call_user_func([$field, 'getAssets']);
1410
1411
            $css->push(array_get($assets, 'css'));
1412
            $js->push(array_get($assets, 'js'));
1413
        }
1414
1415
        return static::$collectedAssets = [
1416
            'css' => $css->flatten()->unique()->filter()->toArray(),
1417
            'js'  => $js->flatten()->unique()->filter()->toArray(),
1418
        ];
1419
    }
1420
1421
    /**
1422
     * Getter.
1423
     *
1424
     * @param string $name
1425
     *
1426
     * @return array|mixed
1427
     */
1428
    public function __get($name)
1429
    {
1430
        return $this->input($name);
1431
    }
1432
1433
    /**
1434
     * Setter.
1435
     *
1436
     * @param string $name
1437
     * @param $value
1438
     */
1439
    public function __set($name, $value)
1440
    {
1441
        $this->input($name, $value);
1442
    }
1443
1444
    /**
1445
     * Generate a Field object and add to form builder if Field exists.
1446
     *
1447
     * @param string $method
1448
     * @param array  $arguments
1449
     *
1450
     * @return Field|void
1451
     */
1452
    public function __call($method, $arguments)
1453
    {
1454
        if ($className = static::findFieldClass($method)) {
1455
            $column = array_get($arguments, 0, ''); //[0];
1456
1457
            $element = new $className($column, array_slice($arguments, 1));
1458
1459
            $this->pushField($element);
1460
1461
            return $element;
1462
        }
1463
    }
1464
}
1465