Form basics

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: 1. Form object containing: 1. input named ‘first’ 2. input named ‘last’ 3. input named ‘email’ with ‘type=“email”’ and validation rule that entered text is a valid email 4. textare named ‘notes’ 5. input named ‘save’ with ‘type=“submit”’ and value ‘Save’ 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: 1. The field alias 2. Driver name 3. Default value 4. Array of other attributes and options ## Field creation with an array 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](./validation). ### Loading posted 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. ### Validation 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`. ## Render a field 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(); ```
## Setting stuff 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’, ], ]); ``` ## Setting extra variables 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); ``` ## Setting the value You can set a field's value two ways ``` $form->val($new_val); $form->set('val', $new_val); $form->add([ ‘alias’ => ‘foo’, ‘val’ => $value, ]); ``` ## Setting using array path 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`. ## Setting stuff for a bunch of fields at once. 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' ] ]); ``` ## Getting stuff **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: 1. The property you are retrieving 2. The default value if the property isn’t found. ## Retrieving custom variables 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. ## Getting the value You can set a field's value two ways ``` $value = $form->val(); $value = $form->get('val'); ``` ## Getting using array path 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: - Field alias - New order (`int`, `'before'`, or `'after'`) - Relative field (default `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. ## How to add a subform 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 and validation Subforms can still validate separately from the rest of the form. ``` $form->add($subform); if ($form->load()->validate()) { } elseif ($form->subform->validate()) { } ``` ## Create a subform on the fly from existing fields 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: - Group alias - Fields to add to group (array) - Order for new subform to be placed (array, default `NULL`) - Subform's driver (default `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. ### Form driver Forms begin and end with a `
` 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()`. ### Input driver 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. ### Group driver Used for subforms. The `group` acts like the `form` driver without the opening and closing form tags. ### Button driver Used for `
Form and field objects have HTML helper methods that make working with objects as html simpler. ### add_class() 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']); ``` ### attr() 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'); ``` ### attr_fields() Set attributes for a set of fields ``` $form->attr_fields([ 'email' => [ 'placeholder' => 'john@doe.com', ], 'first_name' => [ 'disabled' => true', 'style' => 'background: purple', ], ]); ``` ### close(); Return the field's closing tag ``` echo $form->close(); ``` ### html() Get or set HTML part of the tag ``` $button->html('Click me to submit'); $html = $button->html(); ``` ### label() Get field's label ``` $label = $field->label(); ``` ### name() Return a field's HTML 'name' tag value ``` $name = $field->name(); ``` ### open() Return a field's HTML opening tag ``` echo $form->open(); ``` ### remove_class() 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']); ```
## How Formo looks for the config setting For flexibility, when Formo retrieves a config setting, it follows this pattern looking for the setting: 1. Field itself 2. Field's parent 3. Inside the Formo `config.php` file Here'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' ``` ## Config setting explanations
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 `` and set to `FALSE` you get the HTML 5 styled ``
`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 ``
`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. ### ftype config format 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. ### Field 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'); ``` ### Driver 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]); ``` ### Subform 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 Rules are validation rules ### Filters Filters are run on field values before validation
comments powered by Disqus