Completed
Pull Request — master (#2014)
by Song
02:47
created

Builder::removeReservedFields()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 9
nc 2
nop 0
dl 0
loc 16
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Encore\Admin\Form;
4
5
use Encore\Admin\Admin;
6
use Encore\Admin\Form;
7
use Illuminate\Support\Collection;
8
use Illuminate\Support\Facades\URL;
9
use Illuminate\Support\Str;
10
11
/**
12
 * Class Builder.
13
 */
14
class Builder
15
{
16
    /**
17
     *  Previous url key.
18
     */
19
    const PREVIOUS_URL_KEY = '_previous_';
20
21
    /**
22
     * @var mixed
23
     */
24
    protected $id;
25
26
    /**
27
     * @var Form
28
     */
29
    protected $form;
30
31
    /**
32
     * @var
33
     */
34
    protected $action;
35
36
    /**
37
     * @var Collection
38
     */
39
    protected $fields;
40
41
    /**
42
     * @var array
43
     */
44
    protected $options = [
45
        'enableSubmit' => true,
46
        'enableReset'  => true,
47
    ];
48
49
    /**
50
     * Modes constants.
51
     */
52
    const MODE_VIEW = 'view';
53
    const MODE_EDIT = 'edit';
54
    const MODE_CREATE = 'create';
55
56
    /**
57
     * Form action mode, could be create|view|edit.
58
     *
59
     * @var string
60
     */
61
    protected $mode = 'create';
62
63
    /**
64
     * @var array
65
     */
66
    protected $hiddenFields = [];
67
68
    /**
69
     * @var Tools
70
     */
71
    protected $tools;
72
73
    /**
74
     * Width for label and field.
75
     *
76
     * @var array
77
     */
78
    protected $width = [
79
        'label' => 2,
80
        'field' => 8,
81
    ];
82
83
    /**
84
     * View for this form.
85
     *
86
     * @var string
87
     */
88
    protected $view = 'admin::form';
89
90
    /**
91
     * Form title.
92
     *
93
     * @var string
94
     */
95
    protected $title;
96
97
    /**
98
     * Builder constructor.
99
     *
100
     * @param Form $form
101
     */
102
    public function __construct(Form $form)
103
    {
104
        $this->form = $form;
105
106
        $this->fields = new Collection();
107
108
        $this->setupTools();
109
    }
110
111
    /**
112
     * Setup grid tools.
113
     */
114
    public function setupTools()
115
    {
116
        $this->tools = new Tools($this);
117
    }
118
119
    /**
120
     * @return Tools
121
     */
122
    public function getTools()
123
    {
124
        return $this->tools;
125
    }
126
127
    /**
128
     * Set the builder mode.
129
     *
130
     * @param string $mode
131
     *
132
     * @return void
133
     */
134
    public function setMode($mode = 'create')
135
    {
136
        $this->mode = $mode;
137
    }
138
139
    /**
140
     * Returns builder is $mode.
141
     *
142
     * @param $mode
143
     *
144
     * @return bool
145
     */
146
    public function isMode($mode)
147
    {
148
        return $this->mode == $mode;
149
    }
150
151
    /**
152
     * Set resource Id.
153
     *
154
     * @param $id
155
     *
156
     * @return void
157
     */
158
    public function setResourceId($id)
159
    {
160
        $this->id = $id;
161
    }
162
163
    /**
164
     * @return string
165
     */
166
    public function getResource($slice = null)
167
    {
168
        if ($this->mode == self::MODE_CREATE) {
169
            return $this->form->resource(-1);
170
        }
171
        if ($slice !== null) {
172
            return $this->form->resource($slice);
173
        }
174
175
        return $this->form->resource();
176
    }
177
178
    /**
179
     * @param int $field
180
     * @param int $label
181
     *
182
     * @return $this
183
     */
184
    public function setWidth($field = 8, $label = 2)
185
    {
186
        $this->width = [
187
            'label' => $label,
188
            'field' => $field,
189
        ];
190
191
        return $this;
192
    }
193
194
    /**
195
     * Set form action.
196
     *
197
     * @param string $action
198
     */
199
    public function setAction($action)
200
    {
201
        $this->action = $action;
202
    }
203
204
    /**
205
     * Get Form action.
206
     *
207
     * @return string
208
     */
209
    public function getAction()
210
    {
211
        if ($this->action) {
212
            return $this->action;
213
        }
214
215
        if ($this->isMode(static::MODE_EDIT)) {
216
            return $this->form->resource().'/'.$this->id;
217
        }
218
219
        if ($this->isMode(static::MODE_CREATE)) {
220
            return $this->form->resource(-1);
221
        }
222
223
        return '';
224
    }
225
226
    /**
227
     * Set view for this form.
228
     *
229
     * @param string $view
230
     *
231
     * @return $this
232
     */
233
    public function setView($view)
234
    {
235
        $this->view = $view;
236
237
        return $this;
238
    }
239
240
    /**
241
     * Set title for form.
242
     *
243
     * @param string $title
244
     *
245
     * @return $this
246
     */
247
    public function setTitle($title)
248
    {
249
        $this->title = $title;
250
251
        return $this;
252
    }
253
254
    /**
255
     * Get fields of this builder.
256
     *
257
     * @return Collection
258
     */
259
    public function fields()
260
    {
261
        return $this->fields;
262
    }
263
264
    /**
265
     * Get specify field.
266
     *
267
     * @param string $name
268
     *
269
     * @return mixed
270
     */
271
    public function field($name)
272
    {
273
        return $this->fields()->first(function (Field $field) use ($name) {
274
            return $field->column() == $name;
275
        });
276
    }
277
278
    /**
279
     * If the parant form has rows.
280
     *
281
     * @return bool
282
     */
283
    public function hasRows()
284
    {
285
        return !empty($this->form->rows);
286
    }
287
288
    /**
289
     * Get field rows of form.
290
     *
291
     * @return array
292
     */
293
    public function getRows()
294
    {
295
        return $this->form->rows;
296
    }
297
298
    /**
299
     * @return array
300
     */
301
    public function getHiddenFields()
302
    {
303
        return $this->hiddenFields;
304
    }
305
306
    /**
307
     * @param Field $field
308
     *
309
     * @return void
310
     */
311
    public function addHiddenField(Field $field)
312
    {
313
        $this->hiddenFields[] = $field;
314
    }
315
316
    /**
317
     * Add or get options.
318
     *
319
     * @param array $options
320
     *
321
     * @return array|null
322
     */
323
    public function options($options = [])
324
    {
325
        if (empty($options)) {
326
            return $this->options;
327
        }
328
329
        $this->options = array_merge($this->options, $options);
330
    }
331
332
    /**
333
     * Get or set option.
334
     *
335
     * @param string $option
336
     * @param mixed  $value
337
     *
338
     * @return $this
339
     */
340
    public function option($option, $value = null)
341
    {
342
        if (func_num_args() == 1) {
343
            return array_get($this->options, $option);
344
        }
345
346
        $this->options[$option] = $value;
347
348
        return $this;
349
    }
350
351
    /**
352
     * @return string
353
     */
354
    public function title()
355
    {
356
        if ($this->title) {
357
            return $this->title;
358
        }
359
360
        if ($this->mode == static::MODE_CREATE) {
361
            return trans('admin.create');
362
        }
363
364
        if ($this->mode == static::MODE_EDIT) {
365
            return trans('admin.edit');
366
        }
367
368
        if ($this->mode == static::MODE_VIEW) {
369
            return trans('admin.view');
370
        }
371
372
        return '';
373
    }
374
375
    /**
376
     * Determine if form fields has files.
377
     *
378
     * @return bool
379
     */
380
    public function hasFile()
381
    {
382
        foreach ($this->fields() as $field) {
383
            if ($field instanceof Field\File) {
384
                return true;
385
            }
386
        }
387
388
        return false;
389
    }
390
391
    /**
392
     * Add field for store redirect url after update or store.
393
     *
394
     * @return void
395
     */
396
    protected function addRedirectUrlField()
397
    {
398
        $previous = URL::previous();
399
400
        if (!$previous || $previous == URL::current()) {
401
            return;
402
        }
403
404
        if (Str::contains($previous, url($this->getResource()))) {
0 ignored issues
show
Bug introduced by
It seems like url($this->getResource()) targeting url() can also be of type object<Illuminate\Contracts\Routing\UrlGenerator>; however, Illuminate\Support\Str::contains() does only seem to accept string|array, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
405
            $this->addHiddenField((new Form\Field\Hidden(static::PREVIOUS_URL_KEY))->value($previous));
406
        }
407
    }
408
409
    /**
410
     * Open up a new HTML form.
411
     *
412
     * @param array $options
413
     *
414
     * @return string
415
     */
416
    public function open($options = [])
417
    {
418
        $attributes = [];
419
420
        if ($this->mode == self::MODE_EDIT) {
421
            $this->addHiddenField((new Form\Field\Hidden('_method'))->value('PUT'));
422
        }
423
424
        $this->addRedirectUrlField();
425
426
        $attributes['action'] = $this->getAction();
427
        $attributes['method'] = array_get($options, 'method', 'post');
428
        $attributes['accept-charset'] = 'UTF-8';
429
430
        $attributes['class'] = array_get($options, 'class');
431
432
        if ($this->hasFile()) {
433
            $attributes['enctype'] = 'multipart/form-data';
434
        }
435
436
        $html = [];
437
        foreach ($attributes as $name => $value) {
438
            $html[] = "$name=\"$value\"";
439
        }
440
441
        return '<form '.implode(' ', $html).' pjax-container>';
442
    }
443
444
    /**
445
     * Close the current form.
446
     *
447
     * @return string
448
     */
449
    public function close()
450
    {
451
        $this->form = null;
452
        $this->fields = null;
453
454
        return '</form>';
455
    }
456
457
    /**
458
     * Submit button of form..
459
     *
460
     * @return string
461
     */
462
    public function submitButton()
463
    {
464
        if ($this->mode == self::MODE_VIEW) {
465
            return '';
466
        }
467
468
        if (!$this->options['enableSubmit']) {
469
            return '';
470
        }
471
472
        if ($this->mode == self::MODE_EDIT) {
473
            $text = trans('admin.save');
474
        } else {
475
            $text = trans('admin.submit');
476
        }
477
478
        return <<<EOT
479
<div class="btn-group pull-right">
480
    <button type="submit" class="btn btn-info pull-right" data-loading-text="<i class='fa fa-spinner fa-spin '></i> $text">$text</button>
481
</div>
482
EOT;
483
    }
484
485
    /**
486
     * Reset button of form.
487
     *
488
     * @return string
489
     */
490
    public function resetButton()
491
    {
492
        if (!$this->options['enableReset']) {
493
            return '';
494
        }
495
496
        $text = trans('admin.reset');
497
498
        return <<<EOT
499
<div class="btn-group pull-left">
500
    <button type="reset" class="btn btn-warning">$text</button>
501
</div>
502
EOT;
503
    }
504
505
    /**
506
     * Remove reserved fields like `id` `created_at` `updated_at` in form fields.
507
     *
508
     * @return void
509
     */
510
    protected function removeReservedFields()
511
    {
512
        if (!$this->isMode(static::MODE_CREATE)) {
513
            return;
514
        }
515
516
        $reservedColumns = [
517
            $this->form->model()->getKeyName(),
518
            $this->form->model()->getCreatedAtColumn(),
519
            $this->form->model()->getUpdatedAtColumn(),
520
        ];
521
522
        $this->fields = $this->fields()->reject(function (Field $field) use ($reservedColumns) {
523
            return in_array($field->column(), $reservedColumns);
524
        });
525
    }
526
527
    /**
528
     * Render form.
529
     *
530
     * @return string
531
     */
532
    public function render()
533
    {
534
        $this->removeReservedFields();
535
536
        $tabObj = $this->form->getTab();
537
538
        if (!$tabObj->isEmpty()) {
539
            $script = <<<'SCRIPT'
540
541
var hash = document.location.hash;
542
if (hash) {
543
    $('.nav-tabs a[href="' + hash + '"]').tab('show');
544
}
545
546
// Change hash for page-reload
547
$('.nav-tabs a').on('shown.bs.tab', function (e) {
548
    history.pushState(null,null, e.target.hash);
549
});
550
551
if ($('.has-error').length) {
552
    $('.has-error').each(function () {
553
        var tabId = '#'+$(this).closest('.tab-pane').attr('id');
554
        $('li a[href="'+tabId+'"] i').removeClass('hide');
555
    });
556
557
    var first = $('.has-error:first').closest('.tab-pane').attr('id');
558
    $('li a[href="#'+first+'"]').tab('show');
559
}
560
561
SCRIPT;
562
            Admin::script($script);
563
        }
564
565
        $data = [
566
            'form'   => $this,
567
            'tabObj' => $tabObj,
568
            'width'  => $this->width,
569
        ];
570
571
        return view($this->view, $data)->render();
0 ignored issues
show
Bug introduced by
The method render does only exist in Illuminate\View\View, but not in Illuminate\Contracts\View\Factory.

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...
572
    }
573
574
    /**
575
     * @return string
576
     */
577
    public function renderHeaderTools()
578
    {
579
        return $this->tools->render();
580
    }
581
582
    /**
583
     * @return string
584
     */
585
    public function __toString()
586
    {
587
        return $this->render();
588
    }
589
}
590