8

I have a problem with my form validation rules (in a seperate config file). This occurs with checkboxes.

To organize my validation rules, I created a library file called validation_rules. This library contains all my custom callbacks like valid_date etc. To be able to call these rules, I load the library, and then use the following config:

array(
    'field' => 'terms',
    'label' => 'lang:reg_lbl_terms',
    'rules' => array(array('terms_accepted', array($ci->validation_rules, 'terms_accepted')))
    ),

Where $ci is a reference to CodeIgniter ($this).

Now this works fine for most types of input, but it doesn't work for checkboxes that are left empty, probably since they don't get posted.

HOWEVER, when I ditch my library and simply add the callback to the controller, everything works fine with the following config:

array(
    'field' => 'terms',
    'label' => 'lang:reg_lbl_terms',
    'rules' => array('callback_terms_accepted')
    ),

Also, when I add the rules required anywhere in the rules array (or string), the required rule does get called (returning false since the checkbox is not checked), but all other rules are completely ignored.

This must be a bug in CodeIgniter right? Does anybody have a solution or workaround? Of course this is an option, but I really don't like it.

Relevant documentation: http://www.codeigniter.com/user_guide/libraries/form_validation.html

Edit: Checkbox PHP/HTML:

<?php
$data = array(
    'name'    => 'terms',
    'value'   => 'true'
);
echo form_checkbox($data);
// Results in: <input type="checkbox" name="terms" value="true">
// No set_value() is used.
?>
Community
  • 1
  • 1
Wouter Florijn
  • 2,711
  • 2
  • 23
  • 38
  • Can you post the html code of your checkbox?? ANd how you use `set_value` in that – Saty Sep 08 '15 at 12:14
  • @Saty Yep, edited it into the question. – Wouter Florijn Sep 08 '15 at 12:18
  • Please Read [this](https://github.com/bcit-ci/CodeIgniter/issues/949) – Bugfixer Sep 16 '15 at 04:51
  • StackOverflow is only here to help solve problems with code you've written. It is not the place to report bugs. Report bugs directly to the developers on the CodeIgniter forum and/or GitHub page where they can be evaluated and fixed for everyone. – Sparky Sep 16 '15 at 16:29

3 Answers3

2

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';
    }
}
Philip
  • 4,592
  • 2
  • 20
  • 28
0
$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.

0

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.
Wouter Florijn
  • 2,711
  • 2
  • 23
  • 38