• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Patch for Validation class - retain preloaded data

#1
[eluser]Darren Inwood[/eluser]
Hi there

If you use the set_fields() method of the validation class, the current behaviour is to zero the field if a value was not submitted from the previous page.

This means you cannot set default values, and then use this function to overwrite them with the new values, which is especially useful when you have a multi-page form. Eg.
Code:
// (inside controller)
// WARNING: this code is illustrative, it does not work with CI 1.5.4!!!

// set up form with data stored in session
$sessiondata = $this->session->userdata('form');
if ( ! is_array($sessiondata) )
    $sessiondata = array();
foreach ( $sessiondata as $key -> $value ) {
    $this->validation->$key = $sessiondata[$key];
}

// overwrite with new data from $_POST
$this->validation->set_fields($fields);

This is due to the following line of code in Validation.php (line 90):
Code:
$this->$key = ( ! isset($_POST[$key]) OR is_array($_POST[$key])) ? '' : $this->prep_for_form($_POST[$key]);

I've replaced this with the following snippet:
Code:
if ( isset($_POST[$key]) AND ! is_array($_POST[$key]) )
{
    $this->$key = $this->prep_for_form($_POST[$key]);
}

if ( ! isset($this->$key) )
{
    $this->$key = '';
}

AFAIK this is functionally equivalent, but only blanks the field if there isn't an existing value.

NB: Personally I like to escape my data according to the output format I'm using it in, eg using Smarty's {$variable|escape:'format'} commands, so I ommitted the prep_for_form function.

Now the class assigns $_POST variables to internal variables, unfortunately it still refers to $_POST for the actual validation. You also need to alter a few lines throughout:

Code:
// line 183, we still want to test this even if no data was POSTed, so change:
if (count($_POST) == 0 OR count($this->_rules) == 0)
// to:
if (count($this->_rules) == 0)

// line 200, change:
if ( ! isset($_POST[$field]) OR $_POST[$field] == '')
// to:
if ( ! isset($this->$field) OR $this->$field == '')

// line 215, change:
if ( ! isset($_POST[$field]))
// to:
if ( ! isset($this->$field))

// line 271, since we could be calling a callback function from any controller handling
// the form I've added provision to use a helper. Change:
if ( ! method_exists($this->CI, $rule))
// to:
if ( ! method_exists($this->CI, $rule) && ! function_exists($rule) )
//line 276, change:
$result = $this->CI->$rule($_POST[$field], $param);
// to:
if ( method_exists($this->CI, $rule) )
{
    $result = $this->CI->$rule($this->$field, $param);      
}
if ( function_exists($rule) )
{
    $result = $rule($this->$field, $param);
}

// line 298, using a native PHP function, change:
$_POST[$field] = $rule($_POST[$field]);
$this->$field = $_POST[$field];
// to:
$this->$field = $rule($this->$field);

// line 305, using a method of the validation class, change:
$result = $this->$rule($_POST[$field], $param);
// to:
$result = $this->$rule($this->$field, $param);

// line 403, 'match' rule, change:
if ( ! isset($_POST[$field]))
// to:
if ( ! isset($this->$field))
// and change:
return ($str !== $_POST[$field]) ? FALSE : TRUE;
// to:
return ($str !== $this->$field) ? FALSE : TRUE;

If you omit the prep_for_form function, these should also be functionally equivalent to the current class. If you leave it in, I can't see any reason why it wouldn't also work.

HTH someone! =)
--
Darren

#2
[eluser]alpar[/eluser]
Isn't setting up predefined options after you set up fields an option? personally i use the $fields array i set the fields from , in the foreach statement to set some default values. It acts as a white list too.

#3
[eluser]Darren Inwood[/eluser]
I don't understand - how do you use the $fields array to set values? Do you conditionally put the values into $_POST?

Anyway, the upshot is, if you alter the Validation class as above, both of us can work how we're used to. Currently, everything I write turns to custard if I use the Validation class as-is, as I'm not used to its' side-effects.

I think that this class should at least change it's behaviour to stop coupling itself to the $_POST array. set_fields() copies the $_POST data we're interested in into the internal variables. Continuing to use $_POST throughout the run() and various validation methods couples the class to the $_POST global array unecessarily IMO.

Eg:
Code:
$fields['foo'] = 'Foo';
$fields['bar'] = 'Bar';
$this->validation->set_fields($fields);
$rules['foo'] = 'matches[bar]';
$this->validation->set_rules($rules);

if ( $this->session->userdata('bar_always_equals_foo') == true )
{
    $this->validation->bar = $this->validation->foo; // bug, should be $_POST['bar'] = $_POST['foo'];
}

if ( $this->validation->run() ) {
    // success!
    return;
}
// failure :(

#4
[eluser]stefanv[/eluser]
Real simple to solve..

Create a new class in your library directory in you appliction, with the following code:

Code:
<?php  if (!defined('BASEPATH')) exit('No direct script access allowed');

class NMI_Validation extends CI_Validation
{
    function NMI_Validation()
    {
        parent::CI_Validation();
    }

    /**
     * Set Fields
     *
     * This function takes an array of field names as input
     * and generates class variables with the same name, which will
     * either be blank or contain the $_POST value corresponding to it
     *
     * @access    public
     * @param    string
     * @param    string
     * @return    void
     */
    function set_fields($data = '', $field = '')
    {
        if ($data == '')
        {
            if (count($this->_fields) == 0)
            {
                return FALSE;
            }
        }
        else
        {
            if ( ! is_array($data))
            {
                $data = array($data => $field);
            }

            if (count($data) > 0)
            {
                $this->_fields = $data;
            }
        }

        foreach($this->_fields as $key => $val)
        {
            $this->$key = ( ! isset($_POST[$key]) OR is_array($_POST[$key])) ? $val : $this->prep_for_form($_POST[$key]);

            $error = $key.'_error';
            if ( ! isset($this->$error))
            {
                $this->$error = '';
            }
        }
    }
}
?>

This will overwrite the set_fields function in the CI_Validation class.

Now use something like this in your code to either use data from the database or for the post form:

Code:
// Call database
            $customer = $this->Customermodel->get_Customer($iId);


            // Fill the fields
            if ( isset($_POST['name']) ) {
                $fields = $this->_set_fields();
            }
            else {
               $fields = $customer;
            }

Works for me !


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


  Theme © 2014 iAndrew  
Powered By MyBB, © 2002-2021 MyBB Group.