CodeIgniter Forums

Full Version: Use Models, Libraries and Helpers in Form Validation rules
You're currently viewing a stripped down version of our content. View the full version with proper formatting.

El Forum

[eluser]CroNiX[/eluser]
The original idea for this came from Skunkbad in this thread, where he created a way to use models in validation rules. I simply took his concept created a single new validation rule that would allow using Models, Libraries and Helpers as well. Additionally, if your own callback rule requires parameters passed to it via square brackets, you can still use those as you originally would with the original form validation class. Please see the notes in the class for reserved characters that cannot be used as delimiters in your own rules or they will clash with CI's rule delimeters or the ones I use.

The main benefit is to be able to consolidate and separate the validation logic outside of the controller and also avoid having a huge MY_Form_Validation rule set for larger projects.

Code:
<?php
class MY_Form_validation extends CI_Form_validation
{
/**
  * Allows using CI Models, Libraries and Helpers externally for validation rules
  *
  * Use: external_callback[type.name.function] or external_callback[type.name.function[params]]
  * Type = library, model, or helper!
  * Name = name of library/model/helper
  * Function = function/method of rule
  * Params = (optional) parameters set in square brackets to pass to your callback validation function
  *
  * Note 1: When defining external callbacks, the error message must be "external_callback" and not the name of the individual function.
  * Note 2: Do not use the pipe (|) or dots(.) in extra data transmitted within brackets ([]) in your own callback functions.
  *         CI uses pipes to separate rules and this external function uses dots, so using either will break rules.
  *
  * @param String $postdata Same as param1 sent to callback functions
  * @param String $extra    Same as param2 sent to callback functions (within square brackets)
         * See: http://ellislab.com/codeigniter/user-guide/libraries/form_validation.html#callbacks
  */

public function external_callback($postdata, $extra)
{  
  //Separate out the parts we need from $extra
  list($external_type, $name, $function) = explode('.', $extra);
  $external_type = strtolower($external_type);
  
  $CI =& get_instance();
  
  //Try loading the resource!
  $CI->load->$external_type($name);
  
  // Strip the parameter (if exists) from the rule
  // Rules can contain a parameter: max_length[5]
  $param = FALSE;
  if (preg_match("/(.*?)\[(.*)\]/", $function, $match))
  {
   $function = $match[1];
   $param = $match[2];
  }
  
  //If library or model, get the method
  $result = FALSE;
  
  if(($external_type == 'library' || $external_type == 'model') && method_exists($CI->$name, $function))
  {
   $result = ( ! empty($param)) ? $CI->$name->$function($postdata, $param) : $CI->$name->$function($postdata);
  }
  //it was a helper, just use function instead...
  else if(function_exists($function))
  {
   $result = ( ! empty($param)) ? $function($postdata, $param) : $function($postdata);
  }
  
  //return the final result of the external callback back to validation
  return $result;
}
}

Use examples:
1) You have a simple rule in a model.
/application/models/test_model.php
Code:
class Test_model extends CI_Model {
  public function not_word($str)
  {
    $CI =& get_instance();
    //note we must use 'external_callback' as the error message instead of 'test_model' like you normally would
    $CI->form_validation->set_message('external_callback', 'The %s field cannot be "word".');
    return ($str != 'word');
  }
}
Validation Rule:
Code:
$this->form_validation->set_rules('field_name', 'Field Name', 'external_callback[model.test_model.not_word]');
If the person enters "word" into the field it will fail.
external_callback[model.test_model.not_word]
Type of external callback
Name of file
Rule name in file

2) Let's say our rule had additional parameters passed to it. You do it like normal in square brackets.

Code:
class Test_model extends CI_Model {
  public function not_word($str, $param)
  {
    $CI =& get_instance();
    //note we must use 'external_callback' as the error message instead of 'test_model' like you normally would
    $CI->form_validation->set_message('external_callback', 'The %s field cannot be "' . $param . '".');
    return ($str != $param);
  }
}
Validation Rule:
Code:
$this->form_validation->set_rules('field_name', 'Field Name', 'external_callback[model.test_model.not_word[Tuesday]]');
If the person enters "Tuesday" in the field it will fail.

The rest of the code is the same for Libraries and Helpers. You just use the appropriate word instead of "model" in the rules. I hope it's useful and remember skunkbad came up with the concept.

If there are any improvements or problems please post them. I haven't done extensive testing.

Note: even if you have 10 external callbacks on the same form, the error messages still show up for the correct field even though all error messages are named after 'external_callback'.

El Forum

[eluser]ajustesen[/eluser]
Ignore my post on the other thread, got this working. Thanks!

El Forum

[eluser]batfastad[/eluser]
This is an awesome idea. Something that should be available in the core really IMO.
It makes sense to keep shared validation callbacks in a helper rather than repeating across many controllers.

Question though... if I'm doing this with my callback stored in a helper, how do I set the error message within the helper?

Code:
$CI =& get_instance();
$CI->form_validation->set_message('external_callback', '%s field does not contain a valid date.');
... doesn't seem to work.

I get
Quote:Unable to access an error message corresponding to your field name.

Any ideas?

Cheers, B

EDIT: Ok everyone go home! The problem was... I was being an idiot!

El Forum

[eluser]Ashes-to-Ashes[/eluser]
I am running something like this:

Code:
// in library:
function general_form ($form_name)
{
       if ($form_name == 'big_form')
       {
              $this->big_form();
       }
}

function big_form()
{
      $this->set_rules ();
      if ($this->CI->form_validation->run () == false)
      {
            // display form
      }
      else
      {
         // process form
      }
}

function set_rules()
{
      $this->CI->form_validation->set_rules ('account_number', 'Account Number', 'external_callback[model.accounts_model.exists]');
       // more rules here once I get it working
}


// in MY_Form_validation.php
class MY_Form_validation extends CI_Form_validation
{
public function external_callback($postdata, $extra)
{  
  echo 'Attempting Model Call Back';
  //Separate out the parts we need from $extra
  list($external_type, $name, $function) = explode('.', $extra);
  $external_type = strtolower($external_type);
  
  $CI =& get_instance();
  
  //Try loading the resource!
  $CI->load->$external_type($name);
  
  // Strip the parameter (if exists) from the rule
  // Rules can contain a parameter: max_length[5]
  $param = FALSE;
  if (preg_match("/(.*?)\[(.*)\]/", $function, $match))
  {
   $function = $match[1];
   $param = $match[2];
  }
  
  //If library or model, get the method
  $result = FALSE;
  
  if(($external_type == 'library' || $external_type == 'model') && method_exists($CI->$name, $function))
  {
   $result = ( ! empty($param)) ? $CI->$name->$function($postdata, $param) : $CI->$name->$function($postdata);
  }
  //it was a helper, just use function instead...
  else if(function_exists($function))
  {
   $result = ( ! empty($param)) ? $function($postdata, $param) : $function($postdata);
  }
  
  //return the final result of the external callback back to validation
  return $result;
}
}// end class

// in model

public function exists($str)
{
  $CI =& get_instance();
  $sql = "select * from accounts where number = '$str'";
  $query = $this->db->query ($sql);
  if ($query->num_rows == '1')
  {
   return false;
  }
  else {
   //note we must use 'external_callback' as the error message instead of 'test_model' like you normally would
   $CI->form_validation->set_message('external_callback', "%s: $str does not exist");
    return ($str);
  }
}

It appears to never call the call back function

El Forum

[eluser]CroNiX[/eluser]
It doesn't look like you are setting your rules right.

Code:
function set_rules()
{
      $this->CI->form_validation_rules->set_rules ('account_number', 'Account Number', 'external_callback[model.accounts_model.exists]');
       // form_validation_rules??
}

should be
Code:
$this->CI->form_validation->set_rules(...);

El Forum

[eluser]Ashes-to-Ashes[/eluser]
[quote author="CroNiX" date="1354147726"]It doesn't look like you are setting your rules right.[/quote]

You are correct. This problem was already fixed in the code. I fixed it in the post as well. Still having the issue though. I have an echo statement in my callback, that never displays. So it appears that the callback is not getting called versus returning true when it shouldn't.

Smile

El Forum

[eluser]Ashes-to-Ashes[/eluser]
Okay, I have debugged this a bit more. The call back is working in the main controller, but if I try to use the model callback it does nothing (no errors or anything, always returns true)