Completed
Pull Request — master (#2254)
by
unknown
12:23 queued 09:50
created

Builder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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