Completed
Pull Request — master (#1584)
by
unknown
03:00
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
     * Builder constructor.
92
     *
93
     * @param Form $form
94
     */
95
    public function __construct(Form $form)
96
    {
97
        $this->form = $form;
98
99
        $this->fields = new Collection();
100
101
        $this->setupTools();
102
    }
103
104
    /**
105
     * Setup grid tools.
106
     */
107
    public function setupTools()
108
    {
109
        $this->tools = new Tools($this);
110
    }
111
112
    /**
113
     * @return Tools
114
     */
115
    public function getTools()
116
    {
117
        return $this->tools;
118
    }
119
120
    /**
121
     * Set the builder mode.
122
     *
123
     * @param string $mode
124
     *
125
     * @return void
126
     */
127
    public function setMode($mode = 'create')
128
    {
129
        $this->mode = $mode;
130
    }
131
132
    /**
133
     * Returns builder is $mode.
134
     *
135
     * @param $mode
136
     *
137
     * @return bool
138
     */
139
    public function isMode($mode)
140
    {
141
        return $this->mode == $mode;
142
    }
143
144
    /**
145
     * Set resource Id.
146
     *
147
     * @param $id
148
     *
149
     * @return void
150
     */
151
    public function setResourceId($id)
152
    {
153
        $this->id = $id;
154
    }
155
156
    /**
157
     * @return string
158
     */
159
    public function getResource($slice = null)
160
    {
161
        if ($this->mode == self::MODE_CREATE) {
162
            return $this->form->resource(-1);
163
        }
164
        if ($slice !== null) {
165
            return $this->form->resource($slice);
166
        }
167
168
        return $this->form->resource();
169
    }
170
171
    /**
172
     * @param int $field
173
     * @param int $label
174
     *
175
     * @return $this
176
     */
177
    public function setWidth($field = 8, $label = 2)
178
    {
179
        $this->width = [
180
            'label' => $label,
181
            'field' => $field,
182
        ];
183
184
        return $this;
185
    }
186
187
    /**
188
     * Set form action.
189
     *
190
     * @param string $action
191
     */
192
    public function setAction($action)
193
    {
194
        $this->action = $action;
195
    }
196
197
    /**
198
     * Get Form action.
199
     *
200
     * @return string
201
     */
202
    public function getAction()
203
    {
204
        if ($this->action) {
205
            return $this->action;
206
        }
207
208
        if ($this->isMode(static::MODE_EDIT)) {
209
            return $this->form->resource().'/'.$this->id;
210
        }
211
212
        if ($this->isMode(static::MODE_CREATE)) {
213
            return $this->form->resource(-1);
214
        }
215
216
        return '';
217
    }
218
219
    /**
220
     * Set view for this form.
221
     *
222
     * @param string $view
223
     *
224
     * @return $this
225
     */
226
    public function setView($view)
227
    {
228
        $this->view = $view;
229
230
        return $this;
231
    }
232
233
    /**
234
     * Get fields of this builder.
235
     *
236
     * @return Collection
237
     */
238
    public function fields()
239
    {
240
        return $this->fields;
241
    }
242
243
    /**
244
     * Get specify field.
245
     *
246
     * @param string $name
247
     *
248
     * @return mixed
249
     */
250
    public function field($name)
251
    {
252
        return $this->fields()->first(function (Field $field) use ($name) {
253
            return $field->column() == $name;
254
        });
255
    }
256
257
    /**
258
     * If the parant form has rows.
259
     *
260
     * @return bool
261
     */
262
    public function hasRows()
263
    {
264
        return !empty($this->form->rows);
265
    }
266
267
    /**
268
     * Get field rows of form.
269
     *
270
     * @return array
271
     */
272
    public function getRows()
273
    {
274
        return $this->form->rows;
275
    }
276
277
    /**
278
     * @return array
279
     */
280
    public function getHiddenFields()
281
    {
282
        return $this->hiddenFields;
283
    }
284
285
    /**
286
     * @param Field $field
287
     *
288
     * @return void
289
     */
290
    public function addHiddenField(Field $field)
291
    {
292
        $this->hiddenFields[] = $field;
293
    }
294
295
    /**
296
     * Add or get options.
297
     *
298
     * @param array $options
299
     *
300
     * @return array|null
301
     */
302
    public function options($options = [])
303
    {
304
        if (empty($options)) {
305
            return $this->options;
306
        }
307
308
        $this->options = array_merge($this->options, $options);
309
    }
310
311
    /**
312
     * Get or set option.
313
     *
314
     * @param string $option
315
     * @param mixed  $value
316
     *
317
     * @return $this
318
     */
319
    public function option($option, $value = null)
320
    {
321
        if (func_num_args() == 1) {
322
            return array_get($this->options, $option);
323
        }
324
325
        $this->options[$option] = $value;
326
327
        return $this;
328
    }
329
330
    /**
331
     * @return string
332
     */
333
    public function title()
334
    {
335
        if ($this->mode == static::MODE_CREATE) {
336
            return trans('admin.create');
337
        }
338
339
        if ($this->mode == static::MODE_EDIT) {
340
            return trans('admin.edit');
341
        }
342
343
        if ($this->mode == static::MODE_VIEW) {
344
            return trans('admin.view');
345
        }
346
347
        return '';
348
    }
349
350
    /**
351
     * Determine if form fields has files.
352
     *
353
     * @return bool
354
     */
355
    public function hasFile()
356
    {
357
        foreach ($this->fields() as $field) {
358
            if ($field instanceof Field\File) {
359
                return true;
360
            }
361
        }
362
363
        return false;
364
    }
365
366
    /**
367
     * Add field for store redirect url after update or store.
368
     *
369
     * @return void
370
     */
371
    protected function addRedirectUrlField()
372
    {
373
        $previous = URL::previous();
374
375
        if (!$previous || $previous == URL::current()) {
376
            return;
377
        }
378
379
        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...
380
            $this->addHiddenField((new Form\Field\Hidden(static::PREVIOUS_URL_KEY))->value($previous));
381
        }
382
    }
383
384
    /**
385
     * Open up a new HTML form.
386
     *
387
     * @param array $options
388
     *
389
     * @return string
390
     */
391
    public function open($options = [])
392
    {
393
        $attributes = [];
394
395
        if ($this->mode == self::MODE_EDIT) {
396
            $this->addHiddenField((new Form\Field\Hidden('_method'))->value('PUT'));
397
        }
398
399
        $this->addRedirectUrlField();
400
401
        $attributes['action'] = $this->getAction();
402
        $attributes['method'] = array_get($options, 'method', 'post');
403
        $attributes['accept-charset'] = 'UTF-8';
404
405
        $attributes['class'] = array_get($options, 'class');
406
407
        if ($this->hasFile()) {
408
            $attributes['enctype'] = 'multipart/form-data';
409
        }
410
411
        $html = [];
412
        foreach ($attributes as $name => $value) {
413
            $html[] = "$name=\"$value\"";
414
        }
415
416
        return '<form '.implode(' ', $html).' pjax-container>';
417
    }
418
419
    /**
420
     * Close the current form.
421
     *
422
     * @return string
423
     */
424
    public function close()
425
    {
426
        $this->form = null;
427
        $this->fields = null;
428
429
        return '</form>';
430
    }
431
432
    /**
433
     * Submit button of form..
434
     *
435
     * @return string
436
     */
437
    public function submitButton()
438
    {
439
        if ($this->mode == self::MODE_VIEW) {
440
            return '';
441
        }
442
443
        if (!$this->options['enableSubmit']) {
444
            return '';
445
        }
446
447
        if ($this->mode == self::MODE_EDIT) {
448
            $text = trans('admin.save');
449
        } else {
450
            $text = trans('admin.submit');
451
        }
452
453
        return <<<EOT
454
<div class="btn-group pull-right">
455
    <button type="submit" class="btn btn-info pull-right" data-loading-text="<i class='fa fa-spinner fa-spin '></i> $text">$text</button>
456
</div>
457
EOT;
458
    }
459
460
    /**
461
     * Reset button of form.
462
     *
463
     * @return string
464
     */
465
    public function resetButton()
466
    {
467
        if (!$this->options['enableReset']) {
468
            return '';
469
        }
470
471
        $text = trans('admin.reset');
472
473
        return <<<EOT
474
<div class="btn-group pull-left">
475
    <button type="reset" class="btn btn-warning">$text</button>
476
</div>
477
EOT;
478
    }
479
480
    /**
481
     * Remove reserved fields like `id` `created_at` `updated_at` in form fields.
482
     *
483
     * @return void
484
     */
485
    protected function removeReservedFields()
486
    {
487
        if (!$this->isMode(static::MODE_CREATE)) {
488
            return;
489
        }
490
491
        $reservedColumns = [
492
            $this->form->model()->getKeyName(),
493
            $this->form->model()->getCreatedAtColumn(),
494
            $this->form->model()->getUpdatedAtColumn(),
495
        ];
496
497
        $this->fields = $this->fields()->reject(function (Field $field) use ($reservedColumns) {
498
            return in_array($field->column(), $reservedColumns);
499
        });
500
    }
501
502
    /**
503
     * Render form.
504
     *
505
     * @return string
506
     */
507
    public function render()
508
    {
509
        $this->removeReservedFields();
510
511
        $tabObj = $this->form->getTab();
512
513
        if (!$tabObj->isEmpty()) {
514
            $script = <<<'SCRIPT'
515
516
var hash = document.location.hash;
517
if (hash) {
518
    $('.nav-tabs a[href="' + hash + '"]').tab('show');
519
}
520
521
// Change hash for page-reload
522
$('.nav-tabs a').on('shown.bs.tab', function (e) {
523
    history.pushState(null,null, e.target.hash);
524
});
525
526
if ($('.has-error').length) {
527
    $('.has-error').each(function () {
528
        var tabId = '#'+$(this).closest('.tab-pane').attr('id');
529
        $('li a[href="'+tabId+'"] i').removeClass('hide');
530
    });
531
532
    var first = $('.has-error:first').closest('.tab-pane').attr('id');
533
    $('li a[href="#'+first+'"]').tab('show');
534
}
535
536
SCRIPT;
537
            Admin::script($script);
538
        }
539
540
        $data = [
541
            'form'   => $this,
542
            'tabObj' => $tabObj,
543
            'width'  => $this->width,
544
        ];
545
546
        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...
547
    }
548
549
    /**
550
     * @return string
551
     */
552
    public function renderHeaderTools()
553
    {
554
        return $this->tools->render();
555
    }
556
557
    /**
558
     * @return string
559
     */
560
    public function __toString()
561
    {
562
        return $this->render();
563
    }
564
}
565