Completed
Push — master ( 22ac2d...0f92de )
by Song
03:31
created

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