Let's mass-produce input forms quickly, elegantly, and wildly with fuelphp's fieldset

Hello.
This is Mandai, the wild one on the development team.
At our company,FuelPHP, but we ran into a situation where we didn't have enough manpower to create forms one by one. So, we used fieldset to mass-produce them, and we were able to drastically reduce the amount of work required, which we are very happy about these days.
This time, I would like to share the essence of fieldset.
Please note that this has been confirmed with FuelPHP version 1.7.2.

 

What is a fieldset?

I think fieldset is the epitome of FuelPHP's rapid development capabilities, but surprisingly, there seem to be few in-depth articles in Japanese about it.
There are plenty of articles that just say, "I tried it out," though.
fieldset is a mechanism that creates a form with a single method call based on information defined in the model, and
if you're using a web application that uses standard Twitter Bootstrap,
you can output the form part without writing any HTML, if you overlook some minor details.

Once you've carefully written the model, you can then rely on the fieldset to create everything in one go. So, if you
have a lot of input screens to work on and your schedule is set to take you to the last train home for a while, please consider this option and hopefully it will help you find some time to spend with your family.

 

So let's write a model

When using fieldsets, all the information needed to generate a form is placed inside $_properties.
This includes input types and validation rules, allowing for centralized management of form information.
As an example, let's create a Model_Book class, which is based on a book registration screen.

The books table looks like this:

CREATE TABLE `books` ( `id` int(10) unsigned NOT NULL AUTO_INCREMENT, `title` varchar(512) NOT NULL, `author` varchar(256) NOT NULL, `price` int(10) unsigned NOT NULL, `isbn` varchar(13) DEFAULT '', `released_at` date DEFAULT '0000-00-00', `type` tinyint(2) DEFAULT NULL, `created_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', `updated_at` timestamp NOT NULL DEFAULT '0000-00-00 00:00:00', PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

 
Define the model for this books table as follows:

<?php // fuel/app/classes/model/book.php class Model_Book extends \Model\Orm { protected static $_connection = 'default'; protected static $_table_name = 'books'; protected static $_properties = [ 'id', 'title' => [ 'data_type' => 'varchar', 'label' => 'title', 'validation' => [ 'required', 'max_length' => [512], ], 'form' => [ 'type' => 'text', 'class' => 'form-control', ], ], 'author' => [ 'data_type' => 'varchar', 'label' => 'author', 'validation' => [ 'required', 'max_length' => [512], ], 'form' => [ 'type' => 'text', 'class' => 'form-control', ], ], 'price' => [ 'data_type' => 'int', 'label' => 'price', 'validation' => [ 'required', 'numeric_min' => [0], ], 'form' => [ 'type' => 'number', 'class' => 'form-control', 'min' => 0, ], ], 'isbn' => [ 'data_type' => 'varchar', 'label' => 'ISBN', 'validation' => [ 'match_pattern' => ['/^[0-9]{10}([0-9]{3})?$/'], ], 'form' => [ 'type' => 'text', 'class' => 'form-control', ], ], 'released_at' => [ 'data_type' => 'date', 'label' => 'issue date', 'validation' => [ 'valid_date' => [], ], 'form' => [ 'type' => 'text', 'class' => 'form-control datepicker', ], ], 'type' => [ 'date_type' => 'tinyint', 'label' => 'format', 'validation' => [ 'numeric_min' => [0], ], 'form' => [ 'type' => 'select', 'class' => 'form-control', 'options' => [ 0 => 'Unknown', 1 => 'A4 size', 2 => 'A5 size', 3 => 'A6 size', 4 => 'B4 size', 5 => 'B5 size', 6 => 'B6 size', 7 => 'Small B6 size', 8 => 'Kikube size', 9 => 'International size', 10 => 'AB size', 11 => 'Jubako version', 12 => 'Kiku size', 13 => 'Shiroku size', 14 => 'B40 size', 15 => 'Pocket book version', 16 => 'Sango size', 17 => 'Ochiori size', 18 => 'HL size', ], ], ], 'created_at' => [ 'form' => ['type' => false], ], 'updated_at' => [ 'form' => ['type' => false], ], ]; protected static $_observers = [ 'Orm\Observer_CreatedAt' => [ 'events' => ['before_insert'], 'mysql_timestamp' => true, ], 'Orm\Observer_UpdatedAt' => [ 'events' => ['before_update'], 'mysql_timestamp' => true, ], ]; }

 
This pretty much covers all the necessary information.
The information is very compactly organized and written in a structured array, which reduces the chances of errors and
makes later modifications easy.
The standard validation rules arethe Validation - Classes - FuelPHP documentation,so let's make good use of them.

Information regarding form creation is provided under the "form" key for each field.
"type" corresponds to the type attribute of the input tag, and you can use it in the same way as the text, select, hidden, checkbox, and radio tags you normally use, including the textarea tag.
For type="select", type="checkbox", and type="radio", you can define fixed values ​​by writing the option element as an array under "options".

Also, if the value is not fixed, you can set the data in options from the controller as follows

$form = \Fieldset::forge('default', [ 'form_attributes' => ['class' => 'form-horizontal'] ])->add_model('Model_Book'); $newspaper_types = [ 19 => 'Broadsheet', 20 => 'Nordic', 21 => 'Rhenish', 22 => 'Swiss (NZZ)', 23 => 'Berliner', 24 => 'Tabloid Extra', 25 => 'Half Swiss', 26 => 'Half Berliner', 27 => 'Half Rhenish', 28 => 'Half Broadsheet', 29 => 'Tabloid', ]; // Adding a field $form->field('type')->set_options($newspaper_types); // To replace fields $form->field('type')->set_options($newspaper_types, null, true);

 
Setting the third argument of the `set_options` method to `true` overwrites the array without merging it.
This makes it easy to initially input only the unselected values ​​in the model, and then add other possible values ​​if they are available.

You can directly manipulate form elements using `$form->field('type')`, so if you remember this form,
you can control it in the controller even if you only want to slightly change the appearance at certain times.
Methods that connect to `$form->field('type')` and perform various operations can be found in `fuel/core/classes/fieldset/field.php`.
If you look at the methods there as well, you should be able to find a method that suits your purpose.

 

Create a form with a single method

Once the model is defined, all that's left is to fine-tune it in the controller and generate the HTML.
What I mean by fine-tuning is adjusting the form, which would otherwise lack a submit button.
You might think, "It's a form, so a submit button is essential, right?", but
FuelPHP has a philosophy of not interfering unnecessarily, unlike CakePHP.

$form->add('submit', ' ', ['type'=>'submit', 'class'=>'btn btn-primary submit', 'value' => 'registration']);

 
However, since this is all there is to it, you can pretty much just copy and paste it

Finally, we pass the HTML for the form to the View.
In the case of a controller that inherits from Controller_Template, we simply forge the View using the following procedure and then feed in the output of the build method that assembles the form.

$this->template->content = View::forge('manager/master/form.php', $this->contents); $this->template->content->set_safe('form', $form->build());

 
FuelPHP automatically escapes strings passed to the view.
However, the `set_safe` method is used when you want to pass the information in the second argument to the view without escaping it.
From a security standpoint, it might be better not to use this method, but there doesn't seem to be a better way at the moment, so I'm doing it this way.

 

I want to create a form that applies bootstrap beautifully

Earlier, I wrote that you can create a slightly better form if you overlook some flaws,
but it's only natural to still want it to look good, right?!
By default, you'll probably see a form with labels and input elements arranged within a table, but you
can control this behavior by manipulating the template.

The default template is fuel/core/config/form.php, so copy this file to app/config/ and modify it

As an example,Horizontal formhere is an example of a template config file that has been modified to apply

<?php // fuel/app/config/form.php return [ 'form_template' =>"\n\t\t{open}\n{fields}\n\t\t{close}\n", 'field_template' => "\t\t<div class=\"form-group\"> \n\t\t\t{label}\n\t\t\t<div class=\"col-xs-3 {error_class}\"> {field} <span>{description}</span> {error_msg}</div> \n\t\t</div> \n", 'multi_field_template' => "\t\t<div class=\"form-group\"> \n\t\t\t <label class=\"col-xs-2 control-label\">{group_label}{required}</label> \n\t\t\t<div class=\"col-xs-3 {error_class}\"> {fields}\n\t\t\t\t<div> {field} {label}</div> {fields} <span>{description}</span> \t\t\t{error_msg}\n\t\t\t</div> \n\t\t</div> \n", 'label_class' => 'col-xs-2 control-label', 'group_label' => '{label}', ];

 
The format is difficult to understand due to the mix of characters like \t and \n, but they are irrelevant to the screen display, so you can omit them without any problems. It will only make the HTML source slightly harder to read.
Also, there are items that are in the default core/config/form.php but not in app/config/form.php, but these are merged and the default settings will be used, so there is no problem.

which is derived from Twitter Bootstrap,AdminLTEcan be used without any problems.

 

How much labor can actually be reduced?

First and foremost, it's clear that the database design needs to be (to some extent) complete, especially considering the need to centralize information in the model.
When creating screens using fieldsets, it's impossible to implement them in parallel with the database design, so you should focus on finalizing the database design first.

Also, while researching the fieldset mechanism and best practices did incur some costs in the beginning, the speed at which I finished in the later stages, once I got used to it after careful consideration, was so dramatically fast that it was almost sensual.
In fact, I finished with a lot of the planned work time remaining, and I'm writing this blog with the extra time (hey!).

If you haven't tried it yet, why not give it a try and experience how convenient it is?

 
That's all

If you found this article helpful,please give it a "Like"!
2
Loading...
2 votes, average: 1.00 / 12
3,227
X Facebook Hatena Bookmark pocket

The person who wrote this article

About the author

Yoichi Bandai

My main job is developing web APIs for social games, but thankfully I'm also given the opportunity to work on various other tasks, including marketing.
My image rights within Beyond are treated as CC0.