Completed
Push — master ( addbc6...6731dd )
by Song
02:29
created

Form::validationMessages()   A

Complexity

Conditions 6
Paths 8

Size

Total Lines 19

Duplication

Lines 9
Ratio 47.37 %

Importance

Changes 0
Metric Value
cc 6
nc 8
nop 1
dl 9
loc 19
rs 9.0111
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin;
4
5
use Closure;
6
use Encore\Admin\Exception\Handler;
7
use Encore\Admin\Form\Builder;
8
use Encore\Admin\Form\Field;
9
use Encore\Admin\Form\HasHooks;
10
use Encore\Admin\Form\Layout\Layout;
11
use Encore\Admin\Form\Row;
12
use Encore\Admin\Form\Tab;
13
use Encore\Admin\Traits\ShouldSnakeAttributes;
14
use Illuminate\Contracts\Support\Renderable;
15
use Illuminate\Database\Eloquent\Model;
16
use Illuminate\Database\Eloquent\Relations;
17
use Illuminate\Database\Eloquent\SoftDeletes;
18
use Illuminate\Http\Request;
19
use Illuminate\Support\Arr;
20
use Illuminate\Support\Facades\DB;
21
use Illuminate\Support\MessageBag;
22
use Illuminate\Support\Str;
23
use Illuminate\Validation\Validator;
24
use Spatie\EloquentSortable\Sortable;
25
use Symfony\Component\HttpFoundation\Response;
26
27
/**
28
 * Class Form.
29
 *
30
 * @method Field\Text           text($column, $label = '')
31
 * @method Field\Checkbox       checkbox($column, $label = '')
32
 * @method Field\Radio          radio($column, $label = '')
33
 * @method Field\Select         select($column, $label = '')
34
 * @method Field\MultipleSelect multipleSelect($column, $label = '')
35
 * @method Field\Textarea       textarea($column, $label = '')
36
 * @method Field\Hidden         hidden($column, $label = '')
37
 * @method Field\Id             id($column, $label = '')
38
 * @method Field\Ip             ip($column, $label = '')
39
 * @method Field\Url            url($column, $label = '')
40
 * @method Field\Color          color($column, $label = '')
41
 * @method Field\Email          email($column, $label = '')
42
 * @method Field\Mobile         mobile($column, $label = '')
43
 * @method Field\Slider         slider($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, $label = '', $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\Divider        divider($title = '')
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 = '', $callback)
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
 * @method Field\Table          table($column, $label, $builder)
72
 * @method Field\Timezone       timezone($column, $label = '')
73
 * @method Field\KeyValue       keyValue($column, $label = '')
74
 * @method Field\ListField      list($column, $label = '')
75
 */
76
class Form implements Renderable
77
{
78
    use HasHooks, ShouldSnakeAttributes;
79
80
    /**
81
     * Remove flag in `has many` form.
82
     */
83
    const REMOVE_FLAG_NAME = '_remove_';
84
85
    /**
86
     * Eloquent model of the form.
87
     *
88
     * @var Model
89
     */
90
    protected $model;
91
92
    /**
93
     * @var \Illuminate\Validation\Validator
94
     */
95
    protected $validator;
96
97
    /**
98
     * @var Builder
99
     */
100
    protected $builder;
101
102
    /**
103
     * Data for save to current model from input.
104
     *
105
     * @var array
106
     */
107
    protected $updates = [];
108
109
    /**
110
     * Data for save to model's relations from input.
111
     *
112
     * @var array
113
     */
114
    protected $relations = [];
115
116
    /**
117
     * Input data.
118
     *
119
     * @var array
120
     */
121
    protected $inputs = [];
122
123
    /**
124
     * @var Layout
125
     */
126
    protected $layout;
127
128
    /**
129
     * Available fields.
130
     *
131
     * @var array
132
     */
133
    public static $availableFields = [
134
        'button'         => Field\Button::class,
135
        'checkbox'       => Field\Checkbox::class,
136
        'color'          => Field\Color::class,
137
        'currency'       => Field\Currency::class,
138
        'date'           => Field\Date::class,
139
        'dateRange'      => Field\DateRange::class,
140
        'datetime'       => Field\Datetime::class,
141
        'dateTimeRange'  => Field\DatetimeRange::class,
142
        'datetimeRange'  => Field\DatetimeRange::class,
143
        'decimal'        => Field\Decimal::class,
144
        'display'        => Field\Display::class,
145
        'divider'        => Field\Divider::class,
146
        'embeds'         => Field\Embeds::class,
147
        'email'          => Field\Email::class,
148
        'file'           => Field\File::class,
149
        'hasMany'        => Field\HasMany::class,
150
        'hidden'         => Field\Hidden::class,
151
        'id'             => Field\Id::class,
152
        'image'          => Field\Image::class,
153
        'ip'             => Field\Ip::class,
154
        'mobile'         => Field\Mobile::class,
155
        'month'          => Field\Month::class,
156
        'multipleSelect' => Field\MultipleSelect::class,
157
        'number'         => Field\Number::class,
158
        'password'       => Field\Password::class,
159
        'radio'          => Field\Radio::class,
160
        'rate'           => Field\Rate::class,
161
        'select'         => Field\Select::class,
162
        'slider'         => Field\Slider::class,
163
        'switch'         => Field\SwitchField::class,
164
        'text'           => Field\Text::class,
165
        'textarea'       => Field\Textarea::class,
166
        'time'           => Field\Time::class,
167
        'timeRange'      => Field\TimeRange::class,
168
        'url'            => Field\Url::class,
169
        'year'           => Field\Year::class,
170
        'html'           => Field\Html::class,
171
        'tags'           => Field\Tags::class,
172
        'icon'           => Field\Icon::class,
173
        'multipleFile'   => Field\MultipleFile::class,
174
        'multipleImage'  => Field\MultipleImage::class,
175
        'captcha'        => Field\Captcha::class,
176
        'listbox'        => Field\Listbox::class,
177
        'table'          => Field\Table::class,
178
        'timezone'       => Field\Timezone::class,
179
        'keyValue'       => Field\KeyValue::class,
180
        'list'           => Field\ListField::class,
181
    ];
182
183
    /**
184
     * Form field alias.
185
     *
186
     * @var array
187
     */
188
    public static $fieldAlias = [];
189
190
    /**
191
     * Ignored saving fields.
192
     *
193
     * @var array
194
     */
195
    protected $ignored = [];
196
197
    /**
198
     * Collected field assets.
199
     *
200
     * @var array
201
     */
202
    protected static $collectedAssets = [];
203
204
    /**
205
     * @var Form\Tab
206
     */
207
    protected $tab = null;
208
209
    /**
210
     * Field rows in form.
211
     *
212
     * @var array
213
     */
214
    public $rows = [];
215
216
    /**
217
     * @var bool
218
     */
219
    protected $isSoftDeletes = false;
220
221
    /**
222
     * Initialization closure array.
223
     *
224
     * @var []Closure
225
     */
226
    protected static $initCallbacks;
227
228
    /**
229
     * Create a new form instance.
230
     *
231
     * @param $model
232
     * @param \Closure $callback
233
     */
234
    public function __construct($model, Closure $callback = null)
235
    {
236
        $this->model = $model;
237
238
        $this->builder = new Builder($this);
239
240
        $this->initLayout();
241
242
        if ($callback instanceof Closure) {
243
            $callback($this);
244
        }
245
246
        $this->isSoftDeletes = in_array(SoftDeletes::class, class_uses_deep($this->model), true);
247
248
        $this->callInitCallbacks();
249
    }
250
251
    /**
252
     * Initialize with user pre-defined default disables, etc.
253
     *
254
     * @param Closure $callback
255
     */
256
    public static function init(Closure $callback = null)
257
    {
258
        static::$initCallbacks[] = $callback;
259
    }
260
261
    /**
262
     * Call the initialization closure array in sequence.
263
     */
264
    protected function callInitCallbacks()
265
    {
266
        if (empty(static::$initCallbacks)) {
267
            return;
268
        }
269
270
        foreach (static::$initCallbacks as $callback) {
271
            $callback($this);
272
        }
273
    }
274
275
    /**
276
     * @param Field $field
277
     *
278
     * @return $this
279
     */
280
    public function pushField(Field $field): self
281
    {
282
        $field->setForm($this);
283
284
        $width = $this->builder->getWidth();
285
        $field->setWidth($width['field'], $width['label']);
286
287
        $this->builder->fields()->push($field);
288
        $this->layout->addField($field);
289
290
        return $this;
291
    }
292
293
    /**
294
     * @return Model
295
     */
296
    public function model(): Model
297
    {
298
        return $this->model;
299
    }
300
301
    /**
302
     * @return Builder
303
     */
304
    public function builder(): Builder
305
    {
306
        return $this->builder;
307
    }
308
309
    /**
310
     * Generate a edit form.
311
     *
312
     * @param $id
313
     *
314
     * @return $this
315
     */
316
    public function edit($id): self
317
    {
318
        $this->builder->setMode(Builder::MODE_EDIT);
319
        $this->builder->setResourceId($id);
320
321
        $this->setFieldValue($id);
322
323
        return $this;
324
    }
325
326
    /**
327
     * Use tab to split form.
328
     *
329
     * @param string  $title
330
     * @param Closure $content
331
     * @param bool    $active
332
     *
333
     * @return $this
334
     */
335
    public function tab($title, Closure $content, bool $active = false): self
336
    {
337
        $this->setTab()->append($title, $content, $active);
338
339
        return $this;
340
    }
341
342
    /**
343
     * Get Tab instance.
344
     *
345
     * @return Tab
346
     */
347
    public function getTab()
348
    {
349
        return $this->tab;
350
    }
351
352
    /**
353
     * Set Tab instance.
354
     *
355
     * @return Tab
356
     */
357
    public function setTab(): Tab
358
    {
359
        if ($this->tab === null) {
360
            $this->tab = new Tab($this);
361
        }
362
363
        return $this->tab;
364
    }
365
366
    /**
367
     * Destroy data entity and remove files.
368
     *
369
     * @param $id
370
     *
371
     * @return mixed
372
     */
373
    public function destroy($id)
374
    {
375
        try {
376
            if (($ret = $this->callDeleting($id)) instanceof Response) {
377
                return $ret;
378
            }
379
380
            collect(explode(',', $id))->filter()->each(function ($id) {
381
                $builder = $this->model()->newQuery();
382
383
                if ($this->isSoftDeletes) {
384
                    $builder = $builder->withTrashed();
385
                }
386
387
                $model = $builder->with($this->getRelations())->findOrFail($id);
388
389
                if ($this->isSoftDeletes && $model->trashed()) {
390
                    $this->deleteFiles($model, true);
391
                    $model->forceDelete();
392
393
                    return;
394
                }
395
396
                $this->deleteFiles($model);
397
                $model->delete();
398
            });
399
400
            if (($ret = $this->callDeleted()) instanceof Response) {
401
                return $ret;
402
            }
403
404
            $response = [
405
                'status'  => true,
406
                'message' => trans('admin.delete_succeeded'),
407
            ];
408
        } catch (\Exception $exception) {
409
            $response = [
410
                'status'  => false,
411
                'message' => $exception->getMessage() ?: trans('admin.delete_failed'),
412
            ];
413
        }
414
415
        return response()->json($response);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
416
    }
417
418
    /**
419
     * Remove files in record.
420
     *
421
     * @param Model $model
422
     * @param bool  $forceDelete
423
     */
424
    protected function deleteFiles(Model $model, $forceDelete = false)
425
    {
426
        // If it's a soft delete, the files in the data will not be deleted.
427
        if (!$forceDelete && $this->isSoftDeletes) {
428
            return;
429
        }
430
431
        $data = $model->toArray();
432
433
        $this->builder->fields()->filter(function ($field) {
434
            return $field instanceof Field\File;
435
        })->each(function (Field\File $file) use ($data) {
436
            $file->setOriginal($data);
437
438
            $file->destroy();
439
        });
440
    }
441
442
    /**
443
     * Store a new record.
444
     *
445
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
446
     */
447
    public function store()
448
    {
449
        $data = \request()->all();
450
451
        // Handle validation errors.
452
        if ($validationMessages = $this->validationMessages($data)) {
453
            return $this->responseValidationError($validationMessages);
454
        }
455
456
        if (($response = $this->prepare($data)) instanceof Response) {
457
            return $response;
458
        }
459
460 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...
461
            $inserts = $this->prepareInsert($this->updates);
462
463
            foreach ($inserts as $column => $value) {
464
                $this->model->setAttribute($column, $value);
465
            }
466
467
            $this->model->save();
468
469
            $this->updateRelation($this->relations);
470
        });
471
472
        if (($response = $this->callSaved()) instanceof Response) {
473
            return $response;
474
        }
475
476
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
477
            return $response;
478
        }
479
480
        return $this->redirectAfterStore();
481
    }
482
483
    /**
484
     * @param MessageBag $message
485
     *
486
     * @return $this|\Illuminate\Http\JsonResponse
487
     */
488
    protected function responseValidationError(MessageBag $message)
489
    {
490
        if (\request()->ajax() && !\request()->pjax()) {
491
            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...
492
                'status'     => false,
493
                'validation' => $message,
494
                'message'    => $message->first(),
495
            ]);
496
        }
497
498
        return back()->withInput()->withErrors($message);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return back()->withInput()->withErrors($message); (Illuminate\Http\RedirectResponse) is incompatible with the return type documented by Encore\Admin\Form::responseValidationError of type Encore\Admin\Form|Illuminate\Http\JsonResponse.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
499
    }
500
501
    /**
502
     * Get ajax response.
503
     *
504
     * @param string $message
505
     *
506
     * @return bool|\Illuminate\Http\JsonResponse
507
     */
508
    protected function ajaxResponse($message)
509
    {
510
        $request = Request::capture();
511
512
        // ajax but not pjax
513
        if ($request->ajax() && !$request->pjax()) {
514
            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...
515
                'status'  => true,
516
                'message' => $message,
517
            ]);
518
        }
519
520
        return false;
521
    }
522
523
    /**
524
     * Prepare input data for insert or update.
525
     *
526
     * @param array $data
527
     *
528
     * @return mixed
529
     */
530
    protected function prepare($data = [])
531
    {
532
        if (($response = $this->callSubmitted()) instanceof Response) {
533
            return $response;
534
        }
535
536
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
537
538
        if (($response = $this->callSaving()) instanceof Response) {
539
            return $response;
540
        }
541
542
        $this->relations = $this->getRelationInputs($this->inputs);
543
544
        $this->updates = Arr::except($this->inputs, array_keys($this->relations));
545
    }
546
547
    /**
548
     * Remove ignored fields from input.
549
     *
550
     * @param array $input
551
     *
552
     * @return array
553
     */
554
    protected function removeIgnoredFields($input): array
555
    {
556
        Arr::forget($input, $this->ignored);
557
558
        return $input;
559
    }
560
561
    /**
562
     * Get inputs for relations.
563
     *
564
     * @param array $inputs
565
     *
566
     * @return array
567
     */
568
    protected function getRelationInputs($inputs = []): array
569
    {
570
        $relations = [];
571
572
        foreach ($inputs as $column => $value) {
573
            if (method_exists($this->model, $column) ||
574
                method_exists($this->model, $column = Str::camel($column))) {
575
                $relation = call_user_func([$this->model, $column]);
576
577
                if ($relation instanceof Relations\Relation) {
578
                    $relations[$column] = $value;
579
                }
580
            }
581
        }
582
583
        return $relations;
584
    }
585
586
    /**
587
     * Handle update.
588
     *
589
     * @param int  $id
590
     * @param null $data
591
     *
592
     * @return bool|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|null|Response
593
     */
594
    public function update($id, $data = null)
595
    {
596
        $data = ($data) ?: request()->all();
597
598
        $isEditable = $this->isEditable($data);
599
600
        if (($data = $this->handleColumnUpdates($id, $data)) instanceof Response) {
601
            return $data;
602
        }
603
604
        /* @var Model $this->model */
605
        $builder = $this->model();
606
607
        if ($this->isSoftDeletes) {
608
            $builder = $builder->withTrashed();
609
        }
610
611
        $this->model = $builder->with($this->getRelations())->findOrFail($id);
612
613
        $this->setFieldOriginalValue();
614
615
        // Handle validation errors.
616
        if ($validationMessages = $this->validationMessages($data)) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 600 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...
617
            if (!$isEditable) {
618
                return back()->withInput()->withErrors($validationMessages);
619
            }
620
621
            return response()->json(['errors' => Arr::dot($validationMessages->getMessages())], 422);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
622
        }
623
624
        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 600 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...
625
            return $response;
626
        }
627
628 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...
629
            $updates = $this->prepareUpdate($this->updates);
630
631
            foreach ($updates as $column => $value) {
632
                /* @var Model $this->model */
633
                $this->model->setAttribute($column, $value);
634
            }
635
636
            $this->model->save();
637
638
            $this->updateRelation($this->relations);
639
        });
640
641
        if (($result = $this->callSaved()) instanceof Response) {
642
            return $result;
643
        }
644
645
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
646
            return $response;
647
        }
648
649
        return $this->redirectAfterUpdate($id);
650
    }
651
652
    /**
653
     * Get RedirectResponse after store.
654
     *
655
     * @return \Illuminate\Http\RedirectResponse
656
     */
657
    protected function redirectAfterStore()
658
    {
659
        $resourcesPath = $this->resource(0);
660
661
        $key = $this->model->getKey();
662
663
        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 663 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
664
    }
665
666
    /**
667
     * Get RedirectResponse after update.
668
     *
669
     * @param mixed $key
670
     *
671
     * @return \Illuminate\Http\RedirectResponse
672
     */
673
    protected function redirectAfterUpdate($key)
674
    {
675
        $resourcesPath = $this->resource(-1);
676
677
        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 677 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
678
    }
679
680
    /**
681
     * Get RedirectResponse after data saving.
682
     *
683
     * @param string $resourcesPath
684
     * @param string $key
685
     *
686
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
687
     */
688
    protected function redirectAfterSaving($resourcesPath, $key)
689
    {
690
        if (request('after-save') == 1) {
691
            // continue editing
692
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
693
        } elseif (request('after-save') == 2) {
694
            // continue creating
695
            $url = rtrim($resourcesPath, '/').'/create';
696
        } elseif (request('after-save') == 3) {
697
            // view resource
698
            $url = rtrim($resourcesPath, '/')."/{$key}";
699
        } else {
700
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
701
        }
702
703
        admin_toastr(trans('admin.save_succeeded'));
704
705
        return redirect($url);
706
    }
707
708
    /**
709
     * Check if request is from editable.
710
     *
711
     * @param array $input
712
     *
713
     * @return bool
714
     */
715
    protected function isEditable(array $input = []): bool
716
    {
717
        return array_key_exists('_editable', $input);
718
    }
719
720
    /**
721
     * Handle updates for single column.
722
     *
723
     * @param int   $id
724
     * @param array $data
725
     *
726
     * @return array|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|Response
727
     */
728
    protected function handleColumnUpdates($id, $data)
729
    {
730
        $data = $this->handleEditable($data);
731
732
        $data = $this->handleFileDelete($data);
733
734
        $data = $this->handleFileSort($data);
735
736
        if ($this->handleOrderable($id, $data)) {
737
            return response([
738
                'status'  => true,
739
                'message' => trans('admin.update_succeeded'),
740
            ]);
741
        }
742
743
        return $data;
744
    }
745
746
    /**
747
     * Handle editable update.
748
     *
749
     * @param array $input
750
     *
751
     * @return array
752
     */
753
    protected function handleEditable(array $input = []): array
754
    {
755
        if (array_key_exists('_editable', $input)) {
756
            $name = $input['name'];
757
            $value = $input['value'];
758
759
            Arr::forget($input, ['pk', 'value', 'name']);
760
            Arr::set($input, $name, $value);
761
        }
762
763
        return $input;
764
    }
765
766
    /**
767
     * @param array $input
768
     *
769
     * @return array
770
     */
771
    protected function handleFileDelete(array $input = []): array
772
    {
773
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
774
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
775
            unset($input['key']);
776
        }
777
778
        request()->replace($input);
779
780
        return $input;
781
    }
782
783
    /**
784
     * @param array $input
785
     *
786
     * @return array
787
     */
788
    protected function handleFileSort(array $input = []): array
789
    {
790
        if (!array_key_exists(Field::FILE_SORT_FLAG, $input)) {
791
            return $input;
792
        }
793
794
        $sorts = array_filter($input[Field::FILE_SORT_FLAG]);
795
796
        if (empty($sorts)) {
797
            return $input;
798
        }
799
800
        foreach ($sorts as $column => $order) {
801
            $input[$column] = $order;
802
        }
803
804
        request()->replace($input);
805
806
        return $input;
807
    }
808
809
    /**
810
     * Handle orderable update.
811
     *
812
     * @param int   $id
813
     * @param array $input
814
     *
815
     * @return bool
816
     */
817
    protected function handleOrderable($id, array $input = [])
818
    {
819
        if (array_key_exists('_orderable', $input)) {
820
            $model = $this->model->find($id);
821
822
            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...
823
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
824
825
                return true;
826
            }
827
        }
828
829
        return false;
830
    }
831
832
    /**
833
     * Update relation data.
834
     *
835
     * @param array $relationsData
836
     *
837
     * @return void
838
     */
839
    protected function updateRelation($relationsData)
840
    {
841
        foreach ($relationsData as $name => $values) {
842
            if (!method_exists($this->model, $name)) {
843
                continue;
844
            }
845
846
            $relation = $this->model->$name();
847
848
            $oneToOneRelation = $relation instanceof Relations\HasOne
849
                || $relation instanceof Relations\MorphOne
850
                || $relation instanceof Relations\BelongsTo;
851
852
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
853
854
            if (empty($prepared)) {
855
                continue;
856
            }
857
858
            switch (true) {
859
                case $relation instanceof Relations\BelongsToMany:
860
                case $relation instanceof Relations\MorphToMany:
861
                    if (isset($prepared[$name])) {
862
                        $relation->sync($prepared[$name]);
863
                    }
864
                    break;
865
                case $relation instanceof Relations\HasOne:
866
867
                    $related = $this->model->$name;
868
869
                    // if related is empty
870
                    if (is_null($related)) {
871
                        $related = $relation->getRelated();
872
                        $qualifiedParentKeyName = $relation->getQualifiedParentKeyName();
873
                        $localKey = Arr::last(explode('.', $qualifiedParentKeyName));
874
                        $related->{$relation->getForeignKeyName()} = $this->model->{$localKey};
875
                    }
876
877
                    foreach ($prepared[$name] as $column => $value) {
878
                        $related->setAttribute($column, $value);
879
                    }
880
881
                    $related->save();
882
                    break;
883
                case $relation instanceof Relations\BelongsTo:
884
                case $relation instanceof Relations\MorphTo:
885
886
                    $parent = $this->model->$name;
887
888
                    // if related is empty
889
                    if (is_null($parent)) {
890
                        $parent = $relation->getRelated();
891
                    }
892
893
                    foreach ($prepared[$name] as $column => $value) {
894
                        $parent->setAttribute($column, $value);
895
                    }
896
897
                    $parent->save();
898
899
                    // When in creating, associate two models
900
                    $foreignKeyMethod = version_compare(app()->version(), '5.8.0', '<') ? 'getForeignKey' : 'getForeignKeyName';
901
                    if (!$this->model->{$relation->{$foreignKeyMethod}()}) {
902
                        $this->model->{$relation->{$foreignKeyMethod}()} = $parent->getKey();
903
904
                        $this->model->save();
905
                    }
906
907
                    break;
908
                case $relation instanceof Relations\MorphOne:
909
                    $related = $this->model->$name;
910
                    if ($related === null) {
911
                        $related = $relation->make();
912
                    }
913
                    foreach ($prepared[$name] as $column => $value) {
914
                        $related->setAttribute($column, $value);
915
                    }
916
                    $related->save();
917
                    break;
918
                case $relation instanceof Relations\HasMany:
919
                case $relation instanceof Relations\MorphMany:
920
921
                    foreach ($prepared[$name] as $related) {
922
                        /** @var Relations\Relation $relation */
923
                        $relation = $this->model()->$name();
924
925
                        $keyName = $relation->getRelated()->getKeyName();
926
927
                        $instance = $relation->findOrNew(Arr::get($related, $keyName));
928
929
                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
930
                            $instance->delete();
931
932
                            continue;
933
                        }
934
935
                        Arr::forget($related, static::REMOVE_FLAG_NAME);
936
937
                        $instance->fill($related);
938
939
                        $instance->save();
940
                    }
941
942
                    break;
943
            }
944
        }
945
    }
946
947
    /**
948
     * Prepare input data for update.
949
     *
950
     * @param array $updates
951
     * @param bool  $oneToOneRelation If column is one-to-one relation.
952
     *
953
     * @return array
954
     */
955
    protected function prepareUpdate(array $updates, $oneToOneRelation = false): array
956
    {
957
        $prepared = [];
958
959
        /** @var Field $field */
960
        foreach ($this->builder->fields() as $field) {
961
            $columns = $field->column();
962
963
            // If column not in input array data, then continue.
964
            if (!Arr::has($updates, $columns)) {
965
                continue;
966
            }
967
968
            if ($this->isInvalidColumn($columns, $oneToOneRelation || $field->isJsonType)) {
969
                continue;
970
            }
971
972
            $value = $this->getDataByColumn($updates, $columns);
973
974
            $value = $field->prepare($value);
975
976 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...
977
                foreach ($columns as $name => $column) {
978
                    Arr::set($prepared, $column, $value[$name]);
979
                }
980
            } elseif (is_string($columns)) {
981
                Arr::set($prepared, $columns, $value);
982
            }
983
        }
984
985
        return $prepared;
986
    }
987
988
    /**
989
     * @param string|array $columns
990
     * @param bool         $containsDot
991
     *
992
     * @return bool
993
     */
994
    protected function isInvalidColumn($columns, $containsDot = false): bool
995
    {
996
        foreach ((array) $columns as $column) {
997
            if ((!$containsDot && Str::contains($column, '.')) ||
998
                ($containsDot && !Str::contains($column, '.'))) {
999
                return true;
1000
            }
1001
        }
1002
1003
        return false;
1004
    }
1005
1006
    /**
1007
     * Prepare input data for insert.
1008
     *
1009
     * @param $inserts
1010
     *
1011
     * @return array
1012
     */
1013
    protected function prepareInsert($inserts): array
1014
    {
1015
        if ($this->isHasOneRelation($inserts)) {
1016
            $inserts = Arr::dot($inserts);
1017
        }
1018
1019
        foreach ($inserts as $column => $value) {
1020
            if (($field = $this->getFieldByColumn($column)) === null) {
1021
                unset($inserts[$column]);
1022
                continue;
1023
            }
1024
1025
            $inserts[$column] = $field->prepare($value);
1026
        }
1027
1028
        $prepared = [];
1029
1030
        foreach ($inserts as $key => $value) {
1031
            Arr::set($prepared, $key, $value);
1032
        }
1033
1034
        return $prepared;
1035
    }
1036
1037
    /**
1038
     * Is input data is has-one relation.
1039
     *
1040
     * @param array $inserts
1041
     *
1042
     * @return bool
1043
     */
1044
    protected function isHasOneRelation($inserts): bool
1045
    {
1046
        $first = current($inserts);
1047
1048
        if (!is_array($first)) {
1049
            return false;
1050
        }
1051
1052
        if (is_array(current($first))) {
1053
            return false;
1054
        }
1055
1056
        return Arr::isAssoc($first);
1057
    }
1058
1059
    /**
1060
     * Ignore fields to save.
1061
     *
1062
     * @param string|array $fields
1063
     *
1064
     * @return $this
1065
     */
1066
    public function ignore($fields): self
1067
    {
1068
        $this->ignored = array_merge($this->ignored, (array) $fields);
1069
1070
        return $this;
1071
    }
1072
1073
    /**
1074
     * @param array        $data
1075
     * @param string|array $columns
1076
     *
1077
     * @return array|mixed
1078
     */
1079 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...
1080
    {
1081
        if (is_string($columns)) {
1082
            return Arr::get($data, $columns);
1083
        }
1084
1085
        if (is_array($columns)) {
1086
            $value = [];
1087
            foreach ($columns as $name => $column) {
1088
                if (!Arr::has($data, $column)) {
1089
                    continue;
1090
                }
1091
                $value[$name] = Arr::get($data, $column);
1092
            }
1093
1094
            return $value;
1095
        }
1096
    }
1097
1098
    /**
1099
     * Find field object by column.
1100
     *
1101
     * @param $column
1102
     *
1103
     * @return mixed
1104
     */
1105
    protected function getFieldByColumn($column)
1106
    {
1107
        return $this->builder->fields()->first(
1108
            function (Field $field) use ($column) {
1109
                if (is_array($field->column())) {
1110
                    return in_array($column, $field->column());
1111
                }
1112
1113
                return $field->column() == $column;
1114
            }
1115
        );
1116
    }
1117
1118
    /**
1119
     * Set original data for each field.
1120
     *
1121
     * @return void
1122
     */
1123
    protected function setFieldOriginalValue()
1124
    {
1125
        $values = $this->model->toArray();
1126
1127
        $this->builder->fields()->each(function (Field $field) use ($values) {
1128
            $field->setOriginal($values);
1129
        });
1130
    }
1131
1132
    /**
1133
     * Set all fields value in form.
1134
     *
1135
     * @param $id
1136
     *
1137
     * @return void
1138
     */
1139
    protected function setFieldValue($id)
1140
    {
1141
        $relations = $this->getRelations();
1142
1143
        $builder = $this->model();
1144
1145
        if ($this->isSoftDeletes) {
1146
            $builder = $builder->withTrashed();
1147
        }
1148
1149
        $this->model = $builder->with($relations)->findOrFail($id);
1150
1151
        $this->callEditing();
1152
1153
        $data = $this->model->toArray();
1154
1155
        $this->builder->fields()->each(function (Field $field) use ($data) {
1156
            if (!in_array($field->column(), $this->ignored, true)) {
1157
                $field->fill($data);
1158
            }
1159
        });
1160
    }
1161
1162
    /**
1163
     * Add a fieldset to form.
1164
     *
1165
     * @param string  $title
1166
     * @param Closure $setCallback
1167
     *
1168
     * @return Field\Fieldset
1169
     */
1170 View Code Duplication
    public function fieldset(string $title, Closure $setCallback)
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...
1171
    {
1172
        $fieldset = new Field\Fieldset();
1173
1174
        $this->html($fieldset->start($title))->plain();
1175
1176
        $setCallback($this);
1177
1178
        $this->html($fieldset->end())->plain();
1179
1180
        return $fieldset;
1181
    }
1182
1183
    /**
1184
     * Get validation messages.
1185
     *
1186
     * @param array $input
1187
     *
1188
     * @return MessageBag|bool
1189
     */
1190
    public function validationMessages($input)
1191
    {
1192
        $failedValidators = [];
1193
1194
        /** @var Field $field */
1195 View Code Duplication
        foreach ($this->builder->fields() as $field) {
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...
1196
            if (!$validator = $field->getValidator($input)) {
1197
                continue;
1198
            }
1199
1200
            if (($validator instanceof Validator) && !$validator->passes()) {
1201
                $failedValidators[] = $validator;
1202
            }
1203
        }
1204
1205
        $message = $this->mergeValidationMessages($failedValidators);
1206
1207
        return $message->any() ? $message : false;
1208
    }
1209
1210
    /**
1211
     * Merge validation messages from input validators.
1212
     *
1213
     * @param \Illuminate\Validation\Validator[] $validators
1214
     *
1215
     * @return MessageBag
1216
     */
1217
    protected function mergeValidationMessages($validators): MessageBag
1218
    {
1219
        $messageBag = new MessageBag();
1220
1221
        foreach ($validators as $validator) {
1222
            $messageBag = $messageBag->merge($validator->messages());
1223
        }
1224
1225
        return $messageBag;
1226
    }
1227
1228
    /**
1229
     * Get all relations of model from callable.
1230
     *
1231
     * @return array
1232
     */
1233
    public function getRelations(): array
1234
    {
1235
        $relations = $columns = [];
1236
1237
        /** @var Field $field */
1238
        foreach ($this->builder->fields() as $field) {
1239
            $columns[] = $field->column();
1240
        }
1241
1242
        foreach (Arr::flatten($columns) as $column) {
1243
            if (Str::contains($column, '.')) {
1244
                list($relation) = explode('.', $column);
1245
1246
                if (method_exists($this->model, $relation) &&
1247
                    $this->model->$relation() instanceof Relations\Relation
1248
                ) {
1249
                    $relations[] = $relation;
1250
                }
1251
            } elseif (method_exists($this->model, $column) &&
1252
                !method_exists(Model::class, $column)
1253
            ) {
1254
                $relations[] = $column;
1255
            }
1256
        }
1257
1258
        return array_unique($relations);
1259
    }
1260
1261
    /**
1262
     * Set action for form.
1263
     *
1264
     * @param string $action
1265
     *
1266
     * @return $this
1267
     */
1268
    public function setAction($action): self
1269
    {
1270
        $this->builder()->setAction($action);
1271
1272
        return $this;
1273
    }
1274
1275
    /**
1276
     * Set field and label width in current form.
1277
     *
1278
     * @param int $fieldWidth
1279
     * @param int $labelWidth
1280
     *
1281
     * @return $this
1282
     */
1283
    public function setWidth($fieldWidth = 8, $labelWidth = 2): self
1284
    {
1285
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1286
            /* @var Field $field  */
1287
            $field->setWidth($fieldWidth, $labelWidth);
1288
        });
1289
1290
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1291
1292
        return $this;
1293
    }
1294
1295
    /**
1296
     * Set view for form.
1297
     *
1298
     * @param string $view
1299
     *
1300
     * @return $this
1301
     */
1302
    public function setView($view): self
1303
    {
1304
        $this->builder()->setView($view);
1305
1306
        return $this;
1307
    }
1308
1309
    /**
1310
     * Set title for form.
1311
     *
1312
     * @param string $title
1313
     *
1314
     * @return $this
1315
     */
1316
    public function setTitle($title = ''): self
1317
    {
1318
        $this->builder()->setTitle($title);
1319
1320
        return $this;
1321
    }
1322
1323
    /**
1324
     * Add a row in form.
1325
     *
1326
     * @param Closure $callback
1327
     *
1328
     * @return $this
1329
     */
1330
    public function row(Closure $callback): self
1331
    {
1332
        $this->rows[] = new Row($callback, $this);
1333
1334
        return $this;
1335
    }
1336
1337
    /**
1338
     * Tools setting for form.
1339
     *
1340
     * @param Closure $callback
1341
     */
1342
    public function tools(Closure $callback)
1343
    {
1344
        $callback->call($this, $this->builder->getTools());
1345
    }
1346
1347
    /**
1348
     * @param Closure|null $callback
1349
     *
1350
     * @return Form\Tools
1351
     */
1352 View Code Duplication
    public function header(Closure $callback = null)
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...
1353
    {
1354
        if (func_num_args() === 0) {
1355
            return $this->builder->getTools();
1356
        }
1357
1358
        $callback->call($this, $this->builder->getTools());
0 ignored issues
show
Bug introduced by
It seems like $callback is not always an object, but can also be of type null. Maybe add an additional type check?

If a variable is not always an object, we recommend to add an additional type check to ensure your method call is safe:

function someFunction(A $objectMaybe = null)
{
    if ($objectMaybe instanceof A) {
        $objectMaybe->doSomething();
    }
}
Loading history...
1359
    }
1360
1361
    /**
1362
     * Indicates if current form page is creating.
1363
     *
1364
     * @return bool
1365
     */
1366
    public function isCreating(): bool
1367
    {
1368
        return Str::endsWith(\request()->route()->getName(), ['.create', '.store']);
1369
    }
1370
1371
    /**
1372
     * Indicates if current form page is editing.
1373
     *
1374
     * @return bool
1375
     */
1376
    public function isEditing(): bool
1377
    {
1378
        return Str::endsWith(\request()->route()->getName(), '.edit', '.update');
0 ignored issues
show
Unused Code introduced by
The call to Str::endsWith() has too many arguments starting with '.update'.

This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.

If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress.

In this case you can add the @ignore PhpDoc annotation to the duplicate definition and it will be ignored.

Loading history...
1379
    }
1380
1381
    /**
1382
     * Disable form submit.
1383
     *
1384
     * @param bool $disable
1385
     *
1386
     * @return $this
1387
     *
1388
     * @deprecated
1389
     */
1390
    public function disableSubmit(bool $disable = true): self
1391
    {
1392
        $this->builder()->getFooter()->disableSubmit($disable);
1393
1394
        return $this;
1395
    }
1396
1397
    /**
1398
     * Disable form reset.
1399
     *
1400
     * @param bool $disable
1401
     *
1402
     * @return $this
1403
     *
1404
     * @deprecated
1405
     */
1406
    public function disableReset(bool $disable = true): self
1407
    {
1408
        $this->builder()->getFooter()->disableReset($disable);
1409
1410
        return $this;
1411
    }
1412
1413
    /**
1414
     * Disable View Checkbox on footer.
1415
     *
1416
     * @param bool $disable
1417
     *
1418
     * @return $this
1419
     */
1420
    public function disableViewCheck(bool $disable = true): self
1421
    {
1422
        $this->builder()->getFooter()->disableViewCheck($disable);
1423
1424
        return $this;
1425
    }
1426
1427
    /**
1428
     * Disable Editing Checkbox on footer.
1429
     *
1430
     * @param bool $disable
1431
     *
1432
     * @return $this
1433
     */
1434
    public function disableEditingCheck(bool $disable = true): self
1435
    {
1436
        $this->builder()->getFooter()->disableEditingCheck($disable);
1437
1438
        return $this;
1439
    }
1440
1441
    /**
1442
     * Disable Creating Checkbox on footer.
1443
     *
1444
     * @param bool $disable
1445
     *
1446
     * @return $this
1447
     */
1448
    public function disableCreatingCheck(bool $disable = true): self
1449
    {
1450
        $this->builder()->getFooter()->disableCreatingCheck($disable);
1451
1452
        return $this;
1453
    }
1454
1455
    /**
1456
     * Footer setting for form.
1457
     *
1458
     * @param Closure $callback
1459
     *
1460
     * @return \Encore\Admin\Form\Footer
1461
     */
1462 View Code Duplication
    public function footer(Closure $callback = null)
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...
1463
    {
1464
        if (func_num_args() === 0) {
1465
            return $this->builder()->getFooter();
1466
        }
1467
1468
        $callback($this->builder()->getFooter());
1469
    }
1470
1471
    /**
1472
     * Get current resource route url.
1473
     *
1474
     * @param int $slice
1475
     *
1476
     * @return string
1477
     */
1478
    public function resource($slice = -2): string
1479
    {
1480
        $segments = explode('/', trim(\request()->getUri(), '/'));
1481
1482
        if ($slice !== 0) {
1483
            $segments = array_slice($segments, 0, $slice);
1484
        }
1485
1486
        return implode('/', $segments);
1487
    }
1488
1489
    /**
1490
     * Render the form contents.
1491
     *
1492
     * @return string
1493
     */
1494
    public function render()
1495
    {
1496
        try {
1497
            return $this->builder->render();
1498
        } catch (\Exception $e) {
1499
            return Handler::renderException($e);
1500
        }
1501
    }
1502
1503
    /**
1504
     * Get or set input data.
1505
     *
1506
     * @param string $key
1507
     * @param null   $value
1508
     *
1509
     * @return array|mixed
1510
     */
1511
    public function input($key, $value = null)
1512
    {
1513
        if ($value === null) {
1514
            return Arr::get($this->inputs, $key);
1515
        }
1516
1517
        return Arr::set($this->inputs, $key, $value);
1518
    }
1519
1520
    /**
1521
     * Register custom field.
1522
     *
1523
     * @param string $abstract
1524
     * @param string $class
1525
     *
1526
     * @return void
1527
     */
1528
    public static function extend($abstract, $class)
1529
    {
1530
        static::$availableFields[$abstract] = $class;
1531
    }
1532
1533
    /**
1534
     * Set form field alias.
1535
     *
1536
     * @param string $field
1537
     * @param string $alias
1538
     *
1539
     * @return void
1540
     */
1541
    public static function alias($field, $alias)
1542
    {
1543
        static::$fieldAlias[$alias] = $field;
1544
    }
1545
1546
    /**
1547
     * Remove registered field.
1548
     *
1549
     * @param array|string $abstract
1550
     */
1551
    public static function forget($abstract)
1552
    {
1553
        Arr::forget(static::$availableFields, $abstract);
1554
    }
1555
1556
    /**
1557
     * Find field class.
1558
     *
1559
     * @param string $method
1560
     *
1561
     * @return bool|mixed
1562
     */
1563
    public static function findFieldClass($method)
1564
    {
1565
        // If alias exists.
1566
        if (isset(static::$fieldAlias[$method])) {
1567
            $method = static::$fieldAlias[$method];
1568
        }
1569
1570
        $class = Arr::get(static::$availableFields, $method);
1571
1572
        if (class_exists($class)) {
1573
            return $class;
1574
        }
1575
1576
        return false;
1577
    }
1578
1579
    /**
1580
     * Collect assets required by registered field.
1581
     *
1582
     * @return array
1583
     */
1584
    public static function collectFieldAssets(): array
1585
    {
1586
        if (!empty(static::$collectedAssets)) {
1587
            return static::$collectedAssets;
1588
        }
1589
1590
        $css = collect();
1591
        $js = collect();
1592
1593
        foreach (static::$availableFields as $field) {
1594
            if (!method_exists($field, 'getAssets')) {
1595
                continue;
1596
            }
1597
1598
            $assets = call_user_func([$field, 'getAssets']);
1599
1600
            $css->push(Arr::get($assets, 'css'));
1601
            $js->push(Arr::get($assets, 'js'));
1602
        }
1603
1604
        return static::$collectedAssets = [
1605
            'css' => $css->flatten()->unique()->filter()->toArray(),
1606
            'js'  => $js->flatten()->unique()->filter()->toArray(),
1607
        ];
1608
    }
1609
1610
    /**
1611
     * Add a new layout column.
1612
     *
1613
     * @param int      $width
1614
     * @param \Closure $closure
1615
     *
1616
     * @return $this
1617
     */
1618 View Code Duplication
    public function column($width, \Closure $closure): self
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...
1619
    {
1620
        $width = $width < 1 ? round(12 * $width) : $width;
1621
1622
        $this->layout->column($width, $closure);
1623
1624
        return $this;
1625
    }
1626
1627
    /**
1628
     * Initialize filter layout.
1629
     */
1630
    protected function initLayout()
1631
    {
1632
        $this->layout = new Layout($this);
1633
    }
1634
1635
    /**
1636
     * Getter.
1637
     *
1638
     * @param string $name
1639
     *
1640
     * @return array|mixed
1641
     */
1642
    public function __get($name)
1643
    {
1644
        return $this->input($name);
1645
    }
1646
1647
    /**
1648
     * Setter.
1649
     *
1650
     * @param string $name
1651
     * @param mixed  $value
1652
     *
1653
     * @return array
1654
     */
1655
    public function __set($name, $value)
1656
    {
1657
        return Arr::set($this->inputs, $name, $value);
1658
    }
1659
1660
    /**
1661
     * Generate a Field object and add to form builder if Field exists.
1662
     *
1663
     * @param string $method
1664
     * @param array  $arguments
1665
     *
1666
     * @return Field
1667
     */
1668
    public function __call($method, $arguments)
1669
    {
1670
        if ($className = static::findFieldClass($method)) {
1671
            $column = Arr::get($arguments, 0, ''); //[0];
1672
1673
            $element = new $className($column, array_slice($arguments, 1));
1674
1675
            $this->pushField($element);
1676
1677
            return $element;
1678
        }
1679
1680
        admin_error('Error', "Field type [$method] does not exist.");
1681
1682
        return new Field\Nullable();
1683
    }
1684
1685
    /**
1686
     * @return Layout
1687
     */
1688
    public function getLayout(): Layout
1689
    {
1690
        return $this->layout;
1691
    }
1692
}
1693