Completed
Push — master ( 6d13b4...dc194c )
by Song
02:44 queued 11s
created

Form::getRelationInputs()   B

Complexity

Conditions 6
Paths 6

Size

Total Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
nc 6
nop 1
dl 0
loc 24
rs 8.9137
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 Illuminate\Contracts\Support\Renderable;
14
use Illuminate\Database\Eloquent\Model;
15
use Illuminate\Database\Eloquent\Relations;
16
use Illuminate\Database\Eloquent\SoftDeletes;
17
use Illuminate\Http\Request;
18
use Illuminate\Support\Arr;
19
use Illuminate\Support\Facades\DB;
20
use Illuminate\Support\MessageBag;
21
use Illuminate\Support\Str;
22
use Illuminate\Validation\Validator;
23
use Spatie\EloquentSortable\Sortable;
24
use Symfony\Component\HttpFoundation\Response;
25
26
/**
27
 * Class Form.
28
 *
29
 * @method Field\Text           text($column, $label = '')
30
 * @method Field\Checkbox       checkbox($column, $label = '')
31
 * @method Field\Radio          radio($column, $label = '')
32
 * @method Field\Select         select($column, $label = '')
33
 * @method Field\MultipleSelect multipleSelect($column, $label = '')
34
 * @method Field\Textarea       textarea($column, $label = '')
35
 * @method Field\Hidden         hidden($column, $label = '')
36
 * @method Field\Id             id($column, $label = '')
37
 * @method Field\Ip             ip($column, $label = '')
38
 * @method Field\Url            url($column, $label = '')
39
 * @method Field\Color          color($column, $label = '')
40
 * @method Field\Email          email($column, $label = '')
41
 * @method Field\Mobile         mobile($column, $label = '')
42
 * @method Field\Slider         slider($column, $label = '')
43
 * @method Field\File           file($column, $label = '')
44
 * @method Field\Image          image($column, $label = '')
45
 * @method Field\Date           date($column, $label = '')
46
 * @method Field\Datetime       datetime($column, $label = '')
47
 * @method Field\Time           time($column, $label = '')
48
 * @method Field\Year           year($column, $label = '')
49
 * @method Field\Month          month($column, $label = '')
50
 * @method Field\DateRange      dateRange($start, $end, $label = '')
51
 * @method Field\DateTimeRange  datetimeRange($start, $end, $label = '')
52
 * @method Field\TimeRange      timeRange($start, $end, $label = '')
53
 * @method Field\Number         number($column, $label = '')
54
 * @method Field\Currency       currency($column, $label = '')
55
 * @method Field\HasMany        hasMany($relationName, $label = '', $callback)
56
 * @method Field\SwitchField    switch($column, $label = '')
57
 * @method Field\Display        display($column, $label = '')
58
 * @method Field\Rate           rate($column, $label = '')
59
 * @method Field\Divider        divider($title = '')
60
 * @method Field\Password       password($column, $label = '')
61
 * @method Field\Decimal        decimal($column, $label = '')
62
 * @method Field\Html           html($html, $label = '')
63
 * @method Field\Tags           tags($column, $label = '')
64
 * @method Field\Icon           icon($column, $label = '')
65
 * @method Field\Embeds         embeds($column, $label = '', $callback)
66
 * @method Field\MultipleImage  multipleImage($column, $label = '')
67
 * @method Field\MultipleFile   multipleFile($column, $label = '')
68
 * @method Field\Captcha        captcha($column, $label = '')
69
 * @method Field\Listbox        listbox($column, $label = '')
70
 * @method Field\Table          table($column, $label, $builder)
71
 * @method Field\Timezone       timezone($column, $label = '')
72
 * @method Field\KeyValue       keyValue($column, $label = '')
73
 * @method Field\ListField      list($column, $label = '')
74
 */
75
class Form implements Renderable
76
{
77
    use HasHooks;
78
79
    /**
80
     * Remove flag in `has many` form.
81
     */
82
    const REMOVE_FLAG_NAME = '_remove_';
83
84
    /**
85
     * Eloquent model of the form.
86
     *
87
     * @var Model
88
     */
89
    protected $model;
90
91
    /**
92
     * @var \Illuminate\Validation\Validator
93
     */
94
    protected $validator;
95
96
    /**
97
     * @var Builder
98
     */
99
    protected $builder;
100
101
    /**
102
     * Data for save to current model from input.
103
     *
104
     * @var array
105
     */
106
    protected $updates = [];
107
108
    /**
109
     * Data for save to model's relations from input.
110
     *
111
     * @var array
112
     */
113
    protected $relations = [];
114
115
    /**
116
     * Input data.
117
     *
118
     * @var array
119
     */
120
    protected $inputs = [];
121
122
    /**
123
     * @var Layout
124
     */
125
    protected $layout;
126
127
    /**
128
     * Available fields.
129
     *
130
     * @var array
131
     */
132
    public static $availableFields = [
133
        'button'         => Field\Button::class,
134
        'checkbox'       => Field\Checkbox::class,
135
        'color'          => Field\Color::class,
136
        'currency'       => Field\Currency::class,
137
        'date'           => Field\Date::class,
138
        'dateRange'      => Field\DateRange::class,
139
        'datetime'       => Field\Datetime::class,
140
        'dateTimeRange'  => Field\DatetimeRange::class,
141
        'datetimeRange'  => Field\DatetimeRange::class,
142
        'decimal'        => Field\Decimal::class,
143
        'display'        => Field\Display::class,
144
        'divider'        => Field\Divider::class,
145
        'embeds'         => Field\Embeds::class,
146
        'email'          => Field\Email::class,
147
        'file'           => Field\File::class,
148
        'hasMany'        => Field\HasMany::class,
149
        'hidden'         => Field\Hidden::class,
150
        'id'             => Field\Id::class,
151
        'image'          => Field\Image::class,
152
        'ip'             => Field\Ip::class,
153
        'mobile'         => Field\Mobile::class,
154
        'month'          => Field\Month::class,
155
        'multipleSelect' => Field\MultipleSelect::class,
156
        'number'         => Field\Number::class,
157
        'password'       => Field\Password::class,
158
        'radio'          => Field\Radio::class,
159
        'rate'           => Field\Rate::class,
160
        'select'         => Field\Select::class,
161
        'slider'         => Field\Slider::class,
162
        'switch'         => Field\SwitchField::class,
163
        'text'           => Field\Text::class,
164
        'textarea'       => Field\Textarea::class,
165
        'time'           => Field\Time::class,
166
        'timeRange'      => Field\TimeRange::class,
167
        'url'            => Field\Url::class,
168
        'year'           => Field\Year::class,
169
        'html'           => Field\Html::class,
170
        'tags'           => Field\Tags::class,
171
        'icon'           => Field\Icon::class,
172
        'multipleFile'   => Field\MultipleFile::class,
173
        'multipleImage'  => Field\MultipleImage::class,
174
        'captcha'        => Field\Captcha::class,
175
        'listbox'        => Field\Listbox::class,
176
        'table'          => Field\Table::class,
177
        'timezone'       => Field\Timezone::class,
178
        'keyValue'       => Field\KeyValue::class,
179
        'list'           => Field\ListField::class,
180
    ];
181
182
    /**
183
     * Form field alias.
184
     *
185
     * @var array
186
     */
187
    public static $fieldAlias = [];
188
189
    /**
190
     * Ignored saving fields.
191
     *
192
     * @var array
193
     */
194
    protected $ignored = [];
195
196
    /**
197
     * Collected field assets.
198
     *
199
     * @var array
200
     */
201
    protected static $collectedAssets = [];
202
203
    /**
204
     * @var Form\Tab
205
     */
206
    protected $tab = null;
207
208
    /**
209
     * Field rows in form.
210
     *
211
     * @var array
212
     */
213
    public $rows = [];
214
215
    /**
216
     * @var bool
217
     */
218
    protected $isSoftDeletes = false;
219
220
    /**
221
     * Initialization closure array.
222
     *
223
     * @var []Closure
224
     */
225
    protected static $initCallbacks;
226
227
    /**
228
     * Create a new form instance.
229
     *
230
     * @param $model
231
     * @param \Closure $callback
232
     */
233
    public function __construct($model, Closure $callback = null)
234
    {
235
        $this->model = $model;
236
237
        $this->builder = new Builder($this);
238
239
        $this->initLayout();
240
241
        if ($callback instanceof Closure) {
242
            $callback($this);
243
        }
244
245
        $this->isSoftDeletes = in_array(SoftDeletes::class, class_uses_deep($this->model), true);
246
247
        $this->callInitCallbacks();
248
    }
249
250
    /**
251
     * Initialize with user pre-defined default disables, etc.
252
     *
253
     * @param Closure $callback
254
     */
255
    public static function init(Closure $callback = null)
256
    {
257
        static::$initCallbacks[] = $callback;
258
    }
259
260
    /**
261
     * Call the initialization closure array in sequence.
262
     */
263
    protected function callInitCallbacks()
264
    {
265
        if (empty(static::$initCallbacks)) {
266
            return;
267
        }
268
269
        foreach (static::$initCallbacks as $callback) {
270
            $callback($this);
271
        }
272
    }
273
274
    /**
275
     * @param Field $field
276
     *
277
     * @return $this
278
     */
279
    public function pushField(Field $field): self
280
    {
281
        $field->setForm($this);
282
283
        $width = $this->builder->getWidth();
284
        $field->setWidth($width['field'], $width['label']);
285
286
        $this->builder->fields()->push($field);
287
        $this->layout->addField($field);
288
289
        return $this;
290
    }
291
292
    /**
293
     * @return Model
294
     */
295
    public function model(): Model
296
    {
297
        return $this->model;
298
    }
299
300
    /**
301
     * @return Builder
302
     */
303
    public function builder(): Builder
304
    {
305
        return $this->builder;
306
    }
307
308
    /**
309
     * Generate a edit form.
310
     *
311
     * @param $id
312
     *
313
     * @return $this
314
     */
315
    public function edit($id): self
316
    {
317
        $this->builder->setMode(Builder::MODE_EDIT);
318
        $this->builder->setResourceId($id);
319
320
        $this->setFieldValue($id);
321
322
        return $this;
323
    }
324
325
    /**
326
     * Use tab to split form.
327
     *
328
     * @param string  $title
329
     * @param Closure $content
330
     * @param bool    $active
331
     *
332
     * @return $this
333
     */
334
    public function tab($title, Closure $content, bool $active = false): self
335
    {
336
        $this->setTab()->append($title, $content, $active);
337
338
        return $this;
339
    }
340
341
    /**
342
     * Get Tab instance.
343
     *
344
     * @return Tab
345
     */
346
    public function getTab()
347
    {
348
        return $this->tab;
349
    }
350
351
    /**
352
     * Set Tab instance.
353
     *
354
     * @return Tab
355
     */
356
    public function setTab(): Tab
357
    {
358
        if ($this->tab === null) {
359
            $this->tab = new Tab($this);
360
        }
361
362
        return $this->tab;
363
    }
364
365
    /**
366
     * Destroy data entity and remove files.
367
     *
368
     * @param $id
369
     *
370
     * @return mixed
371
     */
372
    public function destroy($id)
373
    {
374
        try {
375
            if (($ret = $this->callDeleting($id)) instanceof Response) {
376
                return $ret;
377
            }
378
379
            collect(explode(',', $id))->filter()->each(function ($id) {
380
                $builder = $this->model()->newQuery();
381
382
                if ($this->isSoftDeletes) {
383
                    $builder = $builder->withTrashed();
384
                }
385
386
                $model = $builder->with($this->getRelations())->findOrFail($id);
387
388
                if ($this->isSoftDeletes && $model->trashed()) {
389
                    $this->deleteFiles($model, true);
390
                    $model->forceDelete();
391
392
                    return;
393
                }
394
395
                $this->deleteFiles($model);
396
                $model->delete();
397
            });
398
399
            if (($ret = $this->callDeleted()) instanceof Response) {
400
                return $ret;
401
            }
402
403
            $response = [
404
                'status'  => true,
405
                'message' => trans('admin.delete_succeeded'),
406
            ];
407
        } catch (\Exception $exception) {
408
            $response = [
409
                'status'  => false,
410
                'message' => $exception->getMessage() ?: trans('admin.delete_failed'),
411
            ];
412
        }
413
414
        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...
415
    }
416
417
    /**
418
     * Remove files in record.
419
     *
420
     * @param Model $model
421
     * @param bool  $forceDelete
422
     */
423
    protected function deleteFiles(Model $model, $forceDelete = false)
424
    {
425
        // If it's a soft delete, the files in the data will not be deleted.
426
        if (!$forceDelete && $this->isSoftDeletes) {
427
            return;
428
        }
429
430
        $data = $model->toArray();
431
432
        $this->builder->fields()->filter(function ($field) {
433
            return $field instanceof Field\File;
434
        })->each(function (Field\File $file) use ($data) {
435
            $file->setOriginal($data);
436
437
            $file->destroy();
438
        });
439
    }
440
441
    /**
442
     * Store a new record.
443
     *
444
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
445
     */
446
    public function store()
447
    {
448
        $data = \request()->all();
449
450
        // Handle validation errors.
451
        if ($validationMessages = $this->validationMessages($data)) {
452
            return $this->responseValidationError($validationMessages);
453
        }
454
455
        if (($response = $this->prepare($data)) instanceof Response) {
456
            return $response;
457
        }
458
459 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...
460
            $inserts = $this->prepareInsert($this->updates);
461
462
            foreach ($inserts as $column => $value) {
463
                $this->model->setAttribute($column, $value);
464
            }
465
466
            $this->model->save();
467
468
            $this->updateRelation($this->relations);
469
        });
470
471
        if (($response = $this->callSaved()) instanceof Response) {
472
            return $response;
473
        }
474
475
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
476
            return $response;
477
        }
478
479
        return $this->redirectAfterStore();
480
    }
481
482
    /**
483
     * @param MessageBag $message
484
     *
485
     * @return $this|\Illuminate\Http\JsonResponse
486
     */
487
    protected function responseValidationError(MessageBag $message)
488
    {
489
        if (\request()->ajax() && !\request()->pjax()) {
490
            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...
491
                'status'     => false,
492
                'validation' => $message,
493
                'message'    => $message->first(),
494
            ]);
495
        }
496
497
        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...
498
    }
499
500
    /**
501
     * Get ajax response.
502
     *
503
     * @param string $message
504
     *
505
     * @return bool|\Illuminate\Http\JsonResponse
506
     */
507
    protected function ajaxResponse($message)
508
    {
509
        $request = Request::capture();
510
511
        // ajax but not pjax
512
        if ($request->ajax() && !$request->pjax()) {
513
            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...
514
                'status'  => true,
515
                'message' => $message,
516
            ]);
517
        }
518
519
        return false;
520
    }
521
522
    /**
523
     * Prepare input data for insert or update.
524
     *
525
     * @param array $data
526
     *
527
     * @return mixed
528
     */
529
    protected function prepare($data = [])
530
    {
531
        if (($response = $this->callSubmitted()) instanceof Response) {
532
            return $response;
533
        }
534
535
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
536
537
        if (($response = $this->callSaving()) instanceof Response) {
538
            return $response;
539
        }
540
541
        $this->relations = $this->getRelationInputs($this->inputs);
542
543
        $this->updates = Arr::except($this->inputs, array_keys($this->relations));
544
    }
545
546
    /**
547
     * Remove ignored fields from input.
548
     *
549
     * @param array $input
550
     *
551
     * @return array
552
     */
553
    protected function removeIgnoredFields($input): array
554
    {
555
        Arr::forget($input, $this->ignored);
556
557
        return $input;
558
    }
559
560
    /**
561
     * Get inputs for relations.
562
     *
563
     * @param array $inputs
564
     *
565
     * @return array
566
     */
567
    protected function getRelationInputs($inputs = []): array
568
    {
569
        $relations = [];
570
571
        foreach ($inputs as $column => $value) {
572
			if (method_exists($this->model, $column)) {
573
				$relation = call_user_func([$this->model, $column]);
574
575
				if ($relation instanceof Relations\Relation) {
576
					$relations[$column] = $value;
577
				}
578
			} elseif (method_exists($this->model, \Illuminate\Support\Str::camel($column))) {
579
				$column = \Illuminate\Support\Str::camel($column);
580
				$relation = call_user_func([$this->model, $column]);
581
582
				if ($relation instanceof Relations\Relation) {
583
					$relations[$column] = $value;
584
				}
585
			}
586
587
        }
588
        
589
        return $relations;
590
    }
591
592
    /**
593
     * Handle update.
594
     *
595
     * @param int  $id
596
     * @param null $data
597
     *
598
     * @return bool|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|null|Response
599
     */
600
    public function update($id, $data = null)
601
    {
602
        $data = ($data) ?: request()->all();
603
604
        $isEditable = $this->isEditable($data);
605
606
        if (($data = $this->handleColumnUpdates($id, $data)) instanceof Response) {
607
            return $data;
608
        }
609
610
        /* @var Model $this->model */
611
        $builder = $this->model();
612
613
        if ($this->isSoftDeletes) {
614
            $builder = $builder->withTrashed();
615
        }
616
617
        $this->model = $builder->with($this->getRelations())->findOrFail($id);
618
619
        $this->setFieldOriginalValue();
620
621
        // Handle validation errors.
622
        if ($validationMessages = $this->validationMessages($data)) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 606 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...
623
            if (!$isEditable) {
624
                return back()->withInput()->withErrors($validationMessages);
625
            }
626
627
            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...
628
        }
629
630
        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 606 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...
631
            return $response;
632
        }
633
634 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...
635
            $updates = $this->prepareUpdate($this->updates);
636
637
            foreach ($updates as $column => $value) {
638
                /* @var Model $this->model */
639
                $this->model->setAttribute($column, $value);
640
            }
641
642
            $this->model->save();
643
644
            $this->updateRelation($this->relations);
645
        });
646
647
        if (($result = $this->callSaved()) instanceof Response) {
648
            return $result;
649
        }
650
651
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
652
            return $response;
653
        }
654
655
        return $this->redirectAfterUpdate($id);
656
    }
657
658
    /**
659
     * Get RedirectResponse after store.
660
     *
661
     * @return \Illuminate\Http\RedirectResponse
662
     */
663
    protected function redirectAfterStore()
664
    {
665
        $resourcesPath = $this->resource(0);
666
667
        $key = $this->model->getKey();
668
669
        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 669 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
670
    }
671
672
    /**
673
     * Get RedirectResponse after update.
674
     *
675
     * @param mixed $key
676
     *
677
     * @return \Illuminate\Http\RedirectResponse
678
     */
679
    protected function redirectAfterUpdate($key)
680
    {
681
        $resourcesPath = $this->resource(-1);
682
683
        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 683 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
684
    }
685
686
    /**
687
     * Get RedirectResponse after data saving.
688
     *
689
     * @param string $resourcesPath
690
     * @param string $key
691
     *
692
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
693
     */
694
    protected function redirectAfterSaving($resourcesPath, $key)
695
    {
696
        if (request('after-save') == 1) {
697
            // continue editing
698
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
699
        } elseif (request('after-save') == 2) {
700
            // continue creating
701
            $url = rtrim($resourcesPath, '/').'/create';
702
        } elseif (request('after-save') == 3) {
703
            // view resource
704
            $url = rtrim($resourcesPath, '/')."/{$key}";
705
        } else {
706
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
707
        }
708
709
        admin_toastr(trans('admin.save_succeeded'));
710
711
        return redirect($url);
712
    }
713
714
    /**
715
     * Check if request is from editable.
716
     *
717
     * @param array $input
718
     *
719
     * @return bool
720
     */
721
    protected function isEditable(array $input = []): bool
722
    {
723
        return array_key_exists('_editable', $input);
724
    }
725
726
    /**
727
     * Handle updates for single column.
728
     *
729
     * @param int   $id
730
     * @param array $data
731
     *
732
     * @return array|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|Response
733
     */
734
    protected function handleColumnUpdates($id, $data)
735
    {
736
        $data = $this->handleEditable($data);
737
738
        $data = $this->handleFileDelete($data);
739
740
        $data = $this->handleFileSort($data);
741
742
        if ($this->handleOrderable($id, $data)) {
743
            return response([
744
                'status'  => true,
745
                'message' => trans('admin.update_succeeded'),
746
            ]);
747
        }
748
749
        return $data;
750
    }
751
752
    /**
753
     * Handle editable update.
754
     *
755
     * @param array $input
756
     *
757
     * @return array
758
     */
759
    protected function handleEditable(array $input = []): array
760
    {
761
        if (array_key_exists('_editable', $input)) {
762
            $name = $input['name'];
763
            $value = $input['value'];
764
765
            Arr::forget($input, ['pk', 'value', 'name']);
766
            Arr::set($input, $name, $value);
767
        }
768
769
        return $input;
770
    }
771
772
    /**
773
     * @param array $input
774
     *
775
     * @return array
776
     */
777
    protected function handleFileDelete(array $input = []): array
778
    {
779
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
780
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
781
            unset($input['key']);
782
        }
783
784
        request()->replace($input);
785
786
        return $input;
787
    }
788
789
    /**
790
     * @param array $input
791
     *
792
     * @return array
793
     */
794
    protected function handleFileSort(array $input = []): array
795
    {
796
        if (!array_key_exists(Field::FILE_SORT_FLAG, $input)) {
797
            return $input;
798
        }
799
800
        $sorts = array_filter($input[Field::FILE_SORT_FLAG]);
801
802
        if (empty($sorts)) {
803
            return $input;
804
        }
805
806
        foreach ($sorts as $column => $order) {
807
            $input[$column] = $order;
808
        }
809
810
        request()->replace($input);
811
812
        return $input;
813
    }
814
815
    /**
816
     * Handle orderable update.
817
     *
818
     * @param int   $id
819
     * @param array $input
820
     *
821
     * @return bool
822
     */
823
    protected function handleOrderable($id, array $input = [])
824
    {
825
        if (array_key_exists('_orderable', $input)) {
826
            $model = $this->model->find($id);
827
828
            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...
829
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
830
831
                return true;
832
            }
833
        }
834
835
        return false;
836
    }
837
838
    /**
839
     * Update relation data.
840
     *
841
     * @param array $relationsData
842
     *
843
     * @return void
844
     */
845
    protected function updateRelation($relationsData)
846
    {
847
        foreach ($relationsData as $name => $values) {
848
            if (!method_exists($this->model, $name)) {
849
                continue;
850
            }
851
852
            $relation = $this->model->$name();
853
854
            $oneToOneRelation = $relation instanceof Relations\HasOne
855
                || $relation instanceof Relations\MorphOne
856
                || $relation instanceof Relations\BelongsTo;
857
858
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
859
860
            if (empty($prepared)) {
861
                continue;
862
            }
863
864
            switch (true) {
865
                case $relation instanceof Relations\BelongsToMany:
866
                case $relation instanceof Relations\MorphToMany:
867
                    if (isset($prepared[$name])) {
868
                        $relation->sync($prepared[$name]);
869
                    }
870
                    break;
871
                case $relation instanceof Relations\HasOne:
872
873
                    $related = $this->model->$name;
874
875
                    // if related is empty
876
                    if (is_null($related)) {
877
                        $related = $relation->getRelated();
878
                        $qualifiedParentKeyName = $relation->getQualifiedParentKeyName();
879
                        $localKey = Arr::last(explode('.', $qualifiedParentKeyName));
880
                        $related->{$relation->getForeignKeyName()} = $this->model->{$localKey};
881
                    }
882
883
                    foreach ($prepared[$name] as $column => $value) {
884
                        $related->setAttribute($column, $value);
885
                    }
886
887
                    $related->save();
888
                    break;
889
                case $relation instanceof Relations\BelongsTo:
890
                case $relation instanceof Relations\MorphTo:
891
892
                    $parent = $this->model->$name;
893
894
                    // if related is empty
895
                    if (is_null($parent)) {
896
                        $parent = $relation->getRelated();
897
                    }
898
899
                    foreach ($prepared[$name] as $column => $value) {
900
                        $parent->setAttribute($column, $value);
901
                    }
902
903
                    $parent->save();
904
905
                    // When in creating, associate two models
906
                    $foreignKeyMethod = version_compare(app()->version(), '5.8.0', '<') ? 'getForeignKey' : 'getForeignKeyName';
907
                    if (!$this->model->{$relation->{$foreignKeyMethod}()}) {
908
                        $this->model->{$relation->{$foreignKeyMethod}()} = $parent->getKey();
909
910
                        $this->model->save();
911
                    }
912
913
                    break;
914
                case $relation instanceof Relations\MorphOne:
915
                    $related = $this->model->$name;
916
                    if ($related === null) {
917
                        $related = $relation->make();
918
                    }
919
                    foreach ($prepared[$name] as $column => $value) {
920
                        $related->setAttribute($column, $value);
921
                    }
922
                    $related->save();
923
                    break;
924
                case $relation instanceof Relations\HasMany:
925
                case $relation instanceof Relations\MorphMany:
926
927
                    foreach ($prepared[$name] as $related) {
928
                        /** @var Relations\Relation $relation */
929
                        $relation = $this->model()->$name();
930
931
                        $keyName = $relation->getRelated()->getKeyName();
932
933
                        $instance = $relation->findOrNew(Arr::get($related, $keyName));
934
935
                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
936
                            $instance->delete();
937
938
                            continue;
939
                        }
940
941
                        Arr::forget($related, static::REMOVE_FLAG_NAME);
942
943
                        $instance->fill($related);
944
945
                        $instance->save();
946
                    }
947
948
                    break;
949
            }
950
        }
951
    }
952
953
    /**
954
     * Prepare input data for update.
955
     *
956
     * @param array $updates
957
     * @param bool  $oneToOneRelation If column is one-to-one relation.
958
     *
959
     * @return array
960
     */
961
    protected function prepareUpdate(array $updates, $oneToOneRelation = false): array
962
    {
963
        $prepared = [];
964
965
        /** @var Field $field */
966
        foreach ($this->builder->fields() as $field) {
967
            $columns = $field->column();
968
969
            // If column not in input array data, then continue.
970
            if (!Arr::has($updates, $columns)) {
971
                continue;
972
            }
973
974
            if ($this->isInvalidColumn($columns, $oneToOneRelation || $field->isJsonType)) {
975
                continue;
976
            }
977
978
            $value = $this->getDataByColumn($updates, $columns);
979
980
            $value = $field->prepare($value);
981
982 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...
983
                foreach ($columns as $name => $column) {
984
                    Arr::set($prepared, $column, $value[$name]);
985
                }
986
            } elseif (is_string($columns)) {
987
                Arr::set($prepared, $columns, $value);
988
            }
989
        }
990
991
        return $prepared;
992
    }
993
994
    /**
995
     * @param string|array $columns
996
     * @param bool         $containsDot
997
     *
998
     * @return bool
999
     */
1000
    protected function isInvalidColumn($columns, $containsDot = false): bool
1001
    {
1002
        foreach ((array) $columns as $column) {
1003
            if ((!$containsDot && Str::contains($column, '.')) ||
1004
                ($containsDot && !Str::contains($column, '.'))) {
1005
                return true;
1006
            }
1007
        }
1008
1009
        return false;
1010
    }
1011
1012
    /**
1013
     * Prepare input data for insert.
1014
     *
1015
     * @param $inserts
1016
     *
1017
     * @return array
1018
     */
1019
    protected function prepareInsert($inserts): array
1020
    {
1021
        if ($this->isHasOneRelation($inserts)) {
1022
            $inserts = Arr::dot($inserts);
1023
        }
1024
1025
        foreach ($inserts as $column => $value) {
1026
            if (($field = $this->getFieldByColumn($column)) === null) {
1027
                unset($inserts[$column]);
1028
                continue;
1029
            }
1030
1031
            $inserts[$column] = $field->prepare($value);
1032
        }
1033
1034
        $prepared = [];
1035
1036
        foreach ($inserts as $key => $value) {
1037
            Arr::set($prepared, $key, $value);
1038
        }
1039
1040
        return $prepared;
1041
    }
1042
1043
    /**
1044
     * Is input data is has-one relation.
1045
     *
1046
     * @param array $inserts
1047
     *
1048
     * @return bool
1049
     */
1050
    protected function isHasOneRelation($inserts): bool
1051
    {
1052
        $first = current($inserts);
1053
1054
        if (!is_array($first)) {
1055
            return false;
1056
        }
1057
1058
        if (is_array(current($first))) {
1059
            return false;
1060
        }
1061
1062
        return Arr::isAssoc($first);
1063
    }
1064
1065
    /**
1066
     * Ignore fields to save.
1067
     *
1068
     * @param string|array $fields
1069
     *
1070
     * @return $this
1071
     */
1072
    public function ignore($fields): self
1073
    {
1074
        $this->ignored = array_merge($this->ignored, (array) $fields);
1075
1076
        return $this;
1077
    }
1078
1079
    /**
1080
     * @param array        $data
1081
     * @param string|array $columns
1082
     *
1083
     * @return array|mixed
1084
     */
1085 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...
1086
    {
1087
        if (is_string($columns)) {
1088
            return Arr::get($data, $columns);
1089
        }
1090
1091
        if (is_array($columns)) {
1092
            $value = [];
1093
            foreach ($columns as $name => $column) {
1094
                if (!Arr::has($data, $column)) {
1095
                    continue;
1096
                }
1097
                $value[$name] = Arr::get($data, $column);
1098
            }
1099
1100
            return $value;
1101
        }
1102
    }
1103
1104
    /**
1105
     * Find field object by column.
1106
     *
1107
     * @param $column
1108
     *
1109
     * @return mixed
1110
     */
1111
    protected function getFieldByColumn($column)
1112
    {
1113
        return $this->builder->fields()->first(
1114
            function (Field $field) use ($column) {
1115
                if (is_array($field->column())) {
1116
                    return in_array($column, $field->column());
1117
                }
1118
1119
                return $field->column() == $column;
1120
            }
1121
        );
1122
    }
1123
1124
    /**
1125
     * Set original data for each field.
1126
     *
1127
     * @return void
1128
     */
1129
    protected function setFieldOriginalValue()
1130
    {
1131
//        static::doNotSnakeAttributes($this->model);
1132
1133
        $values = $this->model->toArray();
1134
1135
        $this->builder->fields()->each(function (Field $field) use ($values) {
1136
            $field->setOriginal($values);
1137
        });
1138
    }
1139
1140
    /**
1141
     * Set all fields value in form.
1142
     *
1143
     * @param $id
1144
     *
1145
     * @return void
1146
     */
1147
    protected function setFieldValue($id)
1148
    {
1149
        $relations = $this->getRelations();
1150
1151
        $builder = $this->model();
1152
1153
        if ($this->isSoftDeletes) {
1154
            $builder = $builder->withTrashed();
1155
        }
1156
1157
        $this->model = $builder->with($relations)->findOrFail($id);
1158
1159
        $this->callEditing();
1160
1161
//        static::doNotSnakeAttributes($this->model);
1162
1163
        $data = $this->model->toArray();
1164
1165
        $this->builder->fields()->each(function (Field $field) use ($data) {
1166
            if (!in_array($field->column(), $this->ignored, true)) {
1167
                $field->fill($data);
1168
            }
1169
        });
1170
    }
1171
1172
    /**
1173
     * Add a fieldset to form.
1174
     *
1175
     * @param string  $title
1176
     * @param Closure $setCallback
1177
     *
1178
     * @return Field\Fieldset
1179
     */
1180 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...
1181
    {
1182
        $fieldset = new Field\Fieldset();
1183
1184
        $this->html($fieldset->start($title))->plain();
1185
1186
        $setCallback($this);
1187
1188
        $this->html($fieldset->end())->plain();
1189
1190
        return $fieldset;
1191
    }
1192
1193
    /**
1194
     * Don't snake case attributes.
1195
     *
1196
     * @param Model $model
1197
     *
1198
     * @return void
1199
     */
1200
    protected static function doNotSnakeAttributes(Model $model)
1201
    {
1202
        $class = get_class($model);
1203
1204
        $class::$snakeAttributes = false;
1205
    }
1206
1207
    /**
1208
     * Get validation messages.
1209
     *
1210
     * @param array $input
1211
     *
1212
     * @return MessageBag|bool
1213
     */
1214
    public function validationMessages($input)
1215
    {
1216
        $failedValidators = [];
1217
1218
        /** @var Field $field */
1219 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...
1220
            if (!$validator = $field->getValidator($input)) {
1221
                continue;
1222
            }
1223
1224
            if (($validator instanceof Validator) && !$validator->passes()) {
1225
                $failedValidators[] = $validator;
1226
            }
1227
        }
1228
1229
        $message = $this->mergeValidationMessages($failedValidators);
1230
1231
        return $message->any() ? $message : false;
1232
    }
1233
1234
    /**
1235
     * Merge validation messages from input validators.
1236
     *
1237
     * @param \Illuminate\Validation\Validator[] $validators
1238
     *
1239
     * @return MessageBag
1240
     */
1241
    protected function mergeValidationMessages($validators): MessageBag
1242
    {
1243
        $messageBag = new MessageBag();
1244
1245
        foreach ($validators as $validator) {
1246
            $messageBag = $messageBag->merge($validator->messages());
1247
        }
1248
1249
        return $messageBag;
1250
    }
1251
1252
    /**
1253
     * Get all relations of model from callable.
1254
     *
1255
     * @return array
1256
     */
1257
    public function getRelations(): array
1258
    {
1259
        $relations = $columns = [];
1260
1261
        /** @var Field $field */
1262
        foreach ($this->builder->fields() as $field) {
1263
            $columns[] = $field->column();
1264
        }
1265
1266
        foreach (Arr::flatten($columns) as $column) {
1267
            if (Str::contains($column, '.')) {
1268
                list($relation) = explode('.', $column);
1269
1270
                if (method_exists($this->model, $relation) &&
1271
                    $this->model->$relation() instanceof Relations\Relation
1272
                ) {
1273
                    $relations[] = $relation;
1274
                }
1275
            } elseif (method_exists($this->model, $column) &&
1276
                !method_exists(Model::class, $column)
1277
            ) {
1278
                $relations[] = $column;
1279
            }
1280
        }
1281
1282
        return array_unique($relations);
1283
    }
1284
1285
    /**
1286
     * Set action for form.
1287
     *
1288
     * @param string $action
1289
     *
1290
     * @return $this
1291
     */
1292
    public function setAction($action): self
1293
    {
1294
        $this->builder()->setAction($action);
1295
1296
        return $this;
1297
    }
1298
1299
    /**
1300
     * Set field and label width in current form.
1301
     *
1302
     * @param int $fieldWidth
1303
     * @param int $labelWidth
1304
     *
1305
     * @return $this
1306
     */
1307
    public function setWidth($fieldWidth = 8, $labelWidth = 2): self
1308
    {
1309
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1310
            /* @var Field $field  */
1311
            $field->setWidth($fieldWidth, $labelWidth);
1312
        });
1313
1314
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1315
1316
        return $this;
1317
    }
1318
1319
    /**
1320
     * Set view for form.
1321
     *
1322
     * @param string $view
1323
     *
1324
     * @return $this
1325
     */
1326
    public function setView($view): self
1327
    {
1328
        $this->builder()->setView($view);
1329
1330
        return $this;
1331
    }
1332
1333
    /**
1334
     * Set title for form.
1335
     *
1336
     * @param string $title
1337
     *
1338
     * @return $this
1339
     */
1340
    public function setTitle($title = ''): self
1341
    {
1342
        $this->builder()->setTitle($title);
1343
1344
        return $this;
1345
    }
1346
1347
    /**
1348
     * Add a row in form.
1349
     *
1350
     * @param Closure $callback
1351
     *
1352
     * @return $this
1353
     */
1354
    public function row(Closure $callback): self
1355
    {
1356
        $this->rows[] = new Row($callback, $this);
1357
1358
        return $this;
1359
    }
1360
1361
    /**
1362
     * Tools setting for form.
1363
     *
1364
     * @param Closure $callback
1365
     */
1366
    public function tools(Closure $callback)
1367
    {
1368
        $callback->call($this, $this->builder->getTools());
1369
    }
1370
1371
    /**
1372
     * @param Closure|null $callback
1373
     *
1374
     * @return Form\Tools
1375
     */
1376 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...
1377
    {
1378
        if (func_num_args() === 0) {
1379
            return $this->builder->getTools();
1380
        }
1381
1382
        $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...
1383
    }
1384
1385
    /**
1386
     * Indicates if current form page is creating.
1387
     *
1388
     * @return bool
1389
     */
1390
    public function isCreating(): bool
1391
    {
1392
        return Str::endsWith(\request()->route()->getName(), ['.create', '.store']);
1393
    }
1394
1395
    /**
1396
     * Indicates if current form page is editing.
1397
     *
1398
     * @return bool
1399
     */
1400
    public function isEditing(): bool
1401
    {
1402
        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...
1403
    }
1404
1405
    /**
1406
     * Disable form submit.
1407
     *
1408
     * @param bool $disable
1409
     *
1410
     * @return $this
1411
     *
1412
     * @deprecated
1413
     */
1414
    public function disableSubmit(bool $disable = true): self
1415
    {
1416
        $this->builder()->getFooter()->disableSubmit($disable);
1417
1418
        return $this;
1419
    }
1420
1421
    /**
1422
     * Disable form reset.
1423
     *
1424
     * @param bool $disable
1425
     *
1426
     * @return $this
1427
     *
1428
     * @deprecated
1429
     */
1430
    public function disableReset(bool $disable = true): self
1431
    {
1432
        $this->builder()->getFooter()->disableReset($disable);
1433
1434
        return $this;
1435
    }
1436
1437
    /**
1438
     * Disable View Checkbox on footer.
1439
     *
1440
     * @param bool $disable
1441
     *
1442
     * @return $this
1443
     */
1444
    public function disableViewCheck(bool $disable = true): self
1445
    {
1446
        $this->builder()->getFooter()->disableViewCheck($disable);
1447
1448
        return $this;
1449
    }
1450
1451
    /**
1452
     * Disable Editing Checkbox on footer.
1453
     *
1454
     * @param bool $disable
1455
     *
1456
     * @return $this
1457
     */
1458
    public function disableEditingCheck(bool $disable = true): self
1459
    {
1460
        $this->builder()->getFooter()->disableEditingCheck($disable);
1461
1462
        return $this;
1463
    }
1464
1465
    /**
1466
     * Disable Creating Checkbox on footer.
1467
     *
1468
     * @param bool $disable
1469
     *
1470
     * @return $this
1471
     */
1472
    public function disableCreatingCheck(bool $disable = true): self
1473
    {
1474
        $this->builder()->getFooter()->disableCreatingCheck($disable);
1475
1476
        return $this;
1477
    }
1478
1479
    /**
1480
     * Footer setting for form.
1481
     *
1482
     * @param Closure $callback
1483
     *
1484
     * @return \Encore\Admin\Form\Footer
1485
     */
1486 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...
1487
    {
1488
        if (func_num_args() === 0) {
1489
            return $this->builder()->getFooter();
1490
        }
1491
1492
        $callback($this->builder()->getFooter());
1493
    }
1494
1495
    /**
1496
     * Get current resource route url.
1497
     *
1498
     * @param int $slice
1499
     *
1500
     * @return string
1501
     */
1502
    public function resource($slice = -2): string
1503
    {
1504
        $segments = explode('/', trim(\request()->getUri(), '/'));
1505
1506
        if ($slice !== 0) {
1507
            $segments = array_slice($segments, 0, $slice);
1508
        }
1509
1510
        return implode('/', $segments);
1511
    }
1512
1513
    /**
1514
     * Render the form contents.
1515
     *
1516
     * @return string
1517
     */
1518
    public function render()
1519
    {
1520
        try {
1521
            return $this->builder->render();
1522
        } catch (\Exception $e) {
1523
            return Handler::renderException($e);
1524
        }
1525
    }
1526
1527
    /**
1528
     * Get or set input data.
1529
     *
1530
     * @param string $key
1531
     * @param null   $value
1532
     *
1533
     * @return array|mixed
1534
     */
1535
    public function input($key, $value = null)
1536
    {
1537
        if ($value === null) {
1538
            return Arr::get($this->inputs, $key);
1539
        }
1540
1541
        return Arr::set($this->inputs, $key, $value);
1542
    }
1543
1544
    /**
1545
     * Register custom field.
1546
     *
1547
     * @param string $abstract
1548
     * @param string $class
1549
     *
1550
     * @return void
1551
     */
1552
    public static function extend($abstract, $class)
1553
    {
1554
        static::$availableFields[$abstract] = $class;
1555
    }
1556
1557
    /**
1558
     * Set form field alias.
1559
     *
1560
     * @param string $field
1561
     * @param string $alias
1562
     *
1563
     * @return void
1564
     */
1565
    public static function alias($field, $alias)
1566
    {
1567
        static::$fieldAlias[$alias] = $field;
1568
    }
1569
1570
    /**
1571
     * Remove registered field.
1572
     *
1573
     * @param array|string $abstract
1574
     */
1575
    public static function forget($abstract)
1576
    {
1577
        Arr::forget(static::$availableFields, $abstract);
1578
    }
1579
1580
    /**
1581
     * Find field class.
1582
     *
1583
     * @param string $method
1584
     *
1585
     * @return bool|mixed
1586
     */
1587
    public static function findFieldClass($method)
1588
    {
1589
        // If alias exists.
1590
        if (isset(static::$fieldAlias[$method])) {
1591
            $method = static::$fieldAlias[$method];
1592
        }
1593
1594
        $class = Arr::get(static::$availableFields, $method);
1595
1596
        if (class_exists($class)) {
1597
            return $class;
1598
        }
1599
1600
        return false;
1601
    }
1602
1603
    /**
1604
     * Collect assets required by registered field.
1605
     *
1606
     * @return array
1607
     */
1608
    public static function collectFieldAssets(): array
1609
    {
1610
        if (!empty(static::$collectedAssets)) {
1611
            return static::$collectedAssets;
1612
        }
1613
1614
        $css = collect();
1615
        $js = collect();
1616
1617
        foreach (static::$availableFields as $field) {
1618
            if (!method_exists($field, 'getAssets')) {
1619
                continue;
1620
            }
1621
1622
            $assets = call_user_func([$field, 'getAssets']);
1623
1624
            $css->push(Arr::get($assets, 'css'));
1625
            $js->push(Arr::get($assets, 'js'));
1626
        }
1627
1628
        return static::$collectedAssets = [
1629
            'css' => $css->flatten()->unique()->filter()->toArray(),
1630
            'js'  => $js->flatten()->unique()->filter()->toArray(),
1631
        ];
1632
    }
1633
1634
    /**
1635
     * Add a new layout column.
1636
     *
1637
     * @param int      $width
1638
     * @param \Closure $closure
1639
     *
1640
     * @return $this
1641
     */
1642 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...
1643
    {
1644
        $width = $width < 1 ? round(12 * $width) : $width;
1645
1646
        $this->layout->column($width, $closure);
1647
1648
        return $this;
1649
    }
1650
1651
    /**
1652
     * Initialize filter layout.
1653
     */
1654
    protected function initLayout()
1655
    {
1656
        $this->layout = new Layout($this);
1657
    }
1658
1659
    /**
1660
     * Getter.
1661
     *
1662
     * @param string $name
1663
     *
1664
     * @return array|mixed
1665
     */
1666
    public function __get($name)
1667
    {
1668
        return $this->input($name);
1669
    }
1670
1671
    /**
1672
     * Setter.
1673
     *
1674
     * @param string $name
1675
     * @param mixed  $value
1676
     *
1677
     * @return array
1678
     */
1679
    public function __set($name, $value)
1680
    {
1681
        return Arr::set($this->inputs, $name, $value);
1682
    }
1683
1684
    /**
1685
     * Generate a Field object and add to form builder if Field exists.
1686
     *
1687
     * @param string $method
1688
     * @param array  $arguments
1689
     *
1690
     * @return Field
1691
     */
1692
    public function __call($method, $arguments)
1693
    {
1694
        if ($className = static::findFieldClass($method)) {
1695
            $column = Arr::get($arguments, 0, ''); //[0];
1696
1697
            $element = new $className($column, array_slice($arguments, 1));
1698
1699
            $this->pushField($element);
1700
1701
            return $element;
1702
        }
1703
1704
        admin_error('Error', "Field type [$method] does not exist.");
1705
1706
        return new Field\Nullable();
1707
    }
1708
1709
    /**
1710
     * @return Layout
1711
     */
1712
    public function getLayout(): Layout
1713
    {
1714
        return $this->layout;
1715
    }
1716
}
1717