• 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Custom form validation rule

#1
Im trying to overwrite the is_unique rule, and make it so you can either use as it is, or do something like: is_unique[table.field|value_to_ignore]
Example:
PHP Code:
$this->form_validation->set_rules('Page_Title''page_title'"is_unique[pages.title|{$current_title}]"); 

This way it can be used to check for unique values, but if its the current value, it wont say its an existing value.

The problem though, is it doesnt seem to be working.. The error that gets returned from the validation is: Unable to access an error message corresponding to your field name Page Title.(is_unique[pages.title)

Heres my code thus far.

MY_Form_validation.php
PHP Code:
<?php defined('BASEPATH') OR exit('No direct script access allowed');

class 
MY_Form_validation extends CI_Form_validation {
    public function 
is_unique($str$field)
    {
        if( ! isset(
$this->CI->db))
            return 
FALSE;

        if( 
preg_match('/^(.*)\.(.*)\|(.*)$/'$field$matches) !== FALSE )
        {
            
$table $matches[1];
            
$field $matches[2];
            
$ignore $matches[3];
        }
        elseif( 
preg_match('/^(.*)\.(.*)$/'$field$matches) !== FALSE )
        {
            
$table $matches[1];
            
$field $matches[2];
            
$ignore NULL;
        }
        else
        {
            return 
FALSE;
        }

        
$this->CI->db
            
->select('*')
            ->
from($table)
            ->
where($field$str);

        if( ! 
is_null($ignore))
            
$this->CI->db->where_not($field$ignore);

        return 
$this->CI->db->num_rows() === 0;
    }


form_validation_lang.php
PHP Code:
<?php
$lang
['form_validation_is_unique'] = 'not unique'

Controller Validation Snippet
PHP Code:
...
$this->form_validation->set_rules(
    
'page_title''Page Title'"required|alpha_numeric_spaces_dash|min_length[5]|max_length[50]|is_unique[pages.title|{$data['settings']->title}]",
    array(
        
'required'  => 'Need to specify a unique page title',
        
'is_unique' => 'The page title specified already exists for another page'
    
)
);
... 

Im guessing that theres something that checks the regex of the rule string before it passes it to the form validation rules...
Reply

#2
Ohhh, I cant use |, since the rules are exploded by pipes..

What CAN I use? Anything I use (. , : ) all get cut off. I just put this to see what it would output:
PHP Code:
public function is_unique($str$field)
{
    die(
"FIELD: $field");


And it always outputs just table.field
Reply

#3
Was just going to say that. Use a period or other char for your delimiter within the brackets and explode on that ($field in your method)
Reply

#4
(08-30-2015, 09:02 AM)CroNiX Wrote: Was just going to say that. Use a period or other char for your delimiter within the brackets and explode on that ($field in your method)

I tried that, I stated above that I tried a period, comma, etc etc, and nothing worked. It always just returned the first two. I believe this is because CI uses sscanf to get the values, and it must be a string

I know I could fix this by pretty much overriding the entire CI_Form_validation:Confusedet_value, but I try as hard as I can not to override methods in CI framework
Reply

#5
You do need to avoid using the pipe (|), because set_rules() will explode the rules on that character (so, in your rules, it would attempt to call a function named "{$current_title}]", and the is_unique rule definition is messed up because it's missing the closing bracket).

You should be able to pass anything you'd like in $field (except the pipe). CI's is_unique() function uses sscanf, but you don't have to.

I would recommend using a different function name, though, and leaving is_unique() alone. Bonfire has a unique() function which does something similar to what you seem to be describing.
Reply

#6
Im going to use something like table.column:existing_value instead of  table.column|existing_value, that should work.

Quick regex question. Anyone know how to use preg_match to match for both of those? And just check if the third matched value is there? Im having trouble with it, im not a Regex ninja..

PHP Code:
<?php
$s 
= [
 
   'table.column',
 
   'table.column:value'
 
   ];

//$pattern = '/^(\S+)\.(\S+)\:(\S+)?$/';
$pattern '/^(\S+)\.(\S+)$/';

foreach(
$s as $str){
 
   if(preg_match($pattern$str$matches))
 
   {
 
       echo "Successfuly matched string: {$str}\n";
 
       print_r($matches);
 
   }
 
   else
    
{
 
       echo "No matches for string: {$str}\n";
 
   }
 
   echo "==============\n";


Result:
Quote:Successfuly matched string: table.column

Array
(
    [0] => table.column
    [1] => table
    [2] => column
)
==============
Successfuly matched string: table.column:value
Array
(
    [0] => table.column:value
    [1] => table
    [2] => column:value
)
==============

What im looking for is:
Quote:Successfuly matched string: table.column
Array
(
   [0] => table.column
   [1] => table
   [2] => column
)
==============
Successfuly matched string: table.column:value
Array
(
   [0] => table.column:value
   [1] => table
   [2] => column
   [2] => value
)
==============

I know I can use the question mark to match for the last string or not, but im having issues including the : in that condition..

What I have now works, its just too complicated when REGEX can get rid of some code..
PHP Code:
<?php defined('BASEPATH') OR exit('No direct script access allowed');

class 
MY_Form_validation extends CI_Form_validation {

 
   public function is_unique($str$field)
 
   {
 
       // No : found, use the default is_unique
 
       if(strpos($field':') === FALSE)
 
       {
 
           return parent::is_unique($str$field);
 
       }

 
       // If we can match for a table.column:value, assume were editing
 
       ifpreg_match('/^(\S+)\.(\S+)\:(\S+)?$/'$field$matches) !== FALSE )
 
       {
 
           $table $matches[1];
 
           $field $matches[2];
 
           $ignore $matches[3];

 
           $this->CI->db
                
->select('*')
 
               ->from($table)
 
               ->where($field$str);

 
           if( ! is_null($ignore))
 
               $this->CI->db->where_not($field$ignore);

 
           return $this->CI->db->num_rows() === 0;
 
       }

 
       // If : was found but regex not matched..
        
Logger_lib::error("Failed to match 'table.column' as well as 'table.column:value' for is_unique form validation check"TRUE);
 
       return FALSE;
 
   }



Thanks
Reply

#7
I'm not great with RegEx, so I would usually go with something like this:
PHP Code:
$fields explode(':'$field);
list(
$table$field) = explode('.'$fields[0], 2);
$ignore = isset($fields[1]) ? $fields[1] : null

This code will work whether a colon is passed in the $field or not, but there is potential for issues if the string doesn't contain a table.field combination (either before the colon or without one), so you may need some additional code if that is a possibility. Generally, unless I need something fairly complex, explode() is easier to understand and gets predictable results.

Also, instead of:
PHP Code:
return $this->CI->db->num_rows() === 0

You may want to use something like this:
PHP Code:
$row $this->CI->db->get()->row();
return ! isset(
$row); 

After all, you don't really care how many rows were returned, and, if num_rows() works at all the way you're using it, it's very likely to be getting the full result set and counting it, rather than using a method provided by the database to determine how many rows were returned by a query.
Reply


Digg   Delicious   Reddit   Facebook   Twitter   StumbleUpon  


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