Completed
Push — master ( d713cf...03896d )
by Song
03:09
created

Form::handleFileSort()   A

Complexity

Conditions 4
Paths 4

Size

Total Lines 20

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
nc 4
nop 1
dl 0
loc 20
rs 9.6
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
     * @var bool
187
     */
188
    protected $isSoftDeletes = false;
189
190
    /**
191
     * Initialization closure array.
192
     *
193
     * @var []Closure
194
     */
195
    protected static $initCallbacks;
196
197
    /**
198
     * Create a new form instance.
199
     *
200
     * @param $model
201
     * @param \Closure $callback
202
     */
203
    public function __construct($model, Closure $callback = null)
204
    {
205
        $this->model = $model;
206
207
        $this->builder = new Builder($this);
208
209
        if ($callback instanceof Closure) {
210
            $callback($this);
211
        }
212
213
        $this->isSoftDeletes = in_array(SoftDeletes::class, class_uses_deep($this->model));
214
215
        $this->callInitCallbacks();
216
    }
217
218
    /**
219
     * Initialize with user pre-defined default disables, etc.
220
     *
221
     * @param Closure $callback
222
     */
223
    public static function init(Closure $callback = null)
224
    {
225
        static::$initCallbacks[] = $callback;
226
    }
227
228
    /**
229
     * Call the initialization closure array in sequence.
230
     */
231
    protected function callInitCallbacks()
232
    {
233
        if (empty(static::$initCallbacks)) {
234
            return;
235
        }
236
237
        foreach (static::$initCallbacks as $callback) {
238
            call_user_func($callback, $this);
239
        }
240
    }
241
242
    /**
243
     * @param Field $field
244
     *
245
     * @return $this
246
     */
247
    public function pushField(Field $field)
248
    {
249
        $field->setForm($this);
250
251
        $this->builder->fields()->push($field);
252
253
        return $this;
254
    }
255
256
    /**
257
     * @return Model
258
     */
259
    public function model()
260
    {
261
        return $this->model;
262
    }
263
264
    /**
265
     * @return Builder
266
     */
267
    public function builder()
268
    {
269
        return $this->builder;
270
    }
271
272
    /**
273
     * Generate a edit form.
274
     *
275
     * @param $id
276
     *
277
     * @return $this
278
     */
279
    public function edit($id)
280
    {
281
        $this->builder->setMode(Builder::MODE_EDIT);
282
        $this->builder->setResourceId($id);
283
284
        $this->setFieldValue($id);
285
286
        return $this;
287
    }
288
289
    /**
290
     * Use tab to split form.
291
     *
292
     * @param string  $title
293
     * @param Closure $content
294
     *
295
     * @return $this
296
     */
297
    public function tab($title, Closure $content, $active = false)
298
    {
299
        $this->getTab()->append($title, $content, $active);
300
301
        return $this;
302
    }
303
304
    /**
305
     * Get Tab instance.
306
     *
307
     * @return Tab
308
     */
309
    public function getTab()
310
    {
311
        if (is_null($this->tab)) {
312
            $this->tab = new Tab($this);
313
        }
314
315
        return $this->tab;
316
    }
317
318
    /**
319
     * Destroy data entity and remove files.
320
     *
321
     * @param $id
322
     *
323
     * @return mixed
324
     */
325
    public function destroy($id)
326
    {
327
        collect(explode(',', $id))->filter()->each(function ($id) {
328
            $builder = $this->model()->newQuery();
329
330
            if ($this->isSoftDeletes) {
331
                $builder = $builder->withTrashed();
332
            }
333
334
            $model = $builder->with($this->getRelations())->findOrFail($id);
335
336
            if ($this->isSoftDeletes && $model->trashed()) {
337
                $this->deleteFiles($model, true);
338
                $model->forceDelete();
339
340
                return;
341
            }
342
343
            $this->deleteFiles($model);
344
            $model->delete();
345
        });
346
347
        return true;
348
    }
349
350
    /**
351
     * Remove files in record.
352
     *
353
     * @param Model $model
354
     * @param bool  $forceDelete
355
     */
356
    protected function deleteFiles(Model $model, $forceDelete = false)
357
    {
358
        // If it's a soft delete, the files in the data will not be deleted.
359
        if (!$forceDelete && $this->isSoftDeletes) {
360
            return;
361
        }
362
363
        $data = $model->toArray();
364
365
        $this->builder->fields()->filter(function ($field) {
366
            return $field instanceof Field\File;
367
        })->each(function (Field\File $file) use ($data) {
368
            $file->setOriginal($data);
369
370
            $file->destroy();
371
        });
372
    }
373
374
    /**
375
     * Store a new record.
376
     *
377
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
378
     */
379
    public function store()
380
    {
381
        $data = Input::all();
382
383
        // Handle validation errors.
384
        if ($validationMessages = $this->validationMessages($data)) {
385
            return back()->withInput()->withErrors($validationMessages);
386
        }
387
388
        if (($response = $this->prepare($data)) instanceof Response) {
389
            return $response;
390
        }
391
392 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...
393
            $inserts = $this->prepareInsert($this->updates);
394
395
            foreach ($inserts as $column => $value) {
396
                $this->model->setAttribute($column, $value);
397
            }
398
399
            $this->model->save();
400
401
            $this->updateRelation($this->relations);
402
        });
403
404
        if (($response = $this->callSaved()) instanceof Response) {
405
            return $response;
406
        }
407
408
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
409
            return $response;
410
        }
411
412
        return $this->redirectAfterStore();
413
    }
414
415
    /**
416
     * Get ajax response.
417
     *
418
     * @param string $message
419
     *
420
     * @return bool|\Illuminate\Http\JsonResponse
421
     */
422
    protected function ajaxResponse($message)
423
    {
424
        $request = Request::capture();
425
426
        // ajax but not pjax
427
        if ($request->ajax() && !$request->pjax()) {
428
            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...
429
                'status'  => true,
430
                'message' => $message,
431
            ]);
432
        }
433
434
        return false;
435
    }
436
437
    /**
438
     * Prepare input data for insert or update.
439
     *
440
     * @param array $data
441
     *
442
     * @return mixed
443
     */
444
    protected function prepare($data = [])
445
    {
446
        if (($response = $this->callSubmitted()) instanceof Response) {
447
            return $response;
448
        }
449
450
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
451
452
        if (($response = $this->callSaving()) instanceof Response) {
453
            return $response;
454
        }
455
456
        $this->relations = $this->getRelationInputs($this->inputs);
457
458
        $this->updates = array_except($this->inputs, array_keys($this->relations));
0 ignored issues
show
Deprecated Code introduced by
The function array_except() has been deprecated with message: Arr::except() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
459
    }
460
461
    /**
462
     * Remove ignored fields from input.
463
     *
464
     * @param array $input
465
     *
466
     * @return array
467
     */
468
    protected function removeIgnoredFields($input)
469
    {
470
        array_forget($input, $this->ignored);
0 ignored issues
show
Deprecated Code introduced by
The function array_forget() has been deprecated with message: Arr::forget() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
471
472
        return $input;
473
    }
474
475
    /**
476
     * Get inputs for relations.
477
     *
478
     * @param array $inputs
479
     *
480
     * @return array
481
     */
482
    protected function getRelationInputs($inputs = [])
483
    {
484
        $relations = [];
485
486
        foreach ($inputs as $column => $value) {
487
            if (method_exists($this->model, $column)) {
488
                $relation = call_user_func([$this->model, $column]);
489
490
                if ($relation instanceof Relations\Relation) {
491
                    $relations[$column] = $value;
492
                }
493
            }
494
        }
495
496
        return $relations;
497
    }
498
499
    /**
500
     * Call editing callbacks.
501
     *
502
     * @return void
503
     */
504
    protected function callEditing()
505
    {
506
        foreach ($this->editing as $func) {
507
            call_user_func($func, $this);
508
        }
509
    }
510
511
    /**
512
     * Call submitted callback.
513
     *
514
     * @return mixed
515
     */
516
    protected function callSubmitted()
517
    {
518
        foreach ($this->submitted as $func) {
519
            if ($func instanceof Closure && ($ret = call_user_func($func, $this)) instanceof Response) {
520
                return $ret;
521
            }
522
        }
523
    }
524
525
    /**
526
     * Call saving callback.
527
     *
528
     * @return mixed
529
     */
530
    protected function callSaving()
531
    {
532
        foreach ($this->saving as $func) {
533
            if ($func instanceof Closure && ($ret = call_user_func($func, $this)) instanceof Response) {
534
                return $ret;
535
            }
536
        }
537
    }
538
539
    /**
540
     * Callback after saving a Model.
541
     *
542
     * @return mixed|null
543
     */
544
    protected function callSaved()
545
    {
546
        foreach ($this->saved as $func) {
547
            if ($func instanceof Closure &&
548
                ($ret = call_user_func($func, $this)) instanceof Response) {
549
                return $ret;
550
            }
551
        }
552
    }
553
554
    /**
555
     * Handle update.
556
     *
557
     * @param int $id
558
     * @param null $data
559
     * @return bool|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|null|Response
560
     */
561
    public function update($id, $data = null)
562
    {
563
        $data = ($data) ?: Input::all();
564
565
        $isEditable = $this->isEditable($data);
566
567
        if (($data = $this->handleColumnUpdates($id, $data)) instanceof Response) {
568
            return $data;
569
        }
570
571
        /* @var Model $this->model */
572
        $builder = $this->model();
573
574
        if ($this->isSoftDeletes) {
575
            $builder = $builder->withTrashed();
576
        }
577
578
        $this->model = $builder->with($this->getRelations())->findOrFail($id);
579
580
        $this->setFieldOriginalValue();
581
582
        // Handle validation errors.
583
        if ($validationMessages = $this->validationMessages($data)) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 567 can also be of type object<Illuminate\Contra...outing\ResponseFactory>; however, Encore\Admin\Form::validationMessages() does only seem to accept array, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
584
            if (!$isEditable) {
585
                return back()->withInput()->withErrors($validationMessages);
586
            }
587
588
            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...
Deprecated Code introduced by
The function array_dot() has been deprecated with message: Arr::dot() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
589
        }
590
591
        if (($response = $this->prepare($data)) instanceof Response) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 567 can also be of type object<Illuminate\Contra...outing\ResponseFactory>; however, Encore\Admin\Form::prepare() does only seem to accept array, maybe add an additional type check?

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

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

    return array();
}

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

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

Loading history...
592
            return $response;
593
        }
594
595 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...
596
            $updates = $this->prepareUpdate($this->updates);
597
598
            foreach ($updates as $column => $value) {
599
                /* @var Model $this->model */
600
                $this->model->setAttribute($column, $value);
601
            }
602
603
            $this->model->save();
604
605
            $this->updateRelation($this->relations);
606
        });
607
608
        if (($result = $this->callSaved()) instanceof Response) {
609
            return $result;
610
        }
611
612
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
613
            return $response;
614
        }
615
616
        return $this->redirectAfterUpdate($id);
617
    }
618
619
    /**
620
     * Get RedirectResponse after store.
621
     *
622
     * @return \Illuminate\Http\RedirectResponse
623
     */
624
    protected function redirectAfterStore()
625
    {
626
        $resourcesPath = $this->resource(0);
627
628
        $key = $this->model->getKey();
629
630
        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 630 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
631
    }
632
633
    /**
634
     * Get RedirectResponse after update.
635
     *
636
     * @param mixed $key
637
     *
638
     * @return \Illuminate\Http\RedirectResponse
639
     */
640
    protected function redirectAfterUpdate($key)
641
    {
642
        $resourcesPath = $this->resource(-1);
643
644
        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 644 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
645
    }
646
647
    /**
648
     * Get RedirectResponse after data saving.
649
     *
650
     * @param string $resourcesPath
651
     * @param string $key
652
     *
653
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
654
     */
655
    protected function redirectAfterSaving($resourcesPath, $key)
656
    {
657
        if (request('after-save') == 1) {
658
            // continue editing
659
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
660
        } elseif (request('after-save') == 2) {
661
            // continue creating
662
            $url = rtrim($resourcesPath, '/').'/create';
663
        } elseif (request('after-save') == 3) {
664
            // view resource
665
            $url = rtrim($resourcesPath, '/')."/{$key}";
666
        } else {
667
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
668
        }
669
670
        admin_toastr(trans('admin.save_succeeded'));
671
672
        return redirect($url);
673
    }
674
675
    /**
676
     * Check if request is from editable.
677
     *
678
     * @param array $input
679
     *
680
     * @return bool
681
     */
682
    protected function isEditable(array $input = [])
683
    {
684
        return array_key_exists('_editable', $input);
685
    }
686
687
    /**
688
     * Handle updates for single column.
689
     *
690
     * @param integer $id
691
     * @param array   $data
692
     * @return array|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|Response
693
     */
694
    protected function handleColumnUpdates($id, $data)
695
    {
696
        $data = $this->handleEditable($data);
697
698
        $data = $this->handleFileDelete($data);
699
700
        $data = $this->handleFileSort($data);
701
702
        if ($this->handleOrderable($id, $data)) {
703
            return response([
704
                'status'  => true,
705
                'message' => trans('admin.update_succeeded'),
706
            ]);
707
        }
708
709
        return $data;
710
    }
711
712
    /**
713
     * Handle editable update.
714
     *
715
     * @param array $input
716
     *
717
     * @return array
718
     */
719
    protected function handleEditable(array $input = [])
720
    {
721
        if (array_key_exists('_editable', $input)) {
722
            $name = $input['name'];
723
            $value = $input['value'];
724
725
            array_forget($input, ['pk', 'value', 'name']);
0 ignored issues
show
Deprecated Code introduced by
The function array_forget() has been deprecated with message: Arr::forget() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
726
            array_set($input, $name, $value);
0 ignored issues
show
Deprecated Code introduced by
The function array_set() has been deprecated with message: Arr::set() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
727
        }
728
729
        return $input;
730
    }
731
732
    /**
733
     * @param array $input
734
     *
735
     * @return array
736
     */
737
    protected function handleFileDelete(array $input = [])
738
    {
739
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
740
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
741
            unset($input['key']);
742
        }
743
744
        Input::replace($input);
745
746
        return $input;
747
    }
748
749
    /**
750
     * @param array $input
751
     *
752
     * @return array
753
     */
754
    protected function handleFileSort(array $input = [])
755
    {
756
        if (!array_key_exists(Field::FILE_SORT_FLAG, $input)) {
757
            return $input;
758
        }
759
760
        $sorts = array_filter($input[Field::FILE_SORT_FLAG]);
761
762
        if (empty($sorts)) {
763
            return $input;
764
        }
765
766
        foreach ($sorts as $column => $order) {
767
            $input[$column] = $order;
768
        }
769
770
        Input::replace($input);
771
772
        return $input;
773
    }
774
775
    /**
776
     * Handle orderable update.
777
     *
778
     * @param int   $id
779
     * @param array $input
780
     *
781
     * @return bool
782
     */
783
    protected function handleOrderable($id, array $input = [])
784
    {
785
        if (array_key_exists('_orderable', $input)) {
786
            $model = $this->model->find($id);
787
788
            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...
789
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
790
791
                return true;
792
            }
793
        }
794
795
        return false;
796
    }
797
798
    /**
799
     * Update relation data.
800
     *
801
     * @param array $relationsData
802
     *
803
     * @return void
804
     */
805
    protected function updateRelation($relationsData)
806
    {
807
        foreach ($relationsData as $name => $values) {
808
            if (!method_exists($this->model, $name)) {
809
                continue;
810
            }
811
812
            $relation = $this->model->$name();
813
814
            $oneToOneRelation = $relation instanceof Relations\HasOne
815
                || $relation instanceof Relations\MorphOne
816
                || $relation instanceof Relations\BelongsTo;
817
818
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
819
820
            if (empty($prepared)) {
821
                continue;
822
            }
823
824
            switch (true) {
825
                case $relation instanceof Relations\BelongsToMany:
826
                case $relation instanceof Relations\MorphToMany:
827
                    if (isset($prepared[$name])) {
828
                        $relation->sync($prepared[$name]);
829
                    }
830
                    break;
831
                case $relation instanceof Relations\HasOne:
832
833
                    $related = $this->model->$name;
834
835
                    // if related is empty
836
                    if (is_null($related)) {
837
                        $related = $relation->getRelated();
838
                        $qualifiedParentKeyName = $relation->getQualifiedParentKeyName();
839
                        $localKey = array_last(explode('.', $qualifiedParentKeyName));
0 ignored issues
show
Deprecated Code introduced by
The function array_last() has been deprecated with message: Arr::last() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
840
                        $related->{$relation->getForeignKeyName()} = $this->model->{$localKey};
841
                    }
842
843
                    foreach ($prepared[$name] as $column => $value) {
844
                        $related->setAttribute($column, $value);
845
                    }
846
847
                    $related->save();
848
                    break;
849
                case $relation instanceof Relations\BelongsTo:
850
                case $relation instanceof Relations\MorphTo:
851
852
                    $parent = $this->model->$name;
853
854
                    // if related is empty
855
                    if (is_null($parent)) {
856
                        $parent = $relation->getRelated();
857
                    }
858
859
                    foreach ($prepared[$name] as $column => $value) {
860
                        $parent->setAttribute($column, $value);
861
                    }
862
863
                    $parent->save();
864
865
                    // When in creating, associate two models
866
                    $foreignKeyMethod = (app()->version() < '5.8.0') ? 'getForeignKey' : 'getForeignKeyName';
867
                    if (!$this->model->{$relation->{$foreignKeyMethod}()}) {
868
                        $this->model->{$relation->{$foreignKeyMethod}()} = $parent->getKey();
869
870
                        $this->model->save();
871
                    }
872
873
                    break;
874
                case $relation instanceof Relations\MorphOne:
875
                    $related = $this->model->$name;
876
                    if (is_null($related)) {
877
                        $related = $relation->make();
878
                    }
879
                    foreach ($prepared[$name] as $column => $value) {
880
                        $related->setAttribute($column, $value);
881
                    }
882
                    $related->save();
883
                    break;
884
                case $relation instanceof Relations\HasMany:
885
                case $relation instanceof Relations\MorphMany:
886
887
                    foreach ($prepared[$name] as $related) {
888
                        /** @var Relations\Relation $relation */
889
                        $relation = $this->model()->$name();
890
891
                        $keyName = $relation->getRelated()->getKeyName();
892
893
                        $instance = $relation->findOrNew(array_get($related, $keyName));
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
894
895
                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
896
                            $instance->delete();
897
898
                            continue;
899
                        }
900
901
                        array_forget($related, static::REMOVE_FLAG_NAME);
0 ignored issues
show
Deprecated Code introduced by
The function array_forget() has been deprecated with message: Arr::forget() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
902
903
                        $instance->fill($related);
904
905
                        $instance->save();
906
                    }
907
908
                    break;
909
            }
910
        }
911
    }
912
913
    /**
914
     * Prepare input data for update.
915
     *
916
     * @param array $updates
917
     * @param bool  $oneToOneRelation If column is one-to-one relation.
918
     *
919
     * @return array
920
     */
921
    protected function prepareUpdate(array $updates, $oneToOneRelation = false)
922
    {
923
        $prepared = [];
924
925
        /** @var Field $field */
926
        foreach ($this->builder->fields() as $field) {
927
            $columns = $field->column();
928
929
            // If column not in input array data, then continue.
930
            if (!array_has($updates, $columns)) {
0 ignored issues
show
Deprecated Code introduced by
The function array_has() has been deprecated with message: Arr::has() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
931
                continue;
932
            }
933
934
            if ($this->invalidColumn($columns, $oneToOneRelation)) {
935
                continue;
936
            }
937
938
            $value = $this->getDataByColumn($updates, $columns);
939
940
            $value = $field->prepare($value);
941
942 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...
943
                foreach ($columns as $name => $column) {
944
                    array_set($prepared, $column, $value[$name]);
0 ignored issues
show
Deprecated Code introduced by
The function array_set() has been deprecated with message: Arr::set() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
945
                }
946
            } elseif (is_string($columns)) {
947
                array_set($prepared, $columns, $value);
0 ignored issues
show
Deprecated Code introduced by
The function array_set() has been deprecated with message: Arr::set() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
948
            }
949
        }
950
951
        return $prepared;
952
    }
953
954
    /**
955
     * @param string|array $columns
956
     * @param bool         $oneToOneRelation
957
     *
958
     * @return bool
959
     */
960
    protected function invalidColumn($columns, $oneToOneRelation = false)
961
    {
962
        foreach ((array) $columns as $column) {
963
            if ((!$oneToOneRelation && Str::contains($column, '.')) ||
964
                ($oneToOneRelation && !Str::contains($column, '.'))) {
965
                return true;
966
            }
967
        }
968
969
        return false;
970
    }
971
972
    /**
973
     * Prepare input data for insert.
974
     *
975
     * @param $inserts
976
     *
977
     * @return array
978
     */
979
    protected function prepareInsert($inserts)
980
    {
981
        if ($this->isHasOneRelation($inserts)) {
982
            $inserts = array_dot($inserts);
0 ignored issues
show
Deprecated Code introduced by
The function array_dot() has been deprecated with message: Arr::dot() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
983
        }
984
985
        foreach ($inserts as $column => $value) {
986
            if (is_null($field = $this->getFieldByColumn($column))) {
987
                unset($inserts[$column]);
988
                continue;
989
            }
990
991
            $inserts[$column] = $field->prepare($value);
992
        }
993
994
        $prepared = [];
995
996
        foreach ($inserts as $key => $value) {
997
            array_set($prepared, $key, $value);
0 ignored issues
show
Deprecated Code introduced by
The function array_set() has been deprecated with message: Arr::set() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
998
        }
999
1000
        return $prepared;
1001
    }
1002
1003
    /**
1004
     * Is input data is has-one relation.
1005
     *
1006
     * @param array $inserts
1007
     *
1008
     * @return bool
1009
     */
1010
    protected function isHasOneRelation($inserts)
1011
    {
1012
        $first = current($inserts);
1013
1014
        if (!is_array($first)) {
1015
            return false;
1016
        }
1017
1018
        if (is_array(current($first))) {
1019
            return false;
1020
        }
1021
1022
        return Arr::isAssoc($first);
1023
    }
1024
1025
    /**
1026
     * Set after getting editing model callback.
1027
     *
1028
     * @param Closure $callback
1029
     *
1030
     * @return void
1031
     */
1032
    public function editing(Closure $callback)
1033
    {
1034
        $this->editing[] = $callback;
1035
    }
1036
1037
    /**
1038
     * Set submitted callback.
1039
     *
1040
     * @param Closure $callback
1041
     *
1042
     * @return void
1043
     */
1044
    public function submitted(Closure $callback)
1045
    {
1046
        $this->submitted[] = $callback;
1047
    }
1048
1049
    /**
1050
     * Set saving callback.
1051
     *
1052
     * @param Closure $callback
1053
     *
1054
     * @return void
1055
     */
1056
    public function saving(Closure $callback)
1057
    {
1058
        $this->saving[] = $callback;
1059
    }
1060
1061
    /**
1062
     * Set saved callback.
1063
     *
1064
     * @param Closure $callback
1065
     *
1066
     * @return void
1067
     */
1068
    public function saved(Closure $callback)
1069
    {
1070
        $this->saved[] = $callback;
1071
    }
1072
1073
    /**
1074
     * Ignore fields to save.
1075
     *
1076
     * @param string|array $fields
1077
     *
1078
     * @return $this
1079
     */
1080
    public function ignore($fields)
1081
    {
1082
        $this->ignored = array_merge($this->ignored, (array) $fields);
1083
1084
        return $this;
1085
    }
1086
1087
    /**
1088
     * @param array        $data
1089
     * @param string|array $columns
1090
     *
1091
     * @return array|mixed
1092
     */
1093 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...
1094
    {
1095
        if (is_string($columns)) {
1096
            return array_get($data, $columns);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1097
        }
1098
1099
        if (is_array($columns)) {
1100
            $value = [];
1101
            foreach ($columns as $name => $column) {
1102
                if (!array_has($data, $column)) {
0 ignored issues
show
Deprecated Code introduced by
The function array_has() has been deprecated with message: Arr::has() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1103
                    continue;
1104
                }
1105
                $value[$name] = array_get($data, $column);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1106
            }
1107
1108
            return $value;
1109
        }
1110
    }
1111
1112
    /**
1113
     * Find field object by column.
1114
     *
1115
     * @param $column
1116
     *
1117
     * @return mixed
1118
     */
1119
    protected function getFieldByColumn($column)
1120
    {
1121
        return $this->builder->fields()->first(
1122
            function (Field $field) use ($column) {
1123
                if (is_array($field->column())) {
1124
                    return in_array($column, $field->column());
1125
                }
1126
1127
                return $field->column() == $column;
1128
            }
1129
        );
1130
    }
1131
1132
    /**
1133
     * Set original data for each field.
1134
     *
1135
     * @return void
1136
     */
1137
    protected function setFieldOriginalValue()
1138
    {
1139
//        static::doNotSnakeAttributes($this->model);
1140
1141
        $values = $this->model->toArray();
1142
1143
        $this->builder->fields()->each(function (Field $field) use ($values) {
1144
            $field->setOriginal($values);
1145
        });
1146
    }
1147
1148
    /**
1149
     * Set all fields value in form.
1150
     *
1151
     * @param $id
1152
     *
1153
     * @return void
1154
     */
1155
    protected function setFieldValue($id)
1156
    {
1157
        $relations = $this->getRelations();
1158
1159
        $builder = $this->model();
1160
1161
        if ($this->isSoftDeletes) {
1162
            $builder = $builder->withTrashed();
1163
        }
1164
1165
        $this->model = $builder->with($relations)->findOrFail($id);
1166
1167
        $this->callEditing();
1168
1169
//        static::doNotSnakeAttributes($this->model);
1170
1171
        $data = $this->model->toArray();
1172
1173
        $this->builder->fields()->each(function (Field $field) use ($data) {
1174
            if (!in_array($field->column(), $this->ignored)) {
1175
                $field->fill($data);
1176
            }
1177
        });
1178
    }
1179
1180
    /**
1181
     * Don't snake case attributes.
1182
     *
1183
     * @param Model $model
1184
     *
1185
     * @return void
1186
     */
1187
    protected static function doNotSnakeAttributes(Model $model)
1188
    {
1189
        $class = get_class($model);
1190
1191
        $class::$snakeAttributes = false;
1192
    }
1193
1194
    /**
1195
     * Get validation messages.
1196
     *
1197
     * @param array $input
1198
     *
1199
     * @return MessageBag|bool
1200
     */
1201
    public function validationMessages($input)
1202
    {
1203
        $failedValidators = [];
1204
1205
        /** @var Field $field */
1206
        foreach ($this->builder->fields() as $field) {
1207
            if (!$validator = $field->getValidator($input)) {
1208
                continue;
1209
            }
1210
1211
            if (($validator instanceof Validator) && !$validator->passes()) {
1212
                $failedValidators[] = $validator;
1213
            }
1214
        }
1215
1216
        $message = $this->mergeValidationMessages($failedValidators);
1217
1218
        return $message->any() ? $message : false;
1219
    }
1220
1221
    /**
1222
     * Merge validation messages from input validators.
1223
     *
1224
     * @param \Illuminate\Validation\Validator[] $validators
1225
     *
1226
     * @return MessageBag
1227
     */
1228
    protected function mergeValidationMessages($validators)
1229
    {
1230
        $messageBag = new MessageBag();
1231
1232
        foreach ($validators as $validator) {
1233
            $messageBag = $messageBag->merge($validator->messages());
1234
        }
1235
1236
        return $messageBag;
1237
    }
1238
1239
    /**
1240
     * Get all relations of model from callable.
1241
     *
1242
     * @return array
1243
     */
1244
    public function getRelations()
1245
    {
1246
        $relations = $columns = [];
1247
1248
        /** @var Field $field */
1249
        foreach ($this->builder->fields() as $field) {
1250
            $columns[] = $field->column();
1251
        }
1252
1253
        foreach (array_flatten($columns) as $column) {
0 ignored issues
show
Deprecated Code introduced by
The function array_flatten() has been deprecated with message: Arr::flatten() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1254
            if (str_contains($column, '.')) {
0 ignored issues
show
Deprecated Code introduced by
The function str_contains() has been deprecated with message: Str::contains() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1255
                list($relation) = explode('.', $column);
1256
1257
                if (method_exists($this->model, $relation) &&
1258
                    $this->model->$relation() instanceof Relations\Relation
1259
                ) {
1260
                    $relations[] = $relation;
1261
                }
1262
            } elseif (method_exists($this->model, $column) &&
1263
                !method_exists(Model::class, $column)
1264
            ) {
1265
                $relations[] = $column;
1266
            }
1267
        }
1268
1269
        return array_unique($relations);
1270
    }
1271
1272
    /**
1273
     * Set action for form.
1274
     *
1275
     * @param string $action
1276
     *
1277
     * @return $this
1278
     */
1279
    public function setAction($action)
1280
    {
1281
        $this->builder()->setAction($action);
1282
1283
        return $this;
1284
    }
1285
1286
    /**
1287
     * Set field and label width in current form.
1288
     *
1289
     * @param int $fieldWidth
1290
     * @param int $labelWidth
1291
     *
1292
     * @return $this
1293
     */
1294
    public function setWidth($fieldWidth = 8, $labelWidth = 2)
1295
    {
1296
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1297
            /* @var Field $field  */
1298
            $field->setWidth($fieldWidth, $labelWidth);
1299
        });
1300
1301
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1302
1303
        return $this;
1304
    }
1305
1306
    /**
1307
     * Set view for form.
1308
     *
1309
     * @param string $view
1310
     *
1311
     * @return $this
1312
     */
1313
    public function setView($view)
1314
    {
1315
        $this->builder()->setView($view);
1316
1317
        return $this;
1318
    }
1319
1320
    /**
1321
     * Set title for form.
1322
     *
1323
     * @param string $title
1324
     *
1325
     * @return $this
1326
     */
1327
    public function setTitle($title = '')
1328
    {
1329
        $this->builder()->setTitle($title);
1330
1331
        return $this;
1332
    }
1333
1334
    /**
1335
     * Add a row in form.
1336
     *
1337
     * @param Closure $callback
1338
     *
1339
     * @return $this
1340
     */
1341
    public function row(Closure $callback)
1342
    {
1343
        $this->rows[] = new Row($callback, $this);
1344
1345
        return $this;
1346
    }
1347
1348
    /**
1349
     * Tools setting for form.
1350
     *
1351
     * @param Closure $callback
1352
     */
1353
    public function tools(Closure $callback)
1354
    {
1355
        $callback->call($this, $this->builder->getTools());
1356
    }
1357
1358
    /**
1359
     * Disable form submit.
1360
     *
1361
     * @return $this
1362
     *
1363
     * @deprecated
1364
     */
1365
    public function disableSubmit(bool $disable = true)
1366
    {
1367
        $this->builder()->getFooter()->disableSubmit($disable);
1368
1369
        return $this;
1370
    }
1371
1372
    /**
1373
     * Disable form reset.
1374
     *
1375
     * @return $this
1376
     *
1377
     * @deprecated
1378
     */
1379
    public function disableReset(bool $disable = true)
1380
    {
1381
        $this->builder()->getFooter()->disableReset($disable);
1382
1383
        return $this;
1384
    }
1385
1386
    /**
1387
     * Disable View Checkbox on footer.
1388
     *
1389
     * @return $this
1390
     */
1391
    public function disableViewCheck(bool $disable = true)
1392
    {
1393
        $this->builder()->getFooter()->disableViewCheck($disable);
1394
1395
        return $this;
1396
    }
1397
1398
    /**
1399
     * Disable Editing Checkbox on footer.
1400
     *
1401
     * @return $this
1402
     */
1403
    public function disableEditingCheck(bool $disable = true)
1404
    {
1405
        $this->builder()->getFooter()->disableEditingCheck($disable);
1406
1407
        return $this;
1408
    }
1409
1410
    /**
1411
     * Disable Creating Checkbox on footer.
1412
     *
1413
     * @return $this
1414
     */
1415
    public function disableCreatingCheck(bool $disable = true)
1416
    {
1417
        $this->builder()->getFooter()->disableCreatingCheck($disable);
1418
1419
        return $this;
1420
    }
1421
1422
    /**
1423
     * Footer setting for form.
1424
     *
1425
     * @param Closure $callback
1426
     */
1427
    public function footer(Closure $callback)
1428
    {
1429
        call_user_func($callback, $this->builder()->getFooter());
1430
    }
1431
1432
    /**
1433
     * Get current resource route url.
1434
     *
1435
     * @param int $slice
1436
     *
1437
     * @return string
1438
     */
1439
    public function resource($slice = -2)
1440
    {
1441
        $segments = explode('/', trim(app('request')->getUri(), '/'));
1442
1443
        if ($slice != 0) {
1444
            $segments = array_slice($segments, 0, $slice);
1445
        }
1446
1447
        return implode('/', $segments);
1448
    }
1449
1450
    /**
1451
     * Render the form contents.
1452
     *
1453
     * @return string
1454
     */
1455
    public function render()
1456
    {
1457
        try {
1458
            return $this->builder->render();
1459
        } catch (\Exception $e) {
1460
            return Handler::renderException($e);
1461
        }
1462
    }
1463
1464
    /**
1465
     * Get or set input data.
1466
     *
1467
     * @param string $key
1468
     * @param null   $value
1469
     *
1470
     * @return array|mixed
1471
     */
1472
    public function input($key, $value = null)
1473
    {
1474
        if (is_null($value)) {
1475
            return array_get($this->inputs, $key);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1476
        }
1477
1478
        return array_set($this->inputs, $key, $value);
0 ignored issues
show
Deprecated Code introduced by
The function array_set() has been deprecated with message: Arr::set() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1479
    }
1480
1481
    /**
1482
     * Register builtin fields.
1483
     *
1484
     * @return void
1485
     */
1486
    public static function registerBuiltinFields()
1487
    {
1488
        $map = [
1489
            'button'         => Field\Button::class,
1490
            'checkbox'       => Field\Checkbox::class,
1491
            'color'          => Field\Color::class,
1492
            'currency'       => Field\Currency::class,
1493
            'date'           => Field\Date::class,
1494
            'dateRange'      => Field\DateRange::class,
1495
            'datetime'       => Field\Datetime::class,
1496
            'dateTimeRange'  => Field\DatetimeRange::class,
1497
            'datetimeRange'  => Field\DatetimeRange::class,
1498
            'decimal'        => Field\Decimal::class,
1499
            'display'        => Field\Display::class,
1500
            'divider'        => Field\Divide::class,
1501
            'divide'         => Field\Divide::class,
1502
            'embeds'         => Field\Embeds::class,
1503
            'editor'         => Field\Editor::class,
1504
            'email'          => Field\Email::class,
1505
            'file'           => Field\File::class,
1506
            'hasMany'        => Field\HasMany::class,
1507
            'hidden'         => Field\Hidden::class,
1508
            'id'             => Field\Id::class,
1509
            'image'          => Field\Image::class,
1510
            'ip'             => Field\Ip::class,
1511
            'map'            => Field\Map::class,
1512
            'mobile'         => Field\Mobile::class,
1513
            'month'          => Field\Month::class,
1514
            'multipleSelect' => Field\MultipleSelect::class,
1515
            'number'         => Field\Number::class,
1516
            'password'       => Field\Password::class,
1517
            'radio'          => Field\Radio::class,
1518
            'rate'           => Field\Rate::class,
1519
            'select'         => Field\Select::class,
1520
            'slider'         => Field\Slider::class,
1521
            'switch'         => Field\SwitchField::class,
1522
            'text'           => Field\Text::class,
1523
            'textarea'       => Field\Textarea::class,
1524
            'time'           => Field\Time::class,
1525
            'timeRange'      => Field\TimeRange::class,
1526
            'url'            => Field\Url::class,
1527
            'year'           => Field\Year::class,
1528
            'html'           => Field\Html::class,
1529
            'tags'           => Field\Tags::class,
1530
            'icon'           => Field\Icon::class,
1531
            'multipleFile'   => Field\MultipleFile::class,
1532
            'multipleImage'  => Field\MultipleImage::class,
1533
            'captcha'        => Field\Captcha::class,
1534
            'listbox'        => Field\Listbox::class,
1535
        ];
1536
1537
        foreach ($map as $abstract => $class) {
1538
            static::extend($abstract, $class);
1539
        }
1540
    }
1541
1542
    /**
1543
     * Register custom field.
1544
     *
1545
     * @param string $abstract
1546
     * @param string $class
1547
     *
1548
     * @return void
1549
     */
1550
    public static function extend($abstract, $class)
1551
    {
1552
        static::$availableFields[$abstract] = $class;
1553
    }
1554
1555
    /**
1556
     * Set form field alias.
1557
     *
1558
     * @param string $field
1559
     * @param string $alias
1560
     *
1561
     * @return void
1562
     */
1563
    public static function alias($field, $alias)
1564
    {
1565
        static::$fieldAlias[$alias] = $field;
1566
    }
1567
1568
    /**
1569
     * Remove registered field.
1570
     *
1571
     * @param array|string $abstract
1572
     */
1573
    public static function forget($abstract)
1574
    {
1575
        array_forget(static::$availableFields, $abstract);
0 ignored issues
show
Deprecated Code introduced by
The function array_forget() has been deprecated with message: Arr::forget() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1576
    }
1577
1578
    /**
1579
     * Find field class.
1580
     *
1581
     * @param string $method
1582
     *
1583
     * @return bool|mixed
1584
     */
1585
    public static function findFieldClass($method)
1586
    {
1587
        // If alias exists.
1588
        if (isset(static::$fieldAlias[$method])) {
1589
            $method = static::$fieldAlias[$method];
1590
        }
1591
1592
        $class = array_get(static::$availableFields, $method);
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1593
1594
        if (class_exists($class)) {
1595
            return $class;
1596
        }
1597
1598
        return false;
1599
    }
1600
1601
    /**
1602
     * Collect assets required by registered field.
1603
     *
1604
     * @return array
1605
     */
1606
    public static function collectFieldAssets()
1607
    {
1608
        if (!empty(static::$collectedAssets)) {
1609
            return static::$collectedAssets;
1610
        }
1611
1612
        $css = collect();
1613
        $js = collect();
1614
1615
        foreach (static::$availableFields as $field) {
1616
            if (!method_exists($field, 'getAssets')) {
1617
                continue;
1618
            }
1619
1620
            $assets = call_user_func([$field, 'getAssets']);
1621
1622
            $css->push(array_get($assets, 'css'));
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1623
            $js->push(array_get($assets, 'js'));
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1624
        }
1625
1626
        return static::$collectedAssets = [
1627
            'css' => $css->flatten()->unique()->filter()->toArray(),
1628
            'js'  => $js->flatten()->unique()->filter()->toArray(),
1629
        ];
1630
    }
1631
1632
    /**
1633
     * Getter.
1634
     *
1635
     * @param string $name
1636
     *
1637
     * @return array|mixed
1638
     */
1639
    public function __get($name)
1640
    {
1641
        return $this->input($name);
1642
    }
1643
1644
    /**
1645
     * Setter.
1646
     *
1647
     * @param string $name
1648
     * @param $value
1649
     */
1650
    public function __set($name, $value)
1651
    {
1652
        return array_set($this->inputs, $name, $value);
0 ignored issues
show
Deprecated Code introduced by
The function array_set() has been deprecated with message: Arr::set() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1653
    }
1654
1655
    /**
1656
     * Generate a Field object and add to form builder if Field exists.
1657
     *
1658
     * @param string $method
1659
     * @param array  $arguments
1660
     *
1661
     * @return Field
1662
     */
1663
    public function __call($method, $arguments)
1664
    {
1665
        if ($className = static::findFieldClass($method)) {
1666
            $column = array_get($arguments, 0, ''); //[0];
0 ignored issues
show
Deprecated Code introduced by
The function array_get() has been deprecated with message: Arr::get() should be used directly instead. Will be removed in Laravel 5.9.

This function has been deprecated. The supplier of the file has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the function will be removed from the class and what other function to use instead.

Loading history...
1667
1668
            $element = new $className($column, array_slice($arguments, 1));
1669
1670
            $this->pushField($element);
1671
1672
            return $element;
1673
        }
1674
1675
        admin_error('Error', "Field type [$method] does not exist.");
1676
1677
        return new Field\Nullable();
1678
    }
1679
}
1680