Completed
Push — master ( dc194c...14cc6b )
by Song
02:52 queued 16s
created

Form::getRelationInputs()   A

Complexity

Conditions 5
Paths 4

Size

Total Lines 18

Duplication

Lines 0
Ratio 0 %

Importance

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

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
415
    }
416
417
    /**
418
     * Remove files in record.
419
     *
420
     * @param Model $model
421
     * @param bool  $forceDelete
422
     */
423
    protected function deleteFiles(Model $model, $forceDelete = false)
424
    {
425
        // If it's a soft delete, the files in the data will not be deleted.
426
        if (!$forceDelete && $this->isSoftDeletes) {
427
            return;
428
        }
429
430
        $data = $model->toArray();
431
432
        $this->builder->fields()->filter(function ($field) {
433
            return $field instanceof Field\File;
434
        })->each(function (Field\File $file) use ($data) {
435
            $file->setOriginal($data);
436
437
            $file->destroy();
438
        });
439
    }
440
441
    /**
442
     * Store a new record.
443
     *
444
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector|\Illuminate\Http\JsonResponse
445
     */
446
    public function store()
447
    {
448
        $data = \request()->all();
449
450
        // Handle validation errors.
451
        if ($validationMessages = $this->validationMessages($data)) {
452
            return $this->responseValidationError($validationMessages);
453
        }
454
455
        if (($response = $this->prepare($data)) instanceof Response) {
456
            return $response;
457
        }
458
459 View Code Duplication
        DB::transaction(function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
460
            $inserts = $this->prepareInsert($this->updates);
461
462
            foreach ($inserts as $column => $value) {
463
                $this->model->setAttribute($column, $value);
464
            }
465
466
            $this->model->save();
467
468
            $this->updateRelation($this->relations);
469
        });
470
471
        if (($response = $this->callSaved()) instanceof Response) {
472
            return $response;
473
        }
474
475
        if ($response = $this->ajaxResponse(trans('admin.save_succeeded'))) {
476
            return $response;
477
        }
478
479
        return $this->redirectAfterStore();
480
    }
481
482
    /**
483
     * @param MessageBag $message
484
     *
485
     * @return $this|\Illuminate\Http\JsonResponse
486
     */
487
    protected function responseValidationError(MessageBag $message)
488
    {
489
        if (\request()->ajax() && !\request()->pjax()) {
490
            return response()->json([
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
491
                'status'     => false,
492
                'validation' => $message,
493
                'message'    => $message->first(),
494
            ]);
495
        }
496
497
        return back()->withInput()->withErrors($message);
0 ignored issues
show
Bug Best Practice introduced by
The return type of return back()->withInput()->withErrors($message); (Illuminate\Http\RedirectResponse) is incompatible with the return type documented by Encore\Admin\Form::responseValidationError of type Encore\Admin\Form|Illuminate\Http\JsonResponse.

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

Let’s take a look at an example:

class Author {
    private $name;

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

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

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

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

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

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

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

Loading history...
498
    }
499
500
    /**
501
     * Get ajax response.
502
     *
503
     * @param string $message
504
     *
505
     * @return bool|\Illuminate\Http\JsonResponse
506
     */
507
    protected function ajaxResponse($message)
508
    {
509
        $request = Request::capture();
510
511
        // ajax but not pjax
512
        if ($request->ajax() && !$request->pjax()) {
513
            return response()->json([
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
514
                'status'  => true,
515
                'message' => $message,
516
            ]);
517
        }
518
519
        return false;
520
    }
521
522
    /**
523
     * Prepare input data for insert or update.
524
     *
525
     * @param array $data
526
     *
527
     * @return mixed
528
     */
529
    protected function prepare($data = [])
530
    {
531
        if (($response = $this->callSubmitted()) instanceof Response) {
532
            return $response;
533
        }
534
535
        $this->inputs = array_merge($this->removeIgnoredFields($data), $this->inputs);
536
537
        if (($response = $this->callSaving()) instanceof Response) {
538
            return $response;
539
        }
540
541
        $this->relations = $this->getRelationInputs($this->inputs);
542
543
        $this->updates = Arr::except($this->inputs, array_keys($this->relations));
544
    }
545
546
    /**
547
     * Remove ignored fields from input.
548
     *
549
     * @param array $input
550
     *
551
     * @return array
552
     */
553
    protected function removeIgnoredFields($input): array
554
    {
555
        Arr::forget($input, $this->ignored);
556
557
        return $input;
558
    }
559
560
    /**
561
     * Get inputs for relations.
562
     *
563
     * @param array $inputs
564
     *
565
     * @return array
566
     */
567
    protected function getRelationInputs($inputs = []): array
568
    {
569
        $relations = [];
570
571
        foreach ($inputs as $column => $value) {
572
            if (method_exists($this->model, $column) ||
573
                method_exists($this->model, $column = Str::camel($column)))
574
            {
575
                $relation = call_user_func([$this->model, $column]);
576
577
                if ($relation instanceof Relations\Relation) {
578
                    $relations[$column] = $value;
579
                }
580
            }
581
        }
582
        
583
        return $relations;
584
    }
585
586
    /**
587
     * Handle update.
588
     *
589
     * @param int  $id
590
     * @param null $data
591
     *
592
     * @return bool|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\JsonResponse|\Illuminate\Http\RedirectResponse|\Illuminate\Http\Response|mixed|null|Response
593
     */
594
    public function update($id, $data = null)
595
    {
596
        $data = ($data) ?: request()->all();
597
598
        $isEditable = $this->isEditable($data);
599
600
        if (($data = $this->handleColumnUpdates($id, $data)) instanceof Response) {
601
            return $data;
602
        }
603
604
        /* @var Model $this->model */
605
        $builder = $this->model();
606
607
        if ($this->isSoftDeletes) {
608
            $builder = $builder->withTrashed();
609
        }
610
611
        $this->model = $builder->with($this->getRelations())->findOrFail($id);
612
613
        $this->setFieldOriginalValue();
614
615
        // Handle validation errors.
616
        if ($validationMessages = $this->validationMessages($data)) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 600 can also be of type object<Illuminate\Contra...outing\ResponseFactory>; however, Encore\Admin\Form::validationMessages() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
617
            if (!$isEditable) {
618
                return back()->withInput()->withErrors($validationMessages);
619
            }
620
621
            return response()->json(['errors' => Arr::dot($validationMessages->getMessages())], 422);
0 ignored issues
show
Bug introduced by
The method json does only exist in Illuminate\Contracts\Routing\ResponseFactory, but not in Illuminate\Http\Response.

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

Let’s take a look at an example:

class A
{
    public function foo() { }
}

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

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

Available Fixes

  1. Add an additional type-check:

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

    function someFunction(B $x) { /** ... */ }
    
Loading history...
622
        }
623
624
        if (($response = $this->prepare($data)) instanceof Response) {
0 ignored issues
show
Bug introduced by
It seems like $data defined by $this->handleColumnUpdates($id, $data) on line 600 can also be of type object<Illuminate\Contra...outing\ResponseFactory>; however, Encore\Admin\Form::prepare() does only seem to accept array, maybe add an additional type check?

If a method or function can return multiple different values and unless you are sure that you only can receive a single value in this context, we recommend to add an additional type check:

/**
 * @return array|string
 */
function returnsDifferentValues($x) {
    if ($x) {
        return 'foo';
    }

    return array();
}

$x = returnsDifferentValues($y);
if (is_array($x)) {
    // $x is an array.
}

If this a common case that PHP Analyzer should handle natively, please let us know by opening an issue.

Loading history...
625
            return $response;
626
        }
627
628 View Code Duplication
        DB::transaction(function () {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
629
            $updates = $this->prepareUpdate($this->updates);
630
631
            foreach ($updates as $column => $value) {
632
                /* @var Model $this->model */
633
                $this->model->setAttribute($column, $value);
634
            }
635
636
            $this->model->save();
637
638
            $this->updateRelation($this->relations);
639
        });
640
641
        if (($result = $this->callSaved()) instanceof Response) {
642
            return $result;
643
        }
644
645
        if ($response = $this->ajaxResponse(trans('admin.update_succeeded'))) {
646
            return $response;
647
        }
648
649
        return $this->redirectAfterUpdate($id);
650
    }
651
652
    /**
653
     * Get RedirectResponse after store.
654
     *
655
     * @return \Illuminate\Http\RedirectResponse
656
     */
657
    protected function redirectAfterStore()
658
    {
659
        $resourcesPath = $this->resource(0);
660
661
        $key = $this->model->getKey();
662
663
        return $this->redirectAfterSaving($resourcesPath, $key);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->redirectAfterSaving($resourcesPath, $key); of type Illuminate\Http\Redirect...nate\Routing\Redirector adds the type Illuminate\Routing\Redirector to the return on line 663 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterStore of type Illuminate\Http\RedirectResponse.
Loading history...
664
    }
665
666
    /**
667
     * Get RedirectResponse after update.
668
     *
669
     * @param mixed $key
670
     *
671
     * @return \Illuminate\Http\RedirectResponse
672
     */
673
    protected function redirectAfterUpdate($key)
674
    {
675
        $resourcesPath = $this->resource(-1);
676
677
        return $this->redirectAfterSaving($resourcesPath, $key);
0 ignored issues
show
Bug Compatibility introduced by
The expression $this->redirectAfterSaving($resourcesPath, $key); of type Illuminate\Http\Redirect...nate\Routing\Redirector adds the type Illuminate\Routing\Redirector to the return on line 677 which is incompatible with the return type documented by Encore\Admin\Form::redirectAfterUpdate of type Illuminate\Http\RedirectResponse.
Loading history...
678
    }
679
680
    /**
681
     * Get RedirectResponse after data saving.
682
     *
683
     * @param string $resourcesPath
684
     * @param string $key
685
     *
686
     * @return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
687
     */
688
    protected function redirectAfterSaving($resourcesPath, $key)
689
    {
690
        if (request('after-save') == 1) {
691
            // continue editing
692
            $url = rtrim($resourcesPath, '/')."/{$key}/edit";
693
        } elseif (request('after-save') == 2) {
694
            // continue creating
695
            $url = rtrim($resourcesPath, '/').'/create';
696
        } elseif (request('after-save') == 3) {
697
            // view resource
698
            $url = rtrim($resourcesPath, '/')."/{$key}";
699
        } else {
700
            $url = request(Builder::PREVIOUS_URL_KEY) ?: $resourcesPath;
701
        }
702
703
        admin_toastr(trans('admin.save_succeeded'));
704
705
        return redirect($url);
706
    }
707
708
    /**
709
     * Check if request is from editable.
710
     *
711
     * @param array $input
712
     *
713
     * @return bool
714
     */
715
    protected function isEditable(array $input = []): bool
716
    {
717
        return array_key_exists('_editable', $input);
718
    }
719
720
    /**
721
     * Handle updates for single column.
722
     *
723
     * @param int   $id
724
     * @param array $data
725
     *
726
     * @return array|\Illuminate\Contracts\Routing\ResponseFactory|\Illuminate\Http\Response|Response
727
     */
728
    protected function handleColumnUpdates($id, $data)
729
    {
730
        $data = $this->handleEditable($data);
731
732
        $data = $this->handleFileDelete($data);
733
734
        $data = $this->handleFileSort($data);
735
736
        if ($this->handleOrderable($id, $data)) {
737
            return response([
738
                'status'  => true,
739
                'message' => trans('admin.update_succeeded'),
740
            ]);
741
        }
742
743
        return $data;
744
    }
745
746
    /**
747
     * Handle editable update.
748
     *
749
     * @param array $input
750
     *
751
     * @return array
752
     */
753
    protected function handleEditable(array $input = []): array
754
    {
755
        if (array_key_exists('_editable', $input)) {
756
            $name = $input['name'];
757
            $value = $input['value'];
758
759
            Arr::forget($input, ['pk', 'value', 'name']);
760
            Arr::set($input, $name, $value);
761
        }
762
763
        return $input;
764
    }
765
766
    /**
767
     * @param array $input
768
     *
769
     * @return array
770
     */
771
    protected function handleFileDelete(array $input = []): array
772
    {
773
        if (array_key_exists(Field::FILE_DELETE_FLAG, $input)) {
774
            $input[Field::FILE_DELETE_FLAG] = $input['key'];
775
            unset($input['key']);
776
        }
777
778
        request()->replace($input);
779
780
        return $input;
781
    }
782
783
    /**
784
     * @param array $input
785
     *
786
     * @return array
787
     */
788
    protected function handleFileSort(array $input = []): array
789
    {
790
        if (!array_key_exists(Field::FILE_SORT_FLAG, $input)) {
791
            return $input;
792
        }
793
794
        $sorts = array_filter($input[Field::FILE_SORT_FLAG]);
795
796
        if (empty($sorts)) {
797
            return $input;
798
        }
799
800
        foreach ($sorts as $column => $order) {
801
            $input[$column] = $order;
802
        }
803
804
        request()->replace($input);
805
806
        return $input;
807
    }
808
809
    /**
810
     * Handle orderable update.
811
     *
812
     * @param int   $id
813
     * @param array $input
814
     *
815
     * @return bool
816
     */
817
    protected function handleOrderable($id, array $input = [])
818
    {
819
        if (array_key_exists('_orderable', $input)) {
820
            $model = $this->model->find($id);
821
822
            if ($model instanceof Sortable) {
0 ignored issues
show
Bug introduced by
The class Spatie\EloquentSortable\Sortable does not exist. Did you forget a USE statement, or did you not list all dependencies?

This error could be the result of:

1. Missing dependencies

PHP Analyzer uses your composer.json file (if available) to determine the dependencies of your project and to determine all the available classes and functions. It expects the composer.json to be in the root folder of your repository.

Are you sure this class is defined by one of your dependencies, or did you maybe not list a dependency in either the require or require-dev section?

2. Missing use statement

PHP does not complain about undefined classes in ìnstanceof checks. For example, the following PHP code will work perfectly fine:

if ($x instanceof DoesNotExist) {
    // Do something.
}

If you have not tested against this specific condition, such errors might go unnoticed.

Loading history...
823
                $input['_orderable'] == 1 ? $model->moveOrderUp() : $model->moveOrderDown();
824
825
                return true;
826
            }
827
        }
828
829
        return false;
830
    }
831
832
    /**
833
     * Update relation data.
834
     *
835
     * @param array $relationsData
836
     *
837
     * @return void
838
     */
839
    protected function updateRelation($relationsData)
840
    {
841
        foreach ($relationsData as $name => $values) {
842
            if (!method_exists($this->model, $name)) {
843
                continue;
844
            }
845
846
            $relation = $this->model->$name();
847
848
            $oneToOneRelation = $relation instanceof Relations\HasOne
849
                || $relation instanceof Relations\MorphOne
850
                || $relation instanceof Relations\BelongsTo;
851
852
            $prepared = $this->prepareUpdate([$name => $values], $oneToOneRelation);
853
854
            if (empty($prepared)) {
855
                continue;
856
            }
857
858
            switch (true) {
859
                case $relation instanceof Relations\BelongsToMany:
860
                case $relation instanceof Relations\MorphToMany:
861
                    if (isset($prepared[$name])) {
862
                        $relation->sync($prepared[$name]);
863
                    }
864
                    break;
865
                case $relation instanceof Relations\HasOne:
866
867
                    $related = $this->model->$name;
868
869
                    // if related is empty
870
                    if (is_null($related)) {
871
                        $related = $relation->getRelated();
872
                        $qualifiedParentKeyName = $relation->getQualifiedParentKeyName();
873
                        $localKey = Arr::last(explode('.', $qualifiedParentKeyName));
874
                        $related->{$relation->getForeignKeyName()} = $this->model->{$localKey};
875
                    }
876
877
                    foreach ($prepared[$name] as $column => $value) {
878
                        $related->setAttribute($column, $value);
879
                    }
880
881
                    $related->save();
882
                    break;
883
                case $relation instanceof Relations\BelongsTo:
884
                case $relation instanceof Relations\MorphTo:
885
886
                    $parent = $this->model->$name;
887
888
                    // if related is empty
889
                    if (is_null($parent)) {
890
                        $parent = $relation->getRelated();
891
                    }
892
893
                    foreach ($prepared[$name] as $column => $value) {
894
                        $parent->setAttribute($column, $value);
895
                    }
896
897
                    $parent->save();
898
899
                    // When in creating, associate two models
900
                    $foreignKeyMethod = version_compare(app()->version(), '5.8.0', '<') ? 'getForeignKey' : 'getForeignKeyName';
901
                    if (!$this->model->{$relation->{$foreignKeyMethod}()}) {
902
                        $this->model->{$relation->{$foreignKeyMethod}()} = $parent->getKey();
903
904
                        $this->model->save();
905
                    }
906
907
                    break;
908
                case $relation instanceof Relations\MorphOne:
909
                    $related = $this->model->$name;
910
                    if ($related === null) {
911
                        $related = $relation->make();
912
                    }
913
                    foreach ($prepared[$name] as $column => $value) {
914
                        $related->setAttribute($column, $value);
915
                    }
916
                    $related->save();
917
                    break;
918
                case $relation instanceof Relations\HasMany:
919
                case $relation instanceof Relations\MorphMany:
920
921
                    foreach ($prepared[$name] as $related) {
922
                        /** @var Relations\Relation $relation */
923
                        $relation = $this->model()->$name();
924
925
                        $keyName = $relation->getRelated()->getKeyName();
926
927
                        $instance = $relation->findOrNew(Arr::get($related, $keyName));
928
929
                        if ($related[static::REMOVE_FLAG_NAME] == 1) {
930
                            $instance->delete();
931
932
                            continue;
933
                        }
934
935
                        Arr::forget($related, static::REMOVE_FLAG_NAME);
936
937
                        $instance->fill($related);
938
939
                        $instance->save();
940
                    }
941
942
                    break;
943
            }
944
        }
945
    }
946
947
    /**
948
     * Prepare input data for update.
949
     *
950
     * @param array $updates
951
     * @param bool  $oneToOneRelation If column is one-to-one relation.
952
     *
953
     * @return array
954
     */
955
    protected function prepareUpdate(array $updates, $oneToOneRelation = false): array
956
    {
957
        $prepared = [];
958
959
        /** @var Field $field */
960
        foreach ($this->builder->fields() as $field) {
961
            $columns = $field->column();
962
963
            // If column not in input array data, then continue.
964
            if (!Arr::has($updates, $columns)) {
965
                continue;
966
            }
967
968
            if ($this->isInvalidColumn($columns, $oneToOneRelation || $field->isJsonType)) {
969
                continue;
970
            }
971
972
            $value = $this->getDataByColumn($updates, $columns);
973
974
            $value = $field->prepare($value);
975
976 View Code Duplication
            if (is_array($columns)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
977
                foreach ($columns as $name => $column) {
978
                    Arr::set($prepared, $column, $value[$name]);
979
                }
980
            } elseif (is_string($columns)) {
981
                Arr::set($prepared, $columns, $value);
982
            }
983
        }
984
985
        return $prepared;
986
    }
987
988
    /**
989
     * @param string|array $columns
990
     * @param bool         $containsDot
991
     *
992
     * @return bool
993
     */
994
    protected function isInvalidColumn($columns, $containsDot = false): bool
995
    {
996
        foreach ((array) $columns as $column) {
997
            if ((!$containsDot && Str::contains($column, '.')) ||
998
                ($containsDot && !Str::contains($column, '.'))) {
999
                return true;
1000
            }
1001
        }
1002
1003
        return false;
1004
    }
1005
1006
    /**
1007
     * Prepare input data for insert.
1008
     *
1009
     * @param $inserts
1010
     *
1011
     * @return array
1012
     */
1013
    protected function prepareInsert($inserts): array
1014
    {
1015
        if ($this->isHasOneRelation($inserts)) {
1016
            $inserts = Arr::dot($inserts);
1017
        }
1018
1019
        foreach ($inserts as $column => $value) {
1020
            if (($field = $this->getFieldByColumn($column)) === null) {
1021
                unset($inserts[$column]);
1022
                continue;
1023
            }
1024
1025
            $inserts[$column] = $field->prepare($value);
1026
        }
1027
1028
        $prepared = [];
1029
1030
        foreach ($inserts as $key => $value) {
1031
            Arr::set($prepared, $key, $value);
1032
        }
1033
1034
        return $prepared;
1035
    }
1036
1037
    /**
1038
     * Is input data is has-one relation.
1039
     *
1040
     * @param array $inserts
1041
     *
1042
     * @return bool
1043
     */
1044
    protected function isHasOneRelation($inserts): bool
1045
    {
1046
        $first = current($inserts);
1047
1048
        if (!is_array($first)) {
1049
            return false;
1050
        }
1051
1052
        if (is_array(current($first))) {
1053
            return false;
1054
        }
1055
1056
        return Arr::isAssoc($first);
1057
    }
1058
1059
    /**
1060
     * Ignore fields to save.
1061
     *
1062
     * @param string|array $fields
1063
     *
1064
     * @return $this
1065
     */
1066
    public function ignore($fields): self
1067
    {
1068
        $this->ignored = array_merge($this->ignored, (array) $fields);
1069
1070
        return $this;
1071
    }
1072
1073
    /**
1074
     * @param array        $data
1075
     * @param string|array $columns
1076
     *
1077
     * @return array|mixed
1078
     */
1079 View Code Duplication
    protected function getDataByColumn($data, $columns)
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1080
    {
1081
        if (is_string($columns)) {
1082
            return Arr::get($data, $columns);
1083
        }
1084
1085
        if (is_array($columns)) {
1086
            $value = [];
1087
            foreach ($columns as $name => $column) {
1088
                if (!Arr::has($data, $column)) {
1089
                    continue;
1090
                }
1091
                $value[$name] = Arr::get($data, $column);
1092
            }
1093
1094
            return $value;
1095
        }
1096
    }
1097
1098
    /**
1099
     * Find field object by column.
1100
     *
1101
     * @param $column
1102
     *
1103
     * @return mixed
1104
     */
1105
    protected function getFieldByColumn($column)
1106
    {
1107
        return $this->builder->fields()->first(
1108
            function (Field $field) use ($column) {
1109
                if (is_array($field->column())) {
1110
                    return in_array($column, $field->column());
1111
                }
1112
1113
                return $field->column() == $column;
1114
            }
1115
        );
1116
    }
1117
1118
    /**
1119
     * Set original data for each field.
1120
     *
1121
     * @return void
1122
     */
1123
    protected function setFieldOriginalValue()
1124
    {
1125
//        static::doNotSnakeAttributes($this->model);
1126
1127
        $values = $this->model->toArray();
1128
1129
        $this->builder->fields()->each(function (Field $field) use ($values) {
1130
            $field->setOriginal($values);
1131
        });
1132
    }
1133
1134
    /**
1135
     * Set all fields value in form.
1136
     *
1137
     * @param $id
1138
     *
1139
     * @return void
1140
     */
1141
    protected function setFieldValue($id)
1142
    {
1143
        $relations = $this->getRelations();
1144
1145
        $builder = $this->model();
1146
1147
        if ($this->isSoftDeletes) {
1148
            $builder = $builder->withTrashed();
1149
        }
1150
1151
        $this->model = $builder->with($relations)->findOrFail($id);
1152
1153
        $this->callEditing();
1154
1155
//        static::doNotSnakeAttributes($this->model);
1156
1157
        $data = $this->model->toArray();
1158
1159
        $this->builder->fields()->each(function (Field $field) use ($data) {
1160
            if (!in_array($field->column(), $this->ignored, true)) {
1161
                $field->fill($data);
1162
            }
1163
        });
1164
    }
1165
1166
    /**
1167
     * Add a fieldset to form.
1168
     *
1169
     * @param string  $title
1170
     * @param Closure $setCallback
1171
     *
1172
     * @return Field\Fieldset
1173
     */
1174 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...
1175
    {
1176
        $fieldset = new Field\Fieldset();
1177
1178
        $this->html($fieldset->start($title))->plain();
1179
1180
        $setCallback($this);
1181
1182
        $this->html($fieldset->end())->plain();
1183
1184
        return $fieldset;
1185
    }
1186
1187
    /**
1188
     * Don't snake case attributes.
1189
     *
1190
     * @param Model $model
1191
     *
1192
     * @return void
1193
     */
1194
    protected static function doNotSnakeAttributes(Model $model)
1195
    {
1196
        $class = get_class($model);
1197
1198
        $class::$snakeAttributes = false;
1199
    }
1200
1201
    /**
1202
     * Get validation messages.
1203
     *
1204
     * @param array $input
1205
     *
1206
     * @return MessageBag|bool
1207
     */
1208
    public function validationMessages($input)
1209
    {
1210
        $failedValidators = [];
1211
1212
        /** @var Field $field */
1213 View Code Duplication
        foreach ($this->builder->fields() as $field) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
1214
            if (!$validator = $field->getValidator($input)) {
1215
                continue;
1216
            }
1217
1218
            if (($validator instanceof Validator) && !$validator->passes()) {
1219
                $failedValidators[] = $validator;
1220
            }
1221
        }
1222
1223
        $message = $this->mergeValidationMessages($failedValidators);
1224
1225
        return $message->any() ? $message : false;
1226
    }
1227
1228
    /**
1229
     * Merge validation messages from input validators.
1230
     *
1231
     * @param \Illuminate\Validation\Validator[] $validators
1232
     *
1233
     * @return MessageBag
1234
     */
1235
    protected function mergeValidationMessages($validators): MessageBag
1236
    {
1237
        $messageBag = new MessageBag();
1238
1239
        foreach ($validators as $validator) {
1240
            $messageBag = $messageBag->merge($validator->messages());
1241
        }
1242
1243
        return $messageBag;
1244
    }
1245
1246
    /**
1247
     * Get all relations of model from callable.
1248
     *
1249
     * @return array
1250
     */
1251
    public function getRelations(): array
1252
    {
1253
        $relations = $columns = [];
1254
1255
        /** @var Field $field */
1256
        foreach ($this->builder->fields() as $field) {
1257
            $columns[] = $field->column();
1258
        }
1259
1260
        foreach (Arr::flatten($columns) as $column) {
1261
            if (Str::contains($column, '.')) {
1262
                list($relation) = explode('.', $column);
1263
1264
                if (method_exists($this->model, $relation) &&
1265
                    $this->model->$relation() instanceof Relations\Relation
1266
                ) {
1267
                    $relations[] = $relation;
1268
                }
1269
            } elseif (method_exists($this->model, $column) &&
1270
                !method_exists(Model::class, $column)
1271
            ) {
1272
                $relations[] = $column;
1273
            }
1274
        }
1275
1276
        return array_unique($relations);
1277
    }
1278
1279
    /**
1280
     * Set action for form.
1281
     *
1282
     * @param string $action
1283
     *
1284
     * @return $this
1285
     */
1286
    public function setAction($action): self
1287
    {
1288
        $this->builder()->setAction($action);
1289
1290
        return $this;
1291
    }
1292
1293
    /**
1294
     * Set field and label width in current form.
1295
     *
1296
     * @param int $fieldWidth
1297
     * @param int $labelWidth
1298
     *
1299
     * @return $this
1300
     */
1301
    public function setWidth($fieldWidth = 8, $labelWidth = 2): self
1302
    {
1303
        $this->builder()->fields()->each(function ($field) use ($fieldWidth, $labelWidth) {
1304
            /* @var Field $field  */
1305
            $field->setWidth($fieldWidth, $labelWidth);
1306
        });
1307
1308
        $this->builder()->setWidth($fieldWidth, $labelWidth);
1309
1310
        return $this;
1311
    }
1312
1313
    /**
1314
     * Set view for form.
1315
     *
1316
     * @param string $view
1317
     *
1318
     * @return $this
1319
     */
1320
    public function setView($view): self
1321
    {
1322
        $this->builder()->setView($view);
1323
1324
        return $this;
1325
    }
1326
1327
    /**
1328
     * Set title for form.
1329
     *
1330
     * @param string $title
1331
     *
1332
     * @return $this
1333
     */
1334
    public function setTitle($title = ''): self
1335
    {
1336
        $this->builder()->setTitle($title);
1337
1338
        return $this;
1339
    }
1340
1341
    /**
1342
     * Add a row in form.
1343
     *
1344
     * @param Closure $callback
1345
     *
1346
     * @return $this
1347
     */
1348
    public function row(Closure $callback): self
1349
    {
1350
        $this->rows[] = new Row($callback, $this);
1351
1352
        return $this;
1353
    }
1354
1355
    /**
1356
     * Tools setting for form.
1357
     *
1358
     * @param Closure $callback
1359
     */
1360
    public function tools(Closure $callback)
1361
    {
1362
        $callback->call($this, $this->builder->getTools());
1363
    }
1364
1365
    /**
1366
     * @param Closure|null $callback
1367
     *
1368
     * @return Form\Tools
1369
     */
1370 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...
1371
    {
1372
        if (func_num_args() === 0) {
1373
            return $this->builder->getTools();
1374
        }
1375
1376
        $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...
1377
    }
1378
1379
    /**
1380
     * Indicates if current form page is creating.
1381
     *
1382
     * @return bool
1383
     */
1384
    public function isCreating(): bool
1385
    {
1386
        return Str::endsWith(\request()->route()->getName(), ['.create', '.store']);
1387
    }
1388
1389
    /**
1390
     * Indicates if current form page is editing.
1391
     *
1392
     * @return bool
1393
     */
1394
    public function isEditing(): bool
1395
    {
1396
        return Str::endsWith(\request()->route()->getName(), '.edit', '.update');
0 ignored issues
show
Unused Code introduced by
The call to Str::endsWith() has too many arguments starting with '.update'.

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

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

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

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