Completed
Pull Request — master (#1350)
by
unknown
03:06
created

Builder::__construct()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

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