Completed
Push — master ( c833c1...b511be )
by Song
03:16
created

Form   F

Complexity

Total Complexity 193

Size/Duplication

Total Lines 1477
Duplicated Lines 6.3 %

Coupling/Cohesion

Components 1
Dependencies 28

Importance

Changes 0
Metric Value
dl 93
loc 1477
rs 0.8
c 0
b 0
f 0
wmc 193
lcom 1
cbo 28

67 Methods

Rating   Name   Duplication   Size   Complexity  
B store() 11 35 6
A __set() 0 4 1
A __call() 0 16 2
A prepareInsert() 0 23 5
A isHasOneRelation() 0 14 3
A handleEditable() 0 12 2
A handleFileDelete() 0 11 2
A disableCreatingCheck() 0 6 1
A row() 0 6 1
A tools() 0 4 1
B update() 12 57 10
A redirectAfterStore() 0 8 1
A redirectAfterUpdate() 0 6 1
A redirectAfterSaving() 0 19 5
A isEditable() 0 4 1
A handleColumnUpdates() 0 17 2
A handleFileSort() 0 20 4
A handleOrderable() 0 14 4
A prepare() 0 16 3
A removeIgnoredFields() 0 6 1
A resource() 0 10 2
A render() 0 8 2
A input() 0 8 2
A disableSubmit() 0 6 1
A disableReset() 0 6 1
A disableViewCheck() 0 6 1
A disableEditingCheck() 0 6 1
A __construct() 0 16 2
A pushField() 0 12 1
A model() 0 4 1
A builder() 0 4 1
A fields() 0 4 1
A edit() 0 9 1
A tab() 0 6 1
A getTab() 0 4 1
A setTab() 0 8 2
B destroy() 0 44 8
A deleteFiles() 0 17 3
A responseValidationError() 0 12 3
A ajaxResponse() 0 15 3
B applayFieldDisplay() 0 29 7
A getRelationInputs() 0 17 5
D updateRelation() 0 79 22
B prepareUpdate() 7 32 8
A isInvalidColumn() 0 11 6
A ignore() 0 6 1
A getDataByColumn() 18 18 5
A getFieldByColumn() 0 12 2
A setFieldOriginalValue() 0 8 1
A setFieldValue() 0 22 3
A fieldset() 12 12 1
A validationMessages() 9 19 6
A mergeValidationMessages() 0 10 2
B getRelations() 0 27 8
A setAction() 0 6 1
A setWidth() 0 11 1
A setView() 0 6 1
A setTitle() 0 6 1
B confirm() 0 18 7
A header() 8 8 2
A isCreating() 0 4 1
A isEditing() 0 4 1
A footer() 8 8 2
A column() 8 8 2
A initLayout() 0 4 1
A __get() 0 4 1
A getLayout() 0 4 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like Form often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Form, and based on these observations, apply Extract Interface, too.

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::capture();
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);
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
                        foreach ($related as $colum => $value) {
817
                            $child->setAttribute($colum, $value);
818
                        }
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
                    $this->model->$relation() instanceof Relations\Relation
1128
                ) {
1129
                    $relations[] = $relation;
1130
                }
1131
            } elseif (method_exists($this->model, $column) &&
1132
                !method_exists(Model::class, $column)
1133
            ) {
1134
                $relations[] = $column;
1135
            }
1136
        }
1137
1138
        return array_unique($relations);
1139
    }
1140
1141
    /**
1142
     * Set action for form.
1143
     *
1144
     * @param string $action
1145
     *
1146
     * @return $this
1147
     */
1148
    public function setAction($action): self
1149
    {
1150
        $this->builder()->setAction($action);
1151
1152
        return $this;
1153
    }
1154
1155
    /**
1156
     * Set field and label width in current form.
1157
     *
1158
     * @param int $fieldWidth
1159
     * @param int $labelWidth
1160
     *
1161
     * @return $this
1162
     */
1163
    public function setWidth($fieldWidth = 8, $labelWidth = 2): self
1164
    {
1165
        $this->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1166
            /* @var Field $field  */
1167
            $field->setWidth($fieldWidth, $labelWidth);
1168
        });
1169
1170
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1171
1172
        return $this;
1173
    }
1174
1175
    /**
1176
     * Set view for form.
1177
     *
1178
     * @param string $view
1179
     *
1180
     * @return $this
1181
     */
1182
    public function setView($view): self
1183
    {
1184
        $this->builder()->setView($view);
1185
1186
        return $this;
1187
    }
1188
1189
    /**
1190
     * Set title for form.
1191
     *
1192
     * @param string $title
1193
     *
1194
     * @return $this
1195
     */
1196
    public function setTitle($title = ''): self
1197
    {
1198
        $this->builder()->setTitle($title);
1199
1200
        return $this;
1201
    }
1202
1203
    /**
1204
     * Set a submit confirm.
1205
     *
1206
     * @param string $message
1207
     * @param string $on
1208
     *
1209
     * @return $this
1210
     */
1211
    public function confirm(string $message, $on = null)
1212
    {
1213
        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...
1214
            throw new \InvalidArgumentException("The second paramater `\$on` must be one of ['create', 'edit']");
1215
        }
1216
1217
        if ($on == 'create' && !$this->isCreating()) {
1218
            return;
1219
        }
1220
1221
        if ($on == 'edit' && !$this->isEditing()) {
1222
            return;
1223
        }
1224
1225
        $this->builder()->confirm($message);
1226
1227
        return $this;
1228
    }
1229
1230
    /**
1231
     * Add a row in form.
1232
     *
1233
     * @param Closure $callback
1234
     *
1235
     * @return $this
1236
     */
1237
    public function row(Closure $callback): self
1238
    {
1239
        $this->rows[] = new Row($callback, $this);
1240
1241
        return $this;
1242
    }
1243
1244
    /**
1245
     * Tools setting for form.
1246
     *
1247
     * @param Closure $callback
1248
     */
1249
    public function tools(Closure $callback)
1250
    {
1251
        $callback->call($this, $this->builder->getTools());
1252
    }
1253
1254
    /**
1255
     * @param Closure|null $callback
1256
     *
1257
     * @return Form\Tools
1258
     */
1259 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...
1260
    {
1261
        if (func_num_args() === 0) {
1262
            return $this->builder->getTools();
1263
        }
1264
1265
        $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...
1266
    }
1267
1268
    /**
1269
     * Indicates if current form page is creating.
1270
     *
1271
     * @return bool
1272
     */
1273
    public function isCreating(): bool
1274
    {
1275
        return Str::endsWith(\request()->route()->getName(), ['.create', '.store']);
1276
    }
1277
1278
    /**
1279
     * Indicates if current form page is editing.
1280
     *
1281
     * @return bool
1282
     */
1283
    public function isEditing(): bool
1284
    {
1285
        return Str::endsWith(\request()->route()->getName(), ['.edit', '.update']);
1286
    }
1287
1288
    /**
1289
     * Disable form submit.
1290
     *
1291
     * @param bool $disable
1292
     *
1293
     * @return $this
1294
     *
1295
     * @deprecated
1296
     */
1297
    public function disableSubmit(bool $disable = true): self
1298
    {
1299
        $this->builder()->getFooter()->disableSubmit($disable);
1300
1301
        return $this;
1302
    }
1303
1304
    /**
1305
     * Disable form reset.
1306
     *
1307
     * @param bool $disable
1308
     *
1309
     * @return $this
1310
     *
1311
     * @deprecated
1312
     */
1313
    public function disableReset(bool $disable = true): self
1314
    {
1315
        $this->builder()->getFooter()->disableReset($disable);
1316
1317
        return $this;
1318
    }
1319
1320
    /**
1321
     * Disable View Checkbox on footer.
1322
     *
1323
     * @param bool $disable
1324
     *
1325
     * @return $this
1326
     */
1327
    public function disableViewCheck(bool $disable = true): self
1328
    {
1329
        $this->builder()->getFooter()->disableViewCheck($disable);
1330
1331
        return $this;
1332
    }
1333
1334
    /**
1335
     * Disable Editing Checkbox on footer.
1336
     *
1337
     * @param bool $disable
1338
     *
1339
     * @return $this
1340
     */
1341
    public function disableEditingCheck(bool $disable = true): self
1342
    {
1343
        $this->builder()->getFooter()->disableEditingCheck($disable);
1344
1345
        return $this;
1346
    }
1347
1348
    /**
1349
     * Disable Creating Checkbox on footer.
1350
     *
1351
     * @param bool $disable
1352
     *
1353
     * @return $this
1354
     */
1355
    public function disableCreatingCheck(bool $disable = true): self
1356
    {
1357
        $this->builder()->getFooter()->disableCreatingCheck($disable);
1358
1359
        return $this;
1360
    }
1361
1362
    /**
1363
     * Footer setting for form.
1364
     *
1365
     * @param Closure $callback
1366
     *
1367
     * @return \Encore\Admin\Form\Footer
1368
     */
1369 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...
1370
    {
1371
        if (func_num_args() === 0) {
1372
            return $this->builder()->getFooter();
1373
        }
1374
1375
        $callback($this->builder()->getFooter());
1376
    }
1377
1378
    /**
1379
     * Get current resource route url.
1380
     *
1381
     * @param int $slice
1382
     *
1383
     * @return string
1384
     */
1385
    public function resource($slice = -2): string
1386
    {
1387
        $segments = explode('/', trim(\request()->getUri(), '/'));
1388
1389
        if ($slice !== 0) {
1390
            $segments = array_slice($segments, 0, $slice);
1391
        }
1392
1393
        return implode('/', $segments);
1394
    }
1395
1396
    /**
1397
     * Render the form contents.
1398
     *
1399
     * @return string
1400
     */
1401
    public function render()
1402
    {
1403
        try {
1404
            return $this->builder->render();
1405
        } catch (\Exception $e) {
1406
            return Handler::renderException($e);
1407
        }
1408
    }
1409
1410
    /**
1411
     * Get or set input data.
1412
     *
1413
     * @param string $key
1414
     * @param null   $value
1415
     *
1416
     * @return array|mixed
1417
     */
1418
    public function input($key, $value = null)
1419
    {
1420
        if ($value === null) {
1421
            return Arr::get($this->inputs, $key);
1422
        }
1423
1424
        return Arr::set($this->inputs, $key, $value);
1425
    }
1426
1427
    /**
1428
     * Add a new layout column.
1429
     *
1430
     * @param int      $width
1431
     * @param \Closure $closure
1432
     *
1433
     * @return $this
1434
     */
1435 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...
1436
    {
1437
        $width = $width < 1 ? round(12 * $width) : $width;
1438
1439
        $this->layout->column($width, $closure);
1440
1441
        return $this;
1442
    }
1443
1444
    /**
1445
     * Initialize filter layout.
1446
     */
1447
    protected function initLayout()
1448
    {
1449
        $this->layout = new Layout($this);
1450
    }
1451
1452
    /**
1453
     * Getter.
1454
     *
1455
     * @param string $name
1456
     *
1457
     * @return array|mixed
1458
     */
1459
    public function __get($name)
1460
    {
1461
        return $this->input($name);
1462
    }
1463
1464
    /**
1465
     * Setter.
1466
     *
1467
     * @param string $name
1468
     * @param mixed  $value
1469
     *
1470
     * @return array
1471
     */
1472
    public function __set($name, $value)
1473
    {
1474
        return Arr::set($this->inputs, $name, $value);
1475
    }
1476
1477
    /**
1478
     * Generate a Field object and add to form builder if Field exists.
1479
     *
1480
     * @param string $method
1481
     * @param array  $arguments
1482
     *
1483
     * @return Field
1484
     */
1485
    public function __call($method, $arguments)
1486
    {
1487
        if ($className = static::findFieldClass($method)) {
1488
            $column = Arr::get($arguments, 0, ''); //[0];
1489
1490
            $element = new $className($column, array_slice($arguments, 1));
1491
1492
            $this->pushField($element);
1493
1494
            return $element;
1495
        }
1496
1497
        admin_error('Error', "Field type [$method] does not exist.");
1498
1499
        return new Field\Nullable();
1500
    }
1501
1502
    /**
1503
     * @return Layout
1504
     */
1505
    public function getLayout(): Layout
1506
    {
1507
        return $this->layout;
1508
    }
1509
}
1510