Completed
Pull Request — master (#21)
by ARCANEDEV
04:30
created

FormBuilder::getModel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 4
c 0
b 0
f 0
ccs 2
cts 2
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 321
    public function __construct(
100
        Contracts\HtmlBuilder $html,
101
        UrlGenerator $url,
102
        Session $session
103
    ) {
104 321
        $this->url       = $url;
105 321
        $this->html      = $html;
106 321
        $this->csrfToken = $session->token();
107
108 321
        $this->setSessionStore($session);
109 321
    }
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 321
    public function setSessionStore(Session $session)
134
    {
135 321
        $this->session = $session;
136
137 321
        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 36
    public function setModel($model)
148
    {
149 36
        $this->model = $model;
150
151 36
        return $this;
152
    }
153
154
    /**
155
     * Get the model instance on the form builder.
156
     *
157
     * @return \Illuminate\Database\Eloquent\Model
158
     */
159 15
    public function getModel()
160
    {
161 15
        return $this->model;
162
    }
163
164
    /**
165
     * Get the ID attribute for a field name.
166
     *
167
     * @param  string  $name
168
     * @param  array   $attributes
169
     *
170
     * @return string
171
     */
172 261
    public function getIdAttribute($name, array $attributes)
173
    {
174 261
        if (array_key_exists('id', $attributes))
175 21
            return $attributes['id'];
176
177 246
        if (in_array($name, $this->labels))
178 6
            return $name;
179
180 240
        return null;
181
    }
182
183
    /**
184
     * Get the value that should be assigned to the field.
185
     *
186
     * @param  string  $name
187
     * @param  mixed   $value
188
     *
189
     * @return mixed
190
     */
191 237
    public function getValueAttribute($name, $value = null)
192
    {
193 237
        if (is_null($name))
194 9
            return $value;
195
196 228
        if ( ! is_null($this->old($name)) && $name !== '_method')
197 12
            return $this->old($name);
198
199 222
        if ( ! is_null($value))
200 126
            return $value;
201
202 126
        return isset($this->model)
203 18
            ? $this->getModelValueAttribute($name)
204 126
            : null;
205
    }
206
207
    /**
208
     * Get the model value that should be assigned to the field.
209
     *
210
     * @param  string                               $name
211
     * @param  \Illuminate\Database\Eloquent\Model  $model
212
     *
213
     * @return mixed
214
     */
215 27
    private function getModelValueAttribute($name, $model = null)
216
    {
217 27
        if (is_null($model)) {
218 27
            $model = $this->model;
219
        }
220
221 27
        $key = $this->transformKey($name);
222
223 27
        if (strpos($key, '.') !== false) {
224 15
            $keys = explode('.', $key, 2);
225
226 15
            return $this->getModelValueAttribute($keys[1],
227 15
                $this->getModelValueAttribute($keys[0], $model)
228
            );
229
        }
230
231 27
        return method_exists($model, 'getFormValue')
232 3
            ? $model->getFormValue($key)
233 27
            : data_get($model, $key);
234
    }
235
236
    /**
237
     * Get a value from the session's old input.
238
     *
239
     * @param  string  $name
240
     *
241
     * @return mixed
242
     */
243 237
    public function old($name)
244
    {
245 237
        return isset($this->session)
246 237
            ? $this->session->getOldInput($this->transformKey($name))
247 237
            : null;
248
    }
249
250
    /**
251
     * Transform key from array to dot syntax.
252
     *
253
     * @param  string  $key
254
     *
255
     * @return string
256
     */
257 237
    private function transformKey($key)
258
    {
259 237
        return str_replace(['.', '[]', '[', ']'], ['_', '', '.', ''], $key);
260
    }
261
262
    /**
263
     * Determine if the old input is empty.
264
     *
265
     * @return bool
266
     */
267 12
    public function oldInputIsEmpty()
268
    {
269 12
        return isset($this->session) && (count($this->session->getOldInput()) == 0);
270
    }
271
272
    /**
273
     * Parse the form action method.
274
     *
275
     * @param  string  $method
276
     *
277
     * @return string
278
     */
279 57
    private function getMethod($method)
280
    {
281 57
        $method = strtoupper($method);
282
283 57
        return $method !== 'GET' ? 'POST' : $method;
284
    }
285
286
    /* -----------------------------------------------------------------
287
     |  Main Methods
288
     | -----------------------------------------------------------------
289
     */
290
291
    /**
292
     * Open up a new HTML form.
293
     *
294
     * @param  array  $options
295
     *
296
     * @return \Illuminate\Support\HtmlString
297
     */
298 57
    public function open(array $options = [])
299
    {
300 57
        $method = Arr::get($options, 'method', 'post');
301
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 57
            'method'         => $this->getMethod($method),
307 57
            'action'         => $this->getAction($options),
308 57
            'accept-charset' => 'UTF-8',
309
        ];
310
311 57
        if (isset($options['files']) && $options['files']) {
312 6
            $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 57
        $attributes = array_merge(
319 57
            $attributes, array_except($options, $this->reserved)
320
        );
321
322
        // Finally, we will concatenate all of the attributes into a single string so
323
        // 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 57
        $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 57
        $append = $this->getAppendage($method);
331
332 57
        return $this->toHtmlString('<form'.$attributes.'>' . $append);
333
    }
334
335
    /**
336
     * Create a new model based form builder.
337
     *
338
     * @param  mixed  $model
339
     * @param  array  $options
340
     *
341
     * @return \Illuminate\Support\HtmlString
342
     */
343 15
    public function model($model, array $options = [])
344
    {
345 15
        $this->setModel($model);
346
347 15
        return $this->open($options);
348
    }
349
350
    /**
351
     * Close the current form.
352
     *
353
     * @return \Illuminate\Support\HtmlString
354
     */
355 6
    public function close()
356
    {
357 6
        $this->labels = [];
358 6
        $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 6
        return $this->toHtmlString('</form>');
361
    }
362
363
    /**
364
     * Generate a hidden field with the current CSRF token.
365
     *
366
     * @return \Illuminate\Support\HtmlString
367
     */
368 30
    public function token()
369
    {
370 30
        $token = ! empty($this->csrfToken)
371 24
            ? $this->csrfToken
372 30
            : $this->session->token();
373
374 30
        return $this->hidden('_token', $token);
375
    }
376
377
    /**
378
     * Create a form label element.
379
     *
380
     * @param  string  $name
381
     * @param  string  $value
382
     * @param  array   $options
383
     * @param  bool    $escaped
384
     *
385
     * @return \Illuminate\Support\HtmlString
386
     */
387 15
    public function label($name, $value = null, array $options = [], $escaped = true)
388
    {
389 15
        $this->labels[] = $name;
390
391 15
        return $this->toHtmlString(
392 15
            Helpers\Label::make($name, $value, $options, $escaped)
393
        );
394
    }
395
396
    /**
397
     * Create a form input field.
398
     *
399
     * @param  string  $type
400
     * @param  string  $name
401
     * @param  string  $value
402
     * @param  array   $options
403
     *
404
     * @return \Illuminate\Support\HtmlString
405
     */
406 198
    public function input($type, $name, $value = null, array $options = [])
407
    {
408 198
        if ( ! isset($options['name'])) {
409 198
            $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
        // in the model instance if one is set. Otherwise we will just use empty.
415 198
        $id = $this->getIdAttribute($name, $options);
416
417 198
        if ( ! in_array($type, $this->skipValueTypes)) {
418 162
            $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 198
        $options = array_merge($options, compact('type', 'value', 'id'));
425
426 198
        return $this->toHtmlString('<input'.$this->html->attributes($options).'>');
427
    }
428
429
    /**
430
     * 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 21
    public function text($name, $value = null, array $options = [])
439
    {
440 21
        return $this->input('text', $name, $value, $options);
441
    }
442
443
    /**
444
     * Create a password input field.
445
     *
446
     * @param  string  $name
447
     * @param  array   $options
448
     *
449
     * @return \Illuminate\Support\HtmlString
450
     */
451 9
    public function password($name, array $options = [])
452
    {
453 9
        return $this->input('password', $name, '', $options);
454
    }
455
456
    /**
457
     * 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 39
    public function hidden($name, $value = null, array $options = [])
466
    {
467 39
        return $this->input('hidden', $name, $value, $options);
468
    }
469
470
    /**
471
     * 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 9
    public function email($name, $value = null, array $options = [])
480
    {
481 9
        return $this->input('email', $name, $value, $options);
482
    }
483
484
    /**
485
     * 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 9
    public function tel($name, $value = null, array $options = [])
494
    {
495 9
        return $this->input('tel', $name, $value, $options);
496
    }
497
498
    /**
499
     * Create a number input field.
500
     *
501
     * @param  string  $name
502
     * @param  string  $value
503
     * @param  array   $options
504
     *
505
     * @return \Illuminate\Support\HtmlString
506
     */
507 9
    public function number($name, $value = null, array $options = [])
508
    {
509 9
        return $this->input('number', $name, $value, $options);
510
    }
511
512
    /**
513
     * Create a date input field.
514
     *
515
     * @param  string  $name
516
     * @param  string  $value
517
     * @param  array   $options
518
     *
519
     * @return \Illuminate\Support\HtmlString
520
     */
521 12
    public function date($name, $value = null, array $options = [])
522
    {
523 12
        if ($value instanceof DateTime) {
524 3
            $value = $value->format('Y-m-d');
525
        }
526
527 12
        return $this->input('date', $name, $value, $options);
528
    }
529
530
    /**
531
     * Create a datetime input field.
532
     *
533
     * @param  string  $name
534
     * @param  string  $value
535
     * @param  array   $options
536
     *
537
     * @return \Illuminate\Support\HtmlString
538
     */
539 12
    public function datetime($name, $value = null, array $options = [])
540
    {
541 12
        if ($value instanceof DateTime) {
542 6
            $value = $value->format(DateTime::RFC3339);
543
        }
544
545 12
        return $this->input('datetime', $name, $value, $options);
546
    }
547
548
    /**
549
     * Create a datetime-local input field.
550
     *
551
     * @param  string  $name
552
     * @param  string  $value
553
     * @param  array   $options
554
     *
555
     * @return \Illuminate\Support\HtmlString
556
     */
557 12
    public function datetimeLocal($name, $value = null, array $options = [])
558
    {
559 12
        if ($value instanceof DateTime) {
560 6
            $value = $value->format('Y-m-d\TH:i');
561
        }
562
563 12
        return $this->input('datetime-local', $name, $value, $options);
564
    }
565
566
    /**
567
     * 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 9
    public function time($name, $value = null, array $options = [])
576
    {
577 9
        return $this->input('time', $name, $value, $options);
578
    }
579
580
    /**
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 6
    public function url($name, $value = null, array $options = [])
590
    {
591 6
        return $this->input('url', $name, $value, $options);
592
    }
593
594
    /**
595
     * Create a file input field.
596
     *
597
     * @param  string  $name
598
     * @param  array   $options
599
     *
600
     * @return \Illuminate\Support\HtmlString
601
     */
602 9
    public function file($name, array $options = [])
603
    {
604 9
        return $this->input('file', $name, null, $options);
605
    }
606
607
    /**
608
     * Create a textarea input field.
609
     *
610
     * @param  string  $name
611
     * @param  string  $value
612
     * @param  array   $options
613
     *
614
     * @return \Illuminate\Support\HtmlString
615
     */
616 18
    public function textarea($name, $value = null, array $options = [])
617
    {
618 18
        if ( ! isset($options['name'])) {
619 18
            $options['name'] = $name;
620
        }
621
622
        // 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
        // assume some sane default values for these attributes for the developer.
625 18
        $options       = $this->setTextAreaSize($options);
626 18
        $options['id'] = $this->getIdAttribute($name, $options);
627 18
        $value         = (string) $this->getValueAttribute($name, $value);
628
629 18
        unset($options['size']);
630
631
        // Next we will convert the attributes into a string form. Also we have removed
632
        // 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 18
        $options = $this->html->attributes($options);
635
636 18
        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
     * @return array
645
     */
646 18
    private function setTextAreaSize(array $options)
647
    {
648 18
        if (isset($options['size'])) {
649 9
            return $this->setQuickTextAreaSize($options);
650
        }
651
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 9
        $cols = Arr::get($options, 'cols', 50);
656 9
        $rows = Arr::get($options, 'rows', 10);
657
658 9
        return array_merge($options, compact('cols', 'rows'));
659
    }
660
661
    /**
662
     * Set the text area size using the quick "size" attribute.
663
     *
664
     * @param  array  $options
665
     *
666
     * @return array
667
     */
668 9
    protected function setQuickTextAreaSize(array $options)
669
    {
670 9
        list($cols, $rows) = explode('x', $options['size']);
671
672 9
        return array_merge($options, compact('cols', 'rows'));
673
    }
674
675
    /**
676
     * Create a select box field.
677
     *
678
     * @param  string                                $name
679
     * @param  array|\Illuminate\Support\Collection  $list
680
     * @param  string                                $selected
681
     * @param  array                                 $attributes
682
     * @param  array                                 $options
683
     *
684
     * @return \Illuminate\Support\HtmlString
685
     */
686 45
    public function select($name, $list = [], $selected = null, array $attributes = [], array $options = [])
687
    {
688
        // When building a select box the "value" attribute is really the selected one
689
        // so we will use that when checking the model or session for a value which
690
        // should provide a convenient method of re-populating the forms on post.
691 45
        $selected = $this->getValueAttribute($name, $selected);
692
693
        // Transform to array if it is a collection
694 45
        if ($selected instanceof Collection) {
695 3
            $selected = $selected->all();
696
        }
697
698 45
        $attributes['id'] = $this->getIdAttribute($name, $attributes);
699
700 45
        if ( ! isset($attributes['name'])) {
701 33
            $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 45
        $html = [];
708
709 45
        if (isset($attributes['placeholder'])) {
710 3
            $html[] = $this->placeholderOption($attributes['placeholder'], $selected);
711 3
            unset($attributes['placeholder']);
712
        }
713
714 45
        foreach($list as $value => $display) {
715 42
            $html[] = $this->getSelectOption(
716 42
                $display, $value, $selected, isset($options[$value]) ? $options[$value] : []
717
            );
718
        }
719
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 45
        $attributes = $this->html->attributes($attributes);
724
725 45
        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
     * @param  string  $end
734
     * @param  string  $selected
735
     * @param  array   $options
736
     *
737
     * @return \Illuminate\Support\HtmlString
738
     */
739 6
    public function selectRange($name, $begin, $end, $selected = null, array $options = [])
740
    {
741 6
        $range = array_combine($range = range($begin, $end), $range);
742
743 6
        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
     * @param  string  $end
752
     * @param  string  $selected
753
     * @param  array   $options
754
     *
755
     * @return \Illuminate\Support\HtmlString
756
     */
757 3
    public function selectYear($name, $begin, $end, $selected = null, array $options = [])
758
    {
759 3
        return call_user_func_array(
760 3
            [$this, 'selectRange'],
761 3
            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
     * @param  string  $format
772
     *
773
     * @return \Illuminate\Support\HtmlString
774
     */
775 3
    public function selectMonth($name, $selected = null, array $options = [], $format = '%B')
776
    {
777 3
        $months = [];
778
779 3
        foreach(range(1, 12) as $month) {
780 3
            $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1));
781
        }
782
783 3
        return $this->select($name, $months, $selected, $options);
784
    }
785
786
    /**
787
     * Get the select option for the given value.
788
     *
789
     * @param  string  $display
790
     * @param  string  $value
791
     * @param  string  $selected
792
     * @param  array   $attributes
793
     *
794
     * @return string
795
     */
796 42
    private function getSelectOption($display, $value, $selected, array $attributes = [])
797
    {
798 42
        return is_array($display)
799 6
            ? $this->optionGroup($display, $value, $selected, $attributes)
800 42
            : $this->option($display, $value, $selected, $attributes);
801
    }
802
803
    /**
804
     * Create an option group form element.
805
     *
806
     * @param  array   $list
807
     * @param  string  $label
808
     * @param  string  $selected
809
     * @param  array   $attributes
810
     *
811
     * @return string
812
     */
813 6
    private function optionGroup(array $list, $label, $selected, array $attributes = [])
814
    {
815 6
        $html = [];
816
817 6
        foreach($list as $value => $display) {
818 6
            $html[] = $this->option($display, $value, $selected, $attributes);
819
        }
820
821 6
        return '<optgroup label="'.$this->html->escape($label).'">'.implode('', $html).'</optgroup>';
822
    }
823
824
    /**
825
     * Create a select element option.
826
     *
827
     * @param  string  $display
828
     * @param  string  $value
829
     * @param  string  $selected
830
     * @param  array   $attributes
831
     *
832
     * @return string
833
     */
834 42
    private function option($display, $value, $selected, array $attributes = [])
835
    {
836 42
        $selected = $this->getSelectedValue($value, $selected);
837 42
        $options  = array_merge(compact('value', 'selected'), $attributes);
838
839 42
        return '<option'.$this->html->attributes($options).'>'.$this->html->escape($display).'</option>';
840
    }
841
842
    /**
843
     * Create a placeholder select element option.
844
     *
845
     * @param  string  $display
846
     * @param  string  $selected
847
     *
848
     * @return string
849
     */
850 3
    private function placeholderOption($display, $selected)
851
    {
852 3
        $selected = $this->getSelectedValue(null, $selected);
853 3
        $options  = array_merge(compact('selected'), ['value' => '']);
854
855 3
        return '<option'.$this->html->attributes($options).'>'.$this->html->escape($display).'</option>';
856
    }
857
858
    /**
859
     * Determine if the value is selected.
860
     *
861
     * @param  string  $value
862
     * @param  string  $selected
863
     *
864
     * @return string|null
865
     */
866 42
    private function getSelectedValue($value, $selected)
867
    {
868 42
        if (is_array($selected)) {
869 12
            return in_array($value, $selected) ? 'selected' : null;
870
        }
871
872 33
        return ((string) $value === (string) $selected) ? 'selected' : null;
873
    }
874
875
    /**
876
     * Create a checkbox input field.
877
     *
878
     * @param  string     $name
879
     * @param  mixed      $value
880
     * @param  bool|null  $checked
881
     * @param  array      $options
882
     *
883
     * @return \Illuminate\Support\HtmlString
884
     */
885 12
    public function checkbox($name, $value = 1, $checked = null, array $options = [])
886
    {
887 12
        return $this->checkable('checkbox', $name, $value, $checked, $options);
888
    }
889
890
    /**
891
     * Create a radio button input field.
892
     *
893
     * @param  string  $name
894
     * @param  mixed   $value
895
     * @param  bool    $checked
896
     * @param  array   $options
897
     *
898
     * @return \Illuminate\Support\HtmlString
899
     */
900 6
    public function radio($name, $value = null, $checked = null, array $options = [])
901
    {
902 6
        $value = $value ?? $name;
903
904 6
        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
     *
916
     * @return \Illuminate\Support\HtmlString
917
     */
918 21
    protected function checkable($type, $name, $value, $checked, array $options)
919
    {
920 21
        $checked = $this->getCheckedState($type, $name, $value, $checked);
921
922 21
        if ( ! is_null($checked) && $checked) {
923 18
            $options['checked'] = 'checked';
924
        }
925
926 21
        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
     */
939 21
    private function getCheckedState($type, $name, $value, $checked)
940
    {
941 7
        switch($type) {
942 21
            case 'checkbox':
943 12
                return $this->getCheckboxCheckedState($name, $value, $checked);
944
945 9
            case 'radio':
946 6
                return $this->getRadioCheckedState($name, $value, $checked);
947
948
            default:
949 3
                return $this->getValueAttribute($name) === $value;
950
        }
951
    }
952
953
    /**
954
     * Get the check state for a checkbox input.
955
     *
956
     * @param  string     $name
957
     * @param  mixed      $value
958
     * @param  bool|null  $checked
959
     *
960
     * @return bool
961
     */
962 12
    private function getCheckboxCheckedState($name, $value, $checked)
963
    {
964 12
        if (isset($this->session) && ! $this->oldInputIsEmpty() && is_null($this->old($name))) {
965 3
            return false;
966
        }
967
968 12
        if ($this->missingOldAndModel($name)) {
969 6
            return $checked;
970
        }
971
972 6
        $posted = $this->getValueAttribute($name, $checked);
973
974 6
        if (is_array($posted)) {
975 3
            return in_array($value, $posted);
976
        }
977
978 6
        if ($posted instanceof Collection) {
979 3
            return $posted->contains('id', $value);
980
        }
981
982 6
        return (bool) $posted;
983
    }
984
985
    /**
986
     * 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 6
    private function getRadioCheckedState($name, $value, $checked)
995
    {
996 6
        return $this->missingOldAndModel($name)
997 2
            ? $checked
998 6
            : $this->getValueAttribute($name) === $value;
999
    }
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 18
    private function missingOldAndModel($name)
1009
    {
1010 18
        return (is_null($this->old($name)) && is_null($this->getModelValueAttribute($name)));
1011
    }
1012
1013
    /**
1014
     * Create a HTML reset input element.
1015
     *
1016
     * @param  string  $value
1017
     * @param  array   $attributes
1018
     *
1019
     * @return \Illuminate\Support\HtmlString
1020
     */
1021 3
    public function reset($value, array $attributes = [])
1022
    {
1023 3
        return $this->input('reset', null, $value, $attributes);
1024
    }
1025
1026
    /**
1027
    * Create a HTML image input element.
1028
    *
1029
    * @param  string  $url
1030
    * @param  string  $name
1031
    * @param  array   $attributes
1032
    *
1033
     * @return \Illuminate\Support\HtmlString
1034
    */
1035 3
    public function image($url, $name = null, array $attributes = [])
1036
    {
1037 3
        $attributes['src'] = $this->url->asset($url);
1038
1039 3
        return $this->input('image', $name, null, $attributes);
1040
    }
1041
1042
    /**
1043
     * Create a submit button element.
1044
     *
1045
     * @param  string  $value
1046
     * @param  array   $options
1047
     *
1048
     * @return \Illuminate\Support\HtmlString
1049
     */
1050 3
    public function submit($value = null, array $options = [])
1051
    {
1052 3
        return $this->input('submit', null, $value, $options);
1053
    }
1054
1055
    /**
1056
     * Create a button element.
1057
     *
1058
     * @param  string  $value
1059
     * @param  array   $options
1060
     *
1061
     * @return \Illuminate\Support\HtmlString
1062
     */
1063 3
    public function button($value = null, array $options = [])
1064
    {
1065 3
        if ( ! array_key_exists('type', $options)) {
1066 3
            $options['type'] = 'button';
1067
        }
1068
1069 3
        return $this->toHtmlString(
1070 3
            '<button'.$this->html->attributes($options).'>'.$value.'</button>'
1071
        );
1072
    }
1073
1074
    /**
1075
     * Create a color input field.
1076
     *
1077
     * @param  string  $name
1078
     * @param  string  $value
1079
     * @param  array   $options
1080
     *
1081
     * @return \Illuminate\Support\HtmlString
1082
     */
1083 9
    public function color($name, $value = null, array $options = [])
1084
    {
1085 9
        return $this->input('color', $name, $value, $options);
1086
    }
1087
1088
    /* -----------------------------------------------------------------
1089
     |  Other Methods
1090
     | -----------------------------------------------------------------
1091
     */
1092
1093
    /**
1094
     * Get the form action from the options.
1095
     *
1096
     * @param  array  $options
1097
     *
1098
     * @return string
1099
     */
1100 57
    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
        // a form providing a convenient interface for creating the form actions.
1105 57
        if (isset($options['url'])) {
1106 18
            return $this->getUrlAction($options['url']);
1107
        }
1108 39
        elseif (isset($options['route'])) {
1109 6
            return $this->getRouteAction($options['route']);
1110
        }
1111 36
        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 6
            return $this->getControllerAction($options['action']);
1116
        }
1117
1118 30
        return $this->url->current();
1119
    }
1120
1121
    /**
1122
     * Get the action for a "url" option.
1123
     *
1124
     * @param  array|string  $options
1125
     *
1126
     * @return string
1127
     */
1128 18
    private function getUrlAction($options)
1129
    {
1130 18
        return is_array($options)
1131 3
            ? $this->url->to($options[0], array_slice($options, 1))
1132 18
            : $this->url->to($options);
1133
    }
1134
1135
    /**
1136
     * Get the action for a "route" option.
1137
     *
1138
     * @param  array|string  $options
1139
     *
1140
     * @return string
1141
     */
1142 6
    private function getRouteAction($options)
1143
    {
1144 6
        return is_array($options)
1145 3
            ? $this->url->route($options[0], array_slice($options, 1))
1146 6
            : $this->url->route($options);
1147
    }
1148
1149
    /**
1150
     * Get the action for an "action" option.
1151
     *
1152
     * @param  array|string  $options
1153
     *
1154
     * @return string
1155
     */
1156 6
    private function getControllerAction($options)
1157
    {
1158 6
        return is_array($options)
1159 3
            ? $this->url->action($options[0], array_slice($options, 1))
1160 6
            : $this->url->action($options);
1161
    }
1162
1163
    /**
1164
     * Get the form appendage for the given method.
1165
     *
1166
     * @param  string  $method
1167
     *
1168
     * @return string
1169
     */
1170 57
    private function getAppendage($method)
1171
    {
1172 57
        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 57
        if (in_array($method, $this->spoofedMethods)) {
1178 6
            $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 57
        if ($method !== 'GET') {
1185 30
            $appendage .= $this->token();
1186
        }
1187
1188 57
        return $appendage;
1189
    }
1190
}
1191