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