From 9ab3cab9151432d411b1ca3b75a055bb7d2022fe Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 10 Mar 2018 21:15:45 +0000 Subject: [PATCH 1/3] Updating the select() method --- src/Contracts/FormBuilder.php | 4 +- src/FormBuilder.php | 53 ++++++++++++++++------- src/Helpers/Attributes.php | 10 +++-- tests/FormBuilderTest.php | 79 ++++++++++++++++++++++++++++++++++- tests/HtmlBuilderTest.php | 2 +- 5 files changed, 124 insertions(+), 24 deletions(-) diff --git a/src/Contracts/FormBuilder.php b/src/Contracts/FormBuilder.php index 0e97952..5f0e3bc 100644 --- a/src/Contracts/FormBuilder.php +++ b/src/Contracts/FormBuilder.php @@ -262,11 +262,11 @@ public function textarea($name, $value = null, array $options = []); * @param array|\Illuminate\Support\Collection $list * @param string $selected * @param array $attributes - * @param array $options + * @param array $optionsAttributes * * @return \Illuminate\Support\HtmlString */ - public function select($name, $list = [], $selected = null, array $attributes = [], array $options = []); + public function select($name, $list = [], $selected = null, array $attributes = [], array $optionsAttributes = []); /** * Create a select range field. diff --git a/src/FormBuilder.php b/src/FormBuilder.php index bfe7823..54bd8a1 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -677,14 +677,21 @@ protected function setQuickTextAreaSize(array $options) * * @param string $name * @param array|\Illuminate\Support\Collection $list - * @param string $selected + * @param string|bool $selected * @param array $attributes - * @param array $options + * @param array $optionsAttributes + * @param array $optgroupsAttributes * * @return \Illuminate\Support\HtmlString */ - public function select($name, $list = [], $selected = null, array $attributes = [], array $options = []) - { + public function select( + $name, + $list = [], + $selected = null, + array $attributes = [], + array $optionsAttributes = [], + array $optgroupsAttributes = [] + ) { // When building a select box the "value" attribute is really the selected one // so we will use that when checking the model or session for a value which // should provide a convenient method of re-populating the forms on post. @@ -712,9 +719,10 @@ public function select($name, $list = [], $selected = null, array $attributes = } foreach($list as $value => $display) { - $html[] = $this->getSelectOption( - $display, $value, $selected, isset($options[$value]) ? $options[$value] : [] - ); + $optionAttributes = $optionsAttributes[$value] ?? []; + $optgroupAttributes = $optgroupsAttributes[$value] ?? []; + + $html[] = $this->getSelectOption($display, $value, $selected, $optionAttributes, $optgroupAttributes); } // Once we have all of this HTML, we can join this into a single element after @@ -790,13 +798,14 @@ public function selectMonth($name, $selected = null, array $options = [], $forma * @param string $value * @param string $selected * @param array $attributes + * @param array $optgroupAttributes * * @return string */ - private function getSelectOption($display, $value, $selected, array $attributes = []) + private function getSelectOption($display, $value, $selected, array $attributes = [], array $optgroupAttributes = []) { return is_array($display) - ? $this->optionGroup($display, $value, $selected, $attributes) + ? $this->optionGroup($display, $value, $selected, $optgroupAttributes, $attributes) : $this->option($display, $value, $selected, $attributes); } @@ -807,18 +816,25 @@ private function getSelectOption($display, $value, $selected, array $attributes * @param string $label * @param string $selected * @param array $attributes + * @param array $optionsAttributes + * @param int $level * * @return string */ - private function optionGroup(array $list, $label, $selected, array $attributes = []) + private function optionGroup(array $list, $label, $selected, array $attributes = [], array $optionsAttributes = [], $level = 0) { - $html = []; + $html = []; + $space = str_repeat(" ", $level); foreach($list as $value => $display) { - $html[] = $this->option($display, $value, $selected, $attributes); + $optionAttributes = $optionsAttributes[$value] ?? []; + + $html[] = is_array($display) + ? $this->optionGroup($display, $value, $selected, $attributes, $optionAttributes, $level + 5) + : $this->option($space.$display, $value, $selected, $optionAttributes); } - return ''.implode('', $html).''; + return 'html->attributes($attributes).'>'.implode('', $html).''; } /** @@ -865,9 +881,14 @@ private function placeholderOption($display, $selected) */ private function getSelectedValue($value, $selected) { - if (is_array($selected)) { - return in_array($value, $selected) ? 'selected' : null; - } + if (is_array($selected)) + return (in_array($value, $selected, true) || in_array((string) $value, $selected, true)) ? 'selected' : null; + + if ($selected instanceof Collection) + return $selected->contains($value) ? 'selected' : null; + + if (is_int($value) && is_bool($selected)) + return (bool) $value === $selected; return ((string) $value === (string) $selected) ? 'selected' : null; } diff --git a/src/Helpers/Attributes.php b/src/Helpers/Attributes.php index 9c2ff8b..08038e3 100644 --- a/src/Helpers/Attributes.php +++ b/src/Helpers/Attributes.php @@ -27,12 +27,11 @@ public static function make(array $attributes) foreach ((array) $attributes as $key => $value) { $element = static::makeAttributeElement($key, $value); - if ( ! is_null($element)) { + if ( ! is_null($element)) $html[] = $element; - } } - return (count($html) > 0) ? ' ' . implode(' ', $html) : ''; + return (count($html) > 0) ? ' '.implode(' ', array_filter($html)) : ''; } /* ----------------------------------------------------------------- @@ -57,7 +56,10 @@ private static function makeAttributeElement($key, $value) // as this will convert HTML attributes such as "required" to a correct // form like required="required" instead of using incorrect numerics. if (is_numeric($key)) - $key = $value; + return $value; + + if (is_bool($value) && $key !== 'value') + return $value ? $key : ''; return $key.'="'.e($value).'"'; } diff --git a/tests/FormBuilderTest.php b/tests/FormBuilderTest.php index 89ecdad..010ebfc 100644 --- a/tests/FormBuilderTest.php +++ b/tests/FormBuilderTest.php @@ -3,6 +3,7 @@ use Arcanedev\LaravelHtml\FormBuilder; use Arcanedev\LaravelHtml\Tests\Stubs\FormBuilderModelStub; use Carbon\Carbon; +use Illuminate\Session\Store; use Illuminate\Support\Collection; use StdClass; @@ -950,7 +951,7 @@ public function it_can_make_select_options_with_attributes() { static::assertEquals( '', $this->form->select( @@ -1121,6 +1122,82 @@ public function it_can_make_select_input_with_nested_options() '', $this->form->select('size', $list, null,['class' => 'class-name', 'id' => 'select-id']) ); + + $list = [ + 'Large sizes' => [ + 'L' => 'Large', + 'XL' => 'Extra Large', + ], + 'M' => 'Medium', + 'Small sizes' => [ + 'S' => 'Small', + 'XS' => 'Extra Small', + ], + ]; + $optionsAttributes = [ + 'Large sizes' => [ + 'L' => ['disabled'] + ], + 'M' => ['disabled'], + ]; + $optgroupAttributes = [ + 'Small sizes' => ['disabled'], + ]; + + $this->assertEquals( + '', + $this->form->select('size', $list, null, [], $optionsAttributes, $optgroupAttributes)->toHtml() + ); + + $this->assertEquals( + '', + $this->form->select('encoded_html', ['no_break_space' => ' ', 'ampersand' => '&', 'lower_than' => '<']) + ); + + $list = ['L' => 'Large', 'S' => 'Small']; + $optionsAttributes = ['L' => ['data-foo' => 'bar', 'disabled']]; + + $this->assertEquals( + '', + $this->form->select('size', $list, null, [], $optionsAttributes) + ); + + $this->form->setSessionStore( + tap(new Store('name', new \SessionHandler()), function (Store $store) { + $store->put('_old_input', ['countries' => ['1']]); + }) + ); + + $this->assertEquals( + '', + $this->form->select('countries', [1 => 'L', 2 => 'M']) + ); + + $this->assertEquals( + '', + $this->form->select('avc', [1 => 'Yes', 0 => 'No'], true, ['placeholder' => 'Select'])->toHtml() + ); } /** @test */ diff --git a/tests/HtmlBuilderTest.php b/tests/HtmlBuilderTest.php index b839212..954a353 100644 --- a/tests/HtmlBuilderTest.php +++ b/tests/HtmlBuilderTest.php @@ -424,7 +424,7 @@ public function it_can_obfuscate() public function it_can_make_attributes() { static::assertEquals( - ' class="strong" required="required"', + ' class="strong" required', $this->html->attributes(['class' => 'strong', 'required']) ); } From 64df47b2da0732060f646fc290fce2330eebea06 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 10 Mar 2018 21:27:48 +0000 Subject: [PATCH 2/3] Cleaning --- src/FormBuilder.php | 93 ++++++++++++++++----------------------- tests/HtmlBuilderTest.php | 2 +- 2 files changed, 38 insertions(+), 57 deletions(-) diff --git a/src/FormBuilder.php b/src/FormBuilder.php index 54bd8a1..7ada1a3 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -45,7 +45,7 @@ class FormBuilder extends Builder implements FormBuilderContract /** * The session store implementation. * - * @var \Illuminate\Contracts\Session\Session + * @var \Illuminate\Contracts\Session\Session|\Illuminate\Session\Store */ protected $session; @@ -214,9 +214,8 @@ public function getValueAttribute($name, $value = null) */ private function getModelValueAttribute($name, $model = null) { - if (is_null($model)) { + if (is_null($model)) $model = $this->model; - } $key = $this->transformKey($name); @@ -308,9 +307,8 @@ public function open(array $options = []) 'accept-charset' => 'UTF-8', ]; - if (isset($options['files']) && $options['files']) { + if (isset($options['files']) && $options['files']) $options['enctype'] = 'multipart/form-data'; - } // Finally we're ready to create the final form HTML field. We will attribute // format the array of attributes. We will also add on the appendage which @@ -367,11 +365,10 @@ public function close() */ public function token() { - $token = ! empty($this->csrfToken) - ? $this->csrfToken - : $this->session->token(); - - return $this->hidden('_token', $token); + return $this->hidden( + '_token', + empty($this->csrfToken) ? $this->session->token() : $this->csrfToken + ); } /** @@ -405,18 +402,16 @@ public function label($name, $value = null, array $options = [], $escaped = true */ public function input($type, $name, $value = null, array $options = []) { - if ( ! isset($options['name'])) { + if ( ! isset($options['name'])) $options['name'] = $name; - } // We will get the appropriate value for the given field. We will look for the // value in the session for the value in the old input data then we'll look // in the model instance if one is set. Otherwise we will just use empty. $id = $this->getIdAttribute($name, $options); - if ( ! in_array($type, $this->skipValueTypes)) { + if ( ! in_array($type, $this->skipValueTypes)) $value = $this->getValueAttribute($name, $value); - } // Once we have the type, value, and ID we can merge them into the rest of the // attributes array so we can convert them into their HTML attribute format @@ -520,9 +515,8 @@ public function number($name, $value = null, array $options = []) */ public function date($name, $value = null, array $options = []) { - if ($value instanceof DateTime) { + if ($value instanceof DateTime) $value = $value->format('Y-m-d'); - } return $this->input('date', $name, $value, $options); } @@ -538,9 +532,8 @@ public function date($name, $value = null, array $options = []) */ public function datetime($name, $value = null, array $options = []) { - if ($value instanceof DateTime) { + if ($value instanceof DateTime) $value = $value->format(DateTime::RFC3339); - } return $this->input('datetime', $name, $value, $options); } @@ -556,9 +549,8 @@ public function datetime($name, $value = null, array $options = []) */ public function datetimeLocal($name, $value = null, array $options = []) { - if ($value instanceof DateTime) { + if ($value instanceof DateTime) $value = $value->format('Y-m-d\TH:i'); - } return $this->input('datetime-local', $name, $value, $options); } @@ -615,9 +607,8 @@ public function file($name, array $options = []) */ public function textarea($name, $value = null, array $options = []) { - if ( ! isset($options['name'])) { + if ( ! isset($options['name'])) $options['name'] = $name; - } // Next we will look for the rows and cols attributes, as each of these are put // on the textarea element definition. If they are not present, we will just @@ -645,9 +636,8 @@ public function textarea($name, $value = null, array $options = []) */ private function setTextAreaSize(array $options) { - if (isset($options['size'])) { + if (isset($options['size'])) return $this->setQuickTextAreaSize($options); - } // If the "size" attribute was not specified, we will just look for the regular // columns and rows attributes, using sane defaults if these do not exist on @@ -698,15 +688,13 @@ public function select( $selected = $this->getValueAttribute($name, $selected); // Transform to array if it is a collection - if ($selected instanceof Collection) { + if ($selected instanceof Collection) $selected = $selected->all(); - } $attributes['id'] = $this->getIdAttribute($name, $attributes); - if ( ! isset($attributes['name'])) { + if ( ! isset($attributes['name'])) $attributes['name'] = $name; - } // We will simply loop through the options and build an HTML value for each of // them until we have an array of HTML declarations. Then we will join them @@ -920,9 +908,7 @@ public function checkbox($name, $value = 1, $checked = null, array $options = [] */ public function radio($name, $value = null, $checked = null, array $options = []) { - $value = $value ?? $name; - - return $this->checkable('radio', $name, $value, $checked, $options); + return $this->checkable('radio', $name, $value ?? $name, $checked, $options); } /** @@ -940,9 +926,8 @@ protected function checkable($type, $name, $value, $checked, array $options) { $checked = $this->getCheckedState($type, $name, $value, $checked); - if ( ! is_null($checked) && $checked) { + if ( ! is_null($checked) && $checked) $options['checked'] = 'checked'; - } return $this->input($type, $name, $value, $options); } @@ -982,23 +967,23 @@ private function getCheckedState($type, $name, $value, $checked) */ private function getCheckboxCheckedState($name, $value, $checked) { - if (isset($this->session) && ! $this->oldInputIsEmpty() && is_null($this->old($name))) { + if ( + isset($this->session) && + ! $this->oldInputIsEmpty() && + is_null($this->old($name)) + ) return false; - } - if ($this->missingOldAndModel($name)) { + if ($this->missingOldAndModel($name)) return $checked; - } $posted = $this->getValueAttribute($name, $checked); - if (is_array($posted)) { + if (is_array($posted)) return in_array($value, $posted); - } - if ($posted instanceof Collection) { + if ($posted instanceof Collection) return $posted->contains('id', $value); - } return (bool) $posted; } @@ -1055,9 +1040,9 @@ public function reset($value, array $attributes = []) */ public function image($url, $name = null, array $attributes = []) { - $attributes['src'] = $this->url->asset($url); - - return $this->input('image', $name, null, $attributes); + return $this->input('image', $name, null, array_merge($attributes, [ + 'src' => $this->url->asset($url), + ])); } /** @@ -1083,9 +1068,8 @@ public function submit($value = null, array $options = []) */ public function button($value = null, array $options = []) { - if ( ! array_key_exists('type', $options)) { + if ( ! array_key_exists('type', $options)) $options['type'] = 'button'; - } return $this->toHtmlString( 'html->attributes($options).'>'.$value.'' @@ -1123,18 +1107,17 @@ private function getAction(array $options) // We will also check for a "route" or "action" parameter on the array so that // developers can easily specify a route or controller action when creating // a form providing a convenient interface for creating the form actions. - if (isset($options['url'])) { + if (isset($options['url'])) return $this->getUrlAction($options['url']); - } - elseif (isset($options['route'])) { + + if (isset($options['route'])) return $this->getRouteAction($options['route']); - } - elseif (isset($options['action'])) { + + if (isset($options['action'])) // If an action is available, we are attempting to open a form to a controller // action route. So, we will use the URL generator to get the path to these // actions and return them from the method. Otherwise, we'll use current. return $this->getControllerAction($options['action']); - } return $this->url->current(); } @@ -1195,16 +1178,14 @@ private function getAppendage($method) // If the HTTP method is in this list of spoofed methods, we will attach the // method spoofer hidden input to the form. This allows us to use regular // form to initiate PUT and DELETE requests in addition to the typical. - if (in_array($method, $this->spoofedMethods)) { + if (in_array($method, $this->spoofedMethods)) $appendage .= $this->hidden('_method', $method); - } // If the method is something other than GET we will go ahead and attach the // CSRF token to the form, as this can't hurt and is convenient to simply // always have available on every form the developers creates for them. - if ($method !== 'GET') { + if ($method !== 'GET') $appendage .= $this->token(); - } return $appendage; } diff --git a/tests/HtmlBuilderTest.php b/tests/HtmlBuilderTest.php index 954a353..ed92621 100644 --- a/tests/HtmlBuilderTest.php +++ b/tests/HtmlBuilderTest.php @@ -461,7 +461,7 @@ public function it_can_register_a_component() * @test * * @expectedException \BadMethodCallException - * @expectedExceptionMessage Method btnSuccess does not exist. + * @expectedExceptionMessage Method Arcanedev\LaravelHtml\HtmlBuilder::btnSuccess does not exist. */ public function it_must_throw_bad_method_call_exception_on_component() { From 6e2224c242465587d15f254dce3ca61a51aac409 Mon Sep 17 00:00:00 2001 From: Unknown Date: Sat, 10 Mar 2018 21:30:34 +0000 Subject: [PATCH 3/3] Updating the Form contract --- src/Contracts/FormBuilder.php | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Contracts/FormBuilder.php b/src/Contracts/FormBuilder.php index 5f0e3bc..6447f34 100644 --- a/src/Contracts/FormBuilder.php +++ b/src/Contracts/FormBuilder.php @@ -260,13 +260,21 @@ public function textarea($name, $value = null, array $options = []); * * @param string $name * @param array|\Illuminate\Support\Collection $list - * @param string $selected + * @param string|bool $selected * @param array $attributes * @param array $optionsAttributes + * @param array $optgroupsAttributes * * @return \Illuminate\Support\HtmlString */ - public function select($name, $list = [], $selected = null, array $attributes = [], array $optionsAttributes = []); + public function select( + $name, + $list = [], + $selected = null, + array $attributes = [], + array $optionsAttributes = [], + array $optgroupsAttributes = [] + ); /** * Create a select range field.