Validation

Add and remove rules, load values into form and validate, validate raw values against form rules.

## Adding rules When adding rules to a field, you can either: 1. Add a rule to a field using `Formo::add_rule()` 2. Add a bunch of rules to a bunch of different fields using `Formo::add_rules()` To add a single rule, you can pass a Kohana Validation rule definition as a single array (similar to `Validation::rules()`): ``` $form->email ->add_rule(['not_empty']) ->add_rule(['email']); $form->confirm ->add_rule(['matches', [':form_val', 'password', 'confirm']]); ``` or as separate `rule` and `params` arguments (similar to `Validation::rule()`): ``` $form->email ->add_rule('not_empty') ->add_rule('email'); $form->confirm ->add_rule('matches', [':form_val', 'password', 'confirm']); ``` To add more than one field at a time, pass an array of Validation rule definition arrays to `Formo::add_rules()`: ``` $form->email->add_rules([ ['not_empty'], ['email'], ]); ``` You can also attach multiple rules for multiple fields using `Formo::add_rules_fields()` on an individual field. To do this, either specify the field's alias as the key or the special token `:self`. ``` $form->add_rules_fields([ 'email' => [ ['not_empty'], ['email'], ], 'password' => [ ['not_empty'] ], 'confirm' => [ ['matches', [':form_val', 'password', 'confirm']] ] ]); ``` Also, you can add a single rule across a set of fields using `Formo::add_rule_fields()`. ``` $rule = ['not_empty']; $form->add_rule_fields($rule, ['email', 'name', 'password']); ``` For both `Formo::add_rule_fields()` and `Foro::add_rules_fields` you can use the `*` syntax to specify a rule for all fields. ``` $form->add_rule_fields(['not_empty'], ['*']); ``` ``` $form->add_rules_fields([ '*' => [ ['not_empty'], ['another_rule'], ], ]); ## Removing rules Removing rules works much like adding rules. You reference the rule you are removing by exactly matching the rule's first parameter (which is the function call). Here are some examples of removing rules. ``` $form->email->add_rule(['not_empty']); $form->email->remove_rule('not_empty'); $form->confirm->add_rule(['matches', [':form_val', 'password', 'confirm']]); $form->confirm->remove_rule('matches'); $form->add_rule([ 'foo' => [ [[$obj, 'bar'], [':value', $other, $other2]] ] ]); $form->remove_rule([$obj, 'bar']); $form->add_rule([ 'foo1' => [ ['not_empty'] ], 'foo2' => [ ['not_empty'] ], 'foo3' => [ ['not_empty'] ], ]); $form->remove_rule([ 'foo1' => [ 'not_empty', ], 'foo2' => [ 'not_empty', ], 'foo3' => [ 'not_empty', ], ]); ``` ## Setting rules directly You can always set a field's rules directly using `Formo::set()`. ``` $form->email->set('rules', [ ['not_empty'], ['email'], ]); ``` Similarly, you can use this method to clear all rules from a field. ``` $form->email->set('rules', []); ``` ## Getting rules You can get all a field's rules using `Formo::get()`. ``` $rules = $form->email->get('rules'); ```
Usually when you validate a form, you will follow a linear process: 1. Create form 2. Load new values from $_POST data 3. Validate against the form with its new values You can do this easily using `Formo::load()` and `Formo::validate()`: ``` $form = Formo::form(); // Add stuff to form if ($form->load($_POST)->validate()) { echo ‘You did it!’; } ``` What this is saying is: “Update all field values from the `$_POST` array, and check if the form validates with those values.” Formo first checks whether the form appears to be sent at all, and returns `FALSE` if it wasn’t sent. If you don’t specify an array in `Formo::load()`, Formo will load values from `Request::$current->post()` by default. Thus, this is the most common syntax for validation: ``` if ($form->load()->validate()) { // Passed! } ``` One important thing to note is that, in most cases, `Formo::load()` loads only what's passed to it and not necessarily what's defined in the form. This means that if you have a field defined in the form but nothing present in the passed array, the value will remain how you set it. This is typically a desired behavior, as it allows you to set and maintain a default value for the field. However, it can sometimes be an issue. Here's a relevant example using CSRF tokens: ``` $csrf = Security::token(); $form = Formo::form(); // Add stuff meaningful stuff to form, then: ->add('csrf', 'input|hidden', $csrf); $form->csrf ->add_rule(['equals', [':value', $csrf]]); if ($form->load()->validate()) { // Passed... but perhaps not securely. } ``` If the user sends a $_POST that has an invalid CSRF token, this will correctly fail validation. However, if the user sends a $_POST with no CSRF set at all, it will pass. To override this default behavior, set the `can_be_empty` parameter. This tells Formo that the contents should be loaded even if they're empty, and is enabled by default for `checkbox` and `checkboxes` drivers. Here's our example slightly reworked: ``` $csrf = Security::token(); $form = Formo::form(); // Add stuff meaningful stuff to form, then: ->add('csrf', 'input|hidden', $csrf); $form->csrf ->add_rule(['equals', [':value', $csrf]]) ->set('can_be_empty', true); if ($form->load()->validate()) { // Passed securely! } ```
Errors exist on fields that don't pass validation. These errors are initially stored as raw Kohana error information and are usually translated in to usable language using Kohana's message files. ## Retrieving errors You can retrieve errors two ways: 1. `Formo::error()` 2. `Formo::errors()` The `error()` method returns either an error message string for that field, or `FALSE` if the field doesn't have an error. The `errors()` method returns an array of formatted error strings for all the fields within that form or subform. When you use this method, the field's own error message exists the `array[':self']` key. ``` $form->rule(array('my_function')) ->add('first_name') ->add('last_name'); $form->load()->validate(); $error = $form->error(); $errors_array = $form->errors(); ``` ## Setting errors You can set a field's error message, use `Formo::error($msg)`; ``` $form->first_name->error('Should be a first name'); ``` ## Error message file Errors are passed through a Kohana message file. The name of the file is contained in the Formo config file. By default, it's `messages/validation.php`. See Kohana's documentation about how to use Kohana message files.
Sometimes you may want to check whether a form or field validates without actually loading a new value into the form or field. You can do this by passing an array of `$alias => $value` of field values you want to validate against. Here’s an example of validating a form without loading any values. The errors returned is an array for a form or group ``` $values = [‘first’ => ‘Billy’, ‘last’ => null, ‘email’ => ‘billy@madison.com’]; // First create a validation object with the values you want to validate against $validation = $form->validation($values); // You can do anything with the validation object, it’s just a regular Kohana Validation object if ( ! $validation->check()) { // Passed validation } else { // Turn errors into strings $errors = $form->validation_errors($validation); } ``` You can also validate a single field this way. The error returned is a string ``` $validation = $form->email->validation([‘email’ => ‘john@doe.com’]); if ( ! $error = $form->email->validation_errors($validation)) { } ```
The following special parameters and values are bound to a validation object for extensive validation.
Parameter Value
`:value` Field's value
`:formo` The formo field object
`:form_val` The parent's value (array)
`:form` The parent formo object
## Binding examples Here's an example of using `in_array` as a rule: ``` $form->add_rule(['in_array', [':value', [$bar, $bar2, $bar3]]]); ``` Here's a common one. This is uses Kohana's built-in `Valid::matches()` rule: ``` $form ->add([ 'alias' => 'password', 'driver' => 'input|password', ]) ->add([ 'alias' => 'confirm', 'driver' => 'input|password', 'rules' => [ ['matches, [':form_val', 'password', 'confirm']] ] ]); ```
Formo supports field filters either as defined by Kohana's orm or as anonymous functions. When you use Kohana style filters, you can specify `:value` as a parameter, and the field's value will be passed into the filter function. Some things to know: - If no parameters are specified, `:value` will be the sole value passed into the filter function - If you use an anonymous function as a filter, only the field's value is passed into that function Here are some examples of using both anonymous function filters and filters that are compatible with Kohana's ORM filters. ``` $form = Formo::form() ->add([ 'alias' => 'email', 'driver' => 'input|email', 'attr.placeholder' => 'john@doe.com', 'filters' => [ ['strtolower'] ] ]) ->add([ 'alias' => 'foo', 'filters' => [ ['bar', [$foobar, ':value']] ] ]) ->add([ 'alias' => 'foo2', 'filters' => [ function($val) { return foobar($val); } ] ]); ```
You can attach 'pass' and 'fail' callbacks to fields and forms. Callbacks are anonymous or named functions that run in the order they are added to the field or form. The field or form object — whichever object contains the callback — is the single value passed into the callback function. ## Adding pass callbacks These callbacks run on the field passing ``` $form->email->callback('pass', function($field) { // Do something }); $form->callback([ 'email' => [ 'pass' => [ function($field) { // Do something }, function($field) { // Do something else }, 'Foo::add_email' // Call Foo::add_email($field) ] ] ]); ``` ## Adding fail callbacks These callbacks run on the field failing ``` $form->email->callback('fail', function($field) { // Do something }); $form->callback([ 'email' => [ 'fail' => [ function($field) { // Do something }, function($field) { // Do something else } ] ] ]); ``` ## Form-level callbacks You can also attach callbacks to form or group. These will run under after validation and callbacks run on all their contained fields. That means, a form or group's 'pass' callbacks only run after every field passes validation and also has run their respective validation. Likewise the form or group's 'fail' callbacks run after all fields finish validating and running their respective callbacks and at least one field failed validation. Thus, you can have scenarios where individual fields would run their attached 'pass' callbacks, but the parent form or subform would run its 'fail' callbacks. ``` $form->callback([ ':self' => [ 'fail' => function($field) { } ], 'email' => [ 'pass' => function($field) { } ], ]); ``` ## Setting errors in the callback Since you have access to the field object, you can set errors to the field ``` $form->email->callback('pass', function($field) { if ($foo) { $field->error($new_error); } }); ```
comments powered by Disqus