Completed
Pull Request — master (#33)
by ARCANEDEV
12:12
created

FormBuilder::getModelValueAttribute()   B

Complexity

Conditions 6
Paths 8

Size

Total Lines 22

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 11
CRAP Score 6

Importance

Changes 0
Metric Value
dl 0
loc 22
ccs 11
cts 11
cp 1
rs 8.9457
c 0
b 0
f 0
cc 6
nc 8
nop 2
crap 6
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 404
    public function __construct(Contracts\HtmlBuilder $html, UrlGenerator $url, Session $session)
102
    {
103 404
        $this->url       = $url;
104 404
        $this->html      = $html;
105 404
        $this->csrfToken = $session->token();
106
107 404
        $this->setSessionStore($session);
108 404
    }
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 312
    public function getSessionStore(): ?Session
121
    {
122 312
        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 404
    public function setSessionStore(Session $session)
133
    {
134 404
        $this->session = $session;
135
136 404
        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 44
    public function setModel($model)
147
    {
148 44
        $this->model = $model;
149
150 44
        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 176
    public function getModel()
159
    {
160 176
        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 320
    public function getIdAttribute($name, array $attributes): ?string
172
    {
173 320
        if (array_key_exists('id', $attributes))
174 28
            return $attributes['id'];
175
176 304
        if ( ! is_null($name) && in_array($name, $this->labels))
177 8
            return $name;
178
179 296
        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 300
    public function getValueAttribute($name, $value = null)
191
    {
192 300
        if (is_null($name))
193 4
            return $value;
194
195 296
        if ( ! is_null($this->old($name)) && $name !== '_method')
196 20
            return $this->old($name);
197
198 288
        if ( ! is_null($value))
199 168
            return $value;
200
201 156
        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 168
    private function getModelValueAttribute(string $name, $model = null)
213
    {
214 168
        $model = $model ?: $this->getModel();
215
216 168
        $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 168
        if (strpos($key, '.') !== false) {
219 20
            $keys = explode('.', $key, 2);
220
221 20
            return $this->getModelValueAttribute(
222 20
                $keys[1],
223 20
                $this->getModelValueAttribute($keys[0], $model)
224
            );
225
        }
226
227 168
        if (is_null($model) || is_array($model))
228 4
            return data_get($model, $key);
229 168
230
        return method_exists($model, 'getFormValue')
231
            ? $model->getFormValue($key)
232
            : data_get($model, $key);
233
    }
234
235
    /**
236
     * Get a value from the session's old input.
237
     *
238
     * @param  string  $name
239 308
     *
240
     * @return mixed
241 308
     */
242
    public function old(string $name)
243 308
    {
244
        $session = $this->getSessionStore();
245
246
        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 308
     *
254
     * @return string
255 308
     */
256 308
    private static function transformKey(string $key): string
257 308
    {
258
        return str_replace(
259
            ['.', '[]', '[', ']'],
260
            ['_', '', '.', ''],
261
            $key
262
        );
263
    }
264
265
    /**
266
     * Determine if the old input is empty.
267 16
     *
268
     * @return bool
269 16
     */
270
    public function oldInputIsEmpty(): bool
271 16
    {
272 16
        $session = $this->getSessionStore();
273
274
        return ! is_null($session)
275
            && (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 56
     *
288
     * @return \Illuminate\Support\HtmlString
289 56
     */
290
    public function open(array $attributes = []): HtmlString
291 56
    {
292 56
        $method = Str::upper(Arr::pull($attributes, 'method', 'POST'));
293 56
294 56
        return Form::make()
295 56
            ->method($method !== 'GET' ? 'POST' : $method)
296 56
            ->action($this->getAction($attributes))
297
            ->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 56
                ['accept-charset' => 'UTF-8'],
299 4
                Arr::except($attributes, $this->reserved)
300 56
            ))
301 56
            ->if(Arr::pull($attributes, 'files', false), function (Form $form) {
302 4
                return $form->acceptsFiles();
303 56
            })
304 56
            ->if(in_array($method, $this->spoofedMethods), function (Form $form) use ($method) {
305 32
                return $form->addChild($this->hidden('_method', $method));
306 56
            })
307 56
            ->if($method !== 'GET', function (Form $form) {
308
                return $form->addChild($this->token());
309
            })
310
            ->open();
311
    }
312
313
    /**
314
     * Create a new model based form builder.
315
     *
316
     * @param  mixed  $model
317
     * @param  array  $attributes
318 20
     *
319
     * @return \Illuminate\Support\HtmlString
320 20
     */
321
    public function model($model, array $attributes = []): HtmlString
322
    {
323
        return $this->setModel($model)->open($attributes);
324
    }
325
326
    /**
327
     * Close the current form.
328 4
     *
329
     * @return \Illuminate\Support\HtmlString
330 4
     */
331 4
    public function close(): HtmlString
332
    {
333 4
        $this->labels = [];
334
        $this->setModel(null);
335
336
        return Form::make()->close();
337
    }
338
339
    /**
340
     * Generate a hidden field with the current CSRF token.
341 32
     *
342
     * @return \Arcanedev\Html\Elements\Input
343 32
     */
344
    public function token(): Input
345 32
    {
346
        $token = empty($this->csrfToken)
347 32
            ? $this->getSessionStore()->token()
348
            : $this->csrfToken;
349
350
        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 20
     *
361
     * @return \Arcanedev\Html\Elements\Label
362 20
     */
363
    public function label(string $name, $value = null, array $attributes = [], $escaped = true): Label
364 20
    {
365
        $this->labels[] = $name;
366 20
367 20
        $value = $value ?: Str::title(str_replace(['_', '-'], ' ', $name));
368 20
369 20
        return Label::make()
370
            ->for($name)
371
            ->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
            ->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 236
     *
383
     * @return \Arcanedev\Html\Elements\Input
384 236
     */
385 200
    public function input(string $type, $name, $value = null, array $attributes = []): Input
386
    {
387 236
        if ( ! in_array($type, $this->skipValueTypes))
388
            $value = $this->getValueAttribute($name, $value);
389 236
390 236
        $id = $this->getIdAttribute($name, $attributes);
391 236
392 236
        return Input::make()
393 236
            ->type($type)
394 236
            ->attributeIfNotNull($name, 'name', $name)
395
            ->attributeIfNotNull($id, 'id', $id)
396
            ->attributeUnless(is_null($value) || empty($value), 'value', $value)
397
            ->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 28
     *
407
     * @return \Arcanedev\Html\Elements\Input
408 28
     */
409
    public function text(string $name, $value = null, array $attributes = []): Input
410
    {
411
        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 12
     *
420
     * @return \Arcanedev\Html\Elements\Input
421 12
     */
422
    public function password(string $name, array $attributes = []): Input
423
    {
424
        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 44
     *
434
     * @return \Arcanedev\Html\Elements\Input
435 44
     */
436
    public function hidden(string $name, $value = null, array $attributes = []): Input
437
    {
438
        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 12
     *
448
     * @return \Arcanedev\Html\Elements\Input
449 12
     */
450
    public function email(string $name, $value = null, array $attributes = []): Input
451
    {
452
        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 12
     *
462
     * @return \Arcanedev\Html\Elements\Input
463 12
     */
464
    public function tel(string $name, $value = null, array $attributes = []): Input
465
    {
466
        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 12
     *
476
     * @return \Arcanedev\Html\Elements\Input
477 12
     */
478
    public function number(string $name, $value = null, array $attributes = []): Input
479
    {
480
        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 16
     *
490
     * @return \Arcanedev\Html\Elements\Input
491 16
     */
492 4
    public function date(string $name, $value = null, array $attributes = []): Input
493
    {
494 16
        if ($value instanceof DateTime)
495
            $value = $value->format('Y-m-d');
496
497
        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 16
     *
507
     * @return \Arcanedev\Html\Elements\Input
508 16
     */
509 8
    public function datetime(string $name, $value = null, array $attributes = []): Input
510
    {
511 16
        if ($value instanceof DateTime)
512
            $value = $value->format(DateTime::RFC3339);
513
514
        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 16
     *
524
     * @return \Arcanedev\Html\Elements\Input
525 16
     */
526 8
    public function datetimeLocal(string $name, $value = null, array $attributes = []): Input
527
    {
528 16
        if ($value instanceof DateTime)
529
            $value = $value->format('Y-m-d\TH:i');
530
531
        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 12
     *
541
     * @return \Arcanedev\Html\Elements\Input
542 12
     */
543
    public function time(string $name, $value = null, array $attributes = []): Input
544
    {
545
        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 8
     *
555
     * @return \Arcanedev\Html\Elements\Input
556 8
     */
557
    public function url(string $name, $value = null, array $attributes = []): Input
558
    {
559
        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 12
     *
568
     * @return \Arcanedev\Html\Elements\File
569 12
     */
570
    public function file(string $name, array $attributes = []): File
571
    {
572
        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 24
     *
582
     * @return \Arcanedev\Html\Elements\Textarea
583 24
     */
584 24
    public function textarea(string $name, $value = null, array $attributes = []): Textarea
585 24
    {
586
        $id    = $this->getIdAttribute($name, $attributes);
587 24
        $size  = Arr::pull($attributes, 'size');
588 24
        $value = (string) $this->getValueAttribute($name, $value);
589 24
590 24
        return Textarea::make()
591 12
            ->name($name)
592 24
            ->attributeUnless(is_null($id), 'id', $id)
593 24
            ->unless(is_null($size), function (Textarea $elt) use ($size) {
594 24
                return $elt->size($size);
595
            })
596
            ->attributes($attributes)
597
            ->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 60
     *
610
     * @return \Arcanedev\Html\Elements\Select
611
     */
612
    public function select(
613
        string $name,
614
        iterable $list = [],
615
        $selected = null,
616
        array $attributes = [],
617 60
        array $optionsAttributes = [],
618 60
        array $optgroupsAttributes = []
619 60
    ): Select {
620 60
        return Select::make()
621 60
            ->name($name)
622 60
            ->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
            ->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
            ->attributeUnless(is_null($id = $this->getIdAttribute($name, $attributes)), 'id', $id)
625
            ->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 8
     *
637
     * @return \Arcanedev\Html\Elements\Select
638 8
     */
639
    public function selectRange(string $name, $begin, $end, $selected = null, array $attributes = []): Select
640 8
    {
641
        $range = array_combine($range = range($begin, $end), $range);
642
643
        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 4
     *
655
     * @return \Arcanedev\Html\Elements\Select
656 4
     */
657
    public function selectYear(string $name, $begin, $end, $selected = null, array $attributes = []): Select
658
    {
659
        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 4
     *
670
     * @return \Arcanedev\Html\Elements\Select
671 4
     */
672
    public function selectMonth(string $name, $selected = null, array $attributes = [], $format = '%B'): Select
673 4
    {
674 4
        $months = [];
675
676
        foreach(range(1, 12) as $month) {
677 4
            $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1));
678
        }
679
680
        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 16
     *
691
     * @return \Arcanedev\Html\Elements\Input
692 16
     */
693
    public function checkbox(string $name, $value = 1, $checked = null, array $attributes = []): Input
694
    {
695
        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 8
     *
706
     * @return \Arcanedev\Html\Elements\Input
707 8
     */
708
    public function radio(string $name, $value = null, $checked = null, array $attributes = []): Input
709
    {
710
        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 4
     *
719
     * @return \Arcanedev\Html\Elements\Button
720 4
     */
721
    public function reset($value, array $attributes = []): Button
722
    {
723
        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 4
    *
733
     * @return \Arcanedev\Html\Elements\Input
734 4
    */
735 4
    public function image(string $url, $name = null, array $attributes = []): Input
736
    {
737
        return $this->input('image', $name, null, array_merge($attributes, [
738
            '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 4
     *
748
     * @return \Arcanedev\Html\Elements\Button
749 4
     */
750
    public function submit($value = null, array $attributes = []): Button
751
    {
752
        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 12
     *
761
     * @return \Arcanedev\Html\Elements\Button
762 12
     */
763 12
    public function button($value = null, array $attributes = []): Button
764 12
    {
765 12
        return Button::make()
766
            ->type(Arr::pull($attributes, 'type', 'button'))
767
            ->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
            ->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 12
     *
778
     * @return \Arcanedev\Html\Elements\Input
779 12
     */
780
    public function color(string $name, $value = null, array $attributes = []): Input
781
    {
782
        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 28
     *
799
     * @return \Arcanedev\Html\Elements\Input
800 28
     */
801
    protected function checkable(string $type, string $name, $value, $checked, array $attributes): Input
802 28
    {
803 24
        $checked = $this->getCheckedState($type, $name, $value, $checked);
804
805 28
        if ( ! is_null($checked) && $checked)
806
            $attributes['checked'] = 'checked';
807
808
        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 28
     *
819
     * @return bool
820 28
     */
821 28
    private function getCheckedState(string $type, string $name, $value, $checked): bool
822 16
    {
823
        switch($type) {
824 12
            case 'checkbox':
825 8
                return $this->getCheckboxCheckedState($name, $value, $checked);
826
827
            case 'radio':
828 4
                return $this->getRadioCheckedState($name, $value, $checked);
829
830
            default:
831
                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 16
     *
842
     * @return bool
843
     */
844 16
    private function getCheckboxCheckedState(string $name, $value, $checked): bool
845 16
    {
846 16
        if (
847
            isset($this->session) &&
848 4
            ! $this->oldInputIsEmpty() &&
849
            is_null($this->old($name))
850
        ) {
851 16
            return false;
852 8
        }
853
854
        if ($this->missingOldAndModel($name)) {
855 8
            return (bool) $checked;
856
        }
857 8
858 4
        $posted = $this->getValueAttribute($name, $checked);
859
860
        if (is_array($posted)) {
861 8
            return in_array($value, $posted);
862 4
        }
863
864
        if ($posted instanceof Collection) {
865 8
            return $posted->contains('id', $value);
866
        }
867
868
        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 8
     *
878
     * @return bool
879 8
     */
880 4
    private function getRadioCheckedState(string $name, $value, $checked): bool
881
    {
882
        if ($this->missingOldAndModel($name)) {
883 4
            return (bool) $checked;
884
        }
885
886
        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 24
     *
894
     * @return bool
895 24
     */
896 24
    private function missingOldAndModel(string $name): bool
897
    {
898
        return is_null($this->old($name))
899
            && is_null($this->getModelValueAttribute($name));
900
    }
901
902
    /**
903
     * Get the form action from the options.
904
     *
905
     * @param  array  $attributes
906 56
     *
907
     * @return string
908 56
     */
909 4
    private function getAction(array $attributes): string
910
    {
911 52
        if (isset($attributes['url']))
912 8
            return $this->getUrlAction($attributes['url']);
913
914 48
        if (isset($attributes['route']))
915 8
            return $this->getRouteAction($attributes['route']);
916
917 40
        if (isset($attributes['action']))
918
            return $this->getControllerAction($attributes['action']);
919
920
        return $this->url->current();
921
    }
922
923
    /**
924
     * Get the action for a "url" option.
925
     *
926
     * @param  array|string  $attribute
927 4
     *
928
     * @return string
929 4
     */
930 4
    private function getUrlAction($attribute): string
931 4
    {
932
        return is_array($attribute)
933
            ? $this->url->to($attribute[0], array_slice($attribute, 1))
934
            : $this->url->to($attribute);
935
    }
936
937
    /**
938
     * Get the action for a "route" option.
939
     *
940
     * @param  array|string  $attribute
941 8
     *
942
     * @return string
943 8
     */
944 4
    private function getRouteAction($attribute): string
945 8
    {
946
        return is_array($attribute)
947
            ? $this->url->route($attribute[0], array_slice($attribute, 1))
948
            : $this->url->route($attribute);
949
    }
950
951
    /**
952
     * Get the action for an "action" option.
953
     *
954
     * @param  array|string  $attribute
955 8
     *
956
     * @return string
957 8
     */
958 4
    private function getControllerAction($attribute): string
959 8
    {
960
        return is_array($attribute)
961
            ? $this->url->action($attribute[0], array_slice($attribute, 1))
962
            : $this->url->action($attribute);
963
    }
964
}
965