2011
19
Sep

Custom CodeIgniter Validation Methods

CodeIgniter’s Form Validation library isn’t bad. It takes a little getting use to but it can be very powerful. Most of the validation that comes with CodeIgniter are pretty much the only ones you will ever need. Every now and then you will need something else. Extending CodeIgniter libraries is very easy. These are some of the custom form validation I’ve had to do in previous projects

First of all, you have to create a library that will extend CodeIgniter’s library. Create a new file named MY_Form_validation.php and put it in the application/libraries/ directory. Here is what that library will look like:

<?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
 
class MY_Form_validation extends CI_Form_validation {
 
	protected $CI;
 
	function __construct()
	{
		parent::__construct();
 
		$this->CI =& get_instance();
	}
}

The first validation method I used for a project where the user would enter a fraction and I didn’t really want to convert a decimal to a fraction. So I just created a validation method.

/**
 * Fraction
 *
 * @access  public
 * @param   string  $str
 * @return  bool
 */
public function fraction($str)
{
	$this->CI->form_validation->set_message('fraction', 'The %s field must be a valid fraction.');
 
	return ( ! preg_match("/^(\d++(?! */))? *-? *(?:(\d+) */ *(\d+))?.*$/", $str)) ? FALSE : TRUE;
}

Usage example:

$this->form_validation->set_rules('screen_size', 'Screen Size', 'fraction');

This was a fun validation to make. When a user signs up or is changing their password, their password must be PCI compliant. For a password to be PCI compliant, it must meet the following:
1) Must be between 6 and 99 characters in length
2) Must not contain two consecutively repeating characters
3) Must contain at least one upper-case letter
4) Must contain at least one lower-case letter
5) Must contain at least one number
6) Must contain at least one special character

Please note that the validation message is really long. You may want to simplify it a little before using it

/**
 * PCI compliance password
 *
 * @access  public
 * @param   $str
 * @return  bool
 */
public function pci_password($str)
{
	$special = '!@#$%*-_=+.';
 
	$this->CI->form_validation->set_message('pci_password', 'For PCI compliance, %s must be between 6 and 99 characters in length, must not contain two consecutively repeating characters, contain at least one upper-case letter, at least one lower-case letter, at least one number, and at least one special character ('.$special.')');
 
	return (preg_match('/^(?=^.{6,99}$)(?=.*[0-9])(?=.*[A-Z])(?=.*[a-z])(?=.*['.$special.'])(?!.*?(.)\1{1,})^.*$/', $str)) ? TRUE : FALSE;
}

Usage example:

$this->form_validation->set_rules('password', 'Password', 'pci_password');

CodeIgniter already has a validation method for required. But I needed one to be required if a different field had a value. A couple weeks later I needed to have method to make a field required if another field had a certain value.

/**
 * Required if another field has a value (related fields) or if a field has a certain value
 *
 * @access  public
 * @param   string  $str
 * @param   string  $field
 * @return  bool
 */
public function required_if($str, $field)
{
	list($fld, $val) = explode(',', $field, 2);
 
	$this->CI->form_validation->set_message('required_if', 'The %s field is required.');
 
	// $fld is filled out
	if (isset($_POST[$fld]))
	{
		// Must have specific value
		if ($val)
		{
			// Not the specific value we are looking for
			if ($_POST[$fld] == $val AND ! $str)
			{
				return FALSE;
			}
		}
 
		return TRUE;
	}
 
	return FALSE;
}

Usage example:

$this->form_validation->set_rules('bar', 'Bar', 'required_if[foo]'); // required if field 'foo' has a value
$this->form_validation->set_rules('foobar', 'Foo Bar', 'required_if[foo,bar]'); // required if field 'foo' has a value of 'bar'

There are a lot of examples of this validation floating around. It requires that a value being passed has a unique value in the database. This one also takes into account the database prefix.

/**
 * Unique
 *
 * @access	public
 * @param	string
 * @param	field
 * @return	bool
 */
public function unique($str, $field)
{
	list($table, $column) = explode(',', $field, 2);
 
	$this->CI->form_validation->set_message('unique', 'The %s that you requested is already in use.');
 
	$query = $this->CI->db->query("SELECT COUNT(*) AS dupe FROM {$this->CI->db->dbprefix($table)} WHERE {$column} = '{$str}'");
	$row = $query->row();
 
	return ($row->dupe > 0) ? FALSE : TRUE;
}

Usage example:

$this->form_validation->set_rules('email', 'Email', 'unique[DBTABLE,DBFIELD]');	// DBTABLE is the database table and DBFIELD is the database field to validation against

I took the previous validation a step further so that the field value must be unique in the database EXCEPT for a certain record ID. This one is really good for if you have an existing user in the system and they are updating their profile. You want to make sure that their email address is unique. If you run the previous unique validation it will fail because the email address is already in use by the user currently being edited. So we need to exclude that users ID from the validation. This one also takes into account the database prefix.

/**
 * Unique except. Check if a specific value is in use except when the value is attached to a specific row ID
 *
 * @param	string
 * @param	field
 * @return	bool
 */
public function unique_exclude($str, $field)
{
	list($table, $column, $fld, $id) = explode(',', $field, 4);
 
	$this->CI->form_validation->set_message('unique_exclude', 'The %s that you requested is already in use.');
 
	$query = $this->CI->db->query("SELECT COUNT(*) AS dupe FROM {$this->CI->db->dbprefix($table)} WHERE {$column} = '$str' AND {$fld} <> {$id}");
	$row = $query->row();
 
	return ($row->dupe > 0) ? FALSE : TRUE;
}

Usage example:

$this->form_validation->set_rules('email', 'Email', 'unique[DBTABLE,DBFIELD,IDFIELD,ID]');	// DBTABLE is the database table, DBFIELD is the database field, IDFIELD is the primary key for the table, and ID is the unique ID for the user

12 Responses to Custom CodeIgniter Validation Methods

  1. stickgrinder says:

    Very nice post! :) It’s worth noting that you don’t have to extend the class to use any function/helper if takes just one parameter. Just put it in validation chain:

    $this->form_validation->set_rules(‘field’, ‘Field’, ‘rtrim|is_array’);

    Moreover, if you feel the need to implement custom validation method, you could do it in the controller and specify a callback validation, this way

    $this->form_validation->set_rules(‘email’, ‘E-Mail address’, ‘trim|callback_check_if_mail_is_there’);

    // in the same controller
    function check_if_mail_is_there($str):
    {
    // do stuff
    }

    This is not a great place to store validation IMHO, since it should be in the model. That’s why I use a MY_Form_validation too, to support model-based callbacks in the form

    $this->form_validation->set_rules(‘email’, ‘E-Mail address’, ‘trim|callback_check_if_mail_is_there[mail_model]‘);

    Bye! :)

  2. Abu Najiyah says:

    very nice, thanks

  3. Daniel says:

    Nice.. But I don’t think your required_if() method will work because of an unfortunate characteristic of CI’s form validator. For example:

    $this->form_validation->set_rules(‘bar’, ‘Bar’, ‘required_if[foo]‘);

    If the “foo” field has a value, then we would want the “bar” field to be required. However, if the “bar” field has no value, the required_if() method is never run :-( If it’s not marked as the native “required” and doesn’t have a value, only “callback_”-style functions will run on the field. I think it requires overloading one or more of the native CI functions, that’s what I’m working on right now so please correct me if I’m wrong :(

  4. Daniel says:

    Here’s what I have for required_if() … I can’t think of many other ways to go about it :-( Notice it also requires the overloaded set_rules() where it simply swaps out for a native “required” rule where applicable.

    public function required_if( $str, $condition ) {
    if ( preg_match( ‘/^(.+)=(.*)$/’, $condition, $match ) ) {
    $field = $match[1];
    $req_value = (string)$match[2];
    } else {
    $field = $condition;
    $req_value = null;
    }

    $field_value = isset( $_POST[$field] ) ? (string)$_POST[$field] : null;

    if ( ( ! is_null( $req_value ) && $req_value === $field_value ) || ( is_null( $req_value ) && ! is_null( $field_value ) ) ) {
    $this->set_message( ‘required_if’, $this->CI->lang->line( ‘form_validation_required’ ) );
    return $this->required( $str );
    }

    return true;
    }

    public function set_rules( $field, $label = ”, $rules = ” ) {
    parent::set_rules( $field, $label, $rules );

    foreach ($this->_field_data as $field => $row) {
    if ( empty( $row['rules'] ) ) continue;

    if ( preg_match(‘/(^|\|)required_if\[([^]]*)\]($|\|)/’, $row['rules'], $match ) ) {
    $postdata = !empty( $_POST[$field] ) ? $_POST[$field] : ”;
    if ( ! $this->required_if( $postdata, $match[2] ) ) {
    $replace = $match[1] . ‘required’ . $match[3];
    $this->_field_data[$field]['rules'] = trim( str_replace( $match[0], $replace, $row['rules'] ), ‘|’ );
    }
    }
    }
    }

  5. Daniel says:

    UPDATE: My functions aren’t that great, they had just been written and not tested but will probably post improved versions… Coming up with something kinda neat…


    Testing code formatting

  6. Daniel says:

    Okay, the working validators are here: http://pastie.org/private/yq98wrbyvsimswn1fjgr8a

    As you’ll see, I’ve created “hooks” into the native CI functions to allow custom rules to be run once those native CI functions are done. As an added bonus, the required_if rule can contain conditions like “required_if[enabled=1]” or “required_if[enabled>=1]“. Also contains some various other rules.

  7. George says:

    Thank you so much for writing the PCI compliant password validator. One minor point though. In codeigniter, to get the validation callback to properly function, you need “callback_” in front of your validation calls:

    $this->form_validation->set_rules(‘password’, ‘Password’, ‘callback_pci_password’);

    Otherwise, the validation will not call the function at all.

    You rock!

    George

  8. Shaan says:

    Make the call back like this : callback__pci_password. With double underscores the function name _pci_password will not be available for a call directly via the controller this is important from a security standpoint.

  9. Nitin says:

    Hi, your post was posted in “2011″, but it was extremely helpful to me now even in 2014. I experienced trouble in making the codeigniter accept the custom library and your code worked flawless!! Great work man! I have improved your functions and added some more of my custom functions too.

    Cheers and thanks again!

    Nitin :)

  10. jose says:

    your post 2011 , help me 2016.
    Cheers and thanks man!!!!

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>