Completed
Pull Request — master (#5010)
by zi
02:50
created

Form::__isset()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
nc 1
nop 1
dl 0
loc 4
rs 10
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin;
4
5
use Closure;
6
use Encore\Admin\Exception\Handler;
7
use Encore\Admin\Form\Builder;
8
use Encore\Admin\Form\Concerns\HandleCascadeFields;
9
use Encore\Admin\Form\Concerns\HasFields;
10
use Encore\Admin\Form\Concerns\HasHooks;
11
use Encore\Admin\Form\Field;
12
use Encore\Admin\Form\Layout\Layout;
13
use Encore\Admin\Form\Row;
14
use Encore\Admin\Form\Tab;
15
use Encore\Admin\Traits\ShouldSnakeAttributes;
16
use Illuminate\Contracts\Support\Arrayable;
17
use Illuminate\Contracts\Support\Renderable;
18
use Illuminate\Database\Eloquent\Model;
19
use Illuminate\Database\Eloquent\Relations;
20
use Illuminate\Database\Eloquent\SoftDeletes;
21
use Illuminate\Http\Request;
22
use Illuminate\Support\Arr;
23
use Illuminate\Support\Facades\DB;
24
use Illuminate\Support\MessageBag;
25
use Illuminate\Support\Str;
26
use Illuminate\Validation\Validator;
27
use Spatie\EloquentSortable\Sortable;
28
use Symfony\Component\HttpFoundation\Response;
29
30
/**
31
 * Class Form.
32
 */
33
class Form implements Renderable
34
{
35
    use HasHooks;
36
    use HasFields;
37
    use HandleCascadeFields;
38
    use ShouldSnakeAttributes;
39
    /**
40
     * Remove flag in `has many` form.
41
     */
42
    const REMOVE_FLAG_NAME = '_remove_';
43
44
    /**
45
     * Eloquent model of the form.
46
     *
47
     * @var Model
48
     */
49
    protected $model;
50
51
    /**
52
     * @var \Illuminate\Validation\Validator
53
     */
54
    protected $validator;
55
56
    /**
57
     * @var Builder
58
     */
59
    protected $builder;
60
61
    /**
62
     * Data for save to current model from input.
63
     *
64
     * @var array
65
     */
66
    protected $updates = [];
67
68
    /**
69
     * Data for save to model's relations from input.
70
     *
71
     * @var array
72
     */
73
    protected $relations = [];
74
75
    /**
76
     * Input data.
77
     *
78
     * @var array
79
     */
80
    protected $inputs = [];
81
82
    /**
83
     * @var Layout
84
     */
85
    protected $layout;
86
87
    /**
88
     * Ignored saving fields.
89
     *
90
     * @var array
91
     */
92
    protected $ignored = [];
93
94
    /**
95
     * Collected field assets.
96
     *
97
     * @var array
98
     */
99
    protected static $collectedAssets = [];
100
101
    /**
102
     * @var Form\Tab
103
     */
104
    protected $tab = null;
105
106
    /**
107
     * Field rows in form.
108
     *
109
     * @var array
110
     */
111
    public $rows = [];
112
113
    /**
114
     * @var bool
115
     */
116
    protected $isSoftDeletes = false;
117
118
    /**
119
     * Create a new form instance.
120
     *
121
     * @param $model
122
     * @param \Closure $callback
123
     */
124
    public function __construct($model, Closure $callback = null)
125
    {
126
        $this->model = $model;
127
128
        $this->builder = new Builder($this);
129
130
        $this->initLayout();
131
132
        if ($callback instanceof Closure) {
133
            $callback($this);
134
        }
135
136
        $this->isSoftDeletes = in_array(SoftDeletes::class, class_uses_deep($this->model), true);
137
138
        $this->callInitCallbacks();
139
    }
140
141
    /**
142
     * @param Field $field
143
     *
144
     * @return $this
145
     */
146
    public function pushField(Field $field): self
147
    {
148
        $field->setForm($this);
149
150
        $width = $this->builder->getWidth();
151
        $field->setWidth($width['field'], $width['label']);
152
153
        $this->fields()->push($field);
154
        $this->layout->addField($field);
155
156
        return $this;
157
    }
158
159
    /**
160
     * @return Model
161
     */
162
    public function model(): Model
163
    {
164
        return $this->model;
165
    }
166
167
    /**
168
     * @return Builder
169
     */
170
    public function builder(): Builder
171
    {
172
        return $this->builder;
173
    }
174
175
    /**
176
     * @return \Illuminate\Support\Collection
177
     */
178
    public function fields()
179
    {
180
        return $this->builder()->fields();
181
    }
182
183
    /**
184
     * Generate a edit form.
185
     *
186
     * @param $id
187
     *
188
     * @return $this
189
     */
190
    public function edit($id): self
191
    {
192
        $this->builder->setMode(Builder::MODE_EDIT);
193
        $this->builder->setResourceId($id);
194
195
        $this->setFieldValue($id);
196
197
        return $this;
198
    }
199
200
    /**
201
     * Use tab to split form.
202
     *
203
     * @param string  $title
204
     * @param Closure $content
205
     * @param bool    $active
206
     *
207
     * @return $this
208
     */
209
    public function tab($title, Closure $content, bool $active = false): self
210
    {
211
        $this->setTab()->append($title, $content, $active);
212
213
        return $this;
214
    }
215
216
    /**
217
     * Get Tab instance.
218
     *
219
     * @return Tab
220
     */
221
    public function getTab()
222
    {
223
        return $this->tab;
224
    }
225
226
    /**
227
     * Set Tab instance.
228
     *
229
     * @return Tab
230
     */
231
    public function setTab(): Tab
232
    {
233
        if ($this->tab === null) {
234
            $this->tab = new Tab($this);
235
        }
236
237
        return $this->tab;
238
    }
239
240
    /**
241
     * Destroy data entity and remove files.
242
     *
243
     * @param $id
244
     *
245
     * @return mixed
246
     */
247
    public function destroy($id)
248
    {
249
        try {
250
            if (($ret = $this->callDeleting($id)) instanceof Response) {
251
                return $ret;
252
            }
253
254
            collect(explode(',', $id))->filter()->each(function ($id) {
255
                $builder = $this->model()->newQuery();
256
257
                if ($this->isSoftDeletes) {
258
                    $builder = $builder->withTrashed();
259
                }
260
261
                $model = $builder->with($this->getRelations())->findOrFail($id);
262
263
                if ($this->isSoftDeletes && $model->trashed()) {
264
                    $this->deleteFiles($model, true);
265
                    $model->forceDelete();
266
267
                    return;
268
                }
269
270
                $this->deleteFiles($model);
271
                $model->delete();
272
            });
273
274
            if (($ret = $this->callDeleted()) instanceof Response) {
275
                return $ret;
276
            }
277
278
            $response = [
279
                'status'  => true,
280
                'message' => trans('admin.delete_succeeded'),
281
            ];
282
        } catch (\Exception $exception) {
283
            $response = [
284
                'status'  => false,
285
                'message' => $exception->getMessage() ?: trans('admin.delete_failed'),
286
            ];
287
        }
288
289
        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...
290
    }
291
292
    /**
293
     * Remove files in record.
294
     *
295
     * @param Model $model
296
     * @param bool  $forceDelete
297
     */
298
    protected function deleteFiles(Model $model, $forceDelete = false)
299
    {
300
        // If it's a soft delete, the files in the data will not be deleted.
301
        if (!$forceDelete && $this->isSoftDeletes) {
302
            return;
303
        }
304
305
        $data = $model->toArray();
306
307
        $this->fields()->filter(function ($field) {
308
            return $field instanceof Field\File;
309
        })->each(function (Field\File $file) use ($data) {
310
            $file->setOriginal($data);
311
312
            $file->destroy();
313
        });
314
    }
315
316
    /**
317
     * Store a new record.
318
     *
319
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
320
     */
321
    public function store()
322
    {
323
        $data = \request()->all();
324
325
        // Handle validation errors.
326
        if ($validationMessages = $this->validationMessages($data)) {
327
            return $this->responseValidationError($validationMessages);
328
        }
329
330
        if (($response = $this->prepare($data)) instanceof Response) {
331
            return $response;
332
        }
333
334 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...
335
            $inserts = $this->prepareInsert($this->updates);
336
337
            foreach ($inserts as $column => $value) {
338
                $this->model->setAttribute($column, $value);
339
            }
340
341
            $this->model->save();
342
343
            $this->updateRelation($this->relations);
344
        });
345
346
        if (($response = $this->callSaved()) instanceof Response) {
347
            return $response;
348
        }
349
350
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
351
            return $response;
352
        }
353
354
        return $this->redirectAfterStore();
355
    }
356
357
    /**
358
     * @param MessageBag $message
359
     *
360
     * @return $this|\Illuminate\Http\JsonResponse
361
     */
362
    protected function responseValidationError(MessageBag $message)
363
    {
364
        if (\request()->ajax() && !\request()->pjax()) {
365
            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...
366
                'status'     => false,
367
                'validation' => $message,
368
                'message'    => $message->first(),
369
            ]);
370
        }
371
372
        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...
373
    }
374
375
    /**
376
     * Get ajax response.
377
     *
378
     * @param string $message
379
     *
380
     * @return bool|\Illuminate\Http\JsonResponse
381
     */
382
    protected function ajaxResponse($message)
383
    {
384
        $request = \request();
385
386
        // ajax but not pjax
387
        if ($request->ajax() && !$request->pjax()) {
388
            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...
389
                'status'    => true,
390
                'message'   => $message,
391
                'display'   => $this->applayFieldDisplay(),
392
            ]);
393
        }
394
395
        return false;
396
    }
397
398
    /**
399
     * @return array
400
     */
401
    protected function applayFieldDisplay()
402
    {
403
        $editable = [];
404
405
        /** @var Field $field */
406
        foreach ($this->fields() as $field) {
407
            if (!\request()->has($field->column())) {
408
                continue;
409
            }
410
411
            $newValue = $this->model->fresh()->getAttribute($field->column());
412
413
            if ($newValue instanceof Arrayable) {
414
                $newValue = $newValue->toArray();
415
            }
416
417
            if ($field instanceof Field\BelongsTo || $field instanceof Field\BelongsToMany) {
418
                $selectable = $field->getSelectable();
419
420
                if (method_exists($selectable, 'display')) {
421
                    $display = $selectable::display();
422
423
                    $editable[$field->column()] = $display->call($this->model, $newValue);
424
                }
425
            }
426
        }
427
428
        return $editable;
429
    }
430
431
    /**
432
     * Prepare input data for insert or update.
433
     *
434
     * @param array $data
435
     *
436
     * @return mixed
437
     */
438
    protected function prepare($data = [])
439
    {
440
        if (($response = $this->callSubmitted()) instanceof Response) {
441
            return $response;
442
        }
443
444
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
445
446
        if (($response = $this->callSaving()) instanceof Response) {
447
            return $response;
448
        }
449
450
        $this->relations = $this->getRelationInputs($this->inputs);
451
452
        $this->updates = Arr::except($this->inputs, array_keys($this->relations));
453
    }
454
455
    /**
456
     * Remove ignored fields from input.
457
     *
458
     * @param array $input
459
     *
460
     * @return array
461
     */
462
    protected function removeIgnoredFields($input): array
463
    {
464
        Arr::forget($input, $this->ignored);
465
466
        return $input;
467
    }
468
469
    /**
470
     * Get inputs for relations.
471
     *
472
     * @param array $inputs
473
     *
474
     * @return array
475
     */
476
    protected function getRelationInputs($inputs = []): array
477
    {
478
        $relations = [];
479
480
        foreach ($inputs as $column => $value) {
481
            if (method_exists($this->model, $column) ||
482
                method_exists($this->model, $column = Str::camel($column))) {
483
                $relation = call_user_func([$this->model, $column]);
484
485
                if ($relation instanceof Relations\Relation) {
486
                    $relations[$column] = $value;
487
                }
488
            }
489
        }
490
491
        return $relations;
492
    }
493
494
    /**
495
     * Handle update.
496
     *
497
     * @param int  $id
498
     * @param null $data
499
     *
500
     * @return bool|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|null|Response
501
     */
502
    public function update($id, $data = null)
503
    {
504
        $data = ($data) ?: request()->all();
505
506
        $isEditable = $this->isEditable($data);
507
508
        if (($data = $this->handleColumnUpdates($id, $data)) instanceof Response) {
509
            return $data;
510
        }
511
512
        /* @var Model $this ->model */
513
        $builder = $this->model();
514
515
        if ($this->isSoftDeletes) {
516
            $builder = $builder->withTrashed();
517
        }
518
519
        $this->model = $builder->with($this->getRelations())->findOrFail($id);
520
521
        $this->setFieldOriginalValue();
522
523
        // Handle validation errors.
524
        if ($validationMessages = $this->validationMessages($data)) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 508 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...
525
            if (!$isEditable) {
526
                return back()->withInput()->withErrors($validationMessages);
527
            }
528
529
            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...
Documentation introduced by
$validationMessages->getMessages() is of type array, but the function expects a object<Illuminate\Support\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
530
        }
531
532
        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 508 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...
533
            return $response;
534
        }
535
536 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...
537
            $updates = $this->prepareUpdate($this->updates);
538
539
            foreach ($updates as $column => $value) {
540
                /* @var Model $this ->model */
541
                $this->model->setAttribute($column, $value);
542
            }
543
544
            $this->model->save();
545
546
            $this->updateRelation($this->relations);
547
        });
548
549
        if (($result = $this->callSaved()) instanceof Response) {
550
            return $result;
551
        }
552
553
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
554
            return $response;
555
        }
556
557
        return $this->redirectAfterUpdate($id);
558
    }
559
560
    /**
561
     * Get RedirectResponse after store.
562
     *
563
     * @return \Illuminate\Http\RedirectResponse
564
     */
565
    protected function redirectAfterStore()
566
    {
567
        $resourcesPath = $this->resource(0);
568
569
        $key = $this->model->getKey();
570
571
        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 571 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
572
    }
573
574
    /**
575
     * Get RedirectResponse after update.
576
     *
577
     * @param mixed $key
578
     *
579
     * @return \Illuminate\Http\RedirectResponse
580
     */
581
    protected function redirectAfterUpdate($key)
582
    {
583
        $resourcesPath = $this->resource(-1);
584
585
        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 585 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
586
    }
587
588
    /**
589
     * Get RedirectResponse after data saving.
590
     *
591
     * @param string $resourcesPath
592
     * @param string $key
593
     *
594
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
595
     */
596
    protected function redirectAfterSaving($resourcesPath, $key)
597
    {
598
        if (request('after-save') == 1) {
599
            // continue editing
600
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
601
        } elseif (request('after-save') == 2) {
602
            // continue creating
603
            $url = rtrim($resourcesPath, '/').'/create';
604
        } elseif (request('after-save') == 3) {
605
            // view resource
606
            $url = rtrim($resourcesPath, '/')."/{$key}";
607
        } else {
608
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
609
        }
610
611
        admin_toastr(trans('admin.save_succeeded'));
612
613
        return redirect($url);
614
    }
615
616
    /**
617
     * Check if request is from editable.
618
     *
619
     * @param array $input
620
     *
621
     * @return bool
622
     */
623
    protected function isEditable(array $input = []): bool
624
    {
625
        return array_key_exists('_editable', $input) || array_key_exists('_edit_inline', $input);
626
    }
627
628
    /**
629
     * Handle updates for single column.
630
     *
631
     * @param int   $id
632
     * @param array $data
633
     *
634
     * @return array|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|Response
635
     */
636
    protected function handleColumnUpdates($id, $data)
637
    {
638
        $data = $this->handleEditable($data);
639
640
        $data = $this->handleFileDelete($data);
641
642
        $data = $this->handleFileSort($data);
643
644
        if ($this->handleOrderable($id, $data)) {
645
            return response([
646
                'status'  => true,
647
                'message' => trans('admin.update_succeeded'),
648
            ]);
649
        }
650
651
        return $data;
652
    }
653
654
    /**
655
     * Handle editable update.
656
     *
657
     * @param array $input
658
     *
659
     * @return array
660
     */
661
    protected function handleEditable(array $input = []): array
662
    {
663
        if (array_key_exists('_editable', $input)) {
664
            $name = $input['name'];
665
            $value = $input['value'];
666
667
            Arr::forget($input, ['pk', 'value', 'name']);
668
            Arr::set($input, $name, $value);
669
        }
670
671
        return $input;
672
    }
673
674
    /**
675
     * @param array $input
676
     *
677
     * @return array
678
     */
679
    protected function handleFileDelete(array $input = []): array
680
    {
681
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
682
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
683
            unset($input['key']);
684
        }
685
686
        request()->replace($input);
687
688
        return $input;
689
    }
690
691
    /**
692
     * @param array $input
693
     *
694
     * @return array
695
     */
696
    protected function handleFileSort(array $input = []): array
697
    {
698
        if (!array_key_exists(Field::FILE_SORT_FLAG, $input)) {
699
            return $input;
700
        }
701
702
        $sorts = array_filter($input[Field::FILE_SORT_FLAG]);
703
704
        if (empty($sorts)) {
705
            return $input;
706
        }
707
708
        foreach ($sorts as $column => $order) {
709
            $input[$column] = $order;
710
        }
711
712
        request()->replace($input);
713
714
        return $input;
715
    }
716
717
    /**
718
     * Handle orderable update.
719
     *
720
     * @param int   $id
721
     * @param array $input
722
     *
723
     * @return bool
724
     */
725
    protected function handleOrderable($id, array $input = [])
726
    {
727
        if (array_key_exists('_orderable', $input)) {
728
            $model = $this->model->find($id);
729
730
            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...
731
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
732
733
                return true;
734
            }
735
        }
736
737
        return false;
738
    }
739
740
    /**
741
     * Update relation data.
742
     *
743
     * @param array $relationsData
744
     *
745
     * @return void
746
     */
747
    protected function updateRelation($relationsData)
748
    {
749
        foreach ($relationsData as $name => $values) {
750
            if (!method_exists($this->model, $name)) {
751
                continue;
752
            }
753
754
            $relation = $this->model->$name();
755
756
            $oneToOneRelation = $relation instanceof Relations\HasOne
757
                || $relation instanceof Relations\MorphOne
758
                || $relation instanceof Relations\BelongsTo;
759
760
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
761
762
            if (empty($prepared)) {
763
                continue;
764
            }
765
766
            switch (true) {
767
                case $relation instanceof Relations\BelongsToMany:
768
                case $relation instanceof Relations\MorphToMany:
769
                    if (isset($prepared[$name])) {
770
                        $relation->sync($prepared[$name]);
771
                    }
772
                    break;
773
                case $relation instanceof Relations\HasOne:
774
                case $relation instanceof Relations\MorphOne:
775
                    $related = $this->model->getRelationValue($name) ?: $relation->getRelated();
776
777
                    foreach ($prepared[$name] as $column => $value) {
778
                        $related->setAttribute($column, $value);
779
                    }
780
781
                    // save child
782
                    $relation->save($related);
783
                    break;
784
                case $relation instanceof Relations\BelongsTo:
785
                case $relation instanceof Relations\MorphTo:
786
                    $related = $this->model->getRelationValue($name) ?: $relation->getRelated();
787
788
                    foreach ($prepared[$name] as $column => $value) {
789
                        $related->setAttribute($column, $value);
790
                    }
791
792
                    // save parent
793
                    $related->save();
794
795
                    // save child (self)
796
                    $relation->associate($related)->save();
797
                    break;
798
                case $relation instanceof Relations\HasMany:
799
                case $relation instanceof Relations\MorphMany:
800
                    foreach ($prepared[$name] as $related) {
801
                        /** @var Relations\HasOneOrMany $relation */
802
                        $relation = $this->model->$name();
803
804
                        $keyName = $relation->getRelated()->getKeyName();
805
806
                        /** @var Model $child */
807
                        $child = $relation->findOrNew(Arr::get($related, $keyName));
808
809
                        if (Arr::get($related, static::REMOVE_FLAG_NAME) == 1) {
810
                            $child->delete();
811
                            continue;
812
                        }
813
814
                        Arr::forget($related, static::REMOVE_FLAG_NAME);
815
816
                        $child->fill($related);
817
818
                        $child->save();
819
                    }
820
                    break;
821
            }
822
        }
823
    }
824
825
    /**
826
     * Prepare input data for update.
827
     *
828
     * @param array $updates
829
     * @param bool  $oneToOneRelation If column is one-to-one relation.
830
     *
831
     * @return array
832
     */
833
    protected function prepareUpdate(array $updates, $oneToOneRelation = false): array
834
    {
835
        $prepared = [];
836
837
        /** @var Field $field */
838
        foreach ($this->fields() as $field) {
839
            $columns = $field->column();
840
841
            // If column not in input array data, then continue.
842
            if (!Arr::has($updates, $columns)) {
843
                continue;
844
            }
845
846
            if ($this->isInvalidColumn($columns, $oneToOneRelation || $field->isJsonType)) {
847
                continue;
848
            }
849
850
            $value = $this->getDataByColumn($updates, $columns);
851
852
            $value = $field->prepare($value);
853
854 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...
855
                foreach ($columns as $name => $column) {
856
                    Arr::set($prepared, $column, $value[$name]);
857
                }
858
            } elseif (is_string($columns)) {
859
                Arr::set($prepared, $columns, $value);
860
            }
861
        }
862
863
        return $prepared;
864
    }
865
866
    /**
867
     * @param string|array $columns
868
     * @param bool         $containsDot
869
     *
870
     * @return bool
871
     */
872
    protected function isInvalidColumn($columns, $containsDot = false): bool
873
    {
874
        foreach ((array) $columns as $column) {
875
            if ((!$containsDot && Str::contains($column, '.')) ||
876
                ($containsDot && !Str::contains($column, '.'))) {
877
                return true;
878
            }
879
        }
880
881
        return false;
882
    }
883
884
    /**
885
     * Prepare input data for insert.
886
     *
887
     * @param $inserts
888
     *
889
     * @return array
890
     */
891
    protected function prepareInsert($inserts): array
892
    {
893
        if ($this->isHasOneRelation($inserts)) {
894
            $inserts = Arr::dot($inserts);
895
        }
896
897
        foreach ($inserts as $column => $value) {
898
            if (($field = $this->getFieldByColumn($column)) === null) {
899
                unset($inserts[$column]);
900
                continue;
901
            }
902
903
            $inserts[$column] = $field->prepare($value);
904
        }
905
906
        $prepared = [];
907
908
        foreach ($inserts as $key => $value) {
909
            Arr::set($prepared, $key, $value);
910
        }
911
912
        return $prepared;
913
    }
914
915
    /**
916
     * Is input data is has-one relation.
917
     *
918
     * @param array $inserts
919
     *
920
     * @return bool
921
     */
922
    protected function isHasOneRelation($inserts): bool
923
    {
924
        $first = current($inserts);
925
926
        if (!is_array($first)) {
927
            return false;
928
        }
929
930
        if (is_array(current($first))) {
931
            return false;
932
        }
933
934
        return Arr::isAssoc($first);
935
    }
936
937
    /**
938
     * Ignore fields to save.
939
     *
940
     * @param string|array $fields
941
     *
942
     * @return $this
943
     */
944
    public function ignore($fields): self
945
    {
946
        $this->ignored = array_merge($this->ignored, (array) $fields);
947
948
        return $this;
949
    }
950
951
    /**
952
     * @param array        $data
953
     * @param string|array $columns
954
     *
955
     * @return array|mixed
956
     */
957 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...
958
    {
959
        if (is_string($columns)) {
960
            return Arr::get($data, $columns);
961
        }
962
963
        if (is_array($columns)) {
964
            $value = [];
965
            foreach ($columns as $name => $column) {
966
                if (!Arr::has($data, $column)) {
967
                    continue;
968
                }
969
                $value[$name] = Arr::get($data, $column);
970
            }
971
972
            return $value;
973
        }
974
    }
975
976
    /**
977
     * Find field object by column.
978
     *
979
     * @param $column
980
     *
981
     * @return mixed
982
     */
983
    protected function getFieldByColumn($column)
984
    {
985
        return $this->fields()->first(
986
            function (Field $field) use ($column) {
987
                if (is_array($field->column())) {
988
                    return in_array($column, $field->column());
989
                }
990
991
                return $field->column() == $column;
992
            }
993
        );
994
    }
995
996
    /**
997
     * Set original data for each field.
998
     *
999
     * @return void
1000
     */
1001
    protected function setFieldOriginalValue()
1002
    {
1003
        $values = $this->model->toArray();
1004
1005
        $this->fields()->each(function (Field $field) use ($values) {
1006
            $field->setOriginal($values);
1007
        });
1008
    }
1009
1010
    /**
1011
     * Set all fields value in form.
1012
     *
1013
     * @param $id
1014
     *
1015
     * @return void
1016
     */
1017
    protected function setFieldValue($id)
1018
    {
1019
        $relations = $this->getRelations();
1020
1021
        $builder = $this->model();
1022
1023
        if ($this->isSoftDeletes) {
1024
            $builder = $builder->withTrashed();
1025
        }
1026
1027
        $this->model = $builder->with($relations)->findOrFail($id);
1028
1029
        $this->callEditing();
1030
1031
        $data = $this->model->toArray();
1032
1033
        $this->fields()->each(function (Field $field) use ($data) {
1034
            if (!in_array($field->column(), $this->ignored, true)) {
1035
                $field->fill($data);
1036
            }
1037
        });
1038
    }
1039
1040
    /**
1041
     * Add a fieldset to form.
1042
     *
1043
     * @param string  $title
1044
     * @param Closure $setCallback
1045
     *
1046
     * @return Field\Fieldset
1047
     */
1048 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...
1049
    {
1050
        $fieldset = new Field\Fieldset();
1051
1052
        $this->html($fieldset->start($title))->plain();
1053
1054
        $setCallback($this);
1055
1056
        $this->html($fieldset->end())->plain();
1057
1058
        return $fieldset;
1059
    }
1060
1061
    /**
1062
     * Get validation messages.
1063
     *
1064
     * @param array $input
1065
     *
1066
     * @return MessageBag|bool
1067
     */
1068
    public function validationMessages($input)
1069
    {
1070
        $failedValidators = [];
1071
1072
        /** @var Field $field */
1073 View Code Duplication
        foreach ($this->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...
1074
            if (!$validator = $field->getValidator($input)) {
1075
                continue;
1076
            }
1077
1078
            if (($validator instanceof Validator) && !$validator->passes()) {
1079
                $failedValidators[] = $validator;
1080
            }
1081
        }
1082
1083
        $message = $this->mergeValidationMessages($failedValidators);
1084
1085
        return $message->any() ? $message : false;
1086
    }
1087
1088
    /**
1089
     * Merge validation messages from input validators.
1090
     *
1091
     * @param \Illuminate\Validation\Validator[] $validators
1092
     *
1093
     * @return MessageBag
1094
     */
1095
    protected function mergeValidationMessages($validators): MessageBag
1096
    {
1097
        $messageBag = new MessageBag();
1098
1099
        foreach ($validators as $validator) {
1100
            $messageBag = $messageBag->merge($validator->messages());
1101
        }
1102
1103
        return $messageBag;
1104
    }
1105
1106
    /**
1107
     * Get all relations of model from callable.
1108
     *
1109
     * @return array
1110
     */
1111
    public function getRelations(): array
1112
    {
1113
        $relations = $columns = [];
1114
1115
        /** @var Field $field */
1116
        foreach ($this->fields() as $field) {
1117
            $columns[] = $field->column();
1118
        }
1119
1120
        foreach (Arr::flatten($columns) as $column) {
0 ignored issues
show
Documentation introduced by
$columns is of type array, but the function expects a object<Illuminate\Support\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
1121
            if (Str::contains($column, '.')) {
1122
                list($relation) = explode('.', $column);
1123
1124
                if (method_exists($this->model, $relation) &&
1125
                    $this->model->$relation() instanceof Relations\Relation
1126
                ) {
1127
                    $relations[] = $relation;
1128
                }
1129
            } elseif (method_exists($this->model, $column) &&
1130
                !method_exists(Model::class, $column)
1131
            ) {
1132
                $relations[] = $column;
1133
            }
1134
        }
1135
1136
        return array_unique($relations);
1137
    }
1138
1139
    /**
1140
     * Set action for form.
1141
     *
1142
     * @param string $action
1143
     *
1144
     * @return $this
1145
     */
1146
    public function setAction($action): self
1147
    {
1148
        $this->builder()->setAction($action);
1149
1150
        return $this;
1151
    }
1152
1153
    /**
1154
     * Set field and label width in current form.
1155
     *
1156
     * @param int $fieldWidth
1157
     * @param int $labelWidth
1158
     *
1159
     * @return $this
1160
     */
1161
    public function setWidth($fieldWidth = 8, $labelWidth = 2): self
1162
    {
1163
        $this->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1164
            /* @var Field $field  */
1165
            $field->setWidth($fieldWidth, $labelWidth);
1166
        });
1167
1168
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1169
1170
        return $this;
1171
    }
1172
1173
    /**
1174
     * Set view for form.
1175
     *
1176
     * @param string $view
1177
     *
1178
     * @return $this
1179
     */
1180
    public function setView($view): self
1181
    {
1182
        $this->builder()->setView($view);
1183
1184
        return $this;
1185
    }
1186
1187
    /**
1188
     * Set title for form.
1189
     *
1190
     * @param string $title
1191
     *
1192
     * @return $this
1193
     */
1194
    public function setTitle($title = ''): self
1195
    {
1196
        $this->builder()->setTitle($title);
1197
1198
        return $this;
1199
    }
1200
1201
    /**
1202
     * Set a submit confirm.
1203
     *
1204
     * @param string $message
1205
     * @param string $on
1206
     *
1207
     * @return $this
1208
     */
1209
    public function confirm(string $message, $on = null)
1210
    {
1211
        if ($on && !in_array($on, ['create', 'edit'])) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $on of type string|null is loosely compared to true; this is ambiguous if the string can be empty. You might want to explicitly use !== null instead.

In PHP, under loose comparison (like ==, or !=, or switch conditions), values of different types might be equal.

For string values, the empty string '' is a special case, in particular the following results might be unexpected:

''   == false // true
''   == null  // true
'ab' == false // false
'ab' == null  // false

// It is often better to use strict comparison
'' === false // false
'' === null  // false
Loading history...
1212
            throw new \InvalidArgumentException("The second paramater `\$on` must be one of ['create', 'edit']");
1213
        }
1214
1215
        if ($on == 'create' && !$this->isCreating()) {
1216
            return;
1217
        }
1218
1219
        if ($on == 'edit' && !$this->isEditing()) {
1220
            return;
1221
        }
1222
1223
        $this->builder()->confirm($message);
1224
1225
        return $this;
1226
    }
1227
1228
    /**
1229
     * Add a row in form.
1230
     *
1231
     * @param Closure $callback
1232
     *
1233
     * @return $this
1234
     */
1235
    public function row(Closure $callback): self
1236
    {
1237
        $this->rows[] = new Row($callback, $this);
1238
1239
        return $this;
1240
    }
1241
1242
    /**
1243
     * Tools setting for form.
1244
     *
1245
     * @param Closure $callback
1246
     */
1247
    public function tools(Closure $callback)
1248
    {
1249
        $callback->call($this, $this->builder->getTools());
1250
    }
1251
1252
    /**
1253
     * @param Closure|null $callback
1254
     *
1255
     * @return Form\Tools
1256
     */
1257 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...
1258
    {
1259
        if (func_num_args() === 0) {
1260
            return $this->builder->getTools();
1261
        }
1262
1263
        $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...
1264
    }
1265
1266
    /**
1267
     * Indicates if current form page is creating.
1268
     *
1269
     * @return bool
1270
     */
1271
    public function isCreating(): bool
1272
    {
1273
        return Str::endsWith(\request()->route()->getName(), ['.create', '.store']);
1274
    }
1275
1276
    /**
1277
     * Indicates if current form page is editing.
1278
     *
1279
     * @return bool
1280
     */
1281
    public function isEditing(): bool
1282
    {
1283
        return Str::endsWith(\request()->route()->getName(), ['.edit', '.update']);
1284
    }
1285
1286
    /**
1287
     * Disable form submit.
1288
     *
1289
     * @param bool $disable
1290
     *
1291
     * @return $this
1292
     *
1293
     * @deprecated
1294
     */
1295
    public function disableSubmit(bool $disable = true): self
1296
    {
1297
        $this->builder()->getFooter()->disableSubmit($disable);
1298
1299
        return $this;
1300
    }
1301
1302
    /**
1303
     * Disable form reset.
1304
     *
1305
     * @param bool $disable
1306
     *
1307
     * @return $this
1308
     *
1309
     * @deprecated
1310
     */
1311
    public function disableReset(bool $disable = true): self
1312
    {
1313
        $this->builder()->getFooter()->disableReset($disable);
1314
1315
        return $this;
1316
    }
1317
1318
    /**
1319
     * Disable View Checkbox on footer.
1320
     *
1321
     * @param bool $disable
1322
     *
1323
     * @return $this
1324
     */
1325
    public function disableViewCheck(bool $disable = true): self
1326
    {
1327
        $this->builder()->getFooter()->disableViewCheck($disable);
1328
1329
        return $this;
1330
    }
1331
1332
    /**
1333
     * Disable Editing Checkbox on footer.
1334
     *
1335
     * @param bool $disable
1336
     *
1337
     * @return $this
1338
     */
1339
    public function disableEditingCheck(bool $disable = true): self
1340
    {
1341
        $this->builder()->getFooter()->disableEditingCheck($disable);
1342
1343
        return $this;
1344
    }
1345
1346
    /**
1347
     * Disable Creating Checkbox on footer.
1348
     *
1349
     * @param bool $disable
1350
     *
1351
     * @return $this
1352
     */
1353
    public function disableCreatingCheck(bool $disable = true): self
1354
    {
1355
        $this->builder()->getFooter()->disableCreatingCheck($disable);
1356
1357
        return $this;
1358
    }
1359
1360
    /**
1361
     * Footer setting for form.
1362
     *
1363
     * @param Closure $callback
1364
     *
1365
     * @return \Encore\Admin\Form\Footer
1366
     */
1367 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...
1368
    {
1369
        if (func_num_args() === 0) {
1370
            return $this->builder()->getFooter();
1371
        }
1372
1373
        $callback($this->builder()->getFooter());
1374
    }
1375
1376
    /**
1377
     * Get current resource route url.
1378
     *
1379
     * @param int $slice
1380
     *
1381
     * @return string
1382
     */
1383
    public function resource($slice = -2): string
1384
    {
1385
        $segments = explode('/', trim(\request()->getUri(), '/'));
1386
1387
        if ($slice !== 0) {
1388
            $segments = array_slice($segments, 0, $slice);
1389
        }
1390
1391
        return implode('/', $segments);
1392
    }
1393
1394
    /**
1395
     * Render the form contents.
1396
     *
1397
     * @return string
1398
     */
1399
    public function render()
1400
    {
1401
        try {
1402
            return $this->builder->render();
1403
        } catch (\Exception $e) {
1404
            return Handler::renderException($e);
1405
        }
1406
    }
1407
1408
    /**
1409
     * Get or set input data.
1410
     *
1411
     * @param string $key
1412
     * @param null   $value
1413
     *
1414
     * @return array|mixed
1415
     */
1416
    public function input($key, $value = null)
1417
    {
1418
        if ($value === null) {
1419
            return Arr::get($this->inputs, $key);
1420
        }
1421
1422
        return Arr::set($this->inputs, $key, $value);
1423
    }
1424
1425
    /**
1426
     * Add a new layout column.
1427
     *
1428
     * @param int      $width
1429
     * @param \Closure $closure
1430
     *
1431
     * @return $this
1432
     */
1433 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...
1434
    {
1435
        $width = $width < 1 ? round(12 * $width) : $width;
1436
1437
        $this->layout->column($width, $closure);
1438
1439
        return $this;
1440
    }
1441
1442
    /**
1443
     * Initialize filter layout.
1444
     */
1445
    protected function initLayout()
1446
    {
1447
        $this->layout = new Layout($this);
1448
    }
1449
1450
    /**
1451
     * Getter.
1452
     *
1453
     * @param string $name
1454
     *
1455
     * @return array|mixed
1456
     */
1457
    public function __get($name)
1458
    {
1459
        return $this->input($name);
1460
    }
1461
1462
    /**
1463
     * Setter.
1464
     *
1465
     * @param string $name
1466
     * @param mixed  $value
1467
     *
1468
     * @return array
1469
     */
1470
    public function __set($name, $value)
1471
    {
1472
        return Arr::set($this->inputs, $name, $value);
1473
    }
1474
1475
    /**
1476
     * __isset.
1477
     *
1478
     * @param string $name
1479
     *
1480
     * @return bool
1481
     */
1482
    public function __isset($name)
1483
    {
1484
        return isset($this->inputs[$name]);
1485
    }
1486
1487
    /**
1488
     * Generate a Field object and add to form builder if Field exists.
1489
     *
1490
     * @param string $method
1491
     * @param array  $arguments
1492
     *
1493
     * @return Field
1494
     */
1495
    public function __call($method, $arguments)
1496
    {
1497
        if ($className = static::findFieldClass($method)) {
1498
            $column = Arr::get($arguments, 0, ''); //[0];
1499
1500
            $element = new $className($column, array_slice($arguments, 1));
1501
1502
            $this->pushField($element);
1503
1504
            return $element;
1505
        }
1506
1507
        admin_error('Error', "Field type [$method] does not exist.");
1508
1509
        return new Field\Nullable();
1510
    }
1511
1512
    /**
1513
     * @return Layout
1514
     */
1515
    public function getLayout(): Layout
1516
    {
1517
        return $this->layout;
1518
    }
1519
}
1520