-
Notifications
You must be signed in to change notification settings - Fork 7.6k
Assosiative Arrays via POST
This wiki page is about extending CI Input & Validation libraries to support assosiative arrays sent via post.
before this solution, validating a checkbox value inside a checkbox group wasn't available, you had to retrieve the value first as an array and then do manual validation.
this wasn't handy in anyway so I had to write this contribution.
read more about this topic into the original forum thread http://codeigniter.com/forums/viewthread/51260/ (any comments & bug reports should go there also, please )
I've written a small sample controller which illustrates everything for you, just check it out and check the source codes well :)
Don't be silly and try the sample controller before installing the modified libraries as it won't work :P
Create a new controller file and call it test.php fill the following codes:
<?php
class test extends controller{
function index(){
$this->load->library('validation');
$rules = array(
'inputs[textbox]' => 'trim|required',
'inputs[mycheckbox]' => 'trim|numeric',
);
$fields = array(
'inputs[textbox]' => 'TextBox',
'inputs[mycheckbox]' => 'MyCheckBox',
);
$this->validation->set_fields($fields);
$this->validation->set_rules($rules);
$this->validation->run();
?>
<h1>Testing Form:</h1>
<form method="post">
<center><?=$this->validation->error_string?></center>
<b>TextBox Sample</b><br />
<input type="text" name="inputs[textbox]" value="<?=$this->validation->value('inputs[textbox]')?>" /> <?=$this->validation->error('inputs[textbox]')?>
<br /><br />
<b>Radio Sample</b><br />
<input type="radio" name="inputs[mycheckbox]" value="1" <? if($this->validation->value('inputs[mycheckbox]') == 1) echo "checked"; ?> /> 1
<input type="radio" name="inputs[mycheckbox]" value="2" <? if($this->validation->value('inputs[mycheckbox]') == 2) echo "checked"; ?> /> 2
<input type="radio" name="inputs[mycheckbox]" value="abc" <? if($this->validation->value('inputs[mycheckbox]') == 'abc') echo "checked"; ?> /> abc
<?=$this->validation->error('inputs[mycheckbox]')?>
<br /><br />
<input type="submit" value="Test!" />
</form>
<hr />
<h1>Submitted post data:</h1>
<h3>dumping $_POST variable</h3>
<pre><? print_r($_POST) ?></pre>
<span style="font-size:smaller">You should find some extra keys into the post used by the class, just ignore them!</span>
<hr />
<h1>Testing submitted values:</h1>
$this->input->post('inputs[textbox']) <b>prints</b> "<?=$this->input->post('inputs[textbox]')?>" <br />
$this->input->post('inputs[mycheckbox']) <b>prints</b> "<?=$this->input->post('inputs[mycheckbox]')?>" <br />
<hr />
<h1>Redefining post values...</h1>
<?
$this->input->set_post('inputs[textbox]' , 'textbox value changed, great it works!');
$this->input->set_post('inputs[mycheckbox]' , 'checkbox value changed!');
?>
<h3>dumping $_POST variable</h3>
<pre><? print_r($_POST) ?></pre>
<h1>Testing defined values:</h1>
$this->input->post('inputs[textbox']) <b>prints</b> "<?=$this->input->post('inputs[textbox]')?>" <br />
$this->input->post('inputs[mycheckbox']) <b>prints</b> "<?=$this->input->post('inputs[mycheckbox]')?>" <br />
<?
}
}
?>
after applying this contribution you have to note some changes:
1- due to the fact that we can't use brackets [ ] into a class variable, we can't access $this->validation->mycheckbox[element1]_error anymore to print the field error, so use $this->validation->error('mycheckbox[element1]') instead.
2- same to echo a value, instead of $this->validation->mycheckbox[element1], use $this->validation->value('mycheckbox[element1]')
3- to redefine a variable inside the $_POST, use $this->validation->set_post('mycheckbox[element1]' , 'new value here')
all you need is to create two files under your "application/libraries" folder, and name them:
MY_Input.php MY_Validation.php
fill the following codes:
MY_Input.php Codes
<?php
class MY_Input extends CI_Input{
function MY_Input()
{
parent::CI_Input();
}
/**
* Fetch an item from the POST array
*
* @access public
* @param string
* @param bool
* @return string
*/
function post($index = '', $xss_clean = FALSE)
{
$value = $this->brackets_to_index($index , "post");
if ( ! isset($value))
{
return FALSE;
}
if ($xss_clean === TRUE)
{
if (is_array($value))
{
foreach($value as $key => $val)
{
$value[$key] = $this->xss_clean($val);
}
}
else
{
return $this->xss_clean($value);
}
}
return $value;
}
// --------------------------------------------------------------------
/**
* Set an item from the POST array
*
* @access public
* @param string
* @param bool
* @return string
*/
function set_post($index = '', $value)
{
return $this->set_brackets_to_index($index , $value , 'post');
}
// --------------------------------------------------------------------
/**
* Fetch an item from the COOKIE array
*
* @access public
* @param string
* @param bool
* @return string
*/
function cookie($index = '', $xss_clean = FALSE)
{
$value = $this->brackets_to_index($index , "cookie");
if ( ! isset($value))
{
return FALSE;
}
if ($xss_clean === TRUE)
{
if (is_array($value))
{
$cookie = array();
foreach($value as $key => $val)
{
$cookie[$key] = $this->xss_clean($val);
}
return $cookie;
}
else
{
return $this->xss_clean($value);
}
}
else
{
return $value;
}
}
/*
* This function gets the value of an "array item" passed via post, get or cookie which is written on the form
* array[array1][array2][item]
* @access public
* @param string the form of the array
* @param string the method used to send the variable possible values are: "post" , "get" and "cookie" .. default is "post"
* @return string
*/
function brackets_to_index($str , $method="post"){
// first we remove the closing bracket ]
$str2 = str_replace("]" , "" , $str);
// next , we explode the str by opening bracket [
$array = explode("[", $str2);
switch($method){
default:
$value = $_POST;
break;
case "cookie":
$value = $_COOKIE;
break;
case "get":
$value = $_GET;
break;
}
// finally we get the index value from the specified array
foreach($array as $key=>$index){
$value = $value[$index];
}
$value = isset($value) ? $value : NULL;
$value = ($value === false) ? "" : $value;
switch($method){
default:
$_POST[$str] = $value;
break;
case "cookie":
$_COOKIE[$str] = $value;
break;
case "get":
$_GET[$str] = value;
break;
}
return $value;
}
function set_brackets_to_index($str , $value, $method = 'post'){
// first we remove the closing bracket ]
$str2 = $str;
if(strpos($str2 , '[') != 0){
$str2 = substr($str2 , 0 ,strpos($str2 , '[')) . ']' . strstr($str2 , '['); // add ] after the first index
$str2 = "[" . $str2;
}
$str2 = str_replace("]" , "']" , $str2);
$str2 = str_replace("[" , "['" , $str2);
switch($method){
default:
eval( '$_POST'.$str2.' = $value; ');
break;
case "get":
eval( '$_GET'.$str2.' = $value; ');
break;
case "file":
eval( '$_FILES[\'userfile\'][\'name\']'.$str2.' = $value; ');
break;
}
}
function file($str , $key = "name"){
// first we remove the closing bracket ]
$str2 = str_replace("]" , "" , $str);
// next , we explode the str by opening bracket [
$array = explode("[", $str2);
$value = $_FILES['userfile'];
for($i = 0 ; $i < count($array) ; $i++){
if($i == 0) $value = $value[$key];
else $value = $value[$array[$i]];
}
return $value;
}
// --------------------------------------------------------------------
/**
* Set an item from the POST array
*
* @access public
* @param string
* @param bool
* @return string
*/
function set_file($index = '', $value)
{
return $this->set_brackets_to_index($index , $value , 'file');
}
}
?>
MY_Validation.php
<?php
class MY_Validation extends CI_Validation{
var $_value = array();
function MY_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->_value[$key] = ( ! isset($_POST[$key]) OR is_array($_POST[$key])) ? '' : $this->prep_for_form($_POST[$key]);
$this->_value[$key] = $_POST[$key];
/*
$error = $key.'_error';
if ( ! isset($this->$error))
{
$this->$error = '';
}
*/
// USE INSTEAD: $this->error('username');
}
}
// --------------------------------------------------------------------
/**
* Run the Validator
*
* This function does all the work.
*
* @access public
* @return bool
*/
function run()
{
// Do we even have any data to process? Mm?
if (count($_POST) == 0 OR count($this->_rules) == 0)
{
return TRUE;
}
// Load the language file containing error messages
$this->CI->lang->load('validation');
// Cycle through the rules and test for errors
foreach ($this->_rules as $field => $rules)
{
$fieldO = $field; // save the original field name;
$this->CI->input->brackets_to_index($field);
//Explode out the rules!
$ex = explode('|', $rules);
// Is the field required? If not, if the field is blank we'll move on to the next text
if ( ! in_array('required', $ex, TRUE) AND strpos($rules, 'callback_') === FALSE)
{
if ( ! isset($_POST[$field]) OR $_POST[$field] == '')
{
continue;
}
}
/*
* Are we dealing with an "isset" rule?
*
* Before going further, we'll see if one of the rules
* is to check whether the item is set (typically this
* applies only to checkboxes). If so, we'll
* test for it here since there's not reason to go
* further
*/
if ( ! isset($_POST[$field]))
{
if (in_array('isset', $ex, TRUE) OR in_array('required', $ex))
{
if ( ! isset($this->_error_messages['isset']))
{
if (FALSE === ($line = $this->CI->lang->line('isset')))
{
$line = 'The field was not set';
}
}
else
{
$line = $this->_error_messages['isset'];
}
$field = ( ! isset($this->_fields[$field])) ? $field : $this->_fields[$field];
$this->_error_array[$fieldO] = sprintf($line, $field);
}
continue;
}
/*
* Set the current field
*
* The various prepping functions need to know the
* current field name so they can do this:
*
* $_POST[$this->_current_field] == 'bla bla';
*/
$this->_current_field = $field;
// Cycle through the rules!
foreach ($ex As $rule)
{
// Is the rule a callback?
$callback = FALSE;
if (substr($rule, 0, 9) == 'callback_')
{
$rule = substr($rule, 9);
$callback = TRUE;
}
// Strip the parameter (if exists) from the rule
// Rules can contain a parameter: max_length[5]
$param = FALSE;
if (preg_match("/(.*?)\[(.*?)\]/", $rule, $match))
{
$rule = $match[1];
$param = $match[2];
}
// Call the function that corresponds to the rule
if ($callback === TRUE)
{
if ( ! method_exists($this->CI, $rule))
{
continue;
}
$result = $this->CI->$rule($_POST[$field], $param);
// If the field isn't required and we just processed a callback we'll move on...
if ( ! in_array('required', $ex, TRUE) AND $result !== FALSE)
{
continue 2;
}
}
else
{
if ( ! method_exists($this, $rule))
{
/*
* Run the native PHP function if called for
*
* If our own wrapper function doesn't exist we see
* if a native PHP function does. Users can use
* any native PHP function call that has one param.
*/
if (function_exists($rule))
{
$_POST[$field] = $rule($_POST[$field]);
// $this->$field = $_POST[$field];
// USE INSTEAD: $this->value('username');
$this->_value[$field] = $_POST[$field];
}
continue;
}
$result = $this->$rule($_POST[$field], $param);
}
// Did the rule test negatively? If so, grab the error.
if ($result === FALSE)
{
if ( ! isset($this->_error_messages[$rule]))
{
if (FALSE === ($line = $this->CI->lang->line($rule)))
{
$line = 'Unable to access an error message corresponding to your field name.';
}
}
else
{
$line = $this->_error_messages[$rule];;
}
// Build the error message
$mfield = ( ! isset($this->_fields[$field])) ? $field : $this->_fields[$field];
$mparam = ( ! isset($this->_fields[$param])) ? $param : $this->_fields[$param];
$message = sprintf($line, $mfield, $mparam);
// Set the error variable. Example: $this->username_error
// $error = $field.'_error';
// $this->$error = $this->_error_prefix.$message.$this->_error_suffix;
// USE INSTEAD:
// $this->error('username');
// Add the error to the error array
$this->_error_array[$fieldO] = $message;
continue 2;
}
}
}
$total_errors = count($this->_error_array);
/*
* Recompile the class variables
*
* If any prepping functions were called the $_POST data
* might now be different then the corresponding class
* variables so we'll set them anew.
*/
if ($total_errors > 0)
{
$this->_safe_form_data = TRUE;
}
$this->set_fields();
// Did we end up with any errors?
if ($total_errors == 0)
{
return TRUE;
}
// Generate the error string
foreach ($this->_error_array as $val)
{
$this->error_string .= $this->_error_prefix.$val.$this->_error_suffix."\n";
}
return FALSE;
}
// --------------------------------------------------------------------
/**
* Return error message of given field name
*
* @access public
* @param string
* @return string
*/
function error($field){
return $this->_error_array[$field];
}
// --------------------------------------------------------------------
/**
* Return passed value of given field name
*
* @access public
* @param string
* @return string
*/
function value($field){
return $this->_value[$field];
}
// --------------------------------------------------------------------
/**
* Overwrite object values with validation output.
*
* @access public
* @param obj
* @return obj
*/
function error_set($field , $error){
$this->_error_array[$field] .= $error;
}
}
?>
Congratulations, now you are able to access, validate and redefine associative arrays elements with no limits!