sztyup / laravel-form-builder
Based on laravel-form-builder (https://github.com/kristijanhusak/laravel-form-builder). That add default component and add view system.
Requires
- php: >=5.6.4
- illuminate/filesystem: 5.4.*
- illuminate/support: 5.4.*
- kris/laravel-form-builder: 1.12.0
Requires (Dev)
- fzaninotto/faker: 1.6.*
- mockery/mockery: 0.9.*
- orchestra/database: 3.4.*
- orchestra/testbench: 3.4.*
- orchestra/testbench-browser-kit: 3.4.*
- phpunit/phpunit: ~5.7
This package is auto-updated.
Last update: 2022-07-13 11:33:45 UTC
README
Laravel 5 Form Builder
Based on laravel-form-builder (https://github.com/kristijanhusak/laravel-form-builder). I add default no editable form and few complex form fields. I add the validation system directly in the form part.
Table of contents
- Installation
- Basic usage
- Type of form
- Form
- FormView
- FormValidator 1. Use the validation client side 2. Use the validation server side 3. Check the rules on your controller
- List of fields
- Input
- Choice 1. Select 2. Radio 3. Checkbox 4. Ajax
- Tag
- Upload
- TinyMce
- Textarea
- Button
- Address Picker
- Form
- Controller
- Troubleshooting
##Installation
Add on your composer.json
"require": { "distilleries/form-builder": "2.*", }
run composer update
.
Add Service provider to config/app.php
:
'providers' => [ // ... 'Distilleries\FormBuilder\FormBuilderServiceProvider', ]
And Facade (also in config/app.php
)
'aliases' => [ // ... 'FormBuilder' => 'Distilleries\FormBuilder\Facades\FormBuilder', ]
Export the configuration:
php artisan vendor:publish --provider="Distilleries\FormBuilder\FormBuilderServiceProvider"
Export the views (optional):
php artisan vendor:publish --provider="Distilleries\FormBuilder\FormBuilderServiceProvider" --tag="views"
###Basic usage
Creating form classes is easy. With a simple artisan command I can create form:
php artisan make:form Forms/PostForm
you create form class in path app/Forms/PostForm.php
that looks like this:
<?php namespace App\Forms; use Distilleries\FormBuilder\FormValidator; class PostForm extends FormValidator { public static $rules = []; public static $rules_update = null; public function buildForm() { // Add fields here... $this->addDefaultActions(); } }
You can add fields which you want when creating command like this:
php artisan make:form Forms/SongForm --fields="name:text, lyrics:textarea, publish:checkbox"
And that will create form in path app/Forms/SongForm.php
with content:
<?php namespace App\Forms; use Distilleries\FormBuilder\FormValidator; class SongForm extends FormValidator { public static $rules = []; public static $rules_update = null; public function buildForm() { $this ->add('name', 'text') ->add('lyrics', 'textarea') ->add('publish', 'checkbox'); $this->addDefaultActions(); } }
##Type of form
###Form
This is the base class from the package https://github.com/kristijanhusak/laravel-form-builder/tree/laravel-4. It use to add the fields and generate the form. Check the readme to know how use the component.
###FormView Extend the class Form to add a render with edit.
####Use the view
To display an not editable form you can use form_view
or form_rest_view
.
To display a specific field you can use form_widget_view
.
{!! form_view($form) !!}
In your form field you can add an option to not display this field on the view noInEditView
.
For example in user form I add a choice to allow the password change. I don't want it in the view part.
$this->add('change_password', 'checkbox', [ 'default_value' => 1, 'label' => _('Check it if you want change your password'), 'checked' => false, 'noInEditView' => true ]);
Other way, you have a sub form and you don't want display some fields.
You can specify an option call do_not_display_
plus the name of the field.
Example I have a customer form and this form use a sub form user. On the user form I don't want display the role choice:
$this->add('user', 'form', [ 'label' => _('User'), 'icon' => 'user', 'class' => \FormBuilder::create('Distilleries\Expendable\Forms\User\UserForm', [ 'model' => $this->getUserModel(), 'do_not_display_role_id' => true ]) ]);
###FormValidator Extend the FormView and add the system of validation.
public static $rules = []; public static $rules_update = null;
The both table use the rules of laravel.
If the $rules_update
keep in null the $rules
is use to validate the form.
####Use the validation client side
By default I use jQuery validation Engine for the javascript validation.
When you add a field you can add an option validation
to add the javascript validation.
$this->add('email', 'email', [ 'validation' => 'required,custom[email]', ]);
####Use the validation server side
If you have a form User like this:
<?php namespace Project\Forms; use Distilleries\FormBuilder\FormValidator; class UserForm extends FormValidator { public static $rules = [ 'email' => 'required|email|unique:users', 'password' => 'required|min:8', 'status' => 'required|integer', 'role_id' => 'required|integer', ]; public static $rules_update = [ 'id' => 'required', 'email' => 'required|email|unique:users,email', 'status' => 'required|integer', 'role_id' => 'required|integer', ]; // ------------------------------------------------------------------------------------------------ public function buildForm() { $this ->add($this->model->getKeyName(), 'hidden') ->add('email', 'email', [ 'label' => _('Email'), 'validation' => 'required,custom[email]', ]); $id = $this->model->getKey(); if (!empty($id)) { $this->add('change_password', 'checkbox', [ 'default_value' => 1, 'label' => _('Check it if you want change your password'), 'checked' => false, 'noInEditView' => true ]); } $this->add('password', 'password', [ 'label' => _('Password'), 'attr' => ['id'=>'password'], 'validation' => 'required', 'noInEditView' => true ]) ->add('password_match', 'password', [ 'label' => _('Repeat Password'), 'validation' => 'required,equals[password]', 'noInEditView' => true ]) ->add('status', 'choice', [ 'choices' => StaticLabel::status(), 'empty_value' => _('-'), 'validation' => 'required', 'label' => _('Status') ]) ->add('role_id', 'choice', [ 'choices' => \Role::getChoice(), 'empty_value' => _('-'), 'validation' => 'required', 'label' => _('Role') ]) ->addDefaultActions(); } protected function getUpdateRules() { $key = \Input::get($this->model->getKeyName()); static::$rules_update['email'] = 'required|email|unique:users,email,' . $key; return parent::getUpdateRules(); } // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ // ------------------------------------------------------------------------------------------------ }
You can see the password
field is not require on the update.
I have an other specific rule. I want check if the email address is unique without if the email is use by your-self.
In the FormValidator you have two methods to get the update or general rules (getGeneralRules
, getUpdateRules
) . You can override them to return the good rules.
That what I do in the UserForm. I override the method getUpdateRules
to add the id of user for the validation.
####Check the rules on your controller:
$form = FormBuilder::create('Project\Forms\UserForm', [ 'model' => new User ]); if ($form->hasError()) { return $form->validateAndRedirectBack(); }
validateAndRedirectBack
do only a redirect back with errors and Inputs.
Redirect::back()->withErrors($this->validate())->withInput(Input::all());
##List of fields
###1 Input
Can be one of those type:
Field | Type |
---|---|
text | <input type="text" /> |
<input type="email" /> |
|
url | <input type="url" /> |
tel | <input type="tel" /> |
number | <input type="number" /> |
date | <input type="date" /> |
search | <input type="search" /> |
password | <input type="password" /> |
hidden | <input type="hidden" /> |
number | <input type="number" /> |
file | <input type="text" /> |
$this->add('first_name', 'text', [ 'validation' => 'required', 'label' => _('First name') ])
###2 Choice
####2.1 Select
$this->add('subscription', 'choice', [ 'choices' => ['monthly' => 'Monthly', 'yearly' => 'Yearly'], 'empty_value' => '==== Select subscription ===', 'multiple' => false // This is default. If set to true, it creates select with multiple select posibility ])
####2.2 Radio
$this->add('subscription', 'choice', [ 'choices' => ['monthly' => 'Monthly', 'yearly' => 'Yearly'], 'selected' => 'monthly', 'expanded' => true ])
###2.3 Checkbox
$this->add('subscription', 'choice', [ 'choices' => ['monthly' => 'Monthly', 'yearly' => 'Yearly'], 'selected' => ['monthly', 'yearly'] 'expanded' => true, 'multiple' => true ])
###2.4 Ajax The tag component is base on select2.
Add the javascript on your bower components:
"dependencies": { "select2": "~3.5.2" }
This component is use to search an element, or multiple elements.
$this-> ->add('user_id', 'choice_ajax', [ 'action' => action('Admin\UserController@postSearch'), 'validation' => 'required', 'formatter' => [ 'id' => 'id', 'libelle' => 'email', ], 'label' => _('User') ]);
Field | Explain |
---|---|
action | The url of the action for the autocomplete |
validation | The rules of the javascript validation |
formatter | To display an element select2 need to know who is the value and who is the text. You can use , to concat fields ex: 'libelle' => 'first_name,last_name', . |
label | The translation of the label display |
maximum_selection_size | If you want limit the number of elements selectable. By default -1 no limit |
multiple | If is a select mutiple or not |
minimumInputLength | Minimum of char needed before send the search request. By default 2 char. |
allowClear | Allow to remove the value from the field. |
This code is an example of controller method for the search:
// ------------------------------------------------------------------------------------------------ public function postSearch() { $ids = Input::get('ids'); if (!empty($ids)) { $data = $this->model->whereIn($this->model->getKeyName(), $ids)->get(); return Response::json($data); } $term = Input::get('term'); $page = Input::get('page'); $paged = Input::get('page_limit'); if (empty($paged)) { $paged = 10; } if (empty($page)) { $page = 1; } if (empty($term)) { $elements = array(); $total = 0; } else { $elements = $this->model->search($term)->take($paged)->skip(($page - 1) * $paged)->get(); $total = $this->model->search($term)->count(); } return Response::json([ 'total' => $total, 'elements' => $elements ]); }
Render editable:
Render not editable:
###3 Tag The tag component is base on select2.
Add the javascript on your bower components:
"dependencies": { "select2": "~3.5.2" }
$this->add('cc', 'tag', [ 'label' => _('CC') ])
Render editable:
Render not editable:
###4 Upload The upload field use moximanager to link the elements with all the media components.
$this->add('file', 'upload', [ 'label' => _('File'), 'validation' => 'required', 'extensions' => 'csv,xls,xlsx', 'view' => 'files', ]);
Render editable:
###5 TinyMce If you want use a rich content editor you can use tinymce.
$this->add('description', 'tinymce', [ 'label' => _('Description') ]);
Render editable:
Render not editable:
###6 Textarea The textarea work like a text field.
$this->add('description', 'textarea', [ 'label' => _('Description') ]);
###7 Button You can add a button to submit your form or to back at the last page.
$this->add('submit', 'submit', [ 'label' => _('Save'), 'attr' => [ 'class' => 'btn green' ], ], false, true) ->add('back', 'button', [ 'label' => _('Back'), 'attr' => [ 'class' => 'btn default', 'onclick' => 'window.history.back()' ], ], false, true);
Render editable:
The button submit for the part not editable is never render.
###8 Address Picker
The address picker base on http://logicify.github.io/jquery-locationpicker-plugin/
Add the javascript on your bower components:
"dependencies": { "jquery-locationpicker-plugin": "~0.1.12" }
$this->add('address', 'address_picker', [ 'default_value' => [ 'lat' => 10, 'lng' => 10, 'street' => '42 Wallaby Way', 'city' => 'Sydney', 'country' => 'Australia', 'default' => '42 Wallaby Way, Sydney, Australia', ] ] ]);
Render editable:
Render not editable:
###9 Form You can add a form in a form. It pretty cool when you compose a big form to split it in multiple. For example I have a profile form with an address. I use the address on the profile form.
$this ->add('address', 'form', [ 'label' => _('Address'), 'icon' => 'globe', 'class' => \FormBuilder::create('Project\Forms\AddressForm', [ 'model' => $address, 'do_not_display_profile_id' => true ]) ]);
Render editable:
Render not editable:
##Controller
You can use the trait Distilleries\FormBuilder\States\FormStateTrait
to add in your controller the default methods use with the form.
Example:
I created a UserForm
class:
<?php namespace App\Forms; use Distilleries\FormBuilder\FormValidator; class UserForm extends FormValidator { public static $rules = [ 'email'=>'required' ]; public static $rules_update = null; public function buildForm() { $this ->add('id', 'hidden') ->add('email', 'email'); $this->addDefaultActions(); } }
I created a controller app/Http/Controllers/FormController
:
<?php namespace App\Http\Controllers; use App\Forms\UserForm; use Distilleries\FormBuilder\States\FormStateTrait; class FormController extends Controller { use FormStateTrait; /* |-------------------------------------------------------------------------- | Welcome Controller |-------------------------------------------------------------------------- | | This controller renders the "marketing page" for the application and | is configured to only allow guests. Like most of the other sample | controllers, you are free to modify or remove it as you desire. | */ /** * Create a new controller instance. * * @return void */ public function __construct(\App\User $model, UserForm $form) { $this->model = $model; $this->form = $form; } /** * Show the application welcome screen to the user. * * @return Response */ public function getIndex() { return $this->getEdit(); } }
I add the controller on the route file :
Route::controllers([ 'form' => 'FormController' ]);
Like you can see I inject the model and the form on the constructor.
On the published template I add my style resources/views/vendor/form-builder/state/form.blade.php
<html> <head> <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.2/jquery.min.js"></script> <script src="//cdn.datatables.net/1.10.5/js/jquery.dataTables.min.js"></script> <!-- Latest compiled and minified CSS --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> <!-- Optional theme --> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap-theme.min.css"> <link rel="stylesheet" href="//cdn.datatables.net/1.10.5/css/jquery.dataTables.min.css"> <!-- Latest compiled and minified JavaScript --> <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/js/bootstrap.min.js"></script> <script src="/vendor/datatable-builder/js/datatable.js"></script> </head> <body> <div class="container"> <div class="row"> <div class="col-md-12"> @include('form-builder::form.partial.errors') <div class="tabbable tabbable-custom boxless tabbable-reversed "> @yield('form') </div> </div> </div> </div> </body> </html>
That it you have your form link to the user model.
##Troubleshooting
When you use the trait on your controller remove the route cache to be sure the routes are correctly generated.
php artisan route:cache && php artisan route:list