Mass-produce input forms quickly, elegantly, and wildly using fuelphp's fieldset

Hello.
I'm Mandai, the Wild Team member on the development team.
Our company often uses
FuelPHP In this article, I'd like to share the essence of Fieldset.
The version of FuelPHP we've tested is 1.7.2.

 

What is a fieldset?

I think fieldset is the poster child for rapid development in FuelPHP, but surprisingly, there are few Japanese articles that delve deeply into it.
There are plenty of articles that just say, "I tried it out."
Fieldset is a mechanism that creates a form with a single method based on the information defined in the model.
If you're using a web application that uses standard Twitter Bootstrap,
you can output the form without writing any HTML, if you ignore a few things.

Once you have carefully written the model, you can let fieldset take over and create it all in one go, so if you
have a lot of input screens and are due to miss the last train for a while, please consider this and find some time to spend with your family.

 

So let's write a model

When using fieldset, all the information required to generate a form is stored in $_properties.
You can write the input type as well as validation here, allowing you to centrally manage form information.
As an example, let's create a Model_Book class, using a model that assumes 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 fills in most of the necessary information.
The information is very compact and can be written in a structured array, which reduces mistakes and
makes it easy to correct later.
The standard validation rules are the Validation - Class - FuelPHP documentation, so let's use them gratefully.

Information about creating a form is written under the "form" key for each field.
"Type" corresponds to the type attribute of the input tag, and can be used in the same way as the commonly used text, select, hidden, checkbox, radio, etc., including the textarea tag.
For type="select", type="checkbox", and type="radio", you can define fixed values ​​by writing the option elements in 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);


 If you set the third argument of the set_options method to true, the array will be overwritten without being merged.
For now, you can simply enter only the unselected values ​​on the model side, and add other possible values ​​if there are any.

You can directly manipulate form elements with $form->field('type'), so as long as you remember this format,
you can control it in the controller even if you want to change the expression slightly only at certain times.
The methods that can be connected to $form->field('type') and manipulated in various ways are in fuel/core/classes/fieldset/field.php.
If you take a look at the methods, you should be able to find one that suits your purpose.

 

Create a form with a single method

Once the model is defined, all that's left is to make some fine adjustments in the controller and generate HTML.
What I mean by "fine adjustments" is that if nothing is done, a form will be created without a submit button.
You might think, "It's a form, so a submit button is essential," but I think this is unique
to FuelPHP, which has a different philosophy from CakePHP in that it doesn't interfere with unnecessary things.

$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, you pass the HTML for the form to the View.
If your controller inherits Controller_Template, you simply forge the View using the steps below and pass 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 to pass the information in the second argument to the view without escaping it.
It may not be a good idea to use it for security reasons, but there doesn't seem to be any other good way at the moment, so I'm using it this way.

 

I want to create a form that applies bootstrap beautifully

Earlier, I wrote that if you overlook a few things, you can create a better form,
but it's human nature to still want to make sure that it looks good. Right?
By default, a form with labels and input elements arranged in a table will be displayed, 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, here is an example of a template config file modified to apply Horizontal form

<?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, with a mixture of \t and \n, but it is not relevant to the screen display, so there is no problem if you omit it. It just makes the HTML source a little harder to read.
Also, there are some items in the default core/config/form.php that are not in app/config/form.php, but these are merged and the default settings are used, so there is no problem.

You can also use AdminLTE , which is derived from Twitter bootstrap,

 

How much labor can actually be reduced?

First of all, as a prerequisite, the database design must be completed (to some extent), which is clear from the perspective of concentrating information in the model.
When creating a screen using a fieldset, it is impossible to proceed with implementation in parallel with the database design, so you should focus on finalizing the database design first.

Also, if you proceed while researching the mechanisms of fieldset and best practices, it will cost you money in the beginning, but because you have carefully considered it, the speed at which you get used to it in the latter half will be so dramatically fast that it will be almost sensual.
In fact, I ended up leaving a lot of the planned man-hours behind, and am now writing this blog in the spare time (which is coming soon).

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

 
That's it.

If you found this article helpful , please give it a like!
1
Loading...
1 vote, average: 1.00 / 11
3,111
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 I'm also fortunate to be able to do a lot of other work, including marketing.
Furthermore, my portrait rights in Beyond are treated as CC0 by him.