Complex classes like FormBuilder often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use FormBuilder, and based on these observations, apply Extract Interface, too.
1 | <?php namespace Arcanedev\LaravelHtml; |
||
17 | class FormBuilder extends Builder implements FormBuilderContract |
||
18 | { |
||
19 | /* ----------------------------------------------------------------- |
||
20 | | Properties |
||
21 | | ----------------------------------------------------------------- |
||
22 | */ |
||
23 | |||
24 | /** |
||
25 | * The HTML builder instance. |
||
26 | * |
||
27 | * @var \Arcanedev\LaravelHtml\Contracts\HtmlBuilder |
||
28 | */ |
||
29 | protected $html; |
||
30 | |||
31 | /** |
||
32 | * The URL generator instance. |
||
33 | * |
||
34 | * @var \Illuminate\Contracts\Routing\UrlGenerator |
||
35 | */ |
||
36 | protected $url; |
||
37 | |||
38 | /** |
||
39 | * The CSRF token used by the form builder. |
||
40 | * |
||
41 | * @var string |
||
42 | */ |
||
43 | protected $csrfToken; |
||
44 | |||
45 | /** |
||
46 | * The session store implementation. |
||
47 | * |
||
48 | * @var \Illuminate\Contracts\Session\Session |
||
49 | */ |
||
50 | protected $session; |
||
51 | |||
52 | /** |
||
53 | * The current model instance for the form. |
||
54 | * |
||
55 | * @var \Illuminate\Database\Eloquent\Model |
||
56 | */ |
||
57 | protected $model; |
||
58 | |||
59 | /** |
||
60 | * An array of label names we've created. |
||
61 | * |
||
62 | * @var array |
||
63 | */ |
||
64 | protected $labels = []; |
||
65 | |||
66 | /** |
||
67 | * The reserved form open attributes. |
||
68 | * |
||
69 | * @var array |
||
70 | */ |
||
71 | protected $reserved = ['method', 'url', 'route', 'action', 'files']; |
||
72 | |||
73 | /** |
||
74 | * The form methods that should be spoofed, in uppercase. |
||
75 | * |
||
76 | * @var array |
||
77 | */ |
||
78 | protected $spoofedMethods = ['DELETE', 'PATCH', 'PUT']; |
||
79 | |||
80 | /** |
||
81 | * The types of inputs to not fill values on by default. |
||
82 | * |
||
83 | * @var array |
||
84 | */ |
||
85 | protected $skipValueTypes = ['file', 'password', 'checkbox', 'radio']; |
||
86 | |||
87 | /* ----------------------------------------------------------------- |
||
88 | | Constructor |
||
89 | | ----------------------------------------------------------------- |
||
90 | */ |
||
91 | |||
92 | /** |
||
93 | * Create a new form builder instance. |
||
94 | * |
||
95 | * @param \Arcanedev\LaravelHtml\Contracts\HtmlBuilder $html |
||
96 | * @param \Illuminate\Contracts\Routing\UrlGenerator $url |
||
97 | * @param \Illuminate\Contracts\Session\Session $session |
||
98 | */ |
||
99 | 303 | public function __construct( |
|
100 | Contracts\HtmlBuilder $html, |
||
101 | UrlGenerator $url, |
||
102 | Session $session |
||
103 | ) { |
||
104 | 303 | $this->url = $url; |
|
105 | 303 | $this->html = $html; |
|
106 | 303 | $this->csrfToken = $session->token(); |
|
107 | |||
108 | 303 | $this->setSessionStore($session); |
|
109 | 303 | } |
|
110 | |||
111 | /* ----------------------------------------------------------------- |
||
112 | | Getters & Setters |
||
113 | | ----------------------------------------------------------------- |
||
114 | */ |
||
115 | |||
116 | /** |
||
117 | * Get the session store implementation. |
||
118 | * |
||
119 | * @return \Illuminate\Contracts\Session\Session |
||
120 | */ |
||
121 | 3 | public function getSessionStore() |
|
122 | { |
||
123 | 3 | return $this->session; |
|
124 | } |
||
125 | |||
126 | /** |
||
127 | * Set the session store implementation. |
||
128 | * |
||
129 | * @param \Illuminate\Contracts\Session\Session $session |
||
130 | * |
||
131 | * @return self |
||
132 | */ |
||
133 | 303 | public function setSessionStore(Session $session) |
|
134 | { |
||
135 | 303 | $this->session = $session; |
|
136 | |||
137 | 303 | return $this; |
|
138 | } |
||
139 | |||
140 | /** |
||
141 | * Set the model instance on the form builder. |
||
142 | * |
||
143 | * @param \Illuminate\Database\Eloquent\Model $model |
||
144 | * |
||
145 | * @return self |
||
146 | */ |
||
147 | 33 | public function setModel($model) |
|
148 | { |
||
149 | 33 | $this->model = $model; |
|
150 | |||
151 | 33 | return $this; |
|
152 | } |
||
153 | |||
154 | /** |
||
155 | * Get the model instance on the form builder. |
||
156 | * |
||
157 | * @return \Illuminate\Database\Eloquent\Model |
||
158 | */ |
||
159 | 24 | public function getModel() |
|
160 | { |
||
161 | 24 | 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 | 249 | public function getIdAttribute($name, array $attributes) |
|
173 | { |
||
174 | 249 | if (array_key_exists('id', $attributes)) |
|
175 | 21 | return $attributes['id']; |
|
176 | |||
177 | 234 | if (in_array($name, $this->labels)) |
|
178 | 6 | return $name; |
|
179 | |||
180 | 228 | 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 | 222 | public function getValueAttribute($name, $value = null) |
|
192 | { |
||
193 | 222 | if (is_null($name)) |
|
194 | 9 | return $value; |
|
195 | |||
196 | 213 | if ( ! is_null($this->old($name)) && $name !== '_method') |
|
197 | 12 | return $this->old($name); |
|
198 | |||
199 | 207 | if ( ! is_null($value)) |
|
200 | 117 | return $value; |
|
201 | |||
202 | 120 | return isset($this->model) |
|
203 | 15 | ? $this->getModelValueAttribute($name) |
|
204 | 120 | : null; |
|
205 | } |
||
206 | |||
207 | /** |
||
208 | * Get the model value that should be assigned to the field. |
||
209 | * |
||
210 | * @param string $name |
||
211 | * |
||
212 | * @return mixed |
||
213 | */ |
||
214 | 24 | private function getModelValueAttribute($name) |
|
215 | { |
||
216 | 24 | $key = $this->transformKey($name); |
|
217 | |||
218 | 24 | return method_exists($this->getModel(), 'getFormValue') |
|
219 | ? $this->getModel()->getFormValue($key) |
||
220 | 24 | : data_get($this->getModel(), $key); |
|
221 | } |
||
222 | |||
223 | /** |
||
224 | * Get a value from the session's old input. |
||
225 | * |
||
226 | * @param string $name |
||
227 | * |
||
228 | * @return mixed |
||
229 | */ |
||
230 | 222 | public function old($name) |
|
231 | { |
||
232 | 222 | return isset($this->session) |
|
233 | 222 | ? $this->session->getOldInput($this->transformKey($name)) |
|
234 | 222 | : null; |
|
235 | } |
||
236 | |||
237 | /** |
||
238 | * Transform key from array to dot syntax. |
||
239 | * |
||
240 | * @param string $key |
||
241 | * |
||
242 | * @return string |
||
243 | */ |
||
244 | 222 | private function transformKey($key) |
|
245 | { |
||
246 | 222 | return str_replace(['.', '[]', '[', ']'], ['_', '', '.', ''], $key); |
|
247 | } |
||
248 | |||
249 | /** |
||
250 | * Determine if the old input is empty. |
||
251 | * |
||
252 | * @return bool |
||
253 | */ |
||
254 | 12 | public function oldInputIsEmpty() |
|
255 | { |
||
256 | 12 | return isset($this->session) && (count($this->session->getOldInput()) == 0); |
|
257 | } |
||
258 | |||
259 | /** |
||
260 | * Parse the form action method. |
||
261 | * |
||
262 | * @param string $method |
||
263 | * |
||
264 | * @return string |
||
265 | */ |
||
266 | 48 | private function getMethod($method) |
|
267 | { |
||
268 | 48 | $method = strtoupper($method); |
|
269 | |||
270 | 48 | return $method !== 'GET' ? 'POST' : $method; |
|
271 | } |
||
272 | |||
273 | /* ----------------------------------------------------------------- |
||
274 | | Main Methods |
||
275 | | ----------------------------------------------------------------- |
||
276 | */ |
||
277 | |||
278 | /** |
||
279 | * Open up a new HTML form. |
||
280 | * |
||
281 | * @param array $options |
||
282 | * |
||
283 | * @return \Illuminate\Support\HtmlString |
||
284 | */ |
||
285 | 48 | public function open(array $options = []) |
|
286 | { |
||
287 | 48 | $method = Arr::get($options, 'method', 'post'); |
|
288 | |||
289 | // We need to extract the proper method from the attributes. If the method is |
||
290 | // something other than GET or POST we'll use POST since we will spoof the |
||
291 | // actual method since forms don't support the reserved methods in HTML. |
||
292 | $attributes = [ |
||
293 | 48 | 'method' => $this->getMethod($method), |
|
294 | 48 | 'action' => $this->getAction($options), |
|
295 | 48 | 'accept-charset' => 'UTF-8', |
|
296 | ]; |
||
297 | |||
298 | 48 | if (isset($options['files']) && $options['files']) { |
|
299 | 6 | $options['enctype'] = 'multipart/form-data'; |
|
300 | } |
||
301 | |||
302 | // Finally we're ready to create the final form HTML field. We will attribute |
||
303 | // format the array of attributes. We will also add on the appendage which |
||
304 | // is used to spoof requests for this PUT, PATCH, etc. methods on forms. |
||
305 | 48 | $attributes = array_merge( |
|
306 | 48 | $attributes, array_except($options, $this->reserved) |
|
307 | ); |
||
308 | |||
309 | // Finally, we will concatenate all of the attributes into a single string so |
||
310 | // we can build out the final form open statement. We'll also append on an |
||
311 | // extra value for the hidden _method field if it's needed for the form. |
||
312 | 48 | $attributes = $this->html->attributes($attributes); |
|
313 | |||
314 | // If the method is PUT, PATCH or DELETE we will need to add a spoofer hidden |
||
315 | // field that will instruct the Symfony request to pretend the method is a |
||
316 | // different method than it actually is, for convenience from the forms. |
||
317 | 48 | $append = $this->getAppendage($method); |
|
318 | |||
319 | 48 | return $this->toHtmlString('<form'.$attributes.'>' . $append); |
|
320 | } |
||
321 | |||
322 | /** |
||
323 | * Create a new model based form builder. |
||
324 | * |
||
325 | * @param mixed $model |
||
326 | * @param array $options |
||
327 | * |
||
328 | * @return \Illuminate\Support\HtmlString |
||
329 | */ |
||
330 | 15 | public function model($model, array $options = []) |
|
331 | { |
||
332 | 15 | $this->setModel($model); |
|
333 | |||
334 | 15 | return $this->open($options); |
|
335 | } |
||
336 | |||
337 | /** |
||
338 | * Close the current form. |
||
339 | * |
||
340 | * @return \Illuminate\Support\HtmlString |
||
341 | */ |
||
342 | 6 | public function close() |
|
343 | { |
||
344 | 6 | $this->labels = []; |
|
345 | 6 | $this->setModel(null); |
|
|
|||
346 | |||
347 | 6 | return $this->toHtmlString('</form>'); |
|
348 | } |
||
349 | |||
350 | /** |
||
351 | * Generate a hidden field with the current CSRF token. |
||
352 | * |
||
353 | * @return \Illuminate\Support\HtmlString |
||
354 | */ |
||
355 | 21 | public function token() |
|
356 | { |
||
357 | 21 | $token = ! empty($this->csrfToken) |
|
358 | 15 | ? $this->csrfToken |
|
359 | 21 | : $this->session->token(); |
|
360 | |||
361 | 21 | return $this->hidden('_token', $token); |
|
362 | } |
||
363 | |||
364 | /** |
||
365 | * Create a form label element. |
||
366 | * |
||
367 | * @param string $name |
||
368 | * @param string $value |
||
369 | * @param array $options |
||
370 | * @param bool $escaped |
||
371 | * |
||
372 | * @return \Illuminate\Support\HtmlString |
||
373 | */ |
||
374 | 15 | public function label($name, $value = null, array $options = [], $escaped = true) |
|
375 | { |
||
376 | 15 | $this->labels[] = $name; |
|
377 | |||
378 | 15 | return $this->toHtmlString( |
|
379 | 15 | Helpers\Label::make($name, $value, $options, $escaped) |
|
380 | ); |
||
381 | } |
||
382 | |||
383 | /** |
||
384 | * Create a form input field. |
||
385 | * |
||
386 | * @param string $type |
||
387 | * @param string $name |
||
388 | * @param string $value |
||
389 | * @param array $options |
||
390 | * |
||
391 | * @return \Illuminate\Support\HtmlString |
||
392 | */ |
||
393 | 189 | public function input($type, $name, $value = null, array $options = []) |
|
394 | { |
||
395 | 189 | if ( ! isset($options['name'])) { |
|
396 | 189 | $options['name'] = $name; |
|
397 | } |
||
398 | |||
399 | // We will get the appropriate value for the given field. We will look for the |
||
400 | // value in the session for the value in the old input data then we'll look |
||
401 | // in the model instance if one is set. Otherwise we will just use empty. |
||
402 | 189 | $id = $this->getIdAttribute($name, $options); |
|
403 | |||
404 | 189 | if ( ! in_array($type, $this->skipValueTypes)) { |
|
405 | 153 | $value = $this->getValueAttribute($name, $value); |
|
406 | } |
||
407 | |||
408 | // Once we have the type, value, and ID we can merge them into the rest of the |
||
409 | // attributes array so we can convert them into their HTML attribute format |
||
410 | // when creating the HTML element. Then, we will return the entire input. |
||
411 | 189 | $options = array_merge($options, compact('type', 'value', 'id')); |
|
412 | |||
413 | 189 | return $this->toHtmlString('<input'.$this->html->attributes($options).'>'); |
|
414 | } |
||
415 | |||
416 | /** |
||
417 | * Create a text input field. |
||
418 | * |
||
419 | * @param string $name |
||
420 | * @param string $value |
||
421 | * @param array $options |
||
422 | * |
||
423 | * @return \Illuminate\Support\HtmlString |
||
424 | */ |
||
425 | 21 | public function text($name, $value = null, array $options = []) |
|
426 | { |
||
427 | 21 | return $this->input('text', $name, $value, $options); |
|
428 | } |
||
429 | |||
430 | /** |
||
431 | * Create a password input field. |
||
432 | * |
||
433 | * @param string $name |
||
434 | * @param array $options |
||
435 | * |
||
436 | * @return \Illuminate\Support\HtmlString |
||
437 | */ |
||
438 | 9 | public function password($name, array $options = []) |
|
439 | { |
||
440 | 9 | return $this->input('password', $name, '', $options); |
|
441 | } |
||
442 | |||
443 | /** |
||
444 | * Create a hidden input field. |
||
445 | * |
||
446 | * @param string $name |
||
447 | * @param string $value |
||
448 | * @param array $options |
||
449 | * |
||
450 | * @return \Illuminate\Support\HtmlString |
||
451 | */ |
||
452 | 30 | public function hidden($name, $value = null, array $options = []) |
|
453 | { |
||
454 | 30 | return $this->input('hidden', $name, $value, $options); |
|
455 | } |
||
456 | |||
457 | /** |
||
458 | * Create an e-mail input field. |
||
459 | * |
||
460 | * @param string $name |
||
461 | * @param string $value |
||
462 | * @param array $options |
||
463 | * |
||
464 | * @return \Illuminate\Support\HtmlString |
||
465 | */ |
||
466 | 9 | public function email($name, $value = null, array $options = []) |
|
467 | { |
||
468 | 9 | return $this->input('email', $name, $value, $options); |
|
469 | } |
||
470 | |||
471 | /** |
||
472 | * Create a tel input field. |
||
473 | * |
||
474 | * @param string $name |
||
475 | * @param string $value |
||
476 | * @param array $options |
||
477 | * |
||
478 | * @return \Illuminate\Support\HtmlString |
||
479 | */ |
||
480 | 9 | public function tel($name, $value = null, array $options = []) |
|
481 | { |
||
482 | 9 | return $this->input('tel', $name, $value, $options); |
|
483 | } |
||
484 | |||
485 | /** |
||
486 | * Create a number input field. |
||
487 | * |
||
488 | * @param string $name |
||
489 | * @param string $value |
||
490 | * @param array $options |
||
491 | * |
||
492 | * @return \Illuminate\Support\HtmlString |
||
493 | */ |
||
494 | 9 | public function number($name, $value = null, array $options = []) |
|
495 | { |
||
496 | 9 | return $this->input('number', $name, $value, $options); |
|
497 | } |
||
498 | |||
499 | /** |
||
500 | * Create a date input field. |
||
501 | * |
||
502 | * @param string $name |
||
503 | * @param string $value |
||
504 | * @param array $options |
||
505 | * |
||
506 | * @return \Illuminate\Support\HtmlString |
||
507 | */ |
||
508 | 12 | public function date($name, $value = null, array $options = []) |
|
509 | { |
||
510 | 12 | if ($value instanceof DateTime) { |
|
511 | 3 | $value = $value->format('Y-m-d'); |
|
512 | } |
||
513 | |||
514 | 12 | return $this->input('date', $name, $value, $options); |
|
515 | } |
||
516 | |||
517 | /** |
||
518 | * Create a datetime input field. |
||
519 | * |
||
520 | * @param string $name |
||
521 | * @param string $value |
||
522 | * @param array $options |
||
523 | * |
||
524 | * @return \Illuminate\Support\HtmlString |
||
525 | */ |
||
526 | 12 | public function datetime($name, $value = null, array $options = []) |
|
527 | { |
||
528 | 12 | if ($value instanceof DateTime) { |
|
529 | 6 | $value = $value->format(DateTime::RFC3339); |
|
530 | } |
||
531 | |||
532 | 12 | return $this->input('datetime', $name, $value, $options); |
|
533 | } |
||
534 | |||
535 | /** |
||
536 | * Create a datetime-local input field. |
||
537 | * |
||
538 | * @param string $name |
||
539 | * @param string $value |
||
540 | * @param array $options |
||
541 | * |
||
542 | * @return \Illuminate\Support\HtmlString |
||
543 | */ |
||
544 | 12 | public function datetimeLocal($name, $value = null, array $options = []) |
|
545 | { |
||
546 | 12 | if ($value instanceof DateTime) { |
|
547 | 6 | $value = $value->format('Y-m-d\TH:i'); |
|
548 | } |
||
549 | |||
550 | 12 | return $this->input('datetime-local', $name, $value, $options); |
|
551 | } |
||
552 | |||
553 | /** |
||
554 | * Create a time input field. |
||
555 | * |
||
556 | * @param string $name |
||
557 | * @param string $value |
||
558 | * @param array $options |
||
559 | * |
||
560 | * @return \Illuminate\Support\HtmlString |
||
561 | */ |
||
562 | 9 | public function time($name, $value = null, array $options = []) |
|
563 | { |
||
564 | 9 | return $this->input('time', $name, $value, $options); |
|
565 | } |
||
566 | |||
567 | /** |
||
568 | * Create a url input field. |
||
569 | * |
||
570 | * @param string $name |
||
571 | * @param string $value |
||
572 | * @param array $options |
||
573 | * |
||
574 | * @return \Illuminate\Support\HtmlString |
||
575 | */ |
||
576 | 6 | public function url($name, $value = null, array $options = []) |
|
577 | { |
||
578 | 6 | return $this->input('url', $name, $value, $options); |
|
579 | } |
||
580 | |||
581 | /** |
||
582 | * Create a file input field. |
||
583 | * |
||
584 | * @param string $name |
||
585 | * @param array $options |
||
586 | * |
||
587 | * @return \Illuminate\Support\HtmlString |
||
588 | */ |
||
589 | 9 | public function file($name, array $options = []) |
|
590 | { |
||
591 | 9 | return $this->input('file', $name, null, $options); |
|
592 | } |
||
593 | |||
594 | /** |
||
595 | * Create a textarea input field. |
||
596 | * |
||
597 | * @param string $name |
||
598 | * @param string $value |
||
599 | * @param array $options |
||
600 | * |
||
601 | * @return \Illuminate\Support\HtmlString |
||
602 | */ |
||
603 | 18 | public function textarea($name, $value = null, array $options = []) |
|
604 | { |
||
605 | 18 | if ( ! isset($options['name'])) { |
|
606 | 18 | $options['name'] = $name; |
|
607 | } |
||
608 | |||
609 | // Next we will look for the rows and cols attributes, as each of these are put |
||
610 | // on the textarea element definition. If they are not present, we will just |
||
611 | // assume some sane default values for these attributes for the developer. |
||
612 | 18 | $options = $this->setTextAreaSize($options); |
|
613 | 18 | $options['id'] = $this->getIdAttribute($name, $options); |
|
614 | 18 | $value = (string) $this->getValueAttribute($name, $value); |
|
615 | |||
616 | 18 | unset($options['size']); |
|
617 | |||
618 | // Next we will convert the attributes into a string form. Also we have removed |
||
619 | // the size attribute, as it was merely a short-cut for the rows and cols on |
||
620 | // the element. Then we'll create the final textarea elements HTML for us. |
||
621 | 18 | $options = $this->html->attributes($options); |
|
622 | |||
623 | 18 | return $this->toHtmlString('<textarea'.$options.'>'.$this->html->escape($value).'</textarea>'); |
|
624 | } |
||
625 | |||
626 | /** |
||
627 | * Set the text area size on the attributes. |
||
628 | * |
||
629 | * @param array $options |
||
630 | * |
||
631 | * @return array |
||
632 | */ |
||
633 | 18 | private function setTextAreaSize(array $options) |
|
634 | { |
||
635 | 18 | if (isset($options['size'])) { |
|
636 | 9 | return $this->setQuickTextAreaSize($options); |
|
637 | } |
||
638 | |||
639 | // If the "size" attribute was not specified, we will just look for the regular |
||
640 | // columns and rows attributes, using sane defaults if these do not exist on |
||
641 | // the attributes array. We'll then return this entire options array back. |
||
642 | 9 | $cols = Arr::get($options, 'cols', 50); |
|
643 | 9 | $rows = Arr::get($options, 'rows', 10); |
|
644 | |||
645 | 9 | return array_merge($options, compact('cols', 'rows')); |
|
646 | } |
||
647 | |||
648 | /** |
||
649 | * Set the text area size using the quick "size" attribute. |
||
650 | * |
||
651 | * @param array $options |
||
652 | * |
||
653 | * @return array |
||
654 | */ |
||
655 | 9 | protected function setQuickTextAreaSize(array $options) |
|
656 | { |
||
657 | 9 | list($cols, $rows) = explode('x', $options['size']); |
|
658 | |||
659 | 9 | return array_merge($options, compact('cols', 'rows')); |
|
660 | } |
||
661 | |||
662 | /** |
||
663 | * Create a select box field. |
||
664 | * |
||
665 | * @param string $name |
||
666 | * @param array|\Illuminate\Support\Collection $list |
||
667 | * @param string $selected |
||
668 | * @param array $options |
||
669 | * |
||
670 | * @return \Illuminate\Support\HtmlString |
||
671 | */ |
||
672 | 42 | public function select($name, $list = [], $selected = null, array $options = []) |
|
673 | { |
||
674 | // When building a select box the "value" attribute is really the selected one |
||
675 | // so we will use that when checking the model or session for a value which |
||
676 | // should provide a convenient method of re-populating the forms on post. |
||
677 | 42 | $selected = $this->getValueAttribute($name, $selected); |
|
678 | |||
679 | // Transform to array if it is a collection |
||
680 | 42 | if ($selected instanceof Collection) { |
|
681 | 3 | $selected = $selected->all(); |
|
682 | } |
||
683 | |||
684 | 42 | $options['id'] = $this->getIdAttribute($name, $options); |
|
685 | |||
686 | 42 | if ( ! isset($options['name'])) { |
|
687 | 30 | $options['name'] = $name; |
|
688 | } |
||
689 | |||
690 | // We will simply loop through the options and build an HTML value for each of |
||
691 | // them until we have an array of HTML declarations. Then we will join them |
||
692 | // all together into one single HTML element that can be put on the form. |
||
693 | 42 | $html = []; |
|
694 | |||
695 | 42 | if (isset($options['placeholder'])) { |
|
696 | 3 | $html[] = $this->placeholderOption($options['placeholder'], $selected); |
|
697 | 3 | unset($options['placeholder']); |
|
698 | } |
||
699 | |||
700 | 42 | foreach($list as $value => $display) { |
|
701 | 39 | $html[] = $this->getSelectOption($display, $value, $selected); |
|
702 | } |
||
703 | |||
704 | // Once we have all of this HTML, we can join this into a single element after |
||
705 | // formatting the attributes into an HTML "attributes" string, then we will |
||
706 | // build out a final select statement, which will contain all the values. |
||
707 | 42 | $options = $this->html->attributes($options); |
|
708 | |||
709 | 42 | return $this->toHtmlString("<select{$options}>".implode('', $html).'</select>'); |
|
710 | } |
||
711 | |||
712 | /** |
||
713 | * Create a select range field. |
||
714 | * |
||
715 | * @param string $name |
||
716 | * @param string $begin |
||
717 | * @param string $end |
||
718 | * @param string $selected |
||
719 | * @param array $options |
||
720 | * |
||
721 | * @return \Illuminate\Support\HtmlString |
||
722 | */ |
||
723 | 6 | public function selectRange($name, $begin, $end, $selected = null, array $options = []) |
|
724 | { |
||
725 | 6 | $range = array_combine($range = range($begin, $end), $range); |
|
726 | |||
727 | 6 | return $this->select($name, $range, $selected, $options); |
|
728 | } |
||
729 | |||
730 | /** |
||
731 | * Create a select year field. |
||
732 | * |
||
733 | * @param string $name |
||
734 | * @param string $begin |
||
735 | * @param string $end |
||
736 | * @param string $selected |
||
737 | * @param array $options |
||
738 | * |
||
739 | * @return \Illuminate\Support\HtmlString |
||
740 | */ |
||
741 | 3 | public function selectYear($name, $begin, $end, $selected = null, array $options = []) |
|
742 | { |
||
743 | 3 | return call_user_func_array( |
|
744 | 3 | [$this, 'selectRange'], |
|
745 | 3 | compact('name', 'begin', 'end', 'selected', 'options') |
|
746 | ); |
||
747 | } |
||
748 | |||
749 | /** |
||
750 | * Create a select month field. |
||
751 | * |
||
752 | * @param string $name |
||
753 | * @param string $selected |
||
754 | * @param array $options |
||
755 | * @param string $format |
||
756 | * |
||
757 | * @return \Illuminate\Support\HtmlString |
||
758 | */ |
||
759 | 3 | public function selectMonth($name, $selected = null, array $options = [], $format = '%B') |
|
760 | { |
||
761 | 3 | $months = []; |
|
762 | |||
763 | 3 | foreach(range(1, 12) as $month) { |
|
764 | 3 | $months[$month] = strftime($format, mktime(0, 0, 0, $month, 1)); |
|
765 | } |
||
766 | |||
767 | 3 | return $this->select($name, $months, $selected, $options); |
|
768 | } |
||
769 | |||
770 | /** |
||
771 | * Get the select option for the given value. |
||
772 | * |
||
773 | * @param string $display |
||
774 | * @param string $value |
||
775 | * @param string $selected |
||
776 | * |
||
777 | * @return string |
||
778 | */ |
||
779 | 39 | private function getSelectOption($display, $value, $selected) |
|
780 | { |
||
781 | 39 | return is_array($display) |
|
782 | 6 | ? $this->optionGroup($display, $value, $selected) |
|
783 | 39 | : $this->option($display, $value, $selected); |
|
784 | } |
||
785 | |||
786 | /** |
||
787 | * Create an option group form element. |
||
788 | * |
||
789 | * @param array $list |
||
790 | * @param string $label |
||
791 | * @param string $selected |
||
792 | * |
||
793 | * @return string |
||
794 | */ |
||
795 | 6 | private function optionGroup(array $list, $label, $selected) |
|
796 | { |
||
797 | 6 | $html = []; |
|
798 | |||
799 | 6 | foreach($list as $value => $display) { |
|
800 | 6 | $html[] = $this->option($display, $value, $selected); |
|
801 | } |
||
802 | |||
803 | 6 | return '<optgroup label="'.$this->html->escape($label).'">'.implode('', $html).'</optgroup>'; |
|
804 | } |
||
805 | |||
806 | /** |
||
807 | * Create a select element option. |
||
808 | * |
||
809 | * @param string $display |
||
810 | * @param string $value |
||
811 | * @param string $selected |
||
812 | * |
||
813 | * @return string |
||
814 | */ |
||
815 | 39 | private function option($display, $value, $selected) |
|
816 | { |
||
817 | 39 | $selected = $this->getSelectedValue($value, $selected); |
|
818 | 39 | $options = compact('value', 'selected'); |
|
819 | |||
820 | 39 | return '<option'.$this->html->attributes($options).'>'.$this->html->escape($display).'</option>'; |
|
821 | } |
||
822 | |||
823 | /** |
||
824 | * Create a placeholder select element option. |
||
825 | * |
||
826 | * @param string $display |
||
827 | * @param string $selected |
||
828 | * |
||
829 | * @return string |
||
830 | */ |
||
831 | 3 | private function placeholderOption($display, $selected) |
|
832 | { |
||
833 | 3 | $selected = $this->getSelectedValue(null, $selected); |
|
834 | 3 | $options = array_merge(compact('selected'), ['value' => '']); |
|
835 | |||
836 | 3 | return '<option'.$this->html->attributes($options).'>'.$this->html->escape($display).'</option>'; |
|
837 | } |
||
838 | |||
839 | /** |
||
840 | * Determine if the value is selected. |
||
841 | * |
||
842 | * @param string $value |
||
843 | * @param string $selected |
||
844 | * |
||
845 | * @return string|null |
||
846 | */ |
||
847 | 39 | private function getSelectedValue($value, $selected) |
|
848 | { |
||
849 | 39 | if (is_array($selected)) { |
|
850 | 12 | return in_array($value, $selected) ? 'selected' : null; |
|
851 | } |
||
852 | |||
853 | 30 | return ((string) $value === (string) $selected) ? 'selected' : null; |
|
854 | } |
||
855 | |||
856 | /** |
||
857 | * Create a checkbox input field. |
||
858 | * |
||
859 | * @param string $name |
||
860 | * @param mixed $value |
||
861 | * @param bool|null $checked |
||
862 | * @param array $options |
||
863 | * |
||
864 | * @return \Illuminate\Support\HtmlString |
||
865 | */ |
||
866 | 12 | public function checkbox($name, $value = 1, $checked = null, array $options = []) |
|
867 | { |
||
868 | 12 | return $this->checkable('checkbox', $name, $value, $checked, $options); |
|
869 | } |
||
870 | |||
871 | /** |
||
872 | * Create a radio button input field. |
||
873 | * |
||
874 | * @param string $name |
||
875 | * @param mixed $value |
||
876 | * @param bool $checked |
||
877 | * @param array $options |
||
878 | * |
||
879 | * @return \Illuminate\Support\HtmlString |
||
880 | */ |
||
881 | 6 | public function radio($name, $value = null, $checked = null, array $options = []) |
|
882 | { |
||
883 | 6 | $value = $value ?? $name; |
|
884 | |||
885 | 6 | return $this->checkable('radio', $name, $value, $checked, $options); |
|
886 | } |
||
887 | |||
888 | /** |
||
889 | * Create a checkable input field. |
||
890 | * |
||
891 | * @param string $type |
||
892 | * @param string $name |
||
893 | * @param mixed $value |
||
894 | * @param bool|null $checked |
||
895 | * @param array $options |
||
896 | * |
||
897 | * @return \Illuminate\Support\HtmlString |
||
898 | */ |
||
899 | 21 | protected function checkable($type, $name, $value, $checked, array $options) |
|
900 | { |
||
901 | 21 | $checked = $this->getCheckedState($type, $name, $value, $checked); |
|
902 | |||
903 | 21 | if ( ! is_null($checked) && $checked) { |
|
904 | 18 | $options['checked'] = 'checked'; |
|
905 | } |
||
906 | |||
907 | 21 | return $this->input($type, $name, $value, $options); |
|
908 | } |
||
909 | |||
910 | /** |
||
911 | * Get the check state for a checkable input. |
||
912 | * |
||
913 | * @param string $type |
||
914 | * @param string $name |
||
915 | * @param mixed $value |
||
916 | * @param bool|null $checked |
||
917 | * |
||
918 | * @return bool |
||
919 | */ |
||
920 | 21 | private function getCheckedState($type, $name, $value, $checked) |
|
921 | { |
||
922 | 7 | switch($type) { |
|
923 | 14 | case 'checkbox': |
|
924 | 12 | return $this->getCheckboxCheckedState($name, $value, $checked); |
|
925 | |||
926 | 6 | case 'radio': |
|
927 | 6 | return $this->getRadioCheckedState($name, $value, $checked); |
|
928 | |||
929 | default: |
||
930 | 3 | return $this->getValueAttribute($name) === $value; |
|
931 | } |
||
932 | } |
||
933 | |||
934 | /** |
||
935 | * Get the check state for a checkbox input. |
||
936 | * |
||
937 | * @param string $name |
||
938 | * @param mixed $value |
||
939 | * @param bool|null $checked |
||
940 | * |
||
941 | * @return bool |
||
942 | */ |
||
943 | 12 | private function getCheckboxCheckedState($name, $value, $checked) |
|
944 | { |
||
945 | 12 | if (isset($this->session) && ! $this->oldInputIsEmpty() && is_null($this->old($name))) { |
|
946 | 3 | return false; |
|
947 | } |
||
948 | |||
949 | 12 | if ($this->missingOldAndModel($name)) { |
|
950 | 6 | return $checked; |
|
951 | } |
||
952 | |||
953 | 6 | $posted = $this->getValueAttribute($name, $checked); |
|
954 | |||
955 | 6 | if (is_array($posted)) { |
|
956 | 3 | return in_array($value, $posted); |
|
957 | } |
||
958 | |||
959 | 6 | if ($posted instanceof Collection) { |
|
960 | 3 | return $posted->contains('id', $value); |
|
961 | } |
||
962 | |||
963 | 6 | return (bool) $posted; |
|
964 | } |
||
965 | |||
966 | /** |
||
967 | * Get the check state for a radio input. |
||
968 | * |
||
969 | * @param string $name |
||
970 | * @param mixed $value |
||
971 | * @param bool|null $checked |
||
972 | * |
||
973 | * @return bool |
||
974 | */ |
||
975 | 6 | private function getRadioCheckedState($name, $value, $checked) |
|
981 | |||
982 | /** |
||
983 | * Determine if old input or model input exists for a key. |
||
984 | * |
||
985 | * @param string $name |
||
986 | * |
||
987 | * @return bool |
||
988 | */ |
||
989 | 18 | private function missingOldAndModel($name) |
|
993 | |||
994 | /** |
||
995 | * Create a HTML reset input element. |
||
996 | * |
||
997 | * @param string $value |
||
998 | * @param array $attributes |
||
999 | * |
||
1000 | * @return \Illuminate\Support\HtmlString |
||
1001 | */ |
||
1002 | 3 | public function reset($value, array $attributes = []) |
|
1006 | |||
1007 | /** |
||
1008 | * Create a HTML image input element. |
||
1009 | * |
||
1010 | * @param string $url |
||
1011 | * @param string $name |
||
1012 | * @param array $attributes |
||
1013 | * |
||
1014 | * @return \Illuminate\Support\HtmlString |
||
1015 | */ |
||
1016 | 3 | public function image($url, $name = null, array $attributes = []) |
|
1022 | |||
1023 | /** |
||
1024 | * Create a submit button element. |
||
1025 | * |
||
1026 | * @param string $value |
||
1027 | * @param array $options |
||
1028 | * |
||
1029 | * @return \Illuminate\Support\HtmlString |
||
1030 | */ |
||
1031 | 3 | public function submit($value = null, array $options = []) |
|
1035 | |||
1036 | /** |
||
1037 | * Create a button element. |
||
1038 | * |
||
1039 | * @param string $value |
||
1040 | * @param array $options |
||
1041 | * |
||
1042 | * @return \Illuminate\Support\HtmlString |
||
1043 | */ |
||
1044 | 3 | public function button($value = null, array $options = []) |
|
1045 | { |
||
1046 | 3 | if ( ! array_key_exists('type', $options)) { |
|
1047 | 3 | $options['type'] = 'button'; |
|
1048 | } |
||
1049 | |||
1050 | 3 | return $this->toHtmlString( |
|
1051 | 3 | '<button'.$this->html->attributes($options).'>'.$value.'</button>' |
|
1052 | ); |
||
1053 | } |
||
1054 | |||
1055 | /** |
||
1056 | * Create a color input field. |
||
1057 | * |
||
1058 | * @param string $name |
||
1059 | * @param string $value |
||
1060 | * @param array $options |
||
1061 | * |
||
1062 | * @return \Illuminate\Support\HtmlString |
||
1063 | */ |
||
1064 | 9 | public function color($name, $value = null, array $options = []) |
|
1068 | |||
1069 | /* ----------------------------------------------------------------- |
||
1070 | | Other Methods |
||
1071 | | ----------------------------------------------------------------- |
||
1072 | */ |
||
1073 | |||
1074 | /** |
||
1075 | * Get the form action from the options. |
||
1076 | * |
||
1077 | * @param array $options |
||
1078 | * |
||
1079 | * @return string |
||
1080 | */ |
||
1081 | 48 | private function getAction(array $options) |
|
1082 | { |
||
1083 | // We will also check for a "route" or "action" parameter on the array so that |
||
1084 | // developers can easily specify a route or controller action when creating |
||
1085 | // a form providing a convenient interface for creating the form actions. |
||
1086 | 48 | if (isset($options['url'])) { |
|
1087 | 15 | return $this->getUrlAction($options['url']); |
|
1088 | } |
||
1089 | 33 | elseif (isset($options['route'])) { |
|
1090 | 3 | return $this->getRouteAction($options['route']); |
|
1091 | } |
||
1092 | 33 | elseif (isset($options['action'])) { |
|
1093 | // If an action is available, we are attempting to open a form to a controller |
||
1094 | // action route. So, we will use the URL generator to get the path to these |
||
1095 | // actions and return them from the method. Otherwise, we'll use current. |
||
1096 | 3 | return $this->getControllerAction($options['action']); |
|
1097 | } |
||
1098 | |||
1099 | 30 | return $this->url->current(); |
|
1100 | } |
||
1101 | |||
1102 | /** |
||
1103 | * Get the action for a "url" option. |
||
1104 | * |
||
1105 | * @param array|string $options |
||
1106 | * |
||
1107 | * @return string |
||
1108 | */ |
||
1109 | 15 | private function getUrlAction($options) |
|
1110 | { |
||
1111 | 15 | return is_array($options) |
|
1112 | ? $this->url->to($options[0], array_slice($options, 1)) |
||
1113 | 15 | : $this->url->to($options); |
|
1114 | } |
||
1115 | |||
1116 | /** |
||
1117 | * Get the action for a "route" option. |
||
1118 | * |
||
1119 | * @param array|string $options |
||
1120 | * |
||
1121 | * @return string |
||
1122 | */ |
||
1123 | 3 | private function getRouteAction($options) |
|
1129 | |||
1130 | /** |
||
1131 | * Get the action for an "action" option. |
||
1132 | * |
||
1133 | * @param array|string $options |
||
1134 | * |
||
1135 | * @return string |
||
1136 | */ |
||
1137 | 3 | private function getControllerAction($options) |
|
1143 | |||
1144 | /** |
||
1145 | * Get the form appendage for the given method. |
||
1146 | * |
||
1147 | * @param string $method |
||
1148 | * |
||
1149 | * @return string |
||
1150 | */ |
||
1151 | 48 | private function getAppendage($method) |
|
1171 | } |
||
1172 |
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: