How to make CodeIgniter callable work for checkboxes that are left empty, custom form validation bug How to make CodeIgniter callable work for checkboxes that are left empty, custom form validation bug codeigniter codeigniter

How to make CodeIgniter callable work for checkboxes that are left empty, custom form validation bug


You can create your rules in a form_validation file inside /config

application/config/form_validation.php

$config = array(    'controller/method' => array(        array('field'=>'', 'label'=>'', 'rules'=>'required|acceptTerms')        array('field'=>'another', 'label'=>'', 'rules'=>'required')    ),);

Note the controller/method for the key, Codeigniter will use this if you don't set it specifically inside the calling form_validation function.

An example of this would be like so

application/controllers/Shop

class Shop extends CI_Controller{    public function __construct(){ parent::__construct(); }    public function index()    {        // Show the Purchase Form        // in some view        return $this->load->view();    }    public function purchase()    {        // notice we don't pass any params to the run() function        // codeigniter will look inside application/config/form_validation/$config        // for any matching key, ie: shop/purchase        if(!$this->form_validation->run()){             // If validation fails, show the form again            // and stop the method from executing any further            return $this->index();        }    }}

To validate if check boxes are set, we look for the keyword on

application/libraries/MY_Form_validation.php

class MY_Form_validation extends CI_Form_validation{    public function __construct($config)    {        parent::__construct( $config );    }    public function acceptTerms( $field )    {        $this->set_error('acceptTerms', 'your error message');        return (bool) $field == 'on';    }}


$terms = $this->input->post('terms');var_dump((int)$checked); //Just to see the value, then remove this line.if((int)$checked == 1) {  //Checked = True} else {  //Checked = False}

I'll check about the required flag and edit this answer.

Edit:Instead of just required, try to use required|isset, then when you perform callback_terms_accepted, do something like this:

function terms_accepted($value) {  if (isset($checked)) {    return true;  } else {    $this->form_validation->set_message('terms_accepted', 'Error message');    return false;  }}

That should do the trick.

Again, hope that helps mate.


Alright, I managed to fix my problem. The issue here was as I suspected a bug in CodeIgniter related to callables specifically.

NOTE: This bug seems to have been fixed in CI 3.0.1+. I was running version 3.0.0.

The problem

The problem is that the Form_validation library has a piece of code in the _execute function that checks if there is either a required rule or a callback rule set for a field that isn't posted. This applies to checkboxes since they aren't part of the $_POST array when left empty. This is the code that causes the issue:

$callback = FALSE;if ( ! in_array('required', $rules) && ($postdata === NULL OR $postdata === '')){    // Before we bail out, does the rule contain a callback?    foreach ($rules as &$rule)    {        if (is_string($rule))        {            if (strncmp($rule, 'callback_', 9) === 0)            {                $callback = TRUE;                $rules = array(1 => $rule);                break;            }        }        elseif (is_callable($rule))        {            $callback = TRUE;            $rules = array(1 => $rule);            break;        }    }    if ( ! $callback)    {        return;    }}

This code is used to skip validation entirely for a field if it is not required or has a callback. However, the CI devs made the mistake of checking for callbacks with is_callable. This of course is fine for normal callables that are structured like this:

array($this->some_model_or_library, 'function_name')

But, CodeIgniter allows you to name your callback in order to set validation errors for it like this:

array('my_callback_function', array($this->some_model_or_library, 'function_name'))

Unsurprisingly, is_callable returns false when applied to this array, and thus validation is skipped.

Relevant docs: http://www.codeigniter.com/user_guide/libraries/form_validation.html#callable-use-anything-as-a-rule

The solution

Personally, I didn't see the use of the abovementioned code, because I never want to skip validation when a field isn't posted. So I solved the problem by creating a MY_Form_validation class and overriding the _execute function, simply replacing the code with:

$callback = TRUE;

Of course, a slightly more conservative solution would be to check for multidimensional arrays and apply is_callable to the appropriate element like so:

if (is_callable($rule)                            // Original check.|| (is_array($callback) && is_array($callback[1]) // Check if the callback is an array and contains an array as second element.&& is_callable($callback[1]))                     // Check if the second element is a callable.