Completed
Pull Request — master (#21)
by ARCANEDEV
13:53
created

FormBuilder::getModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 4
ccs 1
cts 1
cp 1
rs 10
cc 1
eloc 2
nc 1
nop 0
crap 1
1
<?php namespace Arcanedev\LaravelHtml;
2
3
use Arcanedev\LaravelHtml\Bases\Builder;
4
use Arcanedev\LaravelHtml\Contracts\FormBuilder as FormBuilderContract;
5
use DateTime;
6
use Illuminate\Contracts\Routing\UrlGenerator;
7
use Illuminate\Contracts\Session\Session;
8
use Illuminate\Support\Arr;
9
use Illuminate\Support\Collection;
10
11
/**
12
 * Class     FormBuilder
13
 *
14
 * @package  Arcanedev\LaravelHtml
15
 * @author   ARCANEDEV <[email protected]>
16
 */
17
class FormBuilder extends Builder implements FormBuilderContract
18
{
19
    /* -----------------------------------------------------------------
20
     |  Properties
21
     | -----------------------------------------------------------------
22
     */
23
24
    /**
25
    * The HTML builder instance.
26
    *
27
    * @var \Arcanedev\LaravelHtml\Contracts\HtmlBuilder
28
    */
29
    protected $html;
30
31
    /**
32
    * The URL generator instance.
33
    *
34
    * @var \Illuminate\Contracts\Routing\UrlGenerator
35
    */
36
    protected $url;
37
38
    /**
39
    * The CSRF token used by the form builder.
40
    *
41
    * @var string
42
    */
43
    protected $csrfToken;
44
45
    /**
46
    * The session store implementation.
47
    *
48
    * @var \Illuminate\Contracts\Session\Session
49
    */
50
    protected $session;
51
52
    /**
53
    * The current model instance for the form.
54
    *
55
    * @var \Illuminate\Database\Eloquent\Model
56
    */
57
    protected $model;
58
59
    /**
60
    * An array of label names we've created.
61
    *
62
    * @var array
63
    */
64
    protected $labels = [];
65
66
    /**
67
    * The reserved form open attributes.
68
    *
69
    * @var array
70
    */
71
    protected $reserved = ['method', 'url', 'route', 'action', 'files'];
72
73
    /**
74
    * The form methods that should be spoofed, in uppercase.
75
    *
76
    * @var array
77
    */
78
    protected $spoofedMethods = ['DELETE', 'PATCH', 'PUT'];
79
80
    /**
81
    * The types of inputs to not fill values on by default.
82
    *
83
    * @var array
84
    */
85
    protected $skipValueTypes = ['file', 'password', 'checkbox', 'radio'];
86
87
    /* -----------------------------------------------------------------
88
     |  Constructor
89
     | -----------------------------------------------------------------
90
     */
91
92
    /**
93
    * Create a new form builder instance.
94
    *
95
    * @param  \Arcanedev\LaravelHtml\Contracts\HtmlBuilder  $html
96
    * @param  \Illuminate\Contracts\Routing\UrlGenerator    $url
97
    * @param  \Illuminate\Contracts\Session\Session         $session
98
    */
99 303
    public function __construct(
100
        Contracts\HtmlBuilder $html,
101
        UrlGenerator $url,
102
        Session $session
103
    ) {
104 303
        $this->url       = $url;
105 303
        $this->html      = $html;
106 303
        $this->csrfToken = $session->token();
107
108 303
        $this->setSessionStore($session);
109 303
    }
110
111
    /* -----------------------------------------------------------------
112
     |  Getters & Setters
113
     | -----------------------------------------------------------------
114
     */
115
116
    /**
117
     * Get the session store implementation.
118
     *
119
     * @return  \Illuminate\Contracts\Session\Session
120
     */
121 3
    public function getSessionStore()
122
    {
123 3
        return $this->session;
124
    }
125
126
    /**
127
     * Set the session store implementation.
128
     *
129
     * @param  \Illuminate\Contracts\Session\Session  $session
130
     *
131
     * @return self
132
     */
133 303
    public function setSessionStore(Session $session)
134
    {
135 303
        $this->session = $session;
136
137 303
        return $this;
138
    }
139
140
    /**
141
     * Set the model instance on the form builder.
142
     *
143
     * @param  \Illuminate\Database\Eloquent\Model  $model
144
     *
145
     * @return self
146
     */
147 33
    public function setModel($model)
148
    {
149 33
        $this->model = $model;
150
151 33
        return $this;
152
    }
153
154
    /**
155
     * Get the model instance on the form builder.
156
     *
157
     * @return \Illuminate\Database\Eloquent\Model
158
     */
159
    public function getModel()
160
    {
161
        return $this->model;
162 249
    }
163
164 249
    /**
165 97
     * Get the ID attribute for a field name.
166
     *
167 234
     * @param  string  $name
168 82
     * @param  array   $attributes
169
     *
170 228
     * @return string
171
     */
172
    public function getIdAttribute($name, array $attributes)
173
    {
174
        if (array_key_exists('id', $attributes))
175
            return $attributes['id'];
176
177
        if (in_array($name, $this->labels))
178
            return $name;
179
180
        return null;
181 222
    }
182
183 222
    /**
184 80
     * Get the value that should be assigned to the field.
185
     *
186 213
     * @param  string  $name
187 79
     * @param  mixed   $value
188
     *
189 207
     * @return mixed
190 147
     */
191
    public function getValueAttribute($name, $value = null)
192 120
    {
193 50
        if (is_null($name))
194 120
            return $value;
195
196
        if ( ! is_null($this->old($name)) && $name !== '_method')
197
            return $this->old($name);
198
199
        if ( ! is_null($value))
200
            return $value;
201
202
        return isset($this->model)
203
            ? $this->getModelValueAttribute($name)
204 24
            : null;
205
    }
206 24
207
    /**
208 24
     * Get the model value that should be assigned to the field.
209 8
     *
210 24
     * @param  string                               $name
211
     * @param  \Illuminate\Database\Eloquent\Model  $model
212
     *
213
     * @return mixed
214
     */
215
    private function getModelValueAttribute($name, $model = null)
216
    {
217
        if (is_null($model)) {
218
            $model = $this->model;
219
        }
220 222
221
        $key = $this->transformKey($name);
222 222
223 222
        if (strpos($key, '.') !== false) {
224 222
            $keys = explode('.', $key, 2);
225
226
            return $this->getModelValueAttribute($keys[1],
227
                $this->getModelValueAttribute($keys[0], $model)
228
            );
229
        }
230
231
        return method_exists($model, 'getFormValue')
232
            ? $model->getFormValue($key)
233
            : data_get($model, $key);
234 222
    }
235
236 222
    /**
237
     * Get a value from the session's old input.
238
     *
239
     * @param  string  $name
240
     *
241
     * @return mixed
242
     */
243
    public function old($name)
244 12
    {
245
        return isset($this->session)
246 12
            ? $this->session->getOldInput($this->transformKey($name))
247
            : null;
248
    }
249
250
    /**
251
     * Transform key from array to dot syntax.
252
     *
253
     * @param  string  $key
254
     *
255
     * @return string
256 48
     */
257
    private function transformKey($key)
258 48
    {
259
        return str_replace(['.', '[]', '[', ']'], ['_', '', '.', ''], $key);
260 48
    }
261
262
    /**
263
     * Determine if the old input is empty.
264
     *
265
     * @return bool
266
     */
267
    public function oldInputIsEmpty()
268
    {
269
        return isset($this->session) && (count($this->session->getOldInput()) == 0);
270
    }
271
272
    /**
273
     * Parse the form action method.
274
     *
275 48
     * @param  string  $method
276
     *
277 48
     * @return string
278
     */
279
    private function getMethod($method)
280
    {
281
        $method = strtoupper($method);
282 48
283 48
        return $method !== 'GET' ? 'POST' : $method;
284 48
    }
285 48
286
    /* -----------------------------------------------------------------
287 48
     |  Main Methods
288 6
     | -----------------------------------------------------------------
289 2
     */
290
291
    /**
292
     * Open up a new HTML form.
293
     *
294 48
     * @param  array  $options
295 48
     *
296 16
     * @return \Illuminate\Support\HtmlString
297
     */
298
    public function open(array $options = [])
299
    {
300
        $method = Arr::get($options, 'method', 'post');
301 48
302
        // We need to extract the proper method from the attributes. If the method is
303
        // something other than GET or POST we'll use POST since we will spoof the
304
        // actual method since forms don't support the reserved methods in HTML.
305
        $attributes = [
306 48
            'method'         => $this->getMethod($method),
307
            'action'         => $this->getAction($options),
308 48
            'accept-charset' => 'UTF-8',
309
        ];
310
311
        if (isset($options['files']) && $options['files']) {
312
            $options['enctype'] = 'multipart/form-data';
313
        }
314
315
        // Finally we're ready to create the final form HTML field. We will attribute
316
        // format the array of attributes. We will also add on the appendage which
317
        // is used to spoof requests for this PUT, PATCH, etc. methods on forms.
318
        $attributes = array_merge(
319 15
            $attributes, array_except($options, $this->reserved)
320
        );
321 15
322
        // Finally, we will concatenate all of the attributes into a single string so
323 15
        // we can build out the final form open statement. We'll also append on an
324
        // extra value for the hidden _method field if it's needed for the form.
325
        $attributes = $this->html->attributes($attributes);
326
327
        // If the method is PUT, PATCH or DELETE we will need to add a spoofer hidden
328
        // field that will instruct the Symfony request to pretend the method is a
329
        // different method than it actually is, for convenience from the forms.
330
        $append = $this->getAppendage($method);
331 6
332
        return $this->toHtmlString('<form'.$attributes.'>' . $append);
333 6
    }
334 6
335
    /**
336 6
     * Create a new model based form builder.
337
     *
338
     * @param  mixed  $model
339
     * @param  array  $options
340
     *
341
     * @return \Illuminate\Support\HtmlString
342
     */
343
    public function model($model, array $options = [])
344 21
    {
345
        $this->setModel($model);
346 21
347 17
        return $this->open($options);
348 21
    }
349
350 21
    /**
351
     * Close the current form.
352
     *
353
     * @return \Illuminate\Support\HtmlString
354
     */
355
    public function close()
356
    {
357
        $this->labels = [];
358
        $this->setModel(null);
0 ignored issues
show
Documentation introduced by
null is of type null, but the function expects a object<Illuminate\Database\Eloquent\Model>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
359
360
        return $this->toHtmlString('</form>');
361
    }
362
363 15
    /**
364
     * Generate a hidden field with the current CSRF token.
365 15
     *
366
     * @return \Illuminate\Support\HtmlString
367 15
     */
368 15
    public function token()
369 5
    {
370
        $token = ! empty($this->csrfToken)
371
            ? $this->csrfToken
372
            : $this->session->token();
373
374
        return $this->hidden('_token', $token);
375
    }
376
377
    /**
378
     * Create a form label element.
379
     *
380
     * @param  string  $name
381
     * @param  string  $value
382 189
     * @param  array   $options
383
     * @param  bool    $escaped
384 189
     *
385 189
     * @return \Illuminate\Support\HtmlString
386 63
     */
387
    public function label($name, $value = null, array $options = [], $escaped = true)
388
    {
389
        $this->labels[] = $name;
390
391 189
        return $this->toHtmlString(
392
            Helpers\Label::make($name, $value, $options, $escaped)
393 189
        );
394 153
    }
395 51
396
    /**
397
     * Create a form input field.
398
     *
399
     * @param  string  $type
400 189
     * @param  string  $name
401
     * @param  string  $value
402 189
     * @param  array   $options
403
     *
404
     * @return \Illuminate\Support\HtmlString
405
     */
406
    public function input($type, $name, $value = null, array $options = [])
407
    {
408
        if ( ! isset($options['name'])) {
409
            $options['name'] = $name;
410
        }
411
412
        // We will get the appropriate value for the given field. We will look for the
413
        // value in the session for the value in the old input data then we'll look
414 21
        // in the model instance if one is set. Otherwise we will just use empty.
415
        $id = $this->getIdAttribute($name, $options);
416 21
417
        if ( ! in_array($type, $this->skipValueTypes)) {
418
            $value = $this->getValueAttribute($name, $value);
419
        }
420
421
        // Once we have the type, value, and ID we can merge them into the rest of the
422
        // attributes array so we can convert them into their HTML attribute format
423
        // when creating the HTML element. Then, we will return the entire input.
424
        $options = array_merge($options, compact('type', 'value', 'id'));
425
426
        return $this->toHtmlString('<input'.$this->html->attributes($options).'>');
427 12
    }
428
429 12
    /**
430 3
     * Create a text input field.
431
     *
432
     * @param  string  $name
433
     * @param  string  $value
434
     * @param  array   $options
435
     *
436
     * @return \Illuminate\Support\HtmlString
437
     */
438
    public function text($name, $value = null, array $options = [])
439
    {
440
        return $this->input('text', $name, $value, $options);
441 30
    }
442
443 30
    /**
444
     * Create a password input field.
445
     *
446
     * @param  string  $name
447
     * @param  array   $options
448
     *
449
     * @return \Illuminate\Support\HtmlString
450
     */
451
    public function password($name, array $options = [])
452
    {
453
        return $this->input('password', $name, '', $options);
454
    }
455 9
456
    /**
457 9
     * Create a hidden input field.
458
     *
459
     * @param  string  $name
460
     * @param  string  $value
461
     * @param  array   $options
462
     *
463
     * @return \Illuminate\Support\HtmlString
464
     */
465
    public function hidden($name, $value = null, array $options = [])
466
    {
467
        return $this->input('hidden', $name, $value, $options);
468
    }
469 9
470
    /**
471 9
     * Create an e-mail input field.
472
     *
473
     * @param  string  $name
474
     * @param  string  $value
475
     * @param  array   $options
476
     *
477
     * @return \Illuminate\Support\HtmlString
478
     */
479
    public function email($name, $value = null, array $options = [])
480
    {
481
        return $this->input('email', $name, $value, $options);
482
    }
483 9
484
    /**
485 9
     * Create a tel input field.
486
     *
487
     * @param  string  $name
488
     * @param  string  $value
489
     * @param  array   $options
490
     *
491
     * @return \Illuminate\Support\HtmlString
492
     */
493
    public function tel($name, $value = null, array $options = [])
494
    {
495
        return $this->input('tel', $name, $value, $options);
496
    }
497 12
498
    /**
499 12
     * Create a number input field.
500 3
     *
501 1
     * @param  string  $name
502
     * @param  string  $value
503 12
     * @param  array   $options
504
     *
505
     * @return \Illuminate\Support\HtmlString
506
     */
507
    public function number($name, $value = null, array $options = [])
508
    {
509
        return $this->input('number', $name, $value, $options);
510
    }
511
512
    /**
513
     * Create a date input field.
514
     *
515 12
     * @param  string  $name
516
     * @param  string  $value
517 12
     * @param  array   $options
518 6
     *
519 2
     * @return \Illuminate\Support\HtmlString
520
     */
521 12
    public function date($name, $value = null, array $options = [])
522
    {
523
        if ($value instanceof DateTime) {
524
            $value = $value->format('Y-m-d');
525
        }
526
527
        return $this->input('date', $name, $value, $options);
528
    }
529
530
    /**
531
     * Create a datetime input field.
532
     *
533 12
     * @param  string  $name
534
     * @param  string  $value
535 12
     * @param  array   $options
536 6
     *
537 2
     * @return \Illuminate\Support\HtmlString
538
     */
539 12
    public function datetime($name, $value = null, array $options = [])
540
    {
541
        if ($value instanceof DateTime) {
542
            $value = $value->format(DateTime::RFC3339);
543
        }
544
545
        return $this->input('datetime', $name, $value, $options);
546
    }
547
548
    /**
549
     * Create a datetime-local input field.
550
     *
551 9
     * @param  string  $name
552
     * @param  string  $value
553 9
     * @param  array   $options
554
     *
555
     * @return \Illuminate\Support\HtmlString
556
     */
557
    public function datetimeLocal($name, $value = null, array $options = [])
558
    {
559
        if ($value instanceof DateTime) {
560
            $value = $value->format('Y-m-d\TH:i');
561
        }
562
563
        return $this->input('datetime-local', $name, $value, $options);
564
    }
565 6
566
    /**
567 6
     * Create a time input field.
568
     *
569
     * @param  string  $name
570
     * @param  string  $value
571
     * @param  array   $options
572
     *
573
     * @return \Illuminate\Support\HtmlString
574
     */
575
    public function time($name, $value = null, array $options = [])
576
    {
577
        return $this->input('time', $name, $value, $options);
578 9
    }
579
580 9
    /**
581
     * Create a url input field.
582
     *
583
     * @param  string  $name
584
     * @param  string  $value
585
     * @param  array   $options
586
     *
587
     * @return \Illuminate\Support\HtmlString
588
     */
589
    public function url($name, $value = null, array $options = [])
590
    {
591
        return $this->input('url', $name, $value, $options);
592 18
    }
593
594 18
    /**
595 18
     * Create a file input field.
596 6
     *
597
     * @param  string  $name
598
     * @param  array   $options
599
     *
600
     * @return \Illuminate\Support\HtmlString
601 18
     */
602 18
    public function file($name, array $options = [])
603 18
    {
604
        return $this->input('file', $name, null, $options);
605 18
    }
606
607
    /**
608
     * Create a textarea input field.
609
     *
610 18
     * @param  string  $name
611
     * @param  string  $value
612 18
     * @param  array   $options
613
     *
614
     * @return \Illuminate\Support\HtmlString
615
     */
616
    public function textarea($name, $value = null, array $options = [])
617
    {
618
        if ( ! isset($options['name'])) {
619
            $options['name'] = $name;
620
        }
621
622 18
        // Next we will look for the rows and cols attributes, as each of these are put
623
        // on the textarea element definition. If they are not present, we will just
624 18
        // assume some sane default values for these attributes for the developer.
625 9
        $options       = $this->setTextAreaSize($options);
626
        $options['id'] = $this->getIdAttribute($name, $options);
627
        $value         = (string) $this->getValueAttribute($name, $value);
628
629
        unset($options['size']);
630
631 9
        // Next we will convert the attributes into a string form. Also we have removed
632 9
        // the size attribute, as it was merely a short-cut for the rows and cols on
633
        // the element. Then we'll create the final textarea elements HTML for us.
634 9
        $options = $this->html->attributes($options);
635
636
        return $this->toHtmlString('<textarea'.$options.'>'.$this->html->escape($value).'</textarea>');
637
    }
638
639
    /**
640
     * Set the text area size on the attributes.
641
     *
642
     * @param  array  $options
643
     *
644 9
     * @return array
645
     */
646 9
    private function setTextAreaSize(array $options)
647
    {
648 9
        if (isset($options['size'])) {
649 9
            return $this->setQuickTextAreaSize($options);
650 6
        }
651 3
652
        // If the "size" attribute was not specified, we will just look for the regular
653
        // columns and rows attributes, using sane defaults if these do not exist on
654
        // the attributes array. We'll then return this entire options array back.
655
        $cols = Arr::get($options, 'cols', 50);
656
        $rows = Arr::get($options, 'rows', 10);
657
658
        return array_merge($options, compact('cols', 'rows'));
659
    }
660
661
    /**
662
     * Set the text area size using the quick "size" attribute.
663
     *
664 42
     * @param  array  $options
665
     *
666
     * @return array
667
     */
668
    protected function setQuickTextAreaSize(array $options)
669 42
    {
670
        list($cols, $rows) = explode('x', $options['size']);
671
672 42
        return array_merge($options, compact('cols', 'rows'));
673 3
    }
674 1
675
    /**
676 42
     * Create a select box field.
677
     *
678 42
     * @param  string                                $name
679 30
     * @param  array|\Illuminate\Support\Collection  $list
680 10
     * @param  string                                $selected
681
     * @param  array                                 $attributes
682
     * @param  array                                 $options
683
     *
684
     * @return \Illuminate\Support\HtmlString
685 42
     */
686
    public function select($name, $list = [], $selected = null, array $attributes = [], array $options = [])
687 42
    {
688 3
        // When building a select box the "value" attribute is really the selected one
689 3
        // so we will use that when checking the model or session for a value which
690 1
        // should provide a convenient method of re-populating the forms on post.
691
        $selected = $this->getValueAttribute($name, $selected);
692 42
693 39
        // Transform to array if it is a collection
694 14
        if ($selected instanceof Collection) {
695
            $selected = $selected->all();
696
        }
697
698
        $attributes['id'] = $this->getIdAttribute($name, $attributes);
699 42
700
        if ( ! isset($attributes['name'])) {
701 42
            $attributes['name'] = $name;
702
        }
703
704
        // We will simply loop through the options and build an HTML value for each of
705
        // them until we have an array of HTML declarations. Then we will join them
706
        // all together into one single HTML element that can be put on the form.
707
        $html = [];
708
709
        if (isset($attributes['placeholder'])) {
710
            $html[] = $this->placeholderOption($attributes['placeholder'], $selected);
711
            unset($attributes['placeholder']);
712
        }
713
714
        foreach($list as $value => $display) {
715 6
            $html[] = $this->getSelectOption(
716
                $display, $value, $selected, isset($options[$value]) ? $options[$value] : []
717 6
            );
718
        }
719 6
720
        // Once we have all of this HTML, we can join this into a single element after
721
        // formatting the attributes into an HTML "attributes" string, then we will
722
        // build out a final select statement, which will contain all the values.
723
        $attributes = $this->html->attributes($attributes);
724
725
        return $this->toHtmlString("<select{$attributes}>".implode('', $html).'</select>');
726
    }
727
728
    /**
729
     * Create a select range field.
730
     *
731
     * @param  string  $name
732
     * @param  string  $begin
733 3
     * @param  string  $end
734
     * @param  string  $selected
735 3
     * @param  array   $options
736 3
     *
737 3
     * @return \Illuminate\Support\HtmlString
738 1
     */
739
    public function selectRange($name, $begin, $end, $selected = null, array $options = [])
740
    {
741
        $range = array_combine($range = range($begin, $end), $range);
742
743
        return $this->select($name, $range, $selected, $options);
744
    }
745
746
    /**
747
     * Create a select year field.
748
     *
749
     * @param  string  $name
750
     * @param  string  $begin
751 3
     * @param  string  $end
752
     * @param  string  $selected
753 3
     * @param  array   $options
754
     *
755 3
     * @return \Illuminate\Support\HtmlString
756 3
     */
757 1
    public function selectYear($name, $begin, $end, $selected = null, array $options = [])
758
    {
759 3
        return call_user_func_array(
760
            [$this, 'selectRange'],
761
            compact('name', 'begin', 'end', 'selected', 'options')
762
        );
763
    }
764
765
    /**
766
     * Create a select month field.
767
     *
768
     * @param  string  $name
769
     * @param  string  $selected
770
     * @param  array   $options
771 39
     * @param  string  $format
772
     *
773 39
     * @return \Illuminate\Support\HtmlString
774 17
     */
775 39
    public function selectMonth($name, $selected = null, array $options = [], $format = '%B')
776
    {
777
        $months = [];
778
779
        foreach(range(1, 12) as $month) {
780
            $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1));
781
        }
782
783
        return $this->select($name, $months, $selected, $options);
784
    }
785
786
    /**
787 6
     * Get the select option for the given value.
788
     *
789 6
     * @param  string  $display
790
     * @param  string  $value
791 6
     * @param  string  $selected
792 6
     * @param  array   $attributes
793 2
     *
794
     * @return string
795 6
     */
796
    private function getSelectOption($display, $value, $selected, array $attributes = [])
797
    {
798
        return is_array($display)
799
            ? $this->optionGroup($display, $value, $selected, $attributes)
800
            : $this->option($display, $value, $selected, $attributes);
801
    }
802
803
    /**
804
     * Create an option group form element.
805
     *
806
     * @param  array   $list
807 39
     * @param  string  $label
808
     * @param  string  $selected
809 39
     * @param  array   $attributes
810 39
     *
811
     * @return string
812 39
     */
813
    private function optionGroup(array $list, $label, $selected, array $attributes = [])
814
    {
815
        $html = [];
816
817
        foreach($list as $value => $display) {
818
            $html[] = $this->option($display, $value, $selected, $attributes);
819
        }
820
821
        return '<optgroup label="'.$this->html->escape($label).'">'.implode('', $html).'</optgroup>';
822
    }
823 3
824
    /**
825 3
     * Create a select element option.
826 3
     *
827 3
     * @param  string  $display
828
     * @param  string  $value
829 3
     * @param  string  $selected
830
     * @param  array   $attributes
831
     *
832
     * @return string
833
     */
834
    private function option($display, $value, $selected, array $attributes = [])
835
    {
836
        $selected = $this->getSelectedValue($value, $selected);
837
        $options  = array_merge(compact('value', 'selected'), $attributes);
838
839
        return '<option'.$this->html->attributes($options).'>'.$this->html->escape($display).'</option>';
840 39
    }
841
842 39
    /**
843 12
     * Create a placeholder select element option.
844
     *
845
     * @param  string  $display
846 30
     * @param  string  $selected
847
     *
848
     * @return string
849
     */
850
    private function placeholderOption($display, $selected)
851
    {
852
        $selected = $this->getSelectedValue(null, $selected);
853
        $options  = array_merge(compact('selected'), ['value' => '']);
854
855
        return '<option'.$this->html->attributes($options).'>'.$this->html->escape($display).'</option>';
856
    }
857
858
    /**
859 12
     * Determine if the value is selected.
860
     *
861 12
     * @param  string  $value
862
     * @param  string  $selected
863
     *
864
     * @return string|null
865
     */
866
    private function getSelectedValue($value, $selected)
867
    {
868
        if (is_array($selected)) {
869
            return in_array($value, $selected) ? 'selected' : null;
870
        }
871
872
        return ((string) $value === (string) $selected) ? 'selected' : null;
873
    }
874 6
875
    /**
876 6
     * Create a checkbox input field.
877 3
     *
878 1
     * @param  string     $name
879
     * @param  mixed      $value
880 6
     * @param  bool|null  $checked
881
     * @param  array      $options
882
     *
883
     * @return \Illuminate\Support\HtmlString
884
     */
885
    public function checkbox($name, $value = 1, $checked = null, array $options = [])
886
    {
887
        return $this->checkable('checkbox', $name, $value, $checked, $options);
888
    }
889
890
    /**
891
     * Create a radio button input field.
892
     *
893
     * @param  string  $name
894 21
     * @param  mixed   $value
895
     * @param  bool    $checked
896 21
     * @param  array   $options
897
     *
898 21
     * @return \Illuminate\Support\HtmlString
899 18
     */
900 6
    public function radio($name, $value = null, $checked = null, array $options = [])
901
    {
902 21
        $value = $value ?? $name;
903
904
        return $this->checkable('radio', $name, $value, $checked, $options);
905
    }
906
907
    /**
908
     * Create a checkable input field.
909
     *
910
     * @param  string     $type
911
     * @param  string     $name
912
     * @param  mixed      $value
913
     * @param  bool|null  $checked
914
     * @param  array      $options
915 21
     *
916
     * @return \Illuminate\Support\HtmlString
917
     */
918 21
    protected function checkable($type, $name, $value, $checked, array $options)
919 12
    {
920
        $checked = $this->getCheckedState($type, $name, $value, $checked);
921 9
922 6
        if ( ! is_null($checked) && $checked) {
923
            $options['checked'] = 'checked';
924 1
        }
925 3
926 1
        return $this->input($type, $name, $value, $options);
927
    }
928
929
    /**
930
     * Get the check state for a checkable input.
931
     *
932
     * @param  string     $type
933
     * @param  string     $name
934
     * @param  mixed      $value
935
     * @param  bool|null  $checked
936
     *
937
     * @return bool
938 12
     */
939
    private function getCheckedState($type, $name, $value, $checked)
940 12
    {
941 3
        switch($type) {
942
            case 'checkbox':
943
                return $this->getCheckboxCheckedState($name, $value, $checked);
944 12
945 6
            case 'radio':
946
                return $this->getRadioCheckedState($name, $value, $checked);
947
948 6
            default:
949
                return $this->getValueAttribute($name) === $value;
950 6
        }
951 3
    }
952
953
    /**
954 6
     * Get the check state for a checkbox input.
955 3
     *
956
     * @param  string     $name
957
     * @param  mixed      $value
958 6
     * @param  bool|null  $checked
959
     *
960
     * @return bool
961
     */
962
    private function getCheckboxCheckedState($name, $value, $checked)
963
    {
964
        if (isset($this->session) && ! $this->oldInputIsEmpty() && is_null($this->old($name))) {
965
            return false;
966
        }
967
968
        if ($this->missingOldAndModel($name)) {
969
            return $checked;
970 6
        }
971
972 6
        $posted = $this->getValueAttribute($name, $checked);
973 3
974 6
        if (is_array($posted)) {
975
            return in_array($value, $posted);
976
        }
977
978
        if ($posted instanceof Collection) {
979
            return $posted->contains('id', $value);
980
        }
981
982
        return (bool) $posted;
983
    }
984 18
985
    /**
986 18
     * Get the check state for a radio input.
987
     *
988
     * @param  string     $name
989
     * @param  mixed      $value
990
     * @param  bool|null  $checked
991
     *
992
     * @return bool
993
     */
994
    private function getRadioCheckedState($name, $value, $checked)
995
    {
996
        return $this->missingOldAndModel($name)
997 3
            ? $checked
998
            : $this->getValueAttribute($name) === $value;
999 3
    }
1000
1001
    /**
1002
     * Determine if old input or model input exists for a key.
1003
     *
1004
     * @param  string  $name
1005
     *
1006
     * @return bool
1007
     */
1008
    private function missingOldAndModel($name)
1009
    {
1010
        return (is_null($this->old($name)) && is_null($this->getModelValueAttribute($name)));
1011 3
    }
1012
1013 3
    /**
1014
     * Create a HTML reset input element.
1015 3
     *
1016
     * @param  string  $value
1017
     * @param  array   $attributes
1018
     *
1019
     * @return \Illuminate\Support\HtmlString
1020
     */
1021
    public function reset($value, array $attributes = [])
1022
    {
1023
        return $this->input('reset', null, $value, $attributes);
1024
    }
1025
1026 3
    /**
1027
    * Create a HTML image input element.
1028 3
    *
1029
    * @param  string  $url
1030
    * @param  string  $name
1031
    * @param  array   $attributes
1032
    *
1033
     * @return \Illuminate\Support\HtmlString
1034
    */
1035
    public function image($url, $name = null, array $attributes = [])
1036
    {
1037
        $attributes['src'] = $this->url->asset($url);
1038
1039 3
        return $this->input('image', $name, null, $attributes);
1040
    }
1041 3
1042 3
    /**
1043 1
     * Create a submit button element.
1044
     *
1045 3
     * @param  string  $value
1046 3
     * @param  array   $options
1047 1
     *
1048
     * @return \Illuminate\Support\HtmlString
1049
     */
1050
    public function submit($value = null, array $options = [])
1051
    {
1052
        return $this->input('submit', null, $value, $options);
1053
    }
1054
1055
    /**
1056
     * Create a button element.
1057
     *
1058
     * @param  string  $value
1059 9
     * @param  array   $options
1060
     *
1061 9
     * @return \Illuminate\Support\HtmlString
1062
     */
1063
    public function button($value = null, array $options = [])
1064
    {
1065
        if ( ! array_key_exists('type', $options)) {
1066
            $options['type'] = 'button';
1067
        }
1068
1069
        return $this->toHtmlString(
1070
            '<button'.$this->html->attributes($options).'>'.$value.'</button>'
1071
        );
1072
    }
1073
1074
    /**
1075
     * Create a color input field.
1076 48
     *
1077
     * @param  string  $name
1078
     * @param  string  $value
1079
     * @param  array   $options
1080
     *
1081 48
     * @return \Illuminate\Support\HtmlString
1082 15
     */
1083
    public function color($name, $value = null, array $options = [])
1084 33
    {
1085 3
        return $this->input('color', $name, $value, $options);
1086
    }
1087 33
1088
    /* -----------------------------------------------------------------
1089
     |  Other Methods
1090
     | -----------------------------------------------------------------
1091 3
     */
1092
1093
    /**
1094 30
     * Get the form action from the options.
1095
     *
1096
     * @param  array  $options
1097
     *
1098
     * @return string
1099
     */
1100
    private function getAction(array $options)
1101
    {
1102
        // We will also check for a "route" or "action" parameter on the array so that
1103
        // developers can easily specify a route or controller action when creating
1104 15
        // a form providing a convenient interface for creating the form actions.
1105
        if (isset($options['url'])) {
1106 15
            return $this->getUrlAction($options['url']);
1107 5
        }
1108 15
        elseif (isset($options['route'])) {
1109
            return $this->getRouteAction($options['route']);
1110
        }
1111
        elseif (isset($options['action'])) {
1112
            // If an action is available, we are attempting to open a form to a controller
1113
            // action route. So, we will use the URL generator to get the path to these
1114
            // actions and return them from the method. Otherwise, we'll use current.
1115
            return $this->getControllerAction($options['action']);
1116
        }
1117
1118 3
        return $this->url->current();
1119
    }
1120 3
1121 1
    /**
1122 3
     * Get the action for a "url" option.
1123
     *
1124
     * @param  array|string  $options
1125
     *
1126
     * @return string
1127
     */
1128
    private function getUrlAction($options)
1129
    {
1130
        return is_array($options)
1131
            ? $this->url->to($options[0], array_slice($options, 1))
1132 3
            : $this->url->to($options);
1133
    }
1134 3
1135 1
    /**
1136 3
     * Get the action for a "route" option.
1137
     *
1138
     * @param  array|string  $options
1139
     *
1140
     * @return string
1141
     */
1142
    private function getRouteAction($options)
1143
    {
1144
        return is_array($options)
1145
            ? $this->url->route($options[0], array_slice($options, 1))
1146 48
            : $this->url->route($options);
1147
    }
1148 48
1149
    /**
1150
     * Get the action for an "action" option.
1151
     *
1152
     * @param  array|string  $options
1153 48
     *
1154 6
     * @return string
1155 2
     */
1156
    private function getControllerAction($options)
1157
    {
1158
        return is_array($options)
1159
            ? $this->url->action($options[0], array_slice($options, 1))
1160 48
            : $this->url->action($options);
1161 21
    }
1162 7
1163
    /**
1164 48
     * Get the form appendage for the given method.
1165
     *
1166
     * @param  string  $method
1167
     *
1168
     * @return string
1169
     */
1170
    private function getAppendage($method)
1171
    {
1172
        list($method, $appendage) = [strtoupper($method), ''];
1173
1174
        // If the HTTP method is in this list of spoofed methods, we will attach the
1175
        // method spoofer hidden input to the form. This allows us to use regular
1176
        // form to initiate PUT and DELETE requests in addition to the typical.
1177
        if (in_array($method, $this->spoofedMethods)) {
1178
            $appendage .= $this->hidden('_method', $method);
1179
        }
1180
1181
        // If the method is something other than GET we will go ahead and attach the
1182
        // CSRF token to the form, as this can't hurt and is convenient to simply
1183
        // always have available on every form the developers creates for them.
1184
        if ($method !== 'GET') {
1185
            $appendage .= $this->token();
1186
        }
1187
1188
        return $appendage;
1189
    }
1190
}
1191