TIP: Use Markdown or, <pre> for multi line code blocks / <code> for inline code.
These forums are read-only and for archival purposes only!
Please join our new forums at discourse.kohanaframework.org
Kohana 2.2 New Validation Part One
  • Kohana 2.2 introduces a new (better) way of handling input validation. You will use it primarily for validating form input, but we are not restricted to forms.

    First up, there is a helper, called valid which provides the commonest types of checks, like email, url, alpha, numeric etc. The helper may be used in any context to check if input data is correctly formatted. Usage: Call the helper and expect boolean TRUE or FALSE returned.

    $is_ok = valid::credit_card('4111111111111111');
    

    Next up, there is the Validation library, which can be used to validate input from any source that is an array It works as follows:

    1. Instantiate the Library, passing the array to be validated [[ $validation = new Validation($array_to_validate) ]]
    2. Define (optional) filters to apply to the input data [[ $validation->pre_filter('trim', TRUE); ]]
    3. Define rules for validating individual input elements [[ $validation->add_rules('input_field','required', 'length[3,20]', 'alpha'); ]]
    4. Define (optional) methods as callbacks for individual input elements [[ $validation->add_callbacks('password', array($this, 'pwd_check')); ]]
    5. Call the validate method, to apply the rules, filters and callbacks, expect a boolean FALSE if any rule test fails, otherwise expect TRUE
    6. Access validation errors returned via the [[ $validation->errors() ]] method

    The Validation library is aware of arrays, but it knows nothing about forms per se, it knows about validating and returning errors.

    Our commonest workflow is this: Display a form, validate the input, display any errors, re-populate the form fields, and repeat until success. Lets do it: (Note, I am doing everything here in the controller, using the form helper, to demonstrate better what happens, part two will show how to use Views) Our form has four fields:

    1. name Is required, must be only alphabetic, and has a length limit
    2. number Is required, must be numerals, and has a length limit
    3. password Is required, must match a hard coded password
    4. code Is not required, but if supplied, must be exactly three numerals

    The example code is shown in the following post.

  • The controller/method is welcome/testform

    public function testform()
    {
                // setup and initialize your form field names
        $form = array
        (
            'name'      => '',
            'number'    => '',
            'password'  => '',
            'code'      => '',
        );
    
        //  copy the form as errors, so the errors will be stored with keys corresponding to the form field names
        $errors = $form;
    
            // check, has the form been submitted, if so, setup validation
        if ($_POST)
        {
                    // Instantiate Validation, use $post, so we don't overwrite $_POST fields with our own things
            $post = new Validation($_POST);
    
             //  Add some filters
            $post->pre_filter('trim', TRUE);
            $post->pre_filter('ucfirst', 'name');
    
                     // Add some rules, the input field, followed by a list of checks, carried out in order
            $post->add_rules('name','required', 'length[3,20]', 'alpha');
            $post->add_rules('number', 'required', 'numeric', 'length[3,5]');
            $post->add_rules('password', 'required');
    
                     // We can write the rules with different syntax, in a line, or individually
            $post->add_rules('code',array('valid', 'numeric'));
            $post->add_rules('code','length[3]');
    
                   // Add a callback, to validate the password. This is here a method declared in the same controller
            $post->add_callbacks('password', array($this, 'pwd_check'));
    
                     // Test to see if things passed the rule checks
            if ($post->validate())
            {
                         // Yes! everything is valid
                echo 'Form validated and submitted correctly. <br />';
                // ok, do whatever ...
                die(html::anchor('welcome/testform', 'try it again'));
            }
                        // No! We have validation errors, we need to show the form again, with the errors
            else
            {
                // repopulate the form fields
                $form = arr::overwrite($form, $post->as_array());
    
                // populate the error fields, if any
                               // We need to already have created an error message file, for Kohana to use
                               // Pass the error message file name to the errors() method
                $errors = arr::overwrite($errors, $post->errors('form_error_messages'));
            }
        }
        // Display the Form, if there are any errors, they are displayed next to the input field
        echo form::open();
        echo form::label('name', 'Your Name');
        echo form::input('name', ($form['name']));
        echo (empty ($errors['name'])) ? '' : $errors['name'];
        echo '<br />';
        echo form::label('number', 'Your Number');
        echo form::input('number', $form['number']);
        echo (empty ($errors['number'])) ? '' : $errors['number'];
        echo '<br />';
        echo form::label('password', 'Password');
        echo form::input('password', $form['password']);
        echo (empty ($errors['password'])) ? '' : $errors['password'];
        echo '<br />';
        echo form::label('code', 'Your code');
        echo form::input('code', $form['code']);
        echo (empty ($errors['code'])) ? '' : $errors['code'];
        echo '<br />';
        echo form::submit('submit', 'Send');
        echo '<br />';
                echo form::close();
    }
    

    Edit: Fixed an error: Thanks Hans for picking that one up, and also for clarity on the callback precedence.

  • In the same controller, we must add the callback method. It does not have to be called callback_ The first parameter passed is the Validation object (here $post)

    Edit: Thx Hans for pointing this out

    Rules validation may encounter validation errors and add the messages to the error array. but validation does not stop here Subsequently, if you have a callback defined, it will apply any checks you defined, to the unvalidated data To avoid using possibly invalid data in your your callback, do the following Always check for errors from rules on the field in your callback, and return immediately if found.

    public function pwd_check(Validation $post)
    {
        // If add->rules validation found any errors, get me out of here!
        if (array_key_exists('password', $post->errors()))
            return;
    
        // only valid password is '123'
        if ($post->password != '123')
        {
            // Add a validation error, this will cause $post->validate() to return FALSE
            $post->add_error( 'password', 'pwd_check');
        }
    }
    

    Finally, we need to set up our error messages to display something useful. You will need to create an error messages file in application/i18n Create this as application/i18n/en_US/form_error_messages.php

    <?php defined('SYSPATH') or die('No direct access allowed.');
    
    $lang = array
    (
    'name' => Array 
        ( 
            'required' => 'The name cannot be blank.', 
            'alpha' => 'Only alphabetic characters are allowed.',
            'length' => 'The name must be between three and twenty letters.',
            'default' => 'Invalid Input.',
        ),
    'number' => Array 
        ( 
            'required' => 'The number cannot be blank.', 
            'numeric' => 'Only numbers are allowed.',
            'length' => 'The number must be between three and five numerals.',
            'default' => 'Invalid Input.', 
        ),
    'code' => Array 
        ( 
            'numeric' => 'Only numbers are allowed.',
            'length' => 'The code must be exactly three numerals.',
            'default' => 'Invalid Input.', 
        ),
    'password' => Array 
        ( 
            'required' => 'You must supply a password.',
            'pwd_check' => 'The password is not correct.',
            'default' => 'Invalid Input.', 
        ),
    );
    

    Try it out, and please note something important: The form helper automatically escapes input (using htmlspecialchars) Try removing the alpha check on name field and entering your name as **

    MyName

    ** Look at the page source in the browser. Be careful what you put into your databases!!

  • thanks for the info Ozoned. nice post.
  • Very nice write-up Ozoned, just the kind of information nubs like me are looking for.
  • Looks really good. Especially the way error messages are dealt with. Being able to define messages per field - per error really is what we need. Of course the lazy part of me (admittedly a very large part of my total being) wonders if you *have* to define messages for your errors or will the system still generate any which you don't override?
  • The system will no longer provide generic messages for validation. The rationale is that this will strongly encourage the developer to provide the end user with meaningful and specific error messages. See this post Edited: Thx Henrik :-)

  • Doesn't the word force contradict the stated Kohana aim of 'convention over configuration'? (besides being kinda irritating)

    Could default messages include the validation rules to give the user a clue? Then the developer could substitute friendlier messages over time...

    Just a thought.

    • H
  • $validation->errors() without a given message file will output the rule, ie add->rules('field', 'required') will give this default error message 'required'

    Sometimes Kohana way may seem awkward at first, but after using it for a bit, it becomes second nature ;)

  • That looks fine. Just a question of documentation, then. Hopefully the documentation won't use the word force :-)

  • Would strongly encourages do?

  • Yes, totally. I think that promotion of good practice is a good thing. Just that perception of force without default options gets people's backs up (and is inconvenient during development).

    • H
  • Ok, I am having some serious issues with displaying errors using the new validation in Kohana 2.2. I would just like to post the errors one after the other as I did in the old Kohana. Here is how I used to do it. I'd set the style of the errors like so:
    $validation->error_format('{message}
    ');

    Next I would pass the errors to the view page if there were any:
    $view->error_string = ($validation->error_string) ? ''.$validation->error_string.'' : '';

    Then I would have this code on the view page to display the errors one after the other:


    This no longer works and I cannot figure out how to get it to work. Can anyone help? Thanks a lot.

    Also I even tried it using this guide's code, still with no luck what-so-ever:
    [CONTROLLER] $view->errors = $validation->errors('error_messages');
    [VIEW]
    It gives an "Undefined index: username" error.
  • This is something I have done to display errors in order:

    // Controller
    $this->errors = $validation->errors('my_error_lang_file');
    
    // View
    <?php foreach($this->errors as $error): ?>
    <div class="error"><?=$error;?></div>
    <?php endforeach; ?>
    
  • Awesome intro to the new library. Looking forward to seeing how you implement Views in part II. :)
  • Using the example code from this thread, I'm seeing 'The password is not correct' when the password field is blank rather than 'You must supply a password'. Do callbacks block other rules defined for the same element?
  • @ paradoxic: Pretty sure the undefined index error is a slight bug in the example. Fix it by changing:

    $form = arr::overwrite($post->as_array());
    ...
    $errors = arr::overwrite($post->errors('form_error_messages'));
    

    to:

    $form = arr::overwrite($form, $post->as_array());
    ...
    $errors = arr::overwrite($errors, $post->errors('form_error_messages'));
    
  • Something to be aware of with callbacks... they appear to be called even when other rules don't validate. Thus, if you query a database from within your callback (e.g. to check for a name/password), your queries will execute even if other validation rules suggest the input data is invalid.

    So, to be clear, even if you've add rules so that your 'name' field is 'required', 'length[3,20]', 'alpha'; your database could receive a blank string, a string less than 3 characters, a string greater than 20 characters, a string with values other than alpha characters, etc.

    If this isn't what you're expecting, you'll want to wait to query the database until after $post->validate() == TRUE.

  • See ticket 762 for more information.

  • // We can write the rules with different syntax, in a line, or individually

    $post->add_rules('code',array('valid', 'numeric'));

    It doesn't work for me with an array, like above. 'validation.rule not callable' error occurs.


    When I run it with foreach, for every rule separatelly, it's fine.

  • Someone please rewrite this article, Kohana is running an enormous risk of losing potential adopters of its framework when simple tutorials, such as Validation, are not clearly written.
  • @Ozoned: There's a couple lines here that have been puzzling me, and I haven't been able to figure them out:

    $form = arr::overwrite($form, $post->as_array());
    

    Why do you do this? If you run $post->validate(), it always provides empty values for any field that has a rule or callback. Is there some benefit I'm not seeing to having the $form variable? Couldn't you just use this instead:

    $post = Validation::factory(.....);
    if ($post->validate())
    {
        // Do whatever
    }
    
    $form = $post->as_array();
    

    That also makes me wonder why you use the $form variable at all, because you can access $post['key'] just like $form['key']. Could you explain the rationale?

  • I have a question about callbacks.

    Callbacks look like they have to be declared public but this leads to the issue that if the callback is within your controller it will become available via the URI? Is this correct?

    I have appended an underscore to the function - this can't be called via the URI?
  • You have to prepend an underscore. That should have the same effect as a non-public method.

  • Posted By: Geert De Deckere

    You have toprependan underscore. That should have the same effect as a non-public method.


    Yes sorry that's what I meant - appended to the start of the function. Bad English all round!
  • I think the reason for the $form variable is for populating the fields with default values -- i.e. the first time through, before the form has been submitted.
  • @bunnyhero: I think you must be right!

  • Using the first $form array to insert default values does work, but you need to 'clear' the $errors array after copying $form, otherwise you'll get error messages that contain the field values!
    The following works:

    $user = ORM::factory('user', $user_id);

    $form = array(
    'username' => $user->username,
    'firstname' => $user->firstname,
    'surname' => $user->surname,
    'email' => $user->email,
    'password' => null,
    'password_confirm' => null,
    );

    $errors = arr::clear($form);

    ~

    public static function clear($array)
    {
    foreach($array as $k=>$v)
    {
    $array[$k] = null;
    }
    return $array;
    }
  • Posted By: ghaez// We can write the rules with different syntax, in a line, or individually
    $post->add_rules('code',array('valid', 'numeric'));

    It doesn't work for me with an array, like above. 'validation.rule not callable' error occurs.

    When I run it with foreach, for every rule separatelly, it's fine.



    Working good:
    $vFormData->add_rules( 'nickname', 'required', 'alpha' );


    NOT Working!
    $vFormData->add_rules( 'nickname', array( 'required', 'alpha' ) );

    Why not?? I need this functionality!! :)
  • After the last trunk update, add_rules is not working any more for me.
    They just get ignored, the form always validate.
  • Another question on callbacks....

    Can you pass parameters to callbacks? i.e. _callback_function(Validation $_POST, $type)
    How would this be called? i.e. $_POST->add_callbacks('field', array($this, '_callback_function'))

    Thanks!
  • @NickUK: Callbacks can't have parameters.

  • Ah thanks Shadowhand. I thought there might be as there was an args variable in Validation.php around the call_user_func calls.
  • What to do to validate utf8 characters with this example? When I'm typing łóążźć then get always error.

  • @cieply: You should be able to use alpha[utf8], in this doesn't actually work because of the way that rule arguments work. Please report a bug on Trac.

    One work around would be to create a valid:: class extension and do something like this:

    public static function alpha($str, $utf8 = TRUE)
    {
        return parent::alpha($str, $utf8);
    }
    

    This just overloads the alpha method to use UTF-8 by default.

  • @Shadowhand: This just overloads the alpha method to use UTF-8 by default.

    I've created file MY_vaild.php in application/helpers directory with content:


    class valid extends valid_Core {

    public static function alpha($str, $utf8 = TRUE)
    {
    return parent::alpha($str, $utf8);
    }

    }


    And I stiil get error message.
  • I'm sorry, that should be:

    return parent::alpha($str, TRUE);
    
  • It works, thanks very much :)
  • Will it be corrected that it uses utf8 in default while validating?
  • Why would that be a correction? Why dig out old threads?

Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

In this Discussion