Learn the Formo ropes.
To create a form, use Formo::form()
:
$form = Formo::form();
You can also name your form by giving it an alias
:
$form = Formo::form(['alias' => 'myform']);
To add a field to the form, use Formo::add()
. Here’s a practical example:
$Form = Formo::form()
// The field added here is $form->first
->add(‘first’)
->add(‘last’, ‘input’)
->add(‘email’, ‘input|email’)
->add(‘notes’, ‘textarea’)
->add(‘save’, ‘input|submit’, ‘Save’);
This will create the following:
Every field has an alias
that the field is accessible by. Also, by default, the field name
is the same as its alias
(if no name is specified).
The second parameter for Formo::add()
is the field driver name. Drivers tell Formo how to process each field since different field types behave differently in HTML (think about ways a select field differs from a textarea, for instance).
Thus, when using Formo::add()
, you specify:
Though Formo::add()
supports separate parameters that define the new field added to its parent, you can also pass an array as a field construct. Here's an example:
$form->add([
'alias' => 'first_name',
'driver' => 'input',
'val' => NULL,
'rules' => [
['not_empty'],
],
'filters' => [
function(str) {
return trim($str);
}
],
]);
If array keys are not specified, Formo expects your array to be an exact representation of params for Formo::add()
. Thus, the following three examples are equivelent.
$form->add('email', 'input|email', NULL, ['attr' => ['placeholder' => 'john@doe.com']]);
$form->add([
'email',
'input|email',
NULL,
['attr' => [
'placeholder' => 'john@doe.com',
]]
]);
$form->add([
'alias' => 'email',
'driver' => 'input|email',
'val' => NULL,
'attr' => [
'placeholder' => 'john@doe.com',
]
]);
For more detailed instructions on validation, see the full validation documentation.
The Formo::load()
method tells the form that whatever array is passed as a parameter is what was posted. If no parameter is specified, Formo will use Request::$current->post()
as the posted array.
To validate your form, use Formo::validate()
. This returns TRUE
or FALSE
whether the form passed validation rules or not.
Here’s a practical validation example:
if ($form->load()->validate())
{
}
What this is saying is, if the form loaded with Request::$current->post()
passed validation rules attached to the form.
To render your form using the built-in html templates, use Formo::render()
:
echo $formo->render();
Note that a Formo
object's __toString()
method also runs $form->render()
.
echo $form;
There are three variables Formo uses to decide which view file to use as a field's template:
template_dir
template
template_ext
The default template_dir
is defined in the Formo config.php file.
The template
variable is defined per-driver (and can also be customized per field or form). When the field renders, Formo finds a view file named $template_dir/$template
.
The template_ext
is defined in the Formo config.php file, and can optionally add an extension to the template filenames. This is in addition to the extension Kohana adds, defined by the EXT
constant (typically php
). By default this is set to FALSE, so Formo will search for template filenames like form_template.php
. If set to html
, for example, it will search for filenames like form_template.html.php
.
You can also render an individual field.
To render a full individual field including label:
echo $form->$field->render();
Or you can render just the input part of the Formo field using Formo::input()
:
echo $form->$field->input();
In a Formo object, you cannot directly access object properties except for fields. So, if you have a form object with a first_name
field and want to set its label (which is the $form->_label
protected property), you have to use the Formo::set()
method.
It’s simple. Do $form->set($property_name, $value)
:
$form->first_name->set(‘label’, ‘First Name’);
You can also set an array of stuff. When you pass an array of stuff, you need to specify the alias for each group of values you are setting.
$form->set([
‘first_name’ => [
‘label’ => ‘First Name’,
],
‘last_name’ => [
‘label’ => ‘Last Name’,
],
]);
You can set any variable to any form object whenever you need. Let’s say you have a special variable named $foo
you need to pass into your form. No problem:
$form->first_name->set(‘foo’, $bar);
You can set a field's value two ways
$form->val($new_val);
$form->set('val', $new_val);
$form->add([
‘alias’ => ‘foo’,
‘val’ => $value,
]);
One awesome thing you can do is directly set a property inside an array using Kohana’s Arr::path
method.
In this example, I am setting a custom config parameter for label_message_file
for a particular form.
$form = Formo::form()
->set(‘config.label_message_file’, $new_file);
That sets $form->_config['label_message_file'] = $new_file
.
Use Formo::set_fields()
for setting variables for a bunch of fields at once.
Note that Formo::set_fields()
does not throw an exception if a field doesn't exist in the form object. It is intended to be used in general setup methods to define a bunch of defaults, like in Model::formo()
for instance.
$form->set_fields([
'email' => [
'driver' => 'input|email',
'rules' => [
['unique_email']
],
'attr.placeholder' => 'john@doe.com',
],
'foo' => [
'driver' => 'select',
'rules' [
['bar', ':model']
],
'attr.placeholder' => 'foobar',
'attr.class' => 'myclass',
].
]);
By default, Formo::set_fields()
does not recursively look for fields to set options across. You can search recursively by passing FALSE
as the second parameter:
$form->set_fields([
'email' => [
'driver' => 'input|email',
'rules' => [
['unique_email']
],
'attr.placeholder' => 'john@doe.com',
],
'foo' => [
'driver' => 'select',
'rules' [
['bar', ':model']
],
'attr.placeholder' => 'foobar',
'attr.class' => 'myclass',
].
], FALSE);
You can set for all fields using the *
syntax:
$form->set_fields([
'*' => [
'attr.class' => 'form-control'
]
]);
The only properties you can access directly in a Formo object are fields. Thus, if you have a field named first_name
and want to retrieve the Formo::$_label
variable, you cannot use $form->first_name->_label
. Instead you need to use Formo::get()
.
Formo::get($property, [$default = null])
takes two parameters:
You retrieve custom variables the same as any others. Let’s say you passed a $foo
variable into the first_name
field:
$form->first_name->set(‘foo’, $bar);
$foo_value = $form->first_name->get(‘foo’);
That’s it.
You can set a field's value two ways
$value = $form->val();
$value = $form->get('val');
Formo lets you get a property using Kohana’s Arr::path()
method.
Here’s an example of retrieving $form->_config[‘label_message_file’]
(which is a protected property) in one fell swoop:
$file = $form->get(‘config.label_message_file’);
Sometimes you don’t want to just set a variable value, but rather you need to merge an array of values with a new array of values.
For instance, a field’s filters are an array of filters inside a field.
Let’s say I created a form from a model, but I want to add a new filter to the bunch.
It’s dirt-simple using Formo::merge()
.
$user = ORM::factory(‘User’, $id);
$other_email_filters = [
‘remove_fugly_chars’,
];
$form = $user->get_form();
$form->email
->merge(‘filters’, $other_email_filters);
You can also pass an array of stuff into Formo::merge_fields()
and merge all sorts of stuff at once.
$form->merge_fields([
‘:self’ => [
‘config’ => $new_config_values,
],
‘email’ => [
‘filters’ => $new_filters,
‘rules’ => $new_rules,
‘callbacks’ => $new_callbacks,
],
‘first_name’ => [
‘rules’ => $new_rules,
],
‘hobbies’ => [
‘opts’ => $new_opts,
],
]);
Use Formo::order()
to reorder field and subform elements. The arguments:
int
, 'before'
, or 'after'
)NULL
)If you know the numeric order the field should be in, leave the relative field param empty and specify the direct order of the field (starting with 0).
$form
->add('confirm_password')
->add('password')
->add('last_name')
->add('first_name');
$form->order([
'first_name' => [0],
'password' => ['after', 'first_name'],
'last_name' => ['before', 'password'],
]);
$form->order('confirm_password', 'after', 'password');
// The order of fields afterwords
// 'first_name', 'last_name', 'password', 'confirm_password'
A subform is a form within a form.
Formo supports deep form objects and allows as many forms to exist within other forms as necessary.
Only the parent-most form will have the form
driver. Others added to it will be converted to group
by default.
Since a subform is just a form within a form, it's creation is like a form. Then it's added to a parent form
$address = Formo::form(['alias' => 'address'])
->add('street')
->add('city')
->add('zip');
$user_form = Formo::form()
->add('first name')
->add('last name')
->add($address);
In the example above, the address form is added to $user_form after last_name. It is given the alias address and is using the driver group to handle it.
If you wanted to access street, to set its value for instance, you could do this:
$user_form->address->street->val();
Since PHP passes objects by reference, you could access the same value from the orinal subform object too:
$address->street->val();
Subforms can still validate separately from the rest of the form.
$form->add($subform);
if ($form->load()->validate())
{
}
elseif ($form->subform->validate())
{
}
You may want to namespace a part of your form or turn a few fields into a subform on the fly. There are many reasons you may want to do this, but one is to allow the "group" or other specific driver for formatting reasons.
Doing this always appends your subform to the bottom of its parent.
To do this, use Formo::subform()
. The arguments are:
NULL
)group
)$form = Formo::form()
->add('username')
->add('email')
->add('password')
->add('street')
->add('city')
->add('state');
$form->subform('address', ['street', 'city', 'state'], ['before', 'username']);
You can also specify a numeric order for the subform
$form->subform('address', ['street', 'city', 'state'], [1]);
You can reset any form or field's values using the Formo::reset()
method.
This recursively resets any field values within the field also.
// Reset entire form's values recursively
$form->reset();
// Reset individual field
$form->first_name->reset();
Formo fields use static drivers to handle all field-specific functionality. Here is a list of the default drivers that ship with Formo.
Forms begin and end with a <form>
tag. There can only be one Formo object with a form
driver inside a Formo object. Thus subforms are converted by default to the group
driver when added to a form as a subform.
A field with the form
driver is returned from Formo::form()
.
The input driver is the default Formo field driver. If you add a field without a driver specified, it will be a field with the input
driver.
Since input is used for so many types of field in HTML, Formo allows a short syntax for specifying an input
field with a special type
tag. Don't use these with types checkbox
or radio
as they function much different from regular input type=text
fields.
Used for subforms. The group
acts like the form
driver without the opening and closing form tags.
Used for <button>
Don't forget to set the field's html
that goes inside the </button><button>
tags.
For single <input type="checkbox">
For multiple checkboxes
Used for HTML5 datalists
For file uploads
For single <input type="radio">
For multiple radios
For select dropdowns
For textarea fields
Form and field objects have HTML helper methods that make working with objects as html simpler.
Add a class or classes to a field/form object
$form->add_class('new-class');
$form->add_class('new-class1 new-class2');
$form->add_class(['new-class1', 'new-class2']);
Get or set html tag attribute
$field->attr('placeholder', 'john@doe.com');
$field->attr([
'placeholder' => 'john@doe.com',
'disabled' => true,
'style' => 'width: 300px',
]);
$placeholder = $field->attr('placeholder');
Set attributes for a set of fields
$form->attr_fields([
'email' => [
'placeholder' => 'john@doe.com',
],
'first_name' => [
'disabled' => true',
'style' => 'background: purple',
],
]);
Return the field's closing tag
echo $form->close();
Get or set HTML part of the tag
$button->html('Click me to submit');
$html = $button->html();
Get field's label
$label = $field->label();
Return a field's HTML 'name' tag value
$name = $field->name();
Return a field's HTML opening tag
echo $form->open();
Remove class or classes from a field/form object
$form->removeClass('foo');
$form->removeClass('foo1 foo2');
$form->removeClass(['foo1', 'foo2']); ```
Formo gives a convenient way to convert your form and field definitions to an array, recursively.
You can use this to pass around entire form definitions that you can use to create entirely new forms, send to your client, or whatever you want.
To cast a form to an array, use Formo::to_array()
:
$array = $form->to_array();
You can do the same thing for just a field or subform:
$array1 = $form->email->to_array();
$array2 = $form->my_subform->to_array();
You can also specify which attributes you want to pull out of each object:
$array = $form->to_array(['alias', 'driver', 'rules', 'val']);
For flexibility, when Formo retrieves a config setting, it follows this pattern looking for the setting:
config.php
fileHere's a rather complicated example:
$form = Formo::form()
->add('first_name', 'input', NULL, ['label_message_file' => 'mymessages2']);
$form2 = Formo::form([
'alias' => 'form2',
'label_message_file' => 'mymessages3',
])
->add('last_name', 'input', NULL, ['label_message_file' => FALSE])
->add('middle_initial', 'input', NULL);
$form->add($form2);
Based on the form definition above, here are what each field would use for its label messages file:
$form - Kohana::$config->load('formo.label_message_file');
$form->first_name - mymessages2
$form2 - 'mymessages3'
$form2->last_name - FALSE
$form2->middle_initial - 'mymessages3'
Variable | Explanation |
---|---|
label_message_file |
The file used for label messages. You can set this to FALSE to not use Kohana messages at all. |
validation_message_file |
The file used for Kohana validation messages. |
translate |
Set to TRUE or FALSE whether to run labels and errors through Kohana's translation __() function. |
close_single_html_tags |
Set to TRUE or FALSE whether to add a closing / to single tags. For instance, when TRUE , you would get <input type="text"> and set to FALSE you get the HTML 5 styled <input type="text"> |
auto_id |
Set to TRUE or FALSE whether fields should that don't have manually specified ID tags set will auto-generate them. |
template_dir |
The directory to look for template view files |
namespaces |
Boolean setting whether to namespace field names. For example <input type="text" name="parent_alias[field_alias]"> |
orm_driver |
The driver to use for your ORM |
model_base_rules |
When using ORM driver, whether to automatically add mysql field base rules to formo field |
input_rules |
These are pre-defined rules that get added to input elements with HTML 5 type attributes that are supposed to have built-in validation. These pre-defined rules are supposed to mimic those that are built into the browser with these type tags. |
ftype |
Pre-defined defaults for forms and fields. See ftype docs |
You may want to create a set of default form and field definitions that get reused everywhere. For instance, you may want to add a class attribute to all your inputs to match Twitter Bootstrap or another css framework markup.
Whatever the reason, it's simple to pre-define form defaults.
The format of the ftype
config param looks like this:
'ftype' => [
// This applies to all forms that don't match an ftype in this config array
':all' => [
':self' => [
'attr.class' => 'someclass'
],
// These apply to fields with specific drivers that are within the form
'driver' => [
'input' => [
'attr.class' => 'form-control',
],
'input|email' => [
'attr.class' = 'form-control',
]
'textarea' => [
'attr.class' => 'form-control',
],
],
// These apply to fields based on the ftype. (This overrides the driver-based definition)
'ftype' => [
'special' => [
'attr.class' => 'special',
'rules' => $rules_array,
],
],
],
// This refers to a parent with the ftype 'inline'
'inline' = [
// The form default settings with the ftype 'inline'
':self' => [
'attr.class' => 'form-horizontal',
],
// Look for fields based on drivers within the form with ftype 'inline'
'driver' => [
'input' => [
'attr.class' => 'form-control',
],
],
// Look for fields based on ftype within the form with ftype 'inline'
'ftype' => [
'street' => [
'attr.class' => 'special-street-class',
'rules' => $street_rules_array,
],
],
],
]
Formo allows a config option called ftype
to determine which pre-defined attributes to load into your form and field objects. These definitions look for form and field ftype
to decide whether the options appy to that field. Consider the following form and subform.
To once your form is set up, you can inject all these definitions into your form object with the method Formo::ftype()
.
$form = Formo::form([
'alias' => 'myform'
])
->add('first', 'input')
->add('email', 'input|email')
->add('notes', 'textarea');
$form->address = Formo::form([
'ftype' => 'inline',
])
->add([
'alias' => 'street',
'ftype' => 'street',
])
->add('city')
->add('state', 'select', $states);
$form->ftype();
That's a typical form definition. There's a form and a subform. The suborm has the ftype
of address
and its street.
In the form above, the parent form matches the ':all' ftype definitions, so the appropriate defaults are applied within.
The subform 'address' matches the 'inline' ftype definitions, so the appropriate definitions are all from within that. The field 'street' matches the 'street' ftype within the 'inline' ftype, so it is used instead of the 'input' driver definition.
Formo's ftype
offers a powerful way to create reusable, base configs for all your forms and subforms.
While using Formo, it's important to understand Formo's terminology.
A field is a form field attached to a form. You attach fields using Formo::add()
. Fields are accessed directly from its form.
In this example, email
is the field's alias, and it can be accessed as $form->email
.
$form->add('email', 'input|email');
The driver handles field-specific functionality. For instance, a checkbox
field functions totally different from an input type="text"
field.
Formo drivers handle every field-specific function from setting the value correctly to getting the field's label.
In this example, the driver is select
, and a select
driver requires an extra array of opts
(options).
$form->add('hobbies', 'select', NULL, ['opts' => $options]);
A group of fields added to a form.
In this example, $f2
is the subform.
$form = Formo::form();
$f2 = Formo::form(['alias' => 'mysubform']);
$form->add($f2);
Rules are validation rules
Filters are run on field values before validation