Validate

Note: In Kohana 3.1, Validation is handled differently, and the Validate class has been replaced with the Validation class.

The Validate class is used to validate any array of data following given rules. It is mainly used to validate forms $_POST data. The class has a few rules already available to us and allows user made callback functions. The class enables to affect each field of your array its own error message that you can then populate with your forms.

Filters are processed first, then rules, and finally callbacks.

Getting started

Load the Library

// create a new Validate object using the $_POST variable
$post = new Validate($_POST);
 
// combine different arrays
$post = new Validate(array_merge($_POST, $_FILES));
 
// using the factory enables method chaining
$post = Validate::factory($_POST)->rule('field_name', 'not_empty')
                                 ->rule('field_name2', 'email');
 
// add an array of rules
$rules = array(
 
                  'field_name' => array
                  (
                      'not_empty'   => NULL,
                      'max_length'  => array(32),
                  ),
 
                  'field_name2' => array
                  (
                      'min_length'  => array(4),
                  ),
 
               );
$post = Validate::factory($_POST)->rules('field_name', $rules['field_name'])
                                 ->rules('field_name2', $rules['field_name2']);
 
// you can also use the $_POST array directly (not recommended)
$_POST = new Validate($_POST);

Adding rules (required)

After creating a Validate object you need to add some rules to it. Some of them are defined by the class itself.

Example: These are all equivalent:

$post = new Validate($_POST);
$post->rule('username', 'not_empty')
     ->rule('password', 'not_empty')
     ->rule('password', 'min_length', array(5))
     ->rule('password', 'max_length', array(42));
 
 
$post = Validate::factory($_POST)->rule('username', 'not_empty')
                                 ->rules(
                                           'password', array(
                                                              'min_length' => array(5),
                                                              'max_length' => array(42),
                                                           )
                                         );

Rules are callbacks to functions and work like them but they return a boolean so they can validate the passed data. We can use pre-defined rules by the class, php, or our own rules.

Pre-defined function:

$post = Validate::factory($_POST)->rule('age', 'numeric');

PHP function:

$post = Validate::factory($_POST)->rule('age', 'is_numeric');

User defined function:

$post = Validate::factory($_POST)->rule('age', 'Model_User::check_numeric');
 
	public static function check_numeric($str)
	{
		// Get the decimal point for the current locale
		list($decimal) = array_values(localeconv());
 
		return (bool) preg_match('/^-?[0-9'.$decimal.']++$/D', (string) $str);
	}

Adding a rule to all fields

The same rule can be defined to all fields at once by using TRUE as the field name:

//apply the not_empty rule to all fields in $_POST
$post = Validate::factory($_POST)->rule(TRUE, 'not_empty');

Adding filters (optional)

Filters are processed before validation. You can apply the filter to all fields by using TRUE for the first parameter. Valid filters are any PHP functions that return a string.

You can also pass an array of parameters to the function with the optional third parameter. The functions you used must receive the value to the first parameter of the function call.

Note that Version 3 of Kohana does not have the pre_filter() and post_filter() any longer. This means that you will use filter() for pre-filtering and, if necessary, callbacks for post-filtering.

You can add a filter to all fields by using TRUE as a field name.

// trim username for white spaces
$post = Validate::factory($_POST)->filter('username', 'trim');
 
// pass parameters to the filter function.
// will call: htmlspecialchars($_POST['username'], ENT_QUOTES)
$post = Validate::factory($_POST)->filter('username', 'htmlspecialchars', array(ENT_QUOTES));
 
// use a static defined function for filtering all fields
$post = Validate::factory($_POST)->filter(TRUE, 'Model_User::myfilter', array($param1, $param2));
    class Model_User extends Model_Auth_User {
        public static function myfilter($value, $param1, $param2)
        {     
            $value = ... //some code// ...
 
            return $value;
        }
    }
 
 
// use filters() to defined an array of filters
$post = Validate::factory($_POST)->filters(
                                           'username' => array(
                                                              'Model_User::myfilter' => array($param1, $param2),
                                                              'trim' => NULL,
                                                           )
                                         );

Adding callbacks (optional)

Unlike rules, callbacks are used to do some more complex checks on fields and if necessary add errors to Validate object passed as the first argument.

You can add a callback to all fields by using TRUE as a field name.

Calling callback() method:

User defined function

// call user defined function named reserved_username
$post = Validate::factory($_POST)->callback( 'username', 'reserved_username');

static method as callback

$post = Validate::factory($_POST)->callback('username', 'Model_User::mystatic_callback');

object's method as callback

$post = Validate::factory($_POST)->callback( 'username', array($this, 'username_available'));

Array of callbacks with callbacks()

$post = Validate::factory($_POST)->callbacks( 'username', array(
                                                                 'reserved_username'
                                                                 'Model_User::mystatic_callback',
                                                                  array($this, 'username_available'),
                                                           )
                                            );

Examples:

// Validate our $_POST data using a email_change() callback that checks whether the user 
// can change its email address if the given one is not currently already used by an other user.
$post = Validate::factory($_POST)->callback('email', array($this, 'email_change'));
 
	/**
	 * Check if user can change its email to this one
	 *
	 * @param    Validate  $array   validate object
	 * @param    string    $field   field name
	 */
	public function email_change(Validate $array, $field)
	{
		$exists = (bool) DB::select(array('COUNT("*")', 'total_count'))
						->from($this->_table_name)
						->where('email',   '=',   $array[$field])
						->where('id',     '!=',   $this->id)
						->execute($this->_db)
						->get('total_count');
 
		if ($exists)
			$array->error($field, 'email_change', array($array[$field]));
	}

Checking

Validating is done by the check() method. It first process the filters, then the rules and last callbacks.

If it encounters any errors on an input field, it adds the field name as an array key to the Validation errors array.

If any error was found, boolean FALSE is returned. if there are no errors, returns TRUE.

        #Check that all fields are valid.
        if ($post->check())
	{
		 echo 'No validation errors found ';			
	}
	else
	{
                #Affects errors for further display
		$this->errors = $post->errors('register');
	}

Adding Errors

You can add errors with the error() method:

error($field, $error, array $params = NULL)

Example:

$post->error('username', 'username_available', array('johndoe'));

Define Error Messages

Internationalization files can be found in the messages directories. These directories can be found in system, application or modules directories. Kohana's own messages files can be found in the system directory.

The default key will be used when the error key is not found in the array.

Example: application/messages/register.php

<?php defined('SYSPATH') OR die('No direct access allowed.'); 
 
 return array 
( 
	'email' => array(
		'email_available'    => 'The email address already exists',
                'default'  => 'Invalid Input.',
	),
 
	'username' => array(
		'username_available'    => 'The username already exists',
                'default'  => 'Invalid Input.',
	),
 
); // end of application/messages/register.php

The error messages can be automatically translated if their value exists in your i18n directory. See Translate messages.

Retrieve Error Messages

Error messages are retrieved with the errors() method. By default an array is returned, with the field name as key, and the defined rule as value.

To retrieve customized error messages, an error messages file must be passed to the errors() method.

$errors = $validation->errors();

Assuming one rule defined, rule('username', 'username_available') $errors array contains:

array(
       ('username' => 'username_available')
      )
Fetch errors using an error messages file:
$errors = $validation->errors('register')

Assuming a register.php file exists in your messages directory and containing as the one written above. Then $errors will contain:

array(
       ('username' => 'The username already exists')
      )

Retrieve Input Data

Validation input data is accessible via the as_array() method. This is very useful for re-populating form fields, for example:

$_POST = array_intersect_key( $post->as_array(), $_POST);

Rules

Rules Specific to Validate Class

Rule Parameter Description Example
not_empty No Returns FALSE if form field is empty
min_length Yes Returns FALSE if the field is too short length[5] - minimum 5 characters long
max_length Yes Returns FALSE if the field is too long length[30] - maximum 30 characters long
exact_length Yes Returns FALSE if the field is too short or too long length[25] - 25 characters only
matches Yes Returns FALSE if field does not match field(s) in parameter matches[password_again]
date No Returns FALSE if form field is not a valid date
regex Yes Returns FALSE if form field does not fulfill the regular expression regex[expression] - regular expression to match (including delimiters)
email Optional Returns FALSE if email is not valid mail[TRUE] - rfc822 strict
email_domain No Returns FALSE if domain of an email does not have valid MX record
url No Returns FALSE if url is not valid
ip Optional Returns FALSE if ip is not valid ip[TRUE] - allow private IP networks
credit_card Yes Returns FALSE if credit card is not valid credit_card[mastercard] - card type, or an array of card types
phone Optional Returns FALSE if phone number is not a valid length phone[7,10,11,14] - either 7, 10, 11 or 14 digits long (default is 7, 10 and 11)
alpha Optional Returns FALSE if form field does not consist only of alphabetical characters only alpha[TRUE] - trigger UTF-8 compatibility
alpha_numeric Optional Returns FALSE if form field does not consist only of alphabetical or numeric characters alpha_numeric[TRUE] - trigger UTF-8 compatibility
alpha_dash Optional Returns FALSE if form field does not consist only of alphabetical, numeric, underscore and dash characters alpha_dash[TRUE] - trigger UTF-8 compatibility
digit Optional Returns FALSE if form field does not consist only of digit characters (no dots or dashes). digit[TRUE] - trigger UTF-8 compatibility
numeric No Returns FALSE if form field is not a valid number (positive, negative or decimal)
decimal Optional Returns FALSE if form field is not in proper decimal format
Optional parameter is for a specific decimal format
decimal - is any valid decimal format
decimal[4,2] - is 4 digits and 2 decimal places
range Yes Returns FALSE if form field is not withing the range min and max range[1,10] - between 1 and 10 include
color No Returns FALSE if form field is not proper hexadecimal HTML color value


Examples

Building and Validating a Form

//validate and check a user registration
	function action_register()
	{
			#Instantiate a new user
			$user = ORM::factory('user');	
 
			#Load the validation rules, filters and callbacks
			$post = $user->validate_create($_POST);
 
                        #Check that all fields are valid.
			if ($post->check())
			{
				#Affects the sanitized vars to the user object
				$user->values($post);
 
				#create the account
				$user->save();
 
				#Add the login role to the user
				$login_role = new Model_Role(array('name' =>'login'));
				$user->add('roles',$login_role);
 
				#sign the user in
				Auth::instance()->login($post['username'], $post['password']);
 
				#redirect to the user account
				Request::instance()->redirect('myaccount');
			}
			else
			{
                                #Repopulate $_POST data
                                $_POST = array_intersect_key( $post->as_array(), $_POST);
 
                                #Affects errors for further display
				$this->errors = $post->errors('register');
			}
           }

Our Model_User:

 
class Model_User extends Model_Auth_User {
	protected $_rules = array
	(
		'username'			=> array
		(
			'not_empty'		=> NULL,
			'min_length'		=> array(4),
			'max_length'		=> array(32),
			'regex'			=> array('/^[-\pL\pN_.]++$/uD'),
		),
		'password'			=> array
		(
			'not_empty'		=> NULL,
			'min_length'		=> array(5),
			'max_length'		=> array(42),
		),
		'password_confirm'	=> array
		(
			'matches'		=> array('password'),
		),
		'email'				=> array
		(
			'not_empty'		=> NULL,
			'min_length'		=> array(4),
			'max_length'		=> array(127),
			'validate::email'	=> NULL,
		),
	);
 
	protected $_callbacks = array
	(
		'username'			=> array('username_available'),
		'email'					=> array('email_available'),
	);
 
	public function validate_create($postvalues) 
	{
		// Initialise the validation library and setup some rules		
		$array = Validate::factory($postvalues)
						->rules('password', $this->_rules['password'])
						->rules('username', $this->_rules['username'])
						->rules('email', $this->_rules['email'])
						->rules('password_confirm', $this->_rules['password_confirm'])
						->filter('username', 'trim')
						->filter('email', 'trim')
						->filter('password', 'trim')
						->filter('password_confirm', 'trim');
 
 
                #Add Model_Auth_User callbacks
                foreach ($this->_callbacks as $field => $callbacks)
                {
		    foreach ($callbacks as $callback){
			$array->callback($field, array($this, $callback));
		    }
		}
 
		return $array;
	}
 
 
	public function validate_change($postvalues, $save = FALSE){
		// Initialise the validation library and setup some rules		
		$array = Validate::factory($postvalues)->rules('email', $this->_rules['email'])				
                                                  ->filter('email', 'trim')
						  ->filter('password', 'trim')
				                  ->callback('email', array($this, 'email_change'));
 
		if(trim($array['password']) != '')
			$array->rules('password', array('min_length'=> array(5), 'max_length'=>array(42)));
 
 
		return $array;		
	}	
 
 
	/**
	 * Check if user can change its email to this one
	 *
	 * @param    Validate  $array   validate object
	 * @param    string    $field   field name
	 */
	public function email_change(Validate $array, $field)
	{
		$exists = (bool) DB::select(array('COUNT("*")', 'total_count'))
						->from($this->_table_name)
						->where('email',   '=',   $array[$field])
						->where('id',     '!=',   $this->id)
						->execute($this->_db)
						->get('total_count');
 
		if ($exists)
			$array->error($field, 'email_change', array($array[$field]));
	}
 
	/**
	 * Does the reverse of unique_key_exists() by triggering error if username exists
	 * Validation Rule
	 *
	 * @param    Validate  $array   validate object
	 * @param    string    $field   field name
	 * @param    array     $errors  current validation errors
	 * @return   array
	 */
	public function username_available(Validate $array, $field)
	{
		if ($this->unique_key_exists($array[$field])) {
			$array->error($field, 'username_available', array($array[$field]));
		}
	}
 
	/**
	 * Does the reverse of unique_key_exists() by triggering error if email exists
	 * Validation Rule
	 *
	 * @param    Validate  $array   validate object
	 * @param    string    $field   field name
	 * @param    array     $errors  current validation errors
	 * @return   array
	 */
	public function email_available(Validate $array, $field)
	{
		if ($this->unique_key_exists($array[$field])) {
			$array->error($field, 'email_available', array($array[$field]));
		}
	}
 
 
	/**
	 * Tests if a unique key value exists in the database
	 *
	 * @param   mixed        value  the value to test
	 * @return  boolean
	 */
	public function unique_key_exists($value)
	{
		return (bool) DB::select(array('COUNT("*")', 'total_count'))
						->from($this->_table_name)
						->where($this->unique_key($value), '=', $value)
						->execute($this->_db)
						->get('total_count');
	}
 
	/**
	 * Allows a model use both email and username as unique identifiers for login
	 *
	 * @param  string    $value   unique value
	 * @return string             field name
	 */
	public function unique_key($value)
	{
		return Validate::email($value) ? 'email' : 'username';
	}
}

Validating File Uploads

Here is an example of validating file uploads.

    		$validate = Validate::factory($_FILES);
		$validate->rules('file',
			array('Upload::valid' => array(),
		              'Upload::not_empty' => array(), 
			      'Upload::type' =>array('Upload::type' => array('jpg','png','gif')), 
			      'Upload::size' => array('1M'))
		);
 
 
		if ($validate->check())
		{			
			//ok
			Upload::save($_FILES['file'],'my_image.png','./',0777);			
		}
		else
		{
			//error	
			$this->errors = $validate->errors('upload');
			print_r($this->errors);
		}

application/messages/upload.php

return array
(
	'file' => array(
	'Upload::valid'    	=> 'valid msg',
    	'Upload::not_empty'    	=> 'not_empty msg',
    	'Upload::type'    	=> 'type msg',
    	'Upload::size'    	=> 'size msg',
    	'default'  		=> 'default msg'),
);
validation.txt · Last modified: 2011/03/20 18:26 by veddermatic