Completed
Push — master ( 0f92de...55ae86 )
by Song
03:16
created

Form::__call()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16

Duplication

Lines 0
Ratio 0 %

Importance

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

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

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

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
537
            }
538
        }
539
540
        if (($response = $this->prepare($data)) instanceof Response) {
541
            return $response;
542
        }
543
544 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...
545
            $updates = $this->prepareUpdate($this->updates);
546
547
            foreach ($updates as $column => $value) {
548
                /* @var Model $this->model */
549
                $this->model->setAttribute($column, $value);
550
            }
551
552
            $this->model->save();
553
554
            $this->updateRelation($this->relations);
555
        });
556
557
        if (($result = $this->callSaved()) instanceof Response) {
558
            return $result;
559
        }
560
561
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->ajaxResponse(tran...in.update_succeeded')); of type boolean|Illuminate\Http\JsonResponse adds the type boolean to the return on line 562 which is incompatible with the return type documented by Encore\Admin\Form::update of type Symfony\Component\HttpFoundation\Response.
Loading history...
562
            return $response;
563
        }
564
565
        return $this->redirectAfterUpdate($id);
566
    }
567
568
    /**
569
     * Get RedirectResponse after store.
570
     *
571
     * @return \Illuminate\Http\RedirectResponse
572
     */
573
    protected function redirectAfterStore()
574
    {
575
        $resourcesPath = $this->resource(0);
576
577
        $key = $this->model->getKey();
578
579
        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 579 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
580
    }
581
582
    /**
583
     * Get RedirectResponse after update.
584
     *
585
     * @param mixed $key
586
     *
587
     * @return \Illuminate\Http\RedirectResponse
588
     */
589
    protected function redirectAfterUpdate($key)
590
    {
591
        $resourcesPath = $this->resource(-1);
592
593
        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 593 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
594
    }
595
596
    /**
597
     * Get RedirectResponse after data saving.
598
     *
599
     * @param string $resourcesPath
600
     * @param string $key
601
     *
602
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
603
     */
604
    protected function redirectAfterSaving($resourcesPath, $key)
605
    {
606
        if (request('after-save') == 1) {
607
            // continue editing
608
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
609
        } elseif (request('after-save') == 2) {
610
            // view resource
611
            $url = rtrim($resourcesPath, '/')."/{$key}";
612
        } else {
613
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
614
        }
615
616
        admin_toastr(trans('admin.save_succeeded'));
617
618
        return redirect($url);
619
    }
620
621
    /**
622
     * Check if request is from editable.
623
     *
624
     * @param array $input
625
     *
626
     * @return bool
627
     */
628
    protected function isEditable(array $input = [])
629
    {
630
        return array_key_exists('_editable', $input);
631
    }
632
633
    /**
634
     * Handle editable update.
635
     *
636
     * @param array $input
637
     *
638
     * @return array
639
     */
640
    protected function handleEditable(array $input = [])
641
    {
642
        if (array_key_exists('_editable', $input)) {
643
            $name = $input['name'];
644
            $value = $input['value'];
645
646
            array_forget($input, ['pk', 'value', 'name']);
647
            array_set($input, $name, $value);
648
        }
649
650
        return $input;
651
    }
652
653
    /**
654
     * @param array $input
655
     *
656
     * @return array
657
     */
658
    protected function handleFileDelete(array $input = [])
659
    {
660
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
661
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
662
            unset($input['key']);
663
        }
664
665
        Input::replace($input);
666
667
        return $input;
668
    }
669
670
    /**
671
     * Handle orderable update.
672
     *
673
     * @param int   $id
674
     * @param array $input
675
     *
676
     * @return bool
677
     */
678
    protected function handleOrderable($id, array $input = [])
679
    {
680
        if (array_key_exists('_orderable', $input)) {
681
            $model = $this->model->find($id);
682
683
            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...
684
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
685
686
                return true;
687
            }
688
        }
689
690
        return false;
691
    }
692
693
    /**
694
     * Update relation data.
695
     *
696
     * @param array $relationsData
697
     *
698
     * @return void
699
     */
700
    protected function updateRelation($relationsData)
701
    {
702
        foreach ($relationsData as $name => $values) {
703
            if (!method_exists($this->model, $name)) {
704
                continue;
705
            }
706
707
            $relation = $this->model->$name();
708
709
            $oneToOneRelation = $relation instanceof Relations\HasOne
710
                || $relation instanceof Relations\MorphOne
711
                || $relation instanceof Relations\BelongsTo;
712
713
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
714
715
            if (empty($prepared)) {
716
                continue;
717
            }
718
719
            switch (get_class($relation)) {
720
                case Relations\BelongsToMany::class:
721
                case Relations\MorphToMany::class:
722
                    if (isset($prepared[$name])) {
723
                        $relation->sync($prepared[$name]);
724
                    }
725
                    break;
726 View Code Duplication
                case Relations\HasOne::class:
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...
727
728
                    $related = $this->model->$name;
729
730
                    // if related is empty
731
                    if (is_null($related)) {
732
                        $related = $relation->getRelated();
733
                        $related->{$relation->getForeignKeyName()} = $this->model->{$this->model->getKeyName()};
734
                    }
735
736
                    foreach ($prepared[$name] as $column => $value) {
737
                        $related->setAttribute($column, $value);
738
                    }
739
740
                    $related->save();
741
                    break;
742 View Code Duplication
                case Relations\BelongsTo::class:
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...
743
744
                    $parent = $this->model->$name;
745
746
                    // if related is empty
747
                    if (is_null($parent)) {
748
                        $parent = $relation->getRelated();
749
                    }
750
751
                    foreach ($prepared[$name] as $column => $value) {
752
                        $parent->setAttribute($column, $value);
753
                    }
754
755
                    $parent->save();
756
757
                    // When in creating, associate two models
758
                    if (!$this->model->{$relation->getForeignKey()}) {
759
                        $this->model->{$relation->getForeignKey()} = $parent->getKey();
760
761
                        $this->model->save();
762
                    }
763
764
                    break;
765
                case Relations\MorphOne::class:
766
                    $related = $this->model->$name;
767
                    if (is_null($related)) {
768
                        $related = $relation->make();
769
                    }
770
                    foreach ($prepared[$name] as $column => $value) {
771
                        $related->setAttribute($column, $value);
772
                    }
773
                    $related->save();
774
                    break;
775
                case Relations\HasMany::class:
776
                case Relations\MorphMany::class:
777
778
                    foreach ($prepared[$name] as $related) {
779
                        /** @var Relations\Relation $relation */
780
                        $relation = $this->model()->$name();
781
782
                        $keyName = $relation->getRelated()->getKeyName();
783
784
                        $instance = $relation->findOrNew(array_get($related, $keyName));
785
786
                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
787
                            $instance->delete();
788
789
                            continue;
790
                        }
791
792
                        array_forget($related, static::REMOVE_FLAG_NAME);
793
794
                        $instance->fill($related);
795
796
                        $instance->save();
797
                    }
798
799
                    break;
800
            }
801
        }
802
    }
803
804
    /**
805
     * Prepare input data for update.
806
     *
807
     * @param array $updates
808
     * @param bool  $oneToOneRelation If column is one-to-one relation.
809
     *
810
     * @return array
811
     */
812
    protected function prepareUpdate(array $updates, $oneToOneRelation = false)
813
    {
814
        $prepared = [];
815
816
        /** @var Field $field */
817
        foreach ($this->builder->fields() as $field) {
818
            $columns = $field->column();
819
820
            // If column not in input array data, then continue.
821
            if (!array_has($updates, $columns)) {
822
                continue;
823
            }
824
825
            if ($this->invalidColumn($columns, $oneToOneRelation)) {
826
                continue;
827
            }
828
829
            $value = $this->getDataByColumn($updates, $columns);
830
831
            $value = $field->prepare($value);
832
833 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...
834
                foreach ($columns as $name => $column) {
835
                    array_set($prepared, $column, $value[$name]);
836
                }
837
            } elseif (is_string($columns)) {
838
                array_set($prepared, $columns, $value);
839
            }
840
        }
841
842
        return $prepared;
843
    }
844
845
    /**
846
     * @param string|array $columns
847
     * @param bool         $oneToOneRelation
848
     *
849
     * @return bool
850
     */
851
    protected function invalidColumn($columns, $oneToOneRelation = false)
852
    {
853
        foreach ((array) $columns as $column) {
854
            if ((!$oneToOneRelation && Str::contains($column, '.')) ||
855
                ($oneToOneRelation && !Str::contains($column, '.'))) {
856
                return true;
857
            }
858
        }
859
860
        return false;
861
    }
862
863
    /**
864
     * Prepare input data for insert.
865
     *
866
     * @param $inserts
867
     *
868
     * @return array
869
     */
870
    protected function prepareInsert($inserts)
871
    {
872
        if ($this->isHasOneRelation($inserts)) {
873
            $inserts = array_dot($inserts);
874
        }
875
876
        foreach ($inserts as $column => $value) {
877
            if (is_null($field = $this->getFieldByColumn($column))) {
878
                unset($inserts[$column]);
879
                continue;
880
            }
881
882
            $inserts[$column] = $field->prepare($value);
883
        }
884
885
        $prepared = [];
886
887
        foreach ($inserts as $key => $value) {
888
            array_set($prepared, $key, $value);
889
        }
890
891
        return $prepared;
892
    }
893
894
    /**
895
     * Is input data is has-one relation.
896
     *
897
     * @param array $inserts
898
     *
899
     * @return bool
900
     */
901
    protected function isHasOneRelation($inserts)
902
    {
903
        $first = current($inserts);
904
905
        if (!is_array($first)) {
906
            return false;
907
        }
908
909
        if (is_array(current($first))) {
910
            return false;
911
        }
912
913
        return Arr::isAssoc($first);
914
    }
915
916
    /**
917
     * Set after getting editing model callback.
918
     *
919
     * @param Closure $callback
920
     *
921
     * @return void
922
     */
923
    public function editing(Closure $callback)
924
    {
925
        $this->editing[] = $callback;
926
    }
927
928
    /**
929
     * Set submitted callback.
930
     *
931
     * @param Closure $callback
932
     *
933
     * @return void
934
     */
935
    public function submitted(Closure $callback)
936
    {
937
        $this->submitted[] = $callback;
938
    }
939
940
    /**
941
     * Set saving callback.
942
     *
943
     * @param Closure $callback
944
     *
945
     * @return void
946
     */
947
    public function saving(Closure $callback)
948
    {
949
        $this->saving[] = $callback;
950
    }
951
952
    /**
953
     * Set saved callback.
954
     *
955
     * @param Closure $callback
956
     *
957
     * @return void
958
     */
959
    public function saved(Closure $callback)
960
    {
961
        $this->saved[] = $callback;
962
    }
963
964
    /**
965
     * Ignore fields to save.
966
     *
967
     * @param string|array $fields
968
     *
969
     * @return $this
970
     */
971
    public function ignore($fields)
972
    {
973
        $this->ignored = array_merge($this->ignored, (array) $fields);
974
975
        return $this;
976
    }
977
978
    /**
979
     * @param array        $data
980
     * @param string|array $columns
981
     *
982
     * @return array|mixed
983
     */
984 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...
985
    {
986
        if (is_string($columns)) {
987
            return array_get($data, $columns);
988
        }
989
990
        if (is_array($columns)) {
991
            $value = [];
992
            foreach ($columns as $name => $column) {
993
                if (!array_has($data, $column)) {
994
                    continue;
995
                }
996
                $value[$name] = array_get($data, $column);
997
            }
998
999
            return $value;
1000
        }
1001
    }
1002
1003
    /**
1004
     * Find field object by column.
1005
     *
1006
     * @param $column
1007
     *
1008
     * @return mixed
1009
     */
1010
    protected function getFieldByColumn($column)
1011
    {
1012
        return $this->builder->fields()->first(
1013
            function (Field $field) use ($column) {
1014
                if (is_array($field->column())) {
1015
                    return in_array($column, $field->column());
1016
                }
1017
1018
                return $field->column() == $column;
1019
            }
1020
        );
1021
    }
1022
1023
    /**
1024
     * Set original data for each field.
1025
     *
1026
     * @return void
1027
     */
1028
    protected function setFieldOriginalValue()
1029
    {
1030
//        static::doNotSnakeAttributes($this->model);
1031
1032
        $values = $this->model->toArray();
1033
1034
        $this->builder->fields()->each(function (Field $field) use ($values) {
1035
            $field->setOriginal($values);
1036
        });
1037
    }
1038
1039
    /**
1040
     * Set all fields value in form.
1041
     *
1042
     * @param $id
1043
     *
1044
     * @return void
1045
     */
1046
    protected function setFieldValue($id)
1047
    {
1048
        $relations = $this->getRelations();
1049
1050
        $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...
1051
1052
        $this->callEditing();
1053
1054
//        static::doNotSnakeAttributes($this->model);
1055
1056
        $data = $this->model->toArray();
1057
1058
        $this->builder->fields()->each(function (Field $field) use ($data) {
1059
            if (!in_array($field->column(), $this->ignored)) {
1060
                $field->fill($data);
1061
            }
1062
        });
1063
    }
1064
1065
    /**
1066
     * Don't snake case attributes.
1067
     *
1068
     * @param Model $model
1069
     *
1070
     * @return void
1071
     */
1072
    protected static function doNotSnakeAttributes(Model $model)
1073
    {
1074
        $class = get_class($model);
1075
1076
        $class::$snakeAttributes = false;
1077
    }
1078
1079
    /**
1080
     * Get validation messages.
1081
     *
1082
     * @param array $input
1083
     *
1084
     * @return MessageBag|bool
1085
     */
1086
    public function validationMessages($input)
1087
    {
1088
        $failedValidators = [];
1089
1090
        /** @var Field $field */
1091
        foreach ($this->builder->fields() as $field) {
1092
            if (!$validator = $field->getValidator($input)) {
1093
                continue;
1094
            }
1095
1096
            if (($validator instanceof Validator) && !$validator->passes()) {
1097
                $failedValidators[] = $validator;
1098
            }
1099
        }
1100
1101
        $message = $this->mergeValidationMessages($failedValidators);
1102
1103
        return $message->any() ? $message : false;
1104
    }
1105
1106
    /**
1107
     * Merge validation messages from input validators.
1108
     *
1109
     * @param \Illuminate\Validation\Validator[] $validators
1110
     *
1111
     * @return MessageBag
1112
     */
1113
    protected function mergeValidationMessages($validators)
1114
    {
1115
        $messageBag = new MessageBag();
1116
1117
        foreach ($validators as $validator) {
1118
            $messageBag = $messageBag->merge($validator->messages());
1119
        }
1120
1121
        return $messageBag;
1122
    }
1123
1124
    /**
1125
     * Get all relations of model from callable.
1126
     *
1127
     * @return array
1128
     */
1129
    public function getRelations()
1130
    {
1131
        $relations = $columns = [];
1132
1133
        /** @var Field $field */
1134
        foreach ($this->builder->fields() as $field) {
1135
            $columns[] = $field->column();
1136
        }
1137
1138
        foreach (array_flatten($columns) as $column) {
1139
            if (str_contains($column, '.')) {
1140
                list($relation) = explode('.', $column);
1141
1142
                if (method_exists($this->model, $relation) &&
1143
                    $this->model->$relation() instanceof Relations\Relation
1144
                ) {
1145
                    $relations[] = $relation;
1146
                }
1147
            } elseif (method_exists($this->model, $column) &&
1148
                !method_exists(Model::class, $column)
1149
            ) {
1150
                $relations[] = $column;
1151
            }
1152
        }
1153
1154
        return array_unique($relations);
1155
    }
1156
1157
    /**
1158
     * Set action for form.
1159
     *
1160
     * @param string $action
1161
     *
1162
     * @return $this
1163
     */
1164
    public function setAction($action)
1165
    {
1166
        $this->builder()->setAction($action);
1167
1168
        return $this;
1169
    }
1170
1171
    /**
1172
     * Set field and label width in current form.
1173
     *
1174
     * @param int $fieldWidth
1175
     * @param int $labelWidth
1176
     *
1177
     * @return $this
1178
     */
1179
    public function setWidth($fieldWidth = 8, $labelWidth = 2)
1180
    {
1181
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1182
            /* @var Field $field  */
1183
            $field->setWidth($fieldWidth, $labelWidth);
1184
        });
1185
1186
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1187
1188
        return $this;
1189
    }
1190
1191
    /**
1192
     * Set view for form.
1193
     *
1194
     * @param string $view
1195
     *
1196
     * @return $this
1197
     */
1198
    public function setView($view)
1199
    {
1200
        $this->builder()->setView($view);
1201
1202
        return $this;
1203
    }
1204
1205
    /**
1206
     * Set title for form.
1207
     *
1208
     * @param string $title
1209
     *
1210
     * @return $this
1211
     */
1212
    public function setTitle($title = '')
1213
    {
1214
        $this->builder()->setTitle($title);
1215
1216
        return $this;
1217
    }
1218
1219
    /**
1220
     * Add a row in form.
1221
     *
1222
     * @param Closure $callback
1223
     *
1224
     * @return $this
1225
     */
1226
    public function row(Closure $callback)
1227
    {
1228
        $this->rows[] = new Row($callback, $this);
1229
1230
        return $this;
1231
    }
1232
1233
    /**
1234
     * Tools setting for form.
1235
     *
1236
     * @param Closure $callback
1237
     */
1238
    public function tools(Closure $callback)
1239
    {
1240
        $callback->call($this, $this->builder->getTools());
1241
    }
1242
1243
    /**
1244
     * Disable form submit.
1245
     *
1246
     * @return $this
1247
     *
1248
     * @deprecated
1249
     */
1250
    public function disableSubmit()
1251
    {
1252
        $this->builder()->getFooter()->disableSubmit();
1253
1254
        return $this;
1255
    }
1256
1257
    /**
1258
     * Disable form reset.
1259
     *
1260
     * @return $this
1261
     *
1262
     * @deprecated
1263
     */
1264
    public function disableReset()
1265
    {
1266
        $this->builder()->getFooter()->disableReset();
1267
1268
        return $this;
1269
    }
1270
1271
    /**
1272
     * Disable View Checkbox on footer.
1273
     *
1274
     * @return $this
1275
     */
1276
    public function disableViewCheck()
1277
    {
1278
        $this->builder()->getFooter()->disableViewCheck();
1279
1280
        return $this;
1281
    }
1282
1283
    /**
1284
     * Disable Editing Checkbox on footer.
1285
     *
1286
     * @return $this
1287
     */
1288
    public function disableEditingCheck()
1289
    {
1290
        $this->builder()->getFooter()->disableEditingCheck();
1291
1292
        return $this;
1293
    }
1294
1295
    /**
1296
     * Footer setting for form.
1297
     *
1298
     * @param Closure $callback
1299
     */
1300
    public function footer(Closure $callback)
1301
    {
1302
        call_user_func($callback, $this->builder()->getFooter());
1303
    }
1304
1305
    /**
1306
     * Get current resource route url.
1307
     *
1308
     * @param int $slice
1309
     *
1310
     * @return string
1311
     */
1312
    public function resource($slice = -2)
1313
    {
1314
        $segments = explode('/', trim(app('request')->getUri(), '/'));
1315
1316
        if ($slice != 0) {
1317
            $segments = array_slice($segments, 0, $slice);
1318
        }
1319
        // # fix #1768
1320
        if ($segments[0] == 'http:' && (config('admin.https') || config('admin.secure'))) {
1321
            $segments[0] = 'https:';
1322
        }
1323
1324
        return implode('/', $segments);
1325
    }
1326
1327
    /**
1328
     * Render the form contents.
1329
     *
1330
     * @return string
1331
     */
1332
    public function render()
1333
    {
1334
        try {
1335
            return $this->builder->render();
1336
        } catch (\Exception $e) {
1337
            return Handler::renderException($e);
1338
        }
1339
    }
1340
1341
    /**
1342
     * Get or set input data.
1343
     *
1344
     * @param string $key
1345
     * @param null   $value
1346
     *
1347
     * @return array|mixed
1348
     */
1349
    public function input($key, $value = null)
1350
    {
1351
        if (is_null($value)) {
1352
            return array_get($this->inputs, $key);
1353
        }
1354
1355
        return array_set($this->inputs, $key, $value);
1356
    }
1357
1358
    /**
1359
     * Register builtin fields.
1360
     *
1361
     * @return void
1362
     */
1363
    public static function registerBuiltinFields()
1364
    {
1365
        $map = [
1366
            'button'         => Field\Button::class,
1367
            'checkbox'       => Field\Checkbox::class,
1368
            'color'          => Field\Color::class,
1369
            'currency'       => Field\Currency::class,
1370
            'date'           => Field\Date::class,
1371
            'dateRange'      => Field\DateRange::class,
1372
            'datetime'       => Field\Datetime::class,
1373
            'dateTimeRange'  => Field\DatetimeRange::class,
1374
            'datetimeRange'  => Field\DatetimeRange::class,
1375
            'decimal'        => Field\Decimal::class,
1376
            'display'        => Field\Display::class,
1377
            'divider'        => Field\Divide::class,
1378
            'divide'         => Field\Divide::class,
1379
            'embeds'         => Field\Embeds::class,
1380
            'editor'         => Field\Editor::class,
1381
            'email'          => Field\Email::class,
1382
            'file'           => Field\File::class,
1383
            'hasMany'        => Field\HasMany::class,
1384
            'hidden'         => Field\Hidden::class,
1385
            'id'             => Field\Id::class,
1386
            'image'          => Field\Image::class,
1387
            'ip'             => Field\Ip::class,
1388
            'map'            => Field\Map::class,
1389
            'mobile'         => Field\Mobile::class,
1390
            'month'          => Field\Month::class,
1391
            'multipleSelect' => Field\MultipleSelect::class,
1392
            'number'         => Field\Number::class,
1393
            'password'       => Field\Password::class,
1394
            'radio'          => Field\Radio::class,
1395
            'rate'           => Field\Rate::class,
1396
            'select'         => Field\Select::class,
1397
            'slider'         => Field\Slider::class,
1398
            'switch'         => Field\SwitchField::class,
1399
            'text'           => Field\Text::class,
1400
            'textarea'       => Field\Textarea::class,
1401
            'time'           => Field\Time::class,
1402
            'timeRange'      => Field\TimeRange::class,
1403
            'url'            => Field\Url::class,
1404
            'year'           => Field\Year::class,
1405
            'html'           => Field\Html::class,
1406
            'tags'           => Field\Tags::class,
1407
            'icon'           => Field\Icon::class,
1408
            'multipleFile'   => Field\MultipleFile::class,
1409
            'multipleImage'  => Field\MultipleImage::class,
1410
            'captcha'        => Field\Captcha::class,
1411
            'listbox'        => Field\Listbox::class,
1412
        ];
1413
1414
        foreach ($map as $abstract => $class) {
1415
            static::extend($abstract, $class);
1416
        }
1417
    }
1418
1419
    /**
1420
     * Register custom field.
1421
     *
1422
     * @param string $abstract
1423
     * @param string $class
1424
     *
1425
     * @return void
1426
     */
1427
    public static function extend($abstract, $class)
1428
    {
1429
        static::$availableFields[$abstract] = $class;
1430
    }
1431
1432
    /**
1433
     * Set form field alias.
1434
     *
1435
     * @param string $field
1436
     * @param string $alias
1437
     *
1438
     * @return void
1439
     */
1440
    public static function alias($field, $alias)
1441
    {
1442
        static::$fieldAlias[$alias] = $field;
1443
    }
1444
1445
    /**
1446
     * Remove registered field.
1447
     *
1448
     * @param array|string $abstract
1449
     */
1450
    public static function forget($abstract)
1451
    {
1452
        array_forget(static::$availableFields, $abstract);
1453
    }
1454
1455
    /**
1456
     * Find field class.
1457
     *
1458
     * @param string $method
1459
     *
1460
     * @return bool|mixed
1461
     */
1462
    public static function findFieldClass($method)
1463
    {
1464
        // If alias exists.
1465
        if (isset(static::$fieldAlias[$method])) {
1466
            $method = static::$fieldAlias[$method];
1467
        }
1468
1469
        $class = array_get(static::$availableFields, $method);
1470
1471
        if (class_exists($class)) {
1472
            return $class;
1473
        }
1474
1475
        return false;
1476
    }
1477
1478
    /**
1479
     * Collect assets required by registered field.
1480
     *
1481
     * @return array
1482
     */
1483
    public static function collectFieldAssets()
1484
    {
1485
        if (!empty(static::$collectedAssets)) {
1486
            return static::$collectedAssets;
1487
        }
1488
1489
        $css = collect();
1490
        $js = collect();
1491
1492
        foreach (static::$availableFields as $field) {
1493
            if (!method_exists($field, 'getAssets')) {
1494
                continue;
1495
            }
1496
1497
            $assets = call_user_func([$field, 'getAssets']);
1498
1499
            $css->push(array_get($assets, 'css'));
1500
            $js->push(array_get($assets, 'js'));
1501
        }
1502
1503
        return static::$collectedAssets = [
1504
            'css' => $css->flatten()->unique()->filter()->toArray(),
1505
            'js'  => $js->flatten()->unique()->filter()->toArray(),
1506
        ];
1507
    }
1508
1509
    /**
1510
     * Getter.
1511
     *
1512
     * @param string $name
1513
     *
1514
     * @return array|mixed
1515
     */
1516
    public function __get($name)
1517
    {
1518
        return $this->input($name);
1519
    }
1520
1521
    /**
1522
     * Setter.
1523
     *
1524
     * @param string $name
1525
     * @param $value
1526
     */
1527
    public function __set($name, $value)
1528
    {
1529
        $this->input($name, $value);
1530
    }
1531
1532
    /**
1533
     * Generate a Field object and add to form builder if Field exists.
1534
     *
1535
     * @param string $method
1536
     * @param array  $arguments
1537
     *
1538
     * @return Field
1539
     */
1540
    public function __call($method, $arguments)
1541
    {
1542
        if ($className = static::findFieldClass($method)) {
1543
            $column = array_get($arguments, 0, ''); //[0];
1544
1545
            $element = new $className($column, array_slice($arguments, 1));
1546
1547
            $this->pushField($element);
1548
1549
            return $element;
1550
        }
1551
1552
        admin_error('Error', "Field type [$method] does not exist.");
1553
1554
        return new Field\Nullable();
1555
    }
1556
}
1557