Scott Nellé.com

Extending CodeIgniter’s Validation Library To Check For Unique Values

Updated November 4, 2008: This extension has been updated slightly to work with CI’s new form validation class, added with version 1.7.0. If you’re using an older version of CI you should still use the extension on this page. If you’re using 1.7.0 or later, you should check out the updated extension.

I’m working on small web application and I’m building it with CodeIgniter. Like most web applications, mine requires user registration and login. And like most login systems, mine is happiest if each user has a unique username. While the CodeIgniter validation library is pretty robust, it doesn’t come with a function for checking a value to see if it is unique or if it already exists in the database. Fortunately there are a couple of ways to remedy that.

The first way is outlined in the documentation for the validation library under the heading “Callbacks: Your own Validation Functions.” I tried this and it works well, but it requires you to write the functions in your controller or model. I thought that looked messy and I wanted something a little more streamlined, so I decided to extend the validation library with my own function. Extending CodeIgniter’s libraries is pretty easy. Here’s what I came up with–I’ll explain the important lines afterward:

Updated September 12, 2008: Made some changes based on suggestions from the super-helpful CodeIgniter community. Thanks guys!


<?php if (!defined('BASEPATH')) exit('No direct script access allowed');
/**
 * MY_Validation Class
 *
 * Extends Validation library
 *
 * Adds one validation rule, "unique" and accepts a
 * parameter, the name of the table and column that
 * you are checking, specified in the forum table.column
 */
class MY_Validation extends CI_Validation {

	function My_Validation()
	{
	    parent::CI_Validation();
	}

	// --------------------------------------------------------------------

	/**
	 * Unique
	 *
	 * @access    public
	 * @param    string
	 * @param    field
	 * @return    bool
	 */
	function unique($str, $field)
	{
		$CI =& get_instance();
		list($table, $column) = split("\.", $field, 2);

		$CI->validation->set_message('unique', 'The %s that you requested is unavailable.');

		$query = $CI->db->query("SELECT COUNT(*) dupe FROM $table WHERE $column = '$str'");
		$row = $query->row();
		return ($row->dupe > 0) ? FALSE : TRUE;
	}
}
?>

Save the above code as system/application/libraries/MY_Validation.php and you’re ready to go. You can now add the “unique” rule to your existing validation, like so:


$rules['username'] = "required|min_length[5]|unique[users.username]";
$rules['emailaddress'] = "required|valid_email|unique[users.email]";

This will check the values that entered for username and emailaddress to make sure they don’t already exist in the username and email fields in your table called users. The general format for the rule, then, is : unique[table.column]. This matches the SQL syntax for specifying fields when field names are ambiguous. Pretty cool.

So here’s what’s going on in each of the important lines.


$CI =& get_instance();

This creates a reference to the CodeIgniter super object and allows you to use it’s functions in your library. See the user guide for an explanation (under the heading “Utilizing CodeIgniter Resources within Your Library.”)


list($table, $column) = split("\.", $field, 2);

This line parses out the table name and column name. Note that it only returns 2 strings. This way you can specify something like unique[users.email.address], which would check the users table for a column called email.address. In mysql periods are allowed in field names but not table names, and the validation rule reflects that.


$CI->validation->set_message('unique', 'The %s that you requested is unavailable.');

This line sets the error message in case the value isn’t unique. %s will be replaced by the field description you enter in your validation fields. This would be better off in a language file, but for ease of use I’ve stuck it right in the library. CodeIgniter allows you to replace entire language files but not simply extend them with a single line. If that changes in the future I’ll revisit my decision.


$query = $CI->db->query("SELECT COUNT(*) dupe FROM $table WHERE $column = '$str'");
$row = $query->row();

Here’s where it checks the database to make sure your field is unique. In the query I assigned the row count to the keyword “dupe” (for duplicate.) I wanted to use “exists” or “match” but both are protected words in mysql. Oh well. Note the use of $CI->db->query instead of $this->db->query. That’s explained in the documentation link earlier in this post.


return ($row->dupe > 0) ? FALSE : TRUE;

Finally, this line returns the result of the validation check. In case you’re not accustomed to using ternary operators, here it is as an if/else:


if ($row->dupe > 0)
{
	return FALSE; // a match was found, validation fails
}
else
{
	return TRUE; // value is unique, validation passed
}

So there you have it. A quick extension to CodeIgniter’s validation library that allows you to check your database for unique values.

15 Responses to “Extending CodeIgniter’s Validation Library To Check For Unique Values”

  1. Diego Says:

    I love the simplicity of your extension. Does the new class need to be loaded with $this->load->library? I followed your directions but did not see any affect. Any help? Thanks

  2. Scott Nellé Says:

    You need to load the base validation library ($this->load->library('validation');) but you shouldn’t need to do anything special to load the extension as long as you name the file correctly. The extension should be saved as system/application/libraries/MY_Validation.php, unless you’ve changed the class extension prefix. You can find that in config.php. It’s set to “MY_” by default. Hope you’re able to get it working.

  3. libre Says:

    Hi,
    I find a different way to set the error message:

    $language = $CI->config->item(‘language’);
    $CI->validation->set_message($language,’unique’);

    And set a variable in validator_lang file:

    $lang['unique']= “The %s that you requested is unavailable.”;

    It will make this validator more adaptable to other languages and make it usable for multi-language apps.

    For the new form validation in 1.7.0 this code will work fine if you replace:

    MY_Validation with MY_Form_validation,
    CI_Validation with CI_Form_validation,
    and $CI->validation with $CI->form_validation

    Thanks for this tutorial.
    Keep the good work.

  4. Scott Nellé Says:

    Thanks libre. I’ve meant to look at the new form validation class but I haven’t had a chance yet. It sounds like it’s pretty easy to adapt.

  5. libre Says:

    A small fix in my comment.
    Only with the changes I discribed above the form validation will not work with the rules in a config file.

    It will need a $rules array parameter to run like this:

    function My_Form_validation($rules = array())
    {
    parent::CI_Form_validation($rules);
    }

  6. tresloukadu Says:

    Hello, how unique function would work for an edit form? Because I need to send the id to check too.

  7. Scott Nellé Says:

    I’m not sure this is the right extension for checking the ID of something while editing, because I imagine in that case you would want the ID to match an existing record, and this extension will throw an error if it exists. Let me know if I’m not following you correctly.

    You could add another function to this extension called “exists()” that works something like this:

    
    function exists($str, $field)
    {
    	$CI =& get_instance();
    	list($table, $column) = split("\.", $field, 2);
    
    	$CI->validation->set_message('exists', "The %s you're looking for doesn't exist.");
    
    	$query = $CI->db->query("SELECT COUNT(*) dupe FROM $table WHERE $column = '$str'");
    	$row = $query->row();
    	return ($row->dupe > 0) ? TRUE : FALSE;
    }
    

    I haven’t tested it, but it should work. It does the opposite of the unique function, returning an error if the id doesn’t exist.

  8. nabajit roy Says:

    good but the folling is much easier.you dont need to write the complete class but just add the following function in “system/libraries/Form_validation.php” file ……..

    function unique($str, $field)
    {
    //$this->CI
    //$CI =& get_instance();
    list($table, $column) = split(“\.”, $field, 2);
    //$this->CI->form_validation->set_message(‘unique’, ‘sorry! user ‘.$str.’ doesnot exist.’);
    $query = $this->CI->db->query(“SELECT COUNT(*) dupe FROM $table WHERE $column = ‘$str’”);
    $row = $query->row();
    return ($row->dupe == 0) ? FALSE : TRUE;
    }

    and then call the validation function from you form as follows…….

    $this->form_validation->set_rules(‘user_name’, ‘Admin User Name’, ‘required|unique[prism_users.user_loginname]‘);

    hows that plz rply thank……

  9. Scott Nelle Says:

    I prefer to extend classes rather than modifying the originals because when codeigniter is updated the originals may be modified. When you extend a class you don’t have to keep track of which modifications you’ve made.

  10. nabajit roy Says:

    yes Sir,you are right ,I haven’t thought about this issue .thanx for your advice.you r really great…..

  11. Brian Says:

    Thanks for the extension! Here is updated code that works with current versions of CI (1.7.2) and PHP(5.3):

    form_validation->set_message('unique', 'The %s that you requested is already in use.');

    $query = $CI->db->query("SELECT COUNT(*) dupe FROM $table WHERE $column = '$str'");
    $row = $query->row();
    return ($row->dupe > 0) ? FALSE : TRUE;
    }
    }
    ?>

    Changes are:
    CI: Validation is now Form_validation
    PHP: split() has been deprecated. Use preg_split() with | around the regex as delimiters.

  12. Brian Says:

    Looks like the HTML code tags cut out most of the code…
    The only changes needed are (as noted above):
    Replace “Validation” with “Form_validation” and replace the split() call with: preg_split(“|\.|”, $field, 2);

  13. David Peers Says:

    This blog aided me in narrowing down some problems with the latest release candidate, Why do they often seem to leave out vital information when they release a new version? It may be trivial to them but not to me. I’m sure i’m not alone either.

  14. Carson Shold Says:

    This seems so simple but I just can’t get it to work. My code is below, can you see what I’ve done wrong? Thanks


    validation->set_message('unique', 'The %s that you requested is unavailable.');

    $query = $CI->db->query("SELECT COUNT(*) dupe FROM $table WHERE $column = '$str'");
    $row = $query->row();
    return ($row->dupe > 0) ? FALSE : TRUE;
    }
    }
    ?>

  15. Carson Shold Says:

    Well that didn’t work at all. The important parts are below


    class My_Validation extends CI_Form_validation {

    function My_Validation()
    {
    parent::CI_Form_validation();
    }
    </code
    and

    function unique($str, $field)
    {
    $CI =& get_instance();
    list($table, $column) = preg_split("|\.|", $field, 2);

    $CI->validation->set_message('unique', 'The %s that you requested is unavailable.');

    $query = $CI->db->query("SELECT COUNT(*) dupe FROM $table WHERE $column = '$str'");
    $row = $query->row();
    return ($row->dupe > 0) ? FALSE : TRUE;
    }

Leave a Reply