FormBuilder::textarea()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 15

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 12
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 15
ccs 12
cts 12
cp 1
rs 9.7666
c 0
b 0
f 0
cc 1
nc 1
nop 3
crap 1
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Arcanedev\LaravelHtml;
6
7
use Arcanedev\Html\Elements\{Button, File, Form, Input, Label, Select, Textarea};
8
use Arcanedev\LaravelHtml\Contracts\FormBuilder as FormBuilderContract;
9
use DateTime;
10
use Illuminate\Contracts\Routing\UrlGenerator;
11
use Illuminate\Contracts\Session\Session;
12
use Illuminate\Support\{Arr, Collection, HtmlString, Str};
13
14
/**
15
 * Class     FormBuilder
16
 *
17
 * @author   ARCANEDEV <[email protected]>
18
 */
19
class FormBuilder extends AbstractBuilder implements FormBuilderContract
20
{
21
    /* -----------------------------------------------------------------
22
     |  Properties
23
     | -----------------------------------------------------------------
24
     */
25
26
    /**
27
    * The HTML builder instance.
28
    *
29
    * @var \Arcanedev\LaravelHtml\Contracts\HtmlBuilder
30
    */
31
    protected $html;
32
33
    /**
34
    * The URL generator instance.
35
    *
36
    * @var \Illuminate\Contracts\Routing\UrlGenerator
37
    */
38
    protected $url;
39
40
    /**
41
    * The CSRF token used by the form builder.
42
    *
43
    * @var string
44
    */
45
    protected $csrfToken;
46
47
    /**
48
    * The session store implementation.
49
    *
50
    * @var \Illuminate\Contracts\Session\Session|\Illuminate\Session\Store
51
    */
52
    protected $session;
53
54
    /**
55
    * The current model instance for the form.
56
    *
57
    * @var \Illuminate\Database\Eloquent\Model|null
58
    */
59
    protected $model;
60
61
    /**
62
    * An array of label names we've created.
63
    *
64
    * @var array
65
    */
66
    protected $labels = [];
67
68
    /**
69
    * The reserved form open attributes.
70
    *
71
    * @var array
72
    */
73
    protected $reserved = ['method', 'url', 'route', 'action', 'files'];
74
75
    /**
76
    * The form methods that should be spoofed, in uppercase.
77
    *
78
    * @var array
79
    */
80
    protected $spoofedMethods = ['DELETE', 'PATCH', 'PUT'];
81
82
    /**
83
    * The types of inputs to not fill values on by default.
84
    *
85
    * @var array
86
    */
87
    protected $skipValueTypes = ['file', 'password', 'checkbox', 'radio'];
88
89
    /* -----------------------------------------------------------------
90
     |  Constructor
91
     | -----------------------------------------------------------------
92
     */
93
94
    /**
95
    * Create a new form builder instance.
96
    *
97
    * @param  \Arcanedev\LaravelHtml\Contracts\HtmlBuilder  $html
98
    * @param  \Illuminate\Contracts\Routing\UrlGenerator    $url
99
    * @param  \Illuminate\Contracts\Session\Session         $session
100
    */
101 606
    public function __construct(Contracts\HtmlBuilder $html, UrlGenerator $url, Session $session)
102
    {
103 606
        $this->url       = $url;
104 606
        $this->html      = $html;
105 606
        $this->csrfToken = $session->token();
106
107 606
        $this->setSessionStore($session);
108 606
    }
109
110
    /* -----------------------------------------------------------------
111
     |  Getters & Setters
112
     | -----------------------------------------------------------------
113
     */
114
115
    /**
116
     * Get the session store implementation.
117
     *
118
     * @return  \Illuminate\Contracts\Session\Session|null
119
     */
120 468
    public function getSessionStore(): ?Session
121
    {
122 468
        return $this->session;
123
    }
124
125
    /**
126
     * Set the session store implementation.
127
     *
128
     * @param  \Illuminate\Contracts\Session\Session  $session
129
     *
130
     * @return $this
131
     */
132 606
    public function setSessionStore(Session $session)
133
    {
134 606
        $this->session = $session;
135
136 606
        return $this;
137
    }
138
139
    /**
140
     * Set the model instance on the form builder.
141
     *
142
     * @param  \Illuminate\Database\Eloquent\Model|mixed|null  $model
143
     *
144
     * @return $this
145
     */
146 66
    public function setModel($model)
147
    {
148 66
        $this->model = $model;
149
150 66
        return $this;
151
    }
152
153
    /**
154
     * Get the model instance on the form builder.
155
     *
156
     * @return \Illuminate\Database\Eloquent\Model|mixed|null
157
     */
158 264
    public function getModel()
159
    {
160 264
        return $this->model;
161
    }
162
163
    /**
164
     * Get the ID attribute for a field name.
165
     *
166
     * @param  string|null  $name
167
     * @param  array        $attributes
168
     *
169
     * @return string|null
170
     */
171 480
    public function getIdAttribute($name, array $attributes): ?string
172
    {
173 480
        if (array_key_exists('id', $attributes))
174 42
            return $attributes['id'];
175
176 456
        if ( ! is_null($name) && in_array($name, $this->labels))
177 12
            return $name;
178
179 444
        return null;
180
    }
181
182
    /**
183
     * Get the value that should be assigned to the field.
184
     *
185
     * @param  string  $name
186
     * @param  mixed   $value
187
     *
188
     * @return mixed
189
     */
190 450
    public function getValueAttribute($name, $value = null)
191
    {
192 450
        if (is_null($name))
193 6
            return $value;
194
195 444
        if ( ! is_null($this->old($name)) && $name !== '_method')
196 30
            return $this->old($name);
197
198 432
        if ( ! is_null($value))
199 252
            return $value;
200
201 234
        return $this->getModelValueAttribute($name);
202
    }
203
204
    /**
205
     * Get the model value that should be assigned to the field.
206
     *
207
     * @param  string                                          $name
208
     * @param  \Illuminate\Database\Eloquent\Model|mixed|null  $model
209
     *
210
     * @return mixed
211
     */
212 252
    private function getModelValueAttribute(string $name, $model = null)
213
    {
214 252
        $model = $model ?: $this->getModel();
215
216 252
        $key = static::transformKey($name);
0 ignored issues
show
Bug introduced by
Since transformKey() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of transformKey() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
217
218 252
        if (strpos($key, '.') !== false) {
219 30
            $keys = explode('.', $key, 2);
220
221 30
            return $this->getModelValueAttribute(
222 30
                $keys[1],
223 30
                $this->getModelValueAttribute($keys[0], $model)
224
            );
225
        }
226
227 252
        if (is_null($model) || is_array($model))
228 234
            return data_get($model, $key);
229
230 36
        return method_exists($model, 'getFormValue')
231 6
            ? $model->getFormValue($key)
232 36
            : data_get($model, $key);
233
    }
234
235
    /**
236
     * Get a value from the session's old input.
237
     *
238
     * @param  string  $name
239
     *
240
     * @return mixed
241
     */
242 462
    public function old(string $name)
243
    {
244 462
        $session = $this->getSessionStore();
245
246 462
        return is_null($session) ? null : $session->getOldInput(static::transformKey($name));
0 ignored issues
show
Bug introduced by
Since transformKey() is declared private, calling it with static will lead to errors in possible sub-classes. You can either use self, or increase the visibility of transformKey() to at least protected.

Let’s assume you have a class which uses late-static binding:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
}

public static function getSomeVariable()
{
    return static::getTemperature();
}

}

The code above will run fine in your PHP runtime. However, if you now create a sub-class and call the getSomeVariable() on that sub-class, you will receive a runtime error:

class YourSubClass extends YourClass {
      private static function getTemperature() {
        return "-182 °C";
    }
}

print YourSubClass::getSomeVariable(); // Will cause an access error.

In the case above, it makes sense to update SomeClass to use self instead:

class YourClass
{
    private static function getTemperature() {
        return "3422 °C";
    }

    public static function getSomeVariable()
    {
        return self::getTemperature();
    }
}
Loading history...
247
    }
248
249
    /**
250
     * Transform key from array to dot syntax.
251
     *
252
     * @param  string  $key
253
     *
254
     * @return string
255
     */
256 462
    private static function transformKey(string $key): string
257
    {
258 462
        return str_replace(
259 462
            ['.', '[]', '[', ']'],
260 462
            ['_', '', '.', ''],
261
            $key
262
        );
263
    }
264
265
    /**
266
     * Determine if the old input is empty.
267
     *
268
     * @return bool
269
     */
270 24
    public function oldInputIsEmpty(): bool
271
    {
272 24
        $session = $this->getSessionStore();
273
274 24
        return ! is_null($session)
275 24
            && (count($session->getOldInput()) === 0);
276
    }
277
278
    /* -----------------------------------------------------------------
279
     |  Main Methods
280
     | -----------------------------------------------------------------
281
     */
282
283
    /**
284
     * Open up a new HTML form.
285
     *
286
     * @param  array  $attributes
287
     *
288
     * @return \Illuminate\Support\HtmlString
289
     */
290 84
    public function open(array $attributes = []): HtmlString
291
    {
292 84
        $method = Str::upper(Arr::pull($attributes, 'method', 'POST'));
293
294 84
        return Form::make()
295 84
            ->method($method !== 'GET' ? 'POST' : $method)
296 84
            ->action($this->getAction($attributes))
297 84
            ->attributes(array_merge(
0 ignored issues
show
Documentation introduced by
array_merge(array('accep...utes, $this->reserved)) is of type array, but the function expects a object<Arcanedev\Html\Elements\Concerns\iterable>.

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...
298 84
                ['accept-charset' => 'UTF-8'],
299 84
                Arr::except($attributes, $this->reserved)
300
            ))
301 84
            ->if(Arr::pull($attributes, 'files', false), function (Form $form) {
302 6
                return $form->acceptsFiles();
303 84
            })
304 84
            ->if(in_array($method, $this->spoofedMethods), function (Form $form) use ($method) {
305 6
                return $form->addChild($this->hidden('_method', $method));
306 84
            })
307 84
            ->if($method !== 'GET', function (Form $form) {
308 48
                return $form->addChild($this->token());
309 84
            })
310 84
            ->open();
311
    }
312
313
    /**
314
     * Create a new model based form builder.
315
     *
316
     * @param  mixed  $model
317
     * @param  array  $attributes
318
     *
319
     * @return \Illuminate\Support\HtmlString
320
     */
321 30
    public function model($model, array $attributes = []): HtmlString
322
    {
323 30
        return $this->setModel($model)->open($attributes);
324
    }
325
326
    /**
327
     * Close the current form.
328
     *
329
     * @return \Illuminate\Support\HtmlString
330
     */
331 6
    public function close(): HtmlString
332
    {
333 6
        $this->labels = [];
334 6
        $this->setModel(null);
335
336 6
        return Form::make()->close();
337
    }
338
339
    /**
340
     * Generate a hidden field with the current CSRF token.
341
     *
342
     * @return \Arcanedev\Html\Elements\Input
343
     */
344 48
    public function token(): Input
345
    {
346 48
        $token = empty($this->csrfToken)
347
            ? $this->getSessionStore()->token()
348 48
            : $this->csrfToken;
349
350 48
        return $this->hidden('_token', $token);
351
    }
352
353
    /**
354
     * Create a form label element.
355
     *
356
     * @param  string        $name
357
     * @param  string|mixed  $value
358
     * @param  array         $attributes
359
     * @param  bool          $escaped
360
     *
361
     * @return \Arcanedev\Html\Elements\Label
362
     */
363 30
    public function label(string $name, $value = null, array $attributes = [], $escaped = true): Label
364
    {
365 30
        $this->labels[] = $name;
366
367 30
        $value = $value ?: Str::title(str_replace(['_', '-'], ' ', $name));
368
369 30
        return Label::make()
370 30
            ->for($name)
371 30
            ->attributes($attributes)
0 ignored issues
show
Documentation introduced by
$attributes is of type array, but the function expects a object<Arcanedev\Html\Elements\Concerns\iterable>.

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...
372 30
            ->html($escaped ? e($value) : $value);
373
    }
374
375
    /**
376
     * Create a form input field.
377
     *
378
     * @param  string        $type
379
     * @param  string|null   $name
380
     * @param  string|mixed  $value
381
     * @param  array         $attributes
382
     *
383
     * @return \Arcanedev\Html\Elements\Input
384
     */
385 354
    public function input(string $type, $name, $value = null, array $attributes = []): Input
386
    {
387 354
        if ( ! in_array($type, $this->skipValueTypes))
388 300
            $value = $this->getValueAttribute($name, $value);
389
390 354
        $id = $this->getIdAttribute($name, $attributes);
391
392 354
        return Input::make()
393 354
            ->type($type)
394 354
            ->attributeIfNotNull($name, 'name', $name)
395 354
            ->attributeIfNotNull($id, 'id', $id)
396 354
            ->attributeUnless(is_null($value) || empty($value), 'value', $value)
397 354
            ->attributes($attributes);
398
    }
399
400
    /**
401
     * Create a text input field.
402
     *
403
     * @param  string        $name
404
     * @param  string|mixed  $value
405
     * @param  array         $attributes
406
     *
407
     * @return \Arcanedev\Html\Elements\Input
408
     */
409 42
    public function text(string $name, $value = null, array $attributes = []): Input
410
    {
411 42
        return $this->input('text', $name, $value, $attributes);
412
    }
413
414
    /**
415
     * Create a password input field.
416
     *
417
     * @param  string  $name
418
     * @param  array   $attributes
419
     *
420
     * @return \Arcanedev\Html\Elements\Input
421
     */
422 18
    public function password(string $name, array $attributes = []): Input
423
    {
424 18
        return $this->input('password', $name, null, $attributes);
425
    }
426
427
    /**
428
     * Create a hidden input field.
429
     *
430
     * @param  string        $name
431
     * @param  string|mixed  $value
432
     * @param  array         $attributes
433
     *
434
     * @return \Arcanedev\Html\Elements\Input
435
     */
436 66
    public function hidden(string $name, $value = null, array $attributes = []): Input
437
    {
438 66
        return $this->input('hidden', $name, $value, $attributes);
439
    }
440
441
    /**
442
     * Create an e-mail input field.
443
     *
444
     * @param  string        $name
445
     * @param  string|mixed  $value
446
     * @param  array         $attributes
447
     *
448
     * @return \Arcanedev\Html\Elements\Input
449
     */
450 18
    public function email(string $name, $value = null, array $attributes = []): Input
451
    {
452 18
        return $this->input('email', $name, $value, $attributes);
453
    }
454
455
    /**
456
     * Create a tel input field.
457
     *
458
     * @param  string        $name
459
     * @param  string|mixed  $value
460
     * @param  array         $attributes
461
     *
462
     * @return \Arcanedev\Html\Elements\Input
463
     */
464 18
    public function tel(string $name, $value = null, array $attributes = []): Input
465
    {
466 18
        return $this->input('tel', $name, $value, $attributes);
467
    }
468
469
    /**
470
     * Create a number input field.
471
     *
472
     * @param  string        $name
473
     * @param  string|mixed  $value
474
     * @param  array         $attributes
475
     *
476
     * @return \Arcanedev\Html\Elements\Input
477
     */
478 18
    public function number(string $name, $value = null, array $attributes = []): Input
479
    {
480 18
        return $this->input('number', $name, $value, $attributes);
481
    }
482
483
    /**
484
     * Create a date input field.
485
     *
486
     * @param  string  $name
487
     * @param  string  $value
488
     * @param  array   $attributes
489
     *
490
     * @return \Arcanedev\Html\Elements\Input
491
     */
492 24
    public function date(string $name, $value = null, array $attributes = []): Input
493
    {
494 24
        if ($value instanceof DateTime)
495 6
            $value = $value->format('Y-m-d');
496
497 24
        return $this->input('date', $name, $value, $attributes);
498
    }
499
500
    /**
501
     * Create a datetime input field.
502
     *
503
     * @param  string        $name
504
     * @param  string|mixed  $value
505
     * @param  array         $attributes
506
     *
507
     * @return \Arcanedev\Html\Elements\Input
508
     */
509 24
    public function datetime(string $name, $value = null, array $attributes = []): Input
510
    {
511 24
        if ($value instanceof DateTime)
512 12
            $value = $value->format(DateTime::RFC3339);
513
514 24
        return $this->input('datetime', $name, $value, $attributes);
515
    }
516
517
    /**
518
     * Create a datetime-local input field.
519
     *
520
     * @param  string        $name
521
     * @param  string|mixed  $value
522
     * @param  array         $attributes
523
     *
524
     * @return \Arcanedev\Html\Elements\Input
525
     */
526 24
    public function datetimeLocal(string $name, $value = null, array $attributes = []): Input
527
    {
528 24
        if ($value instanceof DateTime)
529 12
            $value = $value->format('Y-m-d\TH:i');
530
531 24
        return $this->input('datetime-local', $name, $value, $attributes);
532
    }
533
534
    /**
535
     * Create a time input field.
536
     *
537
     * @param  string        $name
538
     * @param  string|mixed  $value
539
     * @param  array         $attributes
540
     *
541
     * @return \Arcanedev\Html\Elements\Input
542
     */
543 18
    public function time(string $name, $value = null, array $attributes = []): Input
544
    {
545 18
        return $this->input('time', $name, $value, $attributes);
546
    }
547
548
    /**
549
     * Create a url input field.
550
     *
551
     * @param  string  $name
552
     * @param  string  $value
553
     * @param  array   $attributes
554
     *
555
     * @return \Arcanedev\Html\Elements\Input
556
     */
557 12
    public function url(string $name, $value = null, array $attributes = []): Input
558
    {
559 12
        return $this->input('url', $name, $value, $attributes);
560
    }
561
562
    /**
563
     * Create a file input field.
564
     *
565
     * @param  string  $name
566
     * @param  array   $attributes
567
     *
568
     * @return \Arcanedev\Html\Elements\File
569
     */
570 18
    public function file(string $name, array $attributes = []): File
571
    {
572 18
        return File::make()->name($name)->attributes($attributes);
0 ignored issues
show
Documentation introduced by
$attributes is of type array, but the function expects a object<Arcanedev\Html\Elements\Concerns\iterable>.

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...
573
    }
574
575
    /**
576
     * Create a textarea input field.
577
     *
578
     * @param  string  $name
579
     * @param  string  $value
580
     * @param  array   $attributes
581
     *
582
     * @return \Arcanedev\Html\Elements\Textarea
583
     */
584 36
    public function textarea(string $name, $value = null, array $attributes = []): Textarea
585
    {
586 36
        $id    = $this->getIdAttribute($name, $attributes);
587 36
        $size  = Arr::pull($attributes, 'size');
588 36
        $value = (string) $this->getValueAttribute($name, $value);
589
590 36
        return Textarea::make()
591 36
            ->name($name)
592 36
            ->attributeUnless(is_null($id), 'id', $id)
593 36
            ->unless(is_null($size), function (Textarea $elt) use ($size) {
594 18
                return $elt->size($size);
595 36
            })
596 36
            ->attributes($attributes)
597 36
            ->html($this->html->escape($value));
598
    }
599
600
    /**
601
     * Create a select box field.
602
     *
603
     * @param  string                                         $name
604
     * @param  array|\Illuminate\Support\Collection|iterable  $list
605
     * @param  string|bool                                    $selected
606
     * @param  array                                          $attributes
607
     * @param  array                                          $optionsAttributes
608
     * @param  array                                          $optgroupsAttributes
609
     *
610
     * @return \Arcanedev\Html\Elements\Select
611
     */
612 90
    public function select(
613
        string $name,
614
        iterable $list = [],
615
        $selected = null,
616
        array $attributes = [],
617
        array $optionsAttributes = [],
618
        array $optgroupsAttributes = []
619
    ): Select {
620 90
        return Select::make()
621 90
            ->name($name)
622 90
            ->options($list, $optionsAttributes, $optgroupsAttributes)
0 ignored issues
show
Bug introduced by
It seems like $list defined by parameter $list on line 614 can also be of type array; however, Arcanedev\Html\Elements\Select::options() does only seem to accept object<Arcanedev\Html\Elements\iterable>, maybe add an additional type check?

This check looks at variables that have been passed in as parameters and 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...
623 90
            ->attributes($attributes)
0 ignored issues
show
Documentation introduced by
$attributes is of type array, but the function expects a object<Arcanedev\Html\Elements\Concerns\iterable>.

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...
624 90
            ->attributeUnless(is_null($id = $this->getIdAttribute($name, $attributes)), 'id', $id)
625 90
            ->value($this->getValueAttribute($name, $selected));
626
    }
627
628
    /**
629
     * Create a select range field.
630
     *
631
     * @param  string  $name
632
     * @param  string  $begin
633
     * @param  string  $end
634
     * @param  string  $selected
635
     * @param  array   $attributes
636
     *
637
     * @return \Arcanedev\Html\Elements\Select
638
     */
639 12
    public function selectRange(string $name, $begin, $end, $selected = null, array $attributes = []): Select
640
    {
641 12
        $range = array_combine($range = range($begin, $end), $range);
642
643 12
        return $this->select($name, $range, $selected, $attributes);
644
    }
645
646
    /**
647
     * Create a select year field.
648
     *
649
     * @param  string  $name
650
     * @param  string  $begin
651
     * @param  string  $end
652
     * @param  string  $selected
653
     * @param  array   $attributes
654
     *
655
     * @return \Arcanedev\Html\Elements\Select
656
     */
657 6
    public function selectYear(string $name, $begin, $end, $selected = null, array $attributes = []): Select
658
    {
659 6
        return $this->selectRange($name, $begin, $end, $selected, $attributes);
660
    }
661
662
    /**
663
     * Create a select month field.
664
     *
665
     * @param  string  $name
666
     * @param  string  $selected
667
     * @param  array   $attributes
668
     * @param  string  $format
669
     *
670
     * @return \Arcanedev\Html\Elements\Select
671
     */
672 6
    public function selectMonth(string $name, $selected = null, array $attributes = [], $format = '%B'): Select
673
    {
674 6
        $months = [];
675
676 6
        foreach(range(1, 12) as $month) {
677 6
            $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1));
678
        }
679
680 6
        return $this->select($name, $months, $selected, $attributes);
681
    }
682
683
    /**
684
     * Create a checkbox input field.
685
     *
686
     * @param  string     $name
687
     * @param  mixed      $value
688
     * @param  bool|null  $checked
689
     * @param  array      $attributes
690
     *
691
     * @return \Arcanedev\Html\Elements\Input
692
     */
693 24
    public function checkbox(string $name, $value = 1, $checked = null, array $attributes = []): Input
694
    {
695 24
        return $this->checkable('checkbox', $name, $value, $checked, $attributes);
696
    }
697
698
    /**
699
     * Create a radio button input field.
700
     *
701
     * @param  string  $name
702
     * @param  mixed   $value
703
     * @param  bool    $checked
704
     * @param  array   $attributes
705
     *
706
     * @return \Arcanedev\Html\Elements\Input
707
     */
708 12
    public function radio(string $name, $value = null, $checked = null, array $attributes = []): Input
709
    {
710 12
        return $this->checkable('radio', $name, $value ?: $name, $checked, $attributes);
711
    }
712
713
    /**
714
     * Create a HTML reset input element.
715
     *
716
     * @param  string|mixed  $value
717
     * @param  array         $attributes
718
     *
719
     * @return \Arcanedev\Html\Elements\Button
720
     */
721 6
    public function reset($value, array $attributes = []): Button
722
    {
723 6
        return $this->button($value, array_merge(['type' => 'reset'], $attributes));
724
    }
725
726
    /**
727
    * Create a HTML image input element.
728
    *
729
    * @param  string       $url
730
    * @param  string|null  $name
731
    * @param  array        $attributes
732
    *
733
     * @return \Arcanedev\Html\Elements\Input
734
    */
735 6
    public function image(string $url, $name = null, array $attributes = []): Input
736
    {
737 6
        return $this->input('image', $name, null, array_merge($attributes, [
738 6
            'src' => $this->url->asset($url),
739
        ]));
740
    }
741
742
    /**
743
     * Create a submit button element.
744
     *
745
     * @param  string|mixed  $value
746
     * @param  array         $attributes
747
     *
748
     * @return \Arcanedev\Html\Elements\Button
749
     */
750 6
    public function submit($value = null, array $attributes = []): Button
751
    {
752 6
        return $this->button($value, array_merge(['type' => 'submit'], $attributes));
753
    }
754
755
    /**
756
     * Create a button element.
757
     *
758
     * @param  string|mixed  $value
759
     * @param  array         $attributes
760
     *
761
     * @return \Arcanedev\Html\Elements\Button
762
     */
763 18
    public function button($value = null, array $attributes = []): Button
764
    {
765 18
        return Button::make()
766 18
            ->type(Arr::pull($attributes, 'type', 'button'))
767 18
            ->attributes($attributes)
0 ignored issues
show
Documentation introduced by
$attributes is of type array, but the function expects a object<Arcanedev\Html\Elements\Concerns\iterable>.

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...
768 18
            ->html($value);
769
    }
770
771
    /**
772
     * Create a color input field.
773
     *
774
     * @param  string        $name
775
     * @param  string|mixed  $value
776
     * @param  array         $attributes
777
     *
778
     * @return \Arcanedev\Html\Elements\Input
779
     */
780 18
    public function color(string $name, $value = null, array $attributes = []): Input
781
    {
782 18
        return $this->input('color', $name, $value, $attributes);
783
    }
784
785
    /* -----------------------------------------------------------------
786
     |  Other Methods
787
     | -----------------------------------------------------------------
788
     */
789
790
    /**
791
     * Create a checkable input field.
792
     *
793
     * @param  string     $type
794
     * @param  string     $name
795
     * @param  mixed      $value
796
     * @param  bool|null  $checked
797
     * @param  array      $attributes
798
     *
799
     * @return \Arcanedev\Html\Elements\Input
800
     */
801 42
    protected function checkable(string $type, string $name, $value, $checked, array $attributes): Input
802
    {
803 42
        $checked = $this->getCheckedState($type, $name, $value, $checked);
804
805 42
        if ( ! is_null($checked) && $checked)
806 36
            $attributes['checked'] = 'checked';
807
808 42
        return $this->input($type, $name, $value, $attributes);
809
    }
810
811
    /**
812
     * Get the check state for a checkable input.
813
     *
814
     * @param  string     $type
815
     * @param  string     $name
816
     * @param  mixed      $value
817
     * @param  bool|null  $checked
818
     *
819
     * @return bool
820
     */
821 42
    private function getCheckedState(string $type, string $name, $value, $checked): bool
822
    {
823 42
        switch($type) {
824 42
            case 'checkbox':
825 24
                return $this->getCheckboxCheckedState($name, $value, $checked);
826
827 18
            case 'radio':
828 12
                return $this->getRadioCheckedState($name, $value, $checked);
829
830
            default:
831 6
                return $this->getValueAttribute($name) === $value;
832
        }
833
    }
834
835
    /**
836
     * Get the check state for a checkbox input.
837
     *
838
     * @param  string     $name
839
     * @param  mixed      $value
840
     * @param  bool|null  $checked
841
     *
842
     * @return bool
843
     */
844 24
    private function getCheckboxCheckedState(string $name, $value, $checked): bool
845
    {
846
        if (
847 24
            isset($this->session) &&
848 24
            ! $this->oldInputIsEmpty() &&
849 24
            is_null($this->old($name))
850
        ) {
851 6
            return false;
852
        }
853
854 24
        if ($this->missingOldAndModel($name)) {
855 12
            return (bool) $checked;
856
        }
857
858 12
        $posted = $this->getValueAttribute($name, $checked);
859
860 12
        if (is_array($posted)) {
861 6
            return in_array($value, $posted);
862
        }
863
864 12
        if ($posted instanceof Collection) {
865 6
            return $posted->contains('id', $value);
866
        }
867
868 12
        return (bool) $posted;
869
    }
870
871
    /**
872
     * Get the check state for a radio input.
873
     *
874
     * @param  string     $name
875
     * @param  mixed      $value
876
     * @param  bool|null  $checked
877
     *
878
     * @return bool
879
     */
880 12
    private function getRadioCheckedState(string $name, $value, $checked): bool
881
    {
882 12
        if ($this->missingOldAndModel($name)) {
883 6
            return (bool) $checked;
884
        }
885
886 6
        return $this->getValueAttribute($name) === $value;
887
    }
888
889
    /**
890
     * Determine if old input or model input exists for a key.
891
     *
892
     * @param  string  $name
893
     *
894
     * @return bool
895
     */
896 36
    private function missingOldAndModel(string $name): bool
897
    {
898 36
        return is_null($this->old($name))
899 36
            && is_null($this->getModelValueAttribute($name));
900
    }
901
902
    /**
903
     * Get the form action from the options.
904
     *
905
     * @param  array  $attributes
906
     *
907
     * @return string
908
     */
909 84
    private function getAction(array $attributes): string
910
    {
911 84
        if (isset($attributes['url']))
912 6
            return $this->getUrlAction($attributes['url']);
913
914 78
        if (isset($attributes['route']))
915 12
            return $this->getRouteAction($attributes['route']);
916
917 72
        if (isset($attributes['action']))
918 12
            return $this->getControllerAction($attributes['action']);
919
920 60
        return $this->url->current();
921
    }
922
923
    /**
924
     * Get the action for a "url" option.
925
     *
926
     * @param  array|string  $attribute
927
     *
928
     * @return string
929
     */
930 6
    private function getUrlAction($attribute): string
931
    {
932 6
        return is_array($attribute)
933 6
            ? $this->url->to($attribute[0], array_slice($attribute, 1))
934 6
            : $this->url->to($attribute);
935
    }
936
937
    /**
938
     * Get the action for a "route" option.
939
     *
940
     * @param  array|string  $attribute
941
     *
942
     * @return string
943
     */
944 12
    private function getRouteAction($attribute): string
945
    {
946 12
        return is_array($attribute)
947 6
            ? $this->url->route($attribute[0], array_slice($attribute, 1))
948 12
            : $this->url->route($attribute);
949
    }
950
951
    /**
952
     * Get the action for an "action" option.
953
     *
954
     * @param  array|string  $attribute
955
     *
956
     * @return string
957
     */
958 12
    private function getControllerAction($attribute): string
959
    {
960 12
        return is_array($attribute)
961 6
            ? $this->url->action($attribute[0], array_slice($attribute, 1))
962 12
            : $this->url->action($attribute);
963
    }
964
}
965