diff --git a/README.md b/README.md index 5cef77c..c1e4581 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,9 @@ -# Bootstrap 4 forms for Laravel 5 +# Bootstrap 4 forms for Laravel 5/6/7/8 -This is a package for creating Bootstrap 4 styled form elements in Laravel 5. +[![Latest Version on Packagist][ico-version]][link-packagist] +[![Total Downloads][ico-downloads]][link-downloads] + +This is a package for creating Bootstrap 4 styled form elements in Laravel 5/6. ## Features @@ -90,7 +93,10 @@ If you is using Laravel 5.5, the auto discovery feature will make everything for ```php // Making all inputs inline -{!!Form::inlineForm()!!} +{!!Form::open()->formInline()!!} + +// You can use FALSE to turn off disable form inline +{!!Form::open()->formInline(false)!!} ``` #### Fieldset @@ -148,10 +154,35 @@ If you is using Laravel 5.5, the auto discovery feature will make everything for {!!Form::select('city', 'Choose your city', [1 => 'Gotham City', 2 => 'Springfield'])!!} ``` -#### Select Default Value +##### Options + +| Param | Type | Default | Description | +| ---------- | -------- | ------- | ------------- | +| \$options | iterable | [] | Options list | +| \$valueKey | string | null | key for value | +| \$idKey | string | null | key for id | ```php -{!!Form::select('city', 'Choose your city', [''=>'--choose your city---',1 => 'Gotham City', 2 => 'Springfield'])!!} +// Example + +// With array +{!!Form::select('city', 'Choose your city')->options([1 => 'Gotham City', 2 => 'Springfield'])!!} + +// With collection +$cities = collect([1 => 'Gotham City', 2 => 'Springfield']) +{!!Form::select('city', 'Choose your city')->options($cities)!!} + +// With model collection +$cities = \App\City::all(); +{!!Form::select('city', 'Choose your city')->options($cities)!!} + +// Your model should have id and name attributes. If these keys are different, you can pass second and/or third parameters (you can use the second parameter to access some model acessor, also) +$cities = \App\City::all(); +{!!Form::select('city', 'Choose your city')->options($cities, 'city_name', 'id_object_field')!!} + +// When you are using collections, you can use prepend method (https://laravel.com/docs/5.8/collections#method-prepend) to add an first empty value, like "Choose your city" +$cities = \App\City::all(); +{!!Form::select('city', 'Choose your city')->options($cities->prepend('Choose your city', ''))!!} ``` ##### Checkbox @@ -161,7 +192,7 @@ If you is using Laravel 5.5, the auto discovery feature will make everything for | \$name | string | null | Input name | | \$label | string | null | Input label | | \$value | string | null | Input value | -| \$default | boolean | null | Default value | +| \$checked | boolean | null | Default value | ```php // Example @@ -175,7 +206,7 @@ If you is using Laravel 5.5, the auto discovery feature will make everything for | \$name | string | null | Input name | | \$label | string | null | Input label | | \$value | string | null | Input value | -| \$default | boolean | null | Default value | +| \$checked | boolean | null | Default value | ```php // Example @@ -207,6 +238,19 @@ If you is using Laravel 5.5, the auto discovery feature will make everything for {!!Form::date('birthday', 'Birthday')!!} ``` +#### Tel inputs + +| Param | Type | Default | Description | +| --------- | ------ | ------- | ------------- | +| \$name | string | null | Input name | +| \$label | string | null | Input label | +| \$default | string | null | Default value | + +```php +// Example +{!!Form::tel('number', 'Phone number')!!} +``` + #### Time inputs | Param | Type | Default | Description | @@ -220,6 +264,19 @@ If you is using Laravel 5.5, the auto discovery feature will make everything for {!!Form::time('hour', 'Meeting hour')!!} ``` +#### URL inputs + +| Param | Type | Default | Description | +| --------- | ------ | ------- | ------------- | +| \$name | string | null | Input name | +| \$label | string | null | Input label | +| \$default | string | null | Default value | + +```php +// Example +{!!Form::urlInput('website', 'You website')!!} +``` + #### Range inputs | Param | Type | Default | Description | @@ -292,9 +349,9 @@ If you is using Laravel 5.5, the auto discovery feature will make everything for ### Filling a form -| Param | Type | Default | Description | -| ------ | ------ | ------- | ----------- | -| \$data | object | array | null | DAta fo fill form inputs | +| Param | Type | Default | Description | +| ------ | ------------ | ------- | ------------------------ | +| \$data | object/array | array | Data fo fill form inputs | ```php // Examples @@ -334,6 +391,89 @@ Use in anchors and forms openings {!!Form::anchor("Link via route")->route('home')!!} ``` +### Error Bag + +Use if you have more then one form per page. You set an identifier for each form, and the errors will be attached for that specific form + +| Param | Type | Default | Description | +| ------- | ------ | ------- | -------------- | +| \$value | string | null | Error bag name | + +```php +// Example: attach this form to a error bag called "registerErrorBag" +{!!Form::open()->route('register.post')->errorBag("registerErrorBag")!!} + +// ------------------------------------------------------ + +// Now, in your controller (register.post route), you can redirect the user to a form page again, with erros inside a error bag called "registerErrorBag" +public function register(Request $request) +{ + $validator = Validator::make($request->all(), [ + // ... rules here + ]); + + if ($validator->fails()) { + return redirect() + ->route('register.form') + ->withInput() + ->withErrors($validator, 'registerErrorBag'); + } + + // Proced to register here +} + +// ------------------------------------------------------ + +// If your validation is on a Form Request, you can add a protected method "$errorBag" to set a ErrorBag name + +class RegisterRequest extends FormRequest +{ + + protected $errorBag = 'registerErrorBag'; + + public function authorize() + { + return true; + } + + public function rules() + { + return [ + // ... rules here + ]; + } +} +``` + +### Errors + +Show all errors inside a panel + +| Param | Type | Default | Description | +| ------- | ------ | ------- | ----------- | +| \$title | string | null | Panel title | + +```php +// Example +{!!Form::errors("The form has errors")!!} +``` + +### Disable validation messages + +Disable success/error status and validation error message + +| Param | Type | Default | Description | +| ---------- | ------- | ------- | --------------- | +| \$disabled | boolean | false | Disabled status | + +```php +// Example +{!!Form::text('username', 'User name')->disableValidation()!!} + +// You can use FALSE to turn off disable validation (to enable it) +{!!Form::text('username', 'User name')->disableValidation(false)!!} +``` + ### Checked Set the checkbox/radio checked status @@ -361,6 +501,9 @@ Set the checkbox/radio checked status {!!Form::radio('orange', 'Orange')->inline()!!} {!!Form::checkbox('orange', 'Orange')->inline()!!} + +// You can use FALSE to turn off inline status +{!!Form::checkbox('orange', 'Orange')->inline(false)!!} ``` ### Placeholder @@ -651,6 +794,32 @@ complete list is in the spec mentioned above. {!!Form::text('email', 'Your email')->type('email')!!} ``` +### Min + +| Param | Type | Default | Description | +| ------- | ------ | ------- | ------------- | +| \$value | number | null | Minimum value | + +Set min attribute for input + +```php +// Example +{!!Form::text('age', 'Your age')->type('number')->min(18)!!} +``` + +### Max + +| Param | Type | Default | Description | +| ------- | ------ | ------- | ------------- | +| \$value | number | null | Minimum value | + +Set max attribute for input + +```php +// Example +{!!Form::text('age', 'Your age')->type('number')->max(18)!!} +``` + ### Name | Param | Type | Default | Description | @@ -697,6 +866,19 @@ complete list is in the spec mentioned above. {!!Form::render('text')->name('age')->label('Your age')!!} ``` +### Disable is-valid CSS Class + +| Param | Type | Default | Description | +| ---------------- | ------- | ------- | -------------------------- | +| \$disableIsValid | boolean | true | Disable is-valid CSS class | + +```php +// Examples + +// Disable Bootstrap's is-valid CSS class +{!!Form::text('name', 'Name')->disableIsValid()!!} +``` + ### Chaining properties You can use chaining feature to use a lot of settings for each component @@ -714,3 +896,8 @@ You can use chaining feature to use a lot of settings for each component {!!Form::close()!!} ``` + +[ico-version]: https://img.shields.io/packagist/v/netojose/laravel-bootstrap-4-forms.svg?style=flat-square +[ico-downloads]: https://img.shields.io/packagist/dt/netojose/laravel-bootstrap-4-forms.svg?style=flat-square +[link-packagist]: https://packagist.org/packages/netojose/laravel-bootstrap-4-forms +[link-downloads]: https://packagist.org/packages/netojose/laravel-bootstrap-4-forms diff --git a/composer.json b/composer.json index 787ef31..9819681 100644 --- a/composer.json +++ b/composer.json @@ -18,7 +18,7 @@ "email": "sputinykster@gmail.com" } ], - "version": "2.0.6", + "version": "3.0.5", "minimum-stability": "dev", "require": {}, "autoload": { diff --git a/src/FormBuilder.php b/src/FormBuilder.php index 40c9b7b..ce7ed23 100644 --- a/src/FormBuilder.php +++ b/src/FormBuilder.php @@ -2,1101 +2,552 @@ namespace NetoJose\Bootstrap4Forms; -class FormBuilder { - - /** - * List of allowed single word - * values for autocomplete attribute - * - * @var array - */ - private $_allowedAutoComplete; - - /** - * Form input labels locale - * - * @var string - */ - private $_Flocale; - - /** - * Form style - * - * @var string - */ - private $_FformStyle; - - /** - * Form method - * - * @var string - */ - private $_Fmethod; - - /** - * Multipart flag - * - * @var boolean - */ - private $_Fmultipart; - - /** - * Form array data - * - * @var array - */ - private $_Fdata; - - /** - * Inputs id prefix - * @var string - */ - private $_FidPrefix; - - /** - * Form autocomplete attribute - * @var string|null - */ - private $_Fautocomplete; - - /** - * Input autocomplete attribute - * @var string|null - */ - private $_autocomplete; - - /** - * Input meta data - * - * @var array - */ - private $_meta; - - /** - * Input attributes - * - * @var array - */ - private $_attrs; - - /** - * Input wrapper attributes - * - * @var array - */ - private $_wrapperAttrs; - - /** - * Input wrapper flag - * - * @var array - */ - private $_wrapper; - - /** - * Form control type - * - * @var string - */ - private $_type; - - /** - * Form/Link - * - * @var string - */ - private $_url; - - /** - * Input placeholder - * - * @var string - */ - private $_placeholder; - - /** - * Flag to determine checkbox/radio style - * - * @var boolean - */ - private $_checkInline; - - /** - * Input size - * - * @var string - */ - private $_size; - - /** - * Readonly flag - * - * @var boolean - */ - private $_readonly; - - /** - * Disabled flag - * - * @var boolean - */ - private $_disabled; - - /** - * Required flag - * - * @var boolean - */ - private $_required; - - /** - * Input id - * - * @var string - */ - private $_id; - - /** - * Input class - * - * @var string - */ - private $_class; - - /** - * Input name - * - * @var string - */ - private $_name; - - /** - * Input label - * - * @var string - */ - private $_label; - - /** - * Select options - * - * @var array - */ - private $_options; - - /** - * Input help text - * - * @var string - */ - private $_help; - - /** - * Input color - * - * @var string - */ - private $_color; - - /** - * Input outline flag - * - * @var boolean - */ - private $_outline; - - /** - * Input block flag - * - * @var boolean - */ - private $_block; - - /** - * Input value - * - * @var boolean - */ - private $_value; - - /** - * Select multiple flag - * - * @var boolean - */ - private $_multiple; - - /** - * Input prefix - * - * @var string - */ - private $_prefix; - - /** - * Input suffix - * - * @var string - */ - private $_suffix; - - public function __construct() - { - $this->_resetFlags(); - $this->_resetFormFlags(); - - //All allowed single word autocomplete values. If the name of input field matches, - //they will be automatically set as autocomplete value - //Flip the array to make searches faster - $this->_allowedAutoComplete = array_flip(['name', 'honorific-prefix', 'given-name', 'additional-name', - 'family-name', 'honorific-suffix', 'nickname', 'username', 'new-password', 'current-password', - 'organization-title', 'organization', 'street-address', 'address-line1', 'address-line2', 'address-line3', - 'address-level4', 'address-level3', 'address-level2', 'address-level1', 'country', 'country-name', - 'postal-code', 'cc-name', 'cc-given-name', 'cc-additional-name', 'cc-family-name', 'cc-number', 'cc-exp', - 'cc-exp-month', 'cc-exp-year', 'cc-csc', 'cc-type', 'transaction-currency', 'transaction-amount', 'language', - 'bday', 'bday-day', 'bday-month', 'bday-year', 'sex', 'url', 'photo', 'tel', 'tel-country-code', 'tel-national', - 'tel-area-code', 'tel-local', 'tel-local-prefix', 'tel-local-suffix', 'tel-extension', 'email', 'impp']); - } +use Illuminate\Support\ViewErrorBag; + +class FormBuilder +{ - /** - * Set a class attribute - * - * @param string $attr - * @param mixed $value - */ - public function set(string $attr, $value) + private $_attrs = []; + + public function set($key, $value) { - $this->{'_' . $attr} = $value; + $formatter = 'format' . ucfirst($key); + if (method_exists($this, $formatter)) { + $value = $this->$formatter($value); + } + $this->attrs[$key] = $value; } - /** - * Retrieve a class attribute - * - * @param string $attr - * @return mixed - */ - public function get(string $attr) + private function formatMethod($value) { - return $this->{'_' . $attr}; + return strtolower($value); } - /** - * Return a open form tag - * - * @return string - */ - public function open(): string + private function formatFormData($value) { - $props = [ - 'action' => $this->_url, - 'method' => $this->_Fmethod === 'get' ? 'get' : 'post' - ]; - - if($this->_Fmultipart){ - $props['enctype'] = 'multipart/form-data'; - } - - if ($this->_FformStyle === 'inline') { - $props['class'] = 'form-inline'; - } - - if (!is_null($this->_Fautocomplete)) { - $props['autocomplete'] = $this->_Fautocomplete; - } - - $attrs = $this->_buildAttrs($props, ['class-form-control']); - - $ret = '
'; - - if ($this->_Fmethod !== 'get') { - $ret .= csrf_field(); - - if ($this->_Fmethod !== 'post' && $this->_Fmethod !== 'get') { - $ret .= method_field($this->_Fmethod); - } + if (is_object($value) && method_exists($value, 'toArray')) { + return $value->toArray(); } - - $this->_resetFlags(); - - return $ret; + return $value; } - /** - * Return a close form tag - * - * @return string - */ - public function close(): string + private function formatOptions($value) { - $ret = '
'; - - $this->_resetFormFlags(); - $this->_resetFlags(); + extract($this->get('optionIdKey', 'optionValueKey')); - return $ret; - } - - /** - * Return a open fieldset tag - * - * @return string - */ - public function fieldsetOpen(): string - { - $this->_class .= ' form-group'; - $attrs = $this->_buildAttrs(); - $ret = ''; + $idKey = $optionIdKey ?? 'id'; + $valueKey = $optionValueKey ?? 'name'; - if ($this->_meta['legend']) { - $ret .= '' . $this->_e($this->_meta['legend']) . ''; + $options = []; + foreach ($value as $key => $item) { + if (is_object($item)) { + $options[$item->{$idKey}] = $item->{$valueKey}; + continue; + } + $options[$key] = $item; } - - $this->_resetFlags(); - - return $ret; + return $options; } - /** - * Return a close fieldset tag - * - * @return string - */ - public function fieldsetClose(): string + public function render(): string { - $this->_resetFlags(); - - return ''; + $render = $this->attrs['render']; + $methodName = 'render' . ucfirst($render); + $output = $this->$methodName(); + $this->resetAttributes(); + return $output; } - /** - * Return a file input tag - * - * @return string - */ - public function file(): string + private function renderFormOpen(): string { - $attrs = $this->_buildAttrs(); - $id = $this->_getId(); - $placeholder = $this->_placeholder ?: 'Choose file'; + extract($this->get('id', 'method', 'url', 'formMultipart', 'formInline', 'autocomplete')); - $input = '
'; + if (!$method) { + $method = 'post'; + } - return $this->_renderWrapperCommonField($input); - } + $enctype = $formMultipart ? 'multipart/form-data' : null; - /** - * Return a text input tag - * - * @return string - */ - public function text(): string - { - return $this->_renderInput(); - } + $attrs = $this->buildHtmlAttrs([ + 'method' => in_array($method, ['get', 'post']) ? $method : 'post', + 'action' => $url, + 'enctype' => $enctype, + 'autocomplete' => $autocomplete, + 'class' => $formInline ? 'form-inline' : null, + 'id' => $id + ]); - /** - * Return a password input tag - * - * @return string - */ - public function password(): string - { - return $this->_renderInput('password'); - } + $output = '
'; - /** - * Return a range input tag - * - * @return string - */ - public function range(): string - { - return $this->_renderInput('range'); - } + if ($method !== 'get') { + $output .= csrf_field(); + if ($method !== 'post') { + $output .= method_field($method); + } + } - /** - * Return a date input tag - * - * @return string - */ - public function date(): string - { - return $this->_renderInput('date'); + return $output; } - /** - * Return a time input tag - * - * @return string - */ - public function time(): string + private function renderFormClose(): string { - return $this->_renderInput('time'); + $this->resetAttributes(true); + return '
'; } - /** - * Return a email input tag - * - * @return string - */ - public function email(): string + private function renderFieldsetOpen(): string { - return $this->_renderInput('email'); - } + $output = '
'; + extract($this->get('legend')); - /** - * Return a tel input tag - * - * @return string - */ - public function tel(): string - { - return $this->_renderInput('tel'); - } + if ($legend) { + $output .= '' . $this->getText($legend) . ''; + } - /** - * Return a url input tag - * - * @return string - */ - public function url(): string - { - return $this->_renderInput('url'); + return $output; } - /** - * Return a number input tag - * - * @return string - */ - public function number(): string + private function renderFieldsetClose(): string { - return $this->_renderInput('number'); + return '
'; } - /** - * Return a hidden input tag - * - * @return string - */ - public function hidden(): string + private function renderErrors(): string { - $value = $this->_getValue(); - $attrs = $this->_buildAttrs(['value' => $value]); - - $this->_resetFlags(); + $errors = $this->errors()->all(); + if (count($errors) < 1) { + return ''; + } - return ''; + extract($this->get('errorsHeader', 'id')); + $attrs = $this->buildHtmlAttrs(['class' => 'alert alert-danger', 'role' => 'alert', 'id' => $id]); + $output = '
'; } - /** - * Return a textarea tag - * - * @return string - */ - public function textarea(): string + private function renderInput(): string { - $attrs = $this->_buildAttrs(['rows' => 3]); - $value = $this->_getValue(); - - return $this->_renderWrapperCommonField(''); + $attributes = $this->getInputAttributes(); + $attrs = $this->buildHtmlAttrs($attributes); + return $this->wrapperInput(''); } - /** - * Return a select tag - * - * @return string - */ - public function select(): string + private function renderSelect(): string { - $attrs = $this->_buildAttrs(); - $value = $this->_getValue(); - $options = ''; - - if ($this->_multiple) { - if (!is_array($value)) { - $value = [$value]; - } - - foreach ($this->_options as $key => $label) { - - if (array_key_exists($key, $value)) { - $match = true; - } else { - $match = false; - } + extract($this->get('options')); - $checked = ($match) ? ' selected' : ''; - $options .= ''; - } - } else { - foreach ($this->_options as $optvalue => $label) { - $checked = $optvalue == $value ? ' selected' : ''; - $options .= ''; + $fieldValue = $this->getValue(); + $arrValues = is_array($fieldValue) ? $fieldValue : [$fieldValue]; + $optionsList = ''; + foreach ($options as $value => $label) { + $config = []; + if (is_array($label)) { + $config = explode(' ', $label['config']); + $label = $label['label']; } + $attrs = $this->buildHtmlAttrs([ + 'value' => $value, + 'hidden' => in_array('hidden', $config), + 'disabled' => in_array('disabled', $config), + 'selected' => in_array(strval($value), $arrValues), + ], false); + $optionsList .= ''; } - return $this->_renderWrapperCommonField(''); - } - - /** - * Return a checkbox tag - * - * @return string - */ - public function checkbox(): string - { - return $this->_renderCheckboxOrRadio(); + $attributes = $this->getInputAttributes(); + $attrs = $this->buildHtmlAttrs($attributes); + return $this->wrapperInput(''); } - /** - * Return a radio tag - * - * @return string - */ - public function radio(): string + private function renderTextarea(): string { - return $this->_renderCheckboxOrRadio(); + $attributes = $this->getInputAttributes(); + $value = $attributes['value']; + unset($attributes['value']); + $attrs = $this->buildHtmlAttrs($attributes); + return $this->wrapperInput(''); } - /** - * Return a button tag - * - * @return string - */ - public function button(): string + private function renderCheckbox(): string { - return $this->_renderButtonOrAnchor(); + $attributes = $this->getInputAttributes(); + $attrs = $this->buildHtmlAttrs($attributes); + return $this->wrapperRadioCheckbox(''); } - /** - * Return a submit input tag - * - * @return string - */ - public function submit(): string + private function renderRadio(): string { - return $this->_renderButtonOrAnchor(); + $attributes = $this->getInputAttributes(); + $attrs = $this->buildHtmlAttrs($attributes); + return $this->wrapperRadioCheckbox(''); } - /** - * Return a reset button tag - * - * @return string - */ - public function reset(): string + private function renderAnchor(): string { - return $this->_renderButtonOrAnchor(); + extract($this->get('url', 'value')); + $class = $this->getBtnAnchorClasses(); + $attrs = $this->buildHtmlAttrs(['href' => $url, 'class' => $class]); + return '' . $value . ''; } - /** - * Return a anchor tag - * - * @return string - */ - public function anchor(): string + private function renderButton(): string { - return $this->_renderButtonOrAnchor(); + extract($this->get('type', 'value', 'disabled')); + $class = $this->getBtnAnchorClasses(); + $attrs = $this->buildHtmlAttrs(['type' => $type, 'class' => $class, 'disabled' => $disabled]); + return ''; } - /** - * Return a generic input tag - * - * @param string $type - * @return string - */ - private function _renderInput($type = 'text'): string + private function getBtnAnchorClasses() { - $value = $this->_getValue(); - $attrs = $this->_buildAttrs(['value' => $value, 'type' => $type]); - - return $this->_renderWrapperCommonField(''); + extract($this->get('size', 'color', 'outline', 'block', 'type', 'value', 'formInline')); + return $this->createAttrsList( + 'btn', + [$size, 'btn-' . $size], + [$color, 'btn-' . ($outline ? 'outline-' : '') . $color], + [$block, 'btn-block'], + [$formInline, 'mx-sm-2'] + ); } - /** - * Return a button or anchor tag - * - * @return string - */ - private function _renderButtonOrAnchor(): string + private function isRadioOrCheckbox(): bool { - $size = $this->_size ? ' btn-' . $this->_size : ''; - $outline = $this->_outline ? 'outline-' : ''; - $block = $this->_block ? ' btn-block' : ''; - $disabled = $this->_disabled ? ' disabled' : ''; - $value = $this->_e($this->_value); - $cls = 'btn btn-' . $outline . $this->_color . $size . $block; - - if ($this->_type == 'anchor') { - $href = $this->_url ?: 'javascript:void(0)'; - $this->_class .= ' ' . $cls . $disabled; - $attrs = $this->_buildAttrs( - [ - 'href' => $href, - 'role' => 'button', - 'aria-disabled' => $disabled ? 'true' : null - ], ['class-form-control'] - ); - $ret = '' . $value . ''; - } else { - $this->_class .= ' ' . $cls; - $attrs = $this->_buildAttrs(['type' => $this->_type], ['class-form-control']); - $ret = ''; - } - - return $this->_renderWrapperCommonField($ret, true); + extract($this->get('render')); + return in_array($render, ['checkbox', 'radio']); } - /** - * Return a label tag - * - * @return string - */ - private function _getLabel(): string + private function getInputAttributes(): array { + extract($this->get('render', 'type', 'multiple', 'name', 'size', 'placeholder', 'help', 'disabled', 'readonly', 'required', 'autocomplete', 'min', 'max', 'value', 'checked', 'formData', 'disableValidation', 'custom')); - $label = $this->_label === true ? $this->_name : $this->_label; - $result = ''; - - if ($label) { + $isRadioOrCheckbox = $this->isRadioOrCheckbox(); + $type = $isRadioOrCheckbox ? $render : $type; - $classStr = ''; - if ($this->_FformStyle === 'inline') { - $classStr = ' class="sr-only"'; - } elseif($this->_FformStyle === 'horizontal') { - $classStr = ' class="col-sm-2 col-form-label"'; + $class = 'form-check-input'; + if($custom){ + $class = $this->customClass('input'); + }elseif (!$isRadioOrCheckbox) { + $class = 'form-control'; + switch ($type) { + case 'file': + $class .= '-file'; + break; + case 'range': + $class .= '-range'; + break; } - $id = $this->_getId(); - $result = ''; - } - - return $result; - } - - /** - * Return a string with HTML element attributes - * - * @param array $props - * @param array $ignore - * @return string - */ - private function _buildAttrs(array $props = [], array $ignore = []): string - { - $props = array_merge($props, - array_filter($this->_attrs, function($k){ - return $k != 'class'; - }, ARRAY_FILTER_USE_KEY)); - - if($this->_type){ - $props['type'] = $this->_type; - } - - if($this->_name){ - $props['name'] = $this->_name; - } - - if (!is_null($this->_autocomplete)) { - $props['autocomplete'] = $this->_autocomplete; - } else if ($this->_name && isset($this->_allowedAutoComplete[$this->_name])) { - $props['autocomplete'] = $this->_name; - } - - $id = $this->_getId(); - if($id){ - $props['id'] = $this->_getId(); - } - - $props['class'] = $this->_class ?: ''; - - if ($this->_type == 'select' && $this->_multiple && $this->_name) { - $props['name'] = $props['name'] . '[]'; - } - - if ($this->_placeholder) { - $props['placeholder'] = $this->_placeholder; - } elseif($this->_FformStyle === 'inline') { - $props['placeholder'] = $this->_label; - } - - if ($this->_help) { - $props['aria-describedby'] = $this->_getIdHelp(); - } - - if($this->_required === true) { - $props['required'] = true; - } - - switch($this->_type) { - case 'file': - $formControlClass = ' custom-file-input'; - break; - case 'range': - $formControlClass = ' form-control-range'; - break; - default: - $formControlClass = ' form-control'; - break; - } - - if (!in_array('class-form-control', $ignore)) { - $props['class'] .= $formControlClass; - } - - if ($this->_size) { - $props['class'] .= ' '.$formControlClass.'-' . $this->_size; - } - - if ($this->_FformStyle === 'inline') { - $props['class'] .= ' mb-2 mr-sm-2'; - } - - $props['class'] .= ' ' . $this->_getValidationFieldClass(); - - if (isset($this->_attrs['class'])) { - $props['class'] .= ' ' . $this->_attrs['class']; + if ($size) { + $class .= ' form-control-' . $size; + } } - $props['class'] = trim($props['class']); + $id = $this->getId(); - if(!$props['class']) { - $props['class'] = false; + if (!$disableValidation && $this->errors()->count() > 0) { + $class .= $this->errors()->has($name) ? ' is-invalid' : ' is-valid'; } - if ($this->_type == 'select' && $this->_multiple) { - $props['multiple'] = true; - } + $attributes = [ + 'type' => $type, + 'name' => $name, + 'id' => $id + ]; - if ($this->_readonly) { - $props['readonly'] = true; + if ($render !== 'select') { + $attributes['value'] = $this->getValue(); + } else { + $attributes['multiple'] = $multiple; } - if ($this->_disabled) { - $props['disabled'] = true; + // If the field is a hidden field, we don't need add more attributes + if ($type === 'hidden') { + return $attributes; } - if (in_array($this->_type, ['radio', 'checkbox'])) { - $value = $this->_getValue(); - if ( - $value && ( - $this->_type === 'checkbox' || $this->_type === 'radio' && $value === $this->_meta['value'] - ) - ) { - $props['checked'] = true; + if ($this->isRadioOrCheckbox()) { + if ($this->hasOldInput()) { + $isChecked = old($name) === $value; + } else { + $isChecked = isset($formData[$name]) ? $formData[$name] === $value : $checked; } + $attributes['checked'] = $isChecked; } - if ($this->_type == 'hidden') { - $props['autocomplete'] = false; - $props['class'] = false; - } - - return $this->_arrayToHtmlAttrs($props); + return array_merge($attributes, [ + 'class' => $class, + 'min' => $min, + 'max' => $max, + 'autocomplete' => $autocomplete, + 'placeholder' => $this->getText($placeholder), + 'aria-describedby' => $help ? 'help-' . $id : null, + 'disabled' => $disabled, + 'readonly' => $readonly, + 'required' => $required + ]); } - /** - * Return a input value - * - * @return mixed - */ - private function _getValue() + private function renderLabel(): string { - $name = $this->_name; + extract($this->get('label', 'formInline', 'render', 'custom')); - if ($this->_hasOldInput()) { - return old($name); + $class = ''; + if($custom){ + $class = $this->customClass('label'); + } elseif (in_array($render, ['checkbox', 'radio'])){ + $class = 'form-check-label'; } - if ($this->_value !== null) { - return $this->_value; + if ($formInline) { + $class = join(' ', [$class, 'mx-sm-2']); } - if (isset($this->_Fdata[$name])) { - return $this->_Fdata[$name]; - } + $id = $this->getId(); + $attrs = $this->buildHtmlAttrs([ + 'for' => $id, + 'class' => $class + ], false); + return ''; } - /** - * Check if has a old request - * - * @return boolean - */ - private function _hasOldInput() + private function getText($key) { - return count((array) old()) != 0; + extract($this->get('formLocale')); + if ($formLocale) { + return __($formLocale . '.' . $key); + } + return $key; } - /** - * Return a element id - * - * @return string - */ - private function _getId() + private function resetAttributes($resetAll = false) { - $id = $this->_id; - - if (!$id && $this->_name) { - $id = $this->_name; - if ($this->_type === 'radio') { - $id .= '-' . str_slug($this->_meta['value']); - } - } - - if(!$id) { - return null; + // Remove all attributes + if ($resetAll) { + $this->attrs = []; + return; } - return $this->_FidPrefix . $id; + // Keep attributes which key starting with 'form' + $this->attrs = array_filter($this->attrs, function ($key) { + return substr($key, 0, 4) === 'form'; + }, ARRAY_FILTER_USE_KEY); } - /** - * Return a help text id HTML element - * - * @return string - */ - private function _getIdHelp() + private function wrapperInput(string $input): string { - $id = $this->_getId(); + extract($this->get('type', 'help', 'wrapperAttrs', 'formInline', 'name', 'custom')); - return $id ? 'help-' . $id : ''; - } + if ($type === 'hidden') { + return $input; + } - /** - * Return a help text - * - * @return string - */ - private function _getHelpText(): string - { - $id = $this->_getIdHelp(); + $id = $this->getId(); + $label = $this->renderLabel(); + $helpText = $help ? '' . $this->getText($help) . '' : ''; + $error = $this->getInputErrorMarkup($name); + $attrs = $wrapperAttrs ?? []; + $attrs['class'] = $this->createAttrsList( + $custom ? $this->customClass('wrapper') : '', + $attrs['class'] ?? null, + $formInline ? 'input-group' : 'form-group' + ); + $attributes = $this->buildHtmlAttrs($attrs, false); - return $this->_help ? '' . $this->_e($this->_help) . '' : ''; + if($custom && $type === 'file'){ + $placeholder = $label; + $this->set('custom', false); + $label = $this->renderLabel(); + $this->set('custom', true); + return '
' . $label . '
' . $placeholder . $input . $helpText . $error . '
'; + }else{ + return '
' . $label . $input . $helpText . $error . '
'; + } } - /** - * Return a prefix id HTML element - * - * @return string - */ - private function _getIdPrefix() + private function wrapperRadioCheckbox(string $input): string { - $id = $this->_getId(); + extract($this->get('inline', 'name', 'wrapperAttrs', 'custom')); - return $id ? 'prefix-' . $id : ''; + $attrs = $wrapperAttrs ?? []; + $attrs['class'] = $this->createAttrsList( + $custom ? $this->customClass('wrapper') : 'form-check', + [$inline, ($custom ? 'form-check-inline' : 'custom-control-inline')], + $attrs['class'] ?? null + ); + $attributes = $this->buildHtmlAttrs($attrs, false); + $label = $this->renderLabel(); + $error = $this->getInputErrorMarkup($name); + return '
' . $input . $label . $error . '
'; } - /** - * Return a prefix - * - * @return string - */ - private function _getPrefix(): string + private function getInputErrorMarkup(string $name): string { - $id = $this->_getIdPrefix(); + extract($this->get('disableValidation')); - return $this->_prefix ? '
' . $this->_e($this->_prefix) . '
' : ''; - } - - /** - * Return a suffix id HTML element - * - * @return string - */ - private function _getIdSuffix() - { - $id = $this->_getId(); + if ($disableValidation) { + return ''; + } - return $id ? 'suffix-' . $id : ''; + $error = $this->errors()->first($name); + if (!$error) { + return ''; + } + return '
' . $this->errors()->first($name) . '
'; } - /** - * Return a suffix - * - * @return string - */ - private function _getSuffix(): string + private function getId() { - $id = $this->_getIdSuffix(); + extract($this->get('id', 'name', 'formIdPrefix', 'render', 'value')); - return $this->_suffix ? '
' . $this->_e($this->_suffix) . '
' : ''; + if ($id) { + return $id; + } + + return ($formIdPrefix ?? 'inp-') . $name . ($render === 'radio' ? '-' . $value : ''); } - /** - * Return a text with translations, if available - * - * @param string $key - * - * @return string - */ - private function _e($key): string + private function hasOldInput() { - $fieldKey = $key ?: $this->_name; - - return $this->_Flocale ? __($this->_Flocale . '.' . $fieldKey) : $fieldKey; + return count((array) old()) != 0; } - private function _getValidationFieldClass(): string + private function getValue() { - if (!$this->_name) { - return ''; + extract($this->get('name', 'value', 'formData')); + if ($this->isRadioOrCheckbox()) { + return $value; } - if (session('errors') === null) { - return ''; + if ($this->hasOldInput()) { + if (isset(old()[$name])) { + return old($name, $value); + } } - if ($this->_getValidationFieldMessage()) { - return ' is-invalid'; - } + $fromFill = $formData[$name] ?? null; - return ' is-valid'; + return $value ?? $fromFill; } - /** - * Return a checkbox or radio HTML element - * - * @return string - */ - private function _renderCheckboxOrRadio(): string + private function buildHtmlAttrs(array $attributes, $appendAttrs = true): string { - $this->_class .= ' custom-control-input'; - $attrs = $this->_buildAttrs(["type" => $this->_type, "value" => $this->_meta['value']], ['class-form-control']); - $inline = $this->_checkInline ? ' form-check-inline' : ''; - $label = $this->_e($this->_label); - $id = $this->_getId(); - $type = $this->_type; - - return $this->_renderWrapperCommonField('
', true); - } - private function _arrayToHtmlAttrs($attributes){ - return join(' ', array_map(function($key) use ($attributes) { - $value = $attributes[$key]; - if(is_bool($value)){ - return $value ? $key : ''; - } else { - return $key.'="'.htmlspecialchars($value).'"'; + if ($appendAttrs) { + extract($this->get('attrs')); + $fieldAttrs = $attrs ?? []; + $class = $this->createAttrsList($attributes['class'] ?? null, $fieldAttrs['class'] ?? null); + if ($class) { + $attributes['class'] = $class; } + $attributes = array_merge($fieldAttrs, $attributes); + } + + return join(' ', array_filter( + array_map(function ($key) use ($attributes) { + $value = $attributes[$key]; + if (is_bool($value)) { + return $value ? $key : ''; + } elseif ($value !== null) { + return $key . '="' . htmlspecialchars($value) . '"'; + } + return ''; }, array_keys($attributes)) - ); + )); } - /** - * Return a input with a wrapper HTML markup - * - * @param string $field - * @param bool $hide_label - * @return string - */ - private function _renderWrapperCommonField(string $field, $hide_label = false): string + private function customClass(string $element = 'input') { - $label = $hide_label ? '' : $this->_getLabel(); - $help = $this->_getHelpText(); - $prefix = $this->_getPrefix(); - $suffix = $this->_getSuffix(); - $error = $this->_getValidationFieldMessage(); - - $formGroupOpen = $formGroupClose = ''; - if ($this->_wrapper && $this->_FformStyle !== 'inline') { - $classList = isset($this->_wrapperAttrs['class']) ? $this->_wrapperAttrs['class'] : ''; - $this->_wrapperAttrs['class'] = "form-group " . $classList; - - if ($this->_FformStyle === 'horizontal' && !$hide_label) { - $this->_wrapperAttrs['class'] .= ' row'; - } - - $wrapperAttrs = $this->_arrayToHtmlAttrs($this->_wrapperAttrs); - - $formGroupOpen = '
'; - $formGroupClose = '
'; - } + extract($this->get('type', 'size', 'inline', 'render')); - $inputGroupOpen = $inputGroupClose = ''; + $type = $type ?? $render; + $input = $element === 'input'; + $wrapper = $element === 'wrapper'; + $label = $element === 'label'; - if ($this->_wrapper && $this->_FformStyle === 'horizontal' && !$hide_label) { - $inputGroupOpen .= '
'; - $inputGroupClose .= '
'; + if($input){ + $class = 'custom-control-input'; + } elseif($wrapper){ + $class = 'custom-control' . (isset($inline) && $inline ? ' custom-control-inline' : ''); + } else { + $class = 'custom-control-label'; } - if ($prefix || $suffix) { - $inputGroupOpen .= '
'; - $inputGroupClose .= '
'; - } - - $this->_resetFlags(); - - return $formGroupOpen . $label . $inputGroupOpen . $prefix . $field . $suffix . $inputGroupClose . $help . $error . $formGroupClose; + switch($type){ + case 'checkbox': + if($wrapper){ + $class .= ' custom-checkbox'; + } + break; + case 'radio': + if($wrapper){ + $class .= ' custom-radio'; + } + break; + case 'switch': + if($wrapper){ + $class .= ' custom-switch'; + } + break; + case 'select': + if($input){ + $class = 'custom-select'; + $class = $size ? $class . '-' . $size : $class; + } elseif($label || $wrapper){ + $class = ''; + } + break; + case 'range': + $class = $input ? 'custom-range' : ''; + break; + case 'file': + if($wrapper) { + $class = 'custom-file'; + } elseif($input) { + $class = 'custom-file-input'; + } else{ + $class = 'custom-file-label'; + } + break; + default: + // do nothing + } + return $class; } - /** - * Return a validation error message - * - * @param string $prefix - * @param string $sufix - * @return string|null - */ - private function _getValidationFieldMessage(string $prefix = '
', string $sufix = '
') + private function createAttrsList(...$items) { - $errors = session('errors'); - if (!$errors) { - return null; - } - $error = $errors->first($this->_name); - - if (!$error) { - return null; + $attrs = []; + foreach ($items as $item) { + if (is_array($item)) { + $item = $item[0] ? $item[1] : null; + } + $attrs[] = $item; } - - return $prefix . $error . $sufix; + return join(' ', array_filter($attrs)); } - /** - * Reset input flags - */ - private function _resetFlags() + private function errors() { - $this->_required = null; - $this->_wrapper = true; - $this->_render = null; - $this->_meta = []; - $this->_attrs = []; - $this->_wrapperAttrs = []; - $this->_type = null; - $this->_url = null; - $this->_placeholder = null; - $this->_checkInline = false; - $this->_size = null; - $this->_readonly = false; - $this->_disabled = false; - $this->_id = null; - $this->_name = null; - $this->_label = null; - $this->_options = []; - $this->_help = null; - $this->_class = null; - $this->_prefix = null; - $this->_suffix = null; - $this->_color = "primary"; - $this->_outline = false; - $this->_block = false; - $this->_value = null; - $this->_multiple = false; - $this->_autocomplete = null; + $errors = session('errors', app(ViewErrorBag::class)); + extract($this->get('formErrorBag')); + if ($formErrorBag) { + $errors = $errors->{$formErrorBag}; + } + return $errors; } - /** - * Reset form flags - */ - private function _resetFormFlags() + private function get(...$keys): array { - - $this->_Flocale = null; - $this->_Fmethod = 'post'; - $this->_Fmultipart = false; - $this->_FformStyle = 'standard'; - $this->_Fdata = null; - $this->_FidPrefix = ''; - $this->_Fautocomplete = null; + $return = []; + foreach ($keys as $key) { + $return[$key] = $this->attrs[$key] ?? null; + } + return $return; } - } diff --git a/src/FormService.php b/src/FormService.php index de7d905..22539fd 100644 --- a/src/FormService.php +++ b/src/FormService.php @@ -7,28 +7,8 @@ * * @author neto */ -class FormService { - - /** - * The Form builder instance - * - * @var FormBuilder - */ - private $_builder; - - /** - * Render to be used - * - * @var string - */ - private $_render; - - /** - * Allowed renders - * - * @var array - */ - private $_allowedRenders = ['open', 'close', 'fieldsetOpen', 'fieldsetClose', 'file', 'text', 'range', 'password', 'date', 'time', 'email', 'tel', 'url', 'number', 'hidden', 'select', 'checkbox', 'radio', 'textarea', 'button', 'submit', 'anchor', 'reset']; +class FormService +{ /** * Create a new FormSevice instance @@ -45,16 +25,18 @@ public function __construct() */ public function __toString() { - $output = ''; - - if (in_array($this->_render, $this->_allowedRenders)) { - - $output = $this->_builder->{$this->_render}(); - } - - $this->_render = null; + return $this->_builder->render(); + } - return $output; + /** + * Set error bag name + * + * @param string $value + * @return FormService + */ + public function errorBag(string $value = null): FormService + { + return $this->_set('formErrorBag', $value); } /** @@ -64,7 +46,7 @@ public function __toString() */ public function open(): FormService { - return $this->render('open'); + return $this->_set('render', 'formOpen'); } /** @@ -74,7 +56,18 @@ public function open(): FormService */ public function close(): FormService { - return $this->render('close'); + return $this->_set('render', 'formClose'); + } + + /** + * Show all validation errors + * + * @param string $title + * @return FormService + */ + public function errors(string $title = null): FormService + { + return $this->_set('render', 'errors')->_set('errorsHeader', $title); } /** @@ -85,7 +78,7 @@ public function close(): FormService */ public function idPrefix(string $prefix = ''): FormService { - return $this->_set('FidPrefix', $prefix); + return $this->_set('formIdPrefix', $prefix); } /** @@ -96,7 +89,7 @@ public function idPrefix(string $prefix = ''): FormService */ public function multipart(bool $multipart = true): FormService { - return $this->_set('Fmultipart', $multipart); + return $this->_set('formMultipart', $multipart); } /** @@ -107,7 +100,7 @@ public function multipart(bool $multipart = true): FormService */ public function method(string $method): FormService { - return $this->_set('Fmethod', $method); + return $this->_set('method', $method); } /** @@ -168,16 +161,7 @@ public function delete(): FormService */ public function fill($data): FormService { - - if (method_exists($data, 'toArray')) { - $data = $data->toArray(); - } - - if (!is_array($data)) { - $data = []; - } - - return $this->_set('Fdata', $data); + return $this->_set('formData', $data); } /** @@ -188,74 +172,29 @@ public function fill($data): FormService */ public function locale(string $path): FormService { - return $this->_set('Flocale', $path); - } - - /** - * Set inline form to inline inputs - * @param bool $inline - * @return FormService - */ - public function inlineForm(bool $inline = true): FormService - { - if($inline) { - return $this->_set('FformStyle', 'inline'); - }else{ - return $this; - } - } - - /** - * @param bool $wrapper - * @return FormService - */ - public function noWrapper(bool $wrapper = false) - { - return $this->_set('wrapper', $wrapper); - } - - /** - * Set horizontal form - * @param bool $horizontal - * @return FormService - */ - public function horizontalForm(bool $horizontal = true): FormService - { - if($horizontal) { - return $this->_set('FformStyle', 'horizontal'); - }else{ - return $this; - } + return $this->_set('formLocale', $path); } /** - * Set autocomplete value ('on', 'off', or one of the permitted values) - * - * If set on the form, only 'on' or 'off' are valid and are inherited by input fields. - * The inherited value can be overridden on individual input fields - * - * See: https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill + * Set autocomplete attribute on form, or on individual input fields * * @param string $value * @return FormService */ - public function autocomplete(string $value = 'on'): FormService + public function autocomplete($value = true): FormService { - if ($this->_render == "open") { - return $this->_set('Fautocomplete', $value); - } - return $this->_set('autocomplete', $value); } /** - * Set inline style for checkbox and radio inputs + * Set inline form style + * * @param bool $inline * @return FormService */ - public function inline(bool $inline = true): FormService + public function formInline(bool $inline = true): FormService { - return $this->_set('checkInline', $inline); + return $this->_set('formInline', $inline); } /** @@ -264,9 +203,9 @@ public function inline(bool $inline = true): FormService * @param string $url * @return FormService */ - public function url(https://github.com/string%20$url): FormService + public function url(https://github.com/string%20$url%20=%20null): FormService { - return $this->_set('url', url(https://github.com/$url)); + return $this->_set('url', url(https://github.com/$url%20??%20%27')); } /** @@ -289,7 +228,7 @@ public function route(string $route, array $params = []): FormService */ public function fieldsetOpen(string $legend = null): FormService { - return $this->_set('meta', ['legend' => $legend])->render('fieldsetOpen'); + return $this->render('fieldsetOpen')->_set('legend', $legend); } /** @@ -322,7 +261,7 @@ public function help(string $text): FormService */ public function file(string $name = null, string $label = null): FormService { - return $this->name($name)->label($label)->type('file'); + return $this->render('input')->type('file')->name($name)->label($label)->custom()->placeholder('Choose file'); } /** @@ -335,7 +274,7 @@ public function file(string $name = null, string $label = null): FormService */ public function text(string $name = null, $label = null, string $default = null): FormService { - return $this->type('text')->name($name)->label($label)->value($default); + return $this->render('input')->type('text')->name($name)->label($label)->value($default); } /** @@ -348,7 +287,7 @@ public function text(string $name = null, $label = null, string $default = null) */ public function date(string $name = null, $label = null, string $default = null): FormService { - return $this->type('date')->name($name)->label($label)->value($default); + return $this->render('input')->type('date')->name($name)->label($label)->value($default); } /** @@ -361,7 +300,33 @@ public function date(string $name = null, $label = null, string $default = null) */ public function time(string $name = null, $label = null, string $default = null): FormService { - return $this->type('time')->name($name)->label($label)->value($default); + return $this->render('input')->type('time')->name($name)->label($label)->value($default); + } + + /** + * Create a telephone input + * + * @param string $name + * @param string $label + * @param string $default + * @return FormService + */ + public function tel(string $name = null, $label = null, string $default = null): FormService + { + return $this->render('input')->type('tel')->name($name)->label($label)->value($default); + } + + /** + * Create a url input + * + * @param string $name + * @param string $label + * @param string $default + * @return FormService + */ + public function urlInput(string $name = null, $label = null, string $default = null): FormService + { + return $this->render('input')->type('url')->name($name)->label($label)->value($default); } /** @@ -374,7 +339,29 @@ public function time(string $name = null, $label = null, string $default = null) */ public function range(string $name = null, $label = null, string $default = null): FormService { - return $this->type('range')->name($name)->label($label)->value($default); + return $this->render('input')->type('range')->name($name)->label($label)->value($default)->custom(); + } + + /** + * Set a minimum value for a field + * + * @param string $value + * @return FormService + */ + public function min($value) + { + return $this->_set('min', $value); + } + + /** + * Set a maximum value for a field + * + * @param string $value + * @return FormService + */ + public function max($value) + { + return $this->_set('max', $value); } /** @@ -386,7 +373,7 @@ public function range(string $name = null, $label = null, string $default = null */ public function hidden(string $name = null, string $default = null): FormService { - return $this->name($name)->value($default)->type('hidden'); + return $this->render('input')->type('hidden')->name($name)->value($default); } /** @@ -400,47 +387,83 @@ public function hidden(string $name = null, string $default = null): FormService */ public function select(string $name = null, string $label = null, $options = [], $default = null): FormService { - return $this->name($name)->label($label)->options($options)->value($default)->type('select'); + return $this->render('select')->name($name)->label($label)->options($options)->value($default)->custom(); } /** * Set options for a select field * - * @param array $options + * @param mixed $options + * @param string $valueKey + * @param string $idKey + * @return FormService + */ + public function options($options = [], string $valueKey = null, string $idKey = null): FormService + { + return $this->_set('optionValueKey', $valueKey)->_set('optionIdKey', $idKey)->_set('options', $options); + } + + /** + * Set a multiple select attribute + * + * @param bool $multiple * @return FormService */ - public function options(array $options = []): FormService + public function multiple(bool $status = true): FormService { - $items = is_iterable($options) ? $options : [0 => 'Must be iterable']; - return $this->_set('options', $items); + return $this->_set('multiple', $status); } /** * Create a checkbox input * * @param string $name - * @param string $label * @param string $value - * @param string $default + * @param string $label + * @param bool $checked * @return FormService */ - public function checkbox(string $name = null, string $label = null, string $value = null, string $default = null): FormService + public function checkbox(string $name = null, string $label = null, string $value = 'on', bool $checked = null): FormService { - return $this->_checkboxRadio('checkbox', $name, $label, $value, $default); + return $this->_radioOrCheckbox('checkbox', $name, $label, $value, $checked); } /** * Create a radio input * * @param string $name - * @param string $label * @param string $value - * @param string $default + * @param string $label + * @param bool $checked * @return FormService */ - public function radio(string $name = null, string $label = null, string $value = null, string $default = null): FormService + public function radio(string $name = null, string $label = null, string $value = null, bool $checked = null): FormService { - return $this->_checkboxRadio('radio', $name, $label, $value, $default); + return $this->_radioOrCheckbox('radio', $name, $label, $value, $checked); + } + + /** + * Create a switch input + * + * @param string $name + * @param string $value + * @param string $label + * @param bool $checked + * @return FormService + */ + public function switch(string $name = null, string $label = null, string $value = null, bool $checked = null): FormService + { + return $this->_radioOrCheckbox('checkbox', $name, $label, $value, $checked)->type('switch'); + } + + /** + * Set inline input style + * @param bool $inline + * @return FormService + */ + public function inline(bool $inline = true): FormService + { + return $this->_set('inline', $inline); } /** @@ -453,7 +476,7 @@ public function radio(string $name = null, string $label = null, string $value = */ public function textarea(string $name = null, $label = null, string $default = null): FormService { - return $this->type('textarea')->name($name)->value($default)->label($label); + return $this->_set('render', 'textarea')->name($name)->label($label)->value($default); } /** @@ -477,7 +500,7 @@ public function label($label): FormService */ public function button(string $value = null, $color = 'primary', $size = null): FormService { - return $this->type('button')->color($color)->size($size)->value($value); + return $this->type('button')->_set('render', 'button')->value($value)->color($color)->size($size); } /** @@ -490,7 +513,7 @@ public function button(string $value = null, $color = 'primary', $size = null): */ public function submit(string $value, $color = 'primary', $size = null): FormService { - return $this->button($value)->type('submit')->color($color)->size($size); + return $this->button($value, $color, $size)->type('submit'); } /** @@ -503,7 +526,7 @@ public function submit(string $value, $color = 'primary', $size = null): FormSer */ public function reset(string $value, $color = 'primary', $size = null): FormService { - return $this->button($value)->type('reset')->color($color)->size($size); + return $this->button($value, $color, $size)->type('reset'); } /** @@ -513,13 +536,9 @@ public function reset(string $value, $color = 'primary', $size = null): FormServ * @param string $url * @return FormService */ - public function anchor(string $value, $url = null): FormService + public function anchor(string $value, $url = null, $color = 'primary', $size = null): FormService { - if ($url) { - $this->url(https://github.com/$url); - } - - return $this->button($value)->type('anchor'); + return $this->_set('render', 'anchor')->value($value)->url(https://github.com/$url)->color($color)->size($size); } /** @@ -530,14 +549,7 @@ public function anchor(string $value, $url = null): FormService */ public function checked(bool $checked = true): FormService { - $type = $this->_builder->get('type'); - $meta = $this->_builder->get('meta'); - - if ($type === 'radio' && $checked) { - $checked = $meta['value']; - } - - return $this->value($checked); + return $this->_set('checked', $checked); } /** @@ -548,11 +560,7 @@ public function checked(bool $checked = true): FormService */ public function value($value = null): FormService { - if ($value !== null) { - return $this->_set('value', $value); - } - - return $this; + return $this->_set('value', $value); } /** @@ -563,7 +571,7 @@ public function value($value = null): FormService */ public function type($type): FormService { - return $this->_set('type', $type)->render($type); + return $this->_set('type', $type); } /** @@ -574,9 +582,7 @@ public function type($type): FormService */ public function render(string $render): FormService { - $this->_render = $render; - - return $this; + return $this->_set('render', $render); } /** @@ -590,17 +596,6 @@ public function id($id): FormService return $this->_set('id', $id); } - /** - * Set a field class - * - * @param string $class - * @return FormService - */ - public function class($class): FormService - { - return $this->_set('class', $class); - } - /** * Set a field name * @@ -623,6 +618,17 @@ public function size(string $size = null): FormService return $this->_set('size', $size); } + /** + * Set the field as bootstrap custom + * + * @param bool $custom + * @return FormService + */ + public function custom(bool $custom = true): FormService + { + return $this->_set('custom', $custom); + } + /** * Set the size as lg * @@ -747,12 +753,12 @@ public function link(): FormService /** * Set outline style * - * @param bool $outline + * @param bool $status * @return FormService */ - public function outline(bool $outline = true): FormService + public function outline(bool $status = true): FormService { - return $this->_set('outline', $outline); + return $this->_set('outline', $status); } /** @@ -761,7 +767,7 @@ public function outline(bool $outline = true): FormService * @param bool $status * @return FormService */ - public function block(bool $status= true): FormService + public function block(bool $status = true): FormService { return $this->_set('block', $status); } @@ -794,7 +800,7 @@ public function disabled($status = true): FormService * @param bool $status * @return FormService */ - public function required($status = true) : FormService + public function required($status = true): FormService { return $this->_set('required', $status); } @@ -822,82 +828,54 @@ public function attrs(array $attrs = []): FormService } /** - * Set custom attributes for a wrapper input + * Disable input states (valid and invalid classes) and error message * - * @param array $attrs + * @param string $disable * @return FormService */ - public function wrapperAttrs(array $attrs = []): FormService + public function disableValidation(bool $disable = true): FormService { - return $this->_set('wrapperAttrs', $attrs); + return $this->_set('disableValidation', $disable); } /** - * Set a multiple select attribute - * - * @param bool $multiple - * @return FormService - */ - public function multiple(bool $multiple = true): FormService - { - return $this->_set('multiple', $multiple); - } - - /** - * Set input group prefix + * Set custom attributes for a wrapper input * - * @param string $prefix + * @param array $attrs * @return FormService */ - public function prefix(string $prefix): FormService + public function wrapperAttrs(array $attrs = []): FormService { - return $this->_set('prefix', $prefix); + return $this->_set('wrapperAttrs', $attrs); } /** - * Set input group suffix + * Create radio or checkbox input * - * @param string $suffix + * @param string $render + * @param string $name + * @param string $value + * @param string $label + * @param mixed $checked * @return FormService */ - public function suffix(string $suffix): FormService + private function _radioOrCheckbox($render, $name, $label, $value, $checked): FormService { - return $this->_set('suffix', $suffix); + if (is_bool($checked)) { + $this->checked($checked); + } + return $this->_set('render', $render)->name($name)->label($label)->value($value)->custom(); } /** - * Set a form builder attribute + * Set the size * - * @param string $attr - * @param mixed $value + * @param string $size * @return FormService */ - private function _set($attr, $value): FormService + private function _set(string $key, $value): FormService { - $this->_builder->set($attr, $value); - + $this->_builder->set($key, $value); return $this; } - - /** - * Render a checkbox or a radio input - * - * @param string $type - * @param string $name - * @param string $label - * @param mixed $value - * @param string $default - * @return FormService - */ - private function _checkboxRadio($type, $name, $label, $value, $default): FormService - { - $inputValue = $value === null ? $name : $value; - - if ($default) { - $default = $inputValue; - } - - return $this->_set('meta', ['value' => $inputValue])->type($type)->name($name)->label($label)->value($default); - } - }