Codeigniter将额外参数传递给自定义验证规则

时间:2017-07-17 10:11:12

标签: php codeigniter codeigniter-3

基于此documentation,如何将第二个参数传递给规则方法?

这是我的自定义规则

public function email_exists($email, $exclude_id=NULL)
{
    if ( $exclude_id !== NULL ) $this->db->where_not_in('id', $exclude_id);

    $result = $this->db->select('id')->from('users')->where('email', $email)->get();

    if ( $result->num_rows() > 0 ) {
        $this->form_validation->set_message('email_exists', '{field} has been used by other user.');
        return FALSE;
    } else {
        return TRUE;
    }
}

这就是我从控制器中调用它的方式

$rules = [
    [
        'field' => 'email',
        'label' => 'Email',
        'rules' => [
            'required',
            'trim',
            'valid_email',
            'xss_clean',
            ['email_exists', [$this->m_user, 'email_exists']]
        ]
    ]
];

$this->form_validation->set_rules($rules);

如何将第二个参数传递给email_exists方法?

3 个答案:

答案 0 :(得分:1)

按照文档中所述,以正确的方式(至少对于CI 2.1+):

$this->form_validation->set_rules('uri', 'URI', 'callback_check_uri['.$this->input->post('id').']');
// Later:
function check_uri($field, $id){
    // your callback code here
}

如果这不起作用,请在表单中为$exclude_id创建隐藏字段,并通过

直接在回调中检查
$exclude_id = $this->input->post('exclude_id');//or whatever the field name is

更多here

答案 1 :(得分:1)

它似乎CI没有为此提供机制。我找到了几种方法来解决这个问题。第一种方法,您可以破解文件系统(Form_validation.php)并在第728行修改一些脚本

if ( preg_match('/(.*?)\[(.*)\]/', $rule[1], $rulea) ) {
    $method = $rulea[1];
    $extra = $rulea[2];
} else {
    $method = $rule[1];
    $extra = NULL;
}

$result = is_array($rule)
    ? $rule[0]->{$method}($postdata, $extra)
    : $rule($postdata);

您可以通过第二种方式扩展CI_Form_validation核心并在其中添加自定义规则。我在codeigniter documentation上找到了有关此内容的详细信息。

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Form_validation extends CI_Form_validation
{

    public function __construct()
    {
        parent::__construct();
    }

    public function check_conflict_email($str, $exclude_id=NULL)
    {
        if ( $exclude_id !== NULL ) $this->CI->db->where_not_in('id', $exclude_id);

        $result = $this->CI->db->select('id')->from('users')->where('email', $str)->get();

        if ( $result->num_rows() > 0 ) {
            $this->set_message('check_conflict_email', '{field} has been used by other user.');
            return FALSE;
        } else {
            return TRUE;
        }
    }

}

/* End of file MY_Form_validation.php */
/* Location: ./application/libraries/MY_Form_validation.php */

第三种方式,我认为这是最好的方法。感谢skunkbad提供solution

$rules = [
    [
        'field' => 'email',
        'label' => 'Email',
        'rules' => [
            'required',
            'trim',
            'valid_email',
            'xss_clean',
            [
                'email_exists', 
                function( $str ) use ( $second_param ){
                    return $this->m_user->email_exists( $str, $second_param ); 
                }
            ]
        ]
    ]
]; 

答案 2 :(得分:0)

我使用CI 3.1.10,并且此问题仍然存在,我扩展了库并使用与回调相同的方式

array('username_callable[param]' => array($this->some_model, 'some_method'))

扩展的Form_validation库:

<?php
defined('BASEPATH') OR exit('No direct script access allowed');

class MY_Form_validation extends CI_Form_validation {

    /**
     * Executes the Validation routines
     *
     * @param   array
     * @param   array
     * @param   mixed
     * @param   int
     * @return  mixed
     */
    protected function _execute($row, $rules, $postdata = NULL, $cycles = 0)
    {
        // If the $_POST data is an array we will run a recursive call
        //
        // Note: We MUST check if the array is empty or not!
        //       Otherwise empty arrays will always pass validation.
        if (is_array($postdata) && ! empty($postdata))
        {
            foreach ($postdata as $key => $val)
            {
                $this->_execute($row, $rules, $val, $key);
            }

            return;
        }

        $rules = $this->_prepare_rules($rules);
        foreach ($rules as $rule)
        {
            $_in_array = FALSE;

            // We set the $postdata variable with the current data in our master array so that
            // each cycle of the loop is dealing with the processed data from the last cycle
            if ($row['is_array'] === TRUE && is_array($this->_field_data[$row['field']]['postdata']))
            {
                // We shouldn't need this safety, but just in case there isn't an array index
                // associated with this cycle we'll bail out
                if ( ! isset($this->_field_data[$row['field']]['postdata'][$cycles]))
                {
                    continue;
                }

                $postdata = $this->_field_data[$row['field']]['postdata'][$cycles];
                $_in_array = TRUE;
            }
            else
            {
                // If we get an array field, but it's not expected - then it is most likely
                // somebody messing with the form on the client side, so we'll just consider
                // it an empty field
                $postdata = is_array($this->_field_data[$row['field']]['postdata'])
                    ? NULL
                    : $this->_field_data[$row['field']]['postdata'];
            }

            // Is the rule a callback?
            $callback = $callable = FALSE;
            if (is_string($rule))
            {
                if (strpos($rule, 'callback_') === 0)
                {
                    $rule = substr($rule, 9);
                    $callback = TRUE;
                }
            }
            elseif (is_callable($rule))
            {
                $callable = TRUE;
            }
            elseif (is_array($rule) && isset($rule[0], $rule[1]) && is_callable($rule[1]))
            {
                // We have a "named" callable, so save the name
                $callable = $rule[0];
                $rule = $rule[1];
            }

            // Strip the parameter (if exists) from the rule
            // Rules can contain a parameter: max_length[5]
            $param = FALSE;
            if ( ! $callable && preg_match('/(.*?)\[(.*)\]/', $rule, $match))
            {
                $rule = $match[1];
                $param = $match[2];
            }
            elseif ( is_string($callable) && preg_match('/(.*?)\[(.*)\]/', $callable, $match))
            {
                $param = $match[2];
            }

            // Ignore empty, non-required inputs with a few exceptions ...
            if (
                ($postdata === NULL OR $postdata === '')
                && $callback === FALSE
                && $callable === FALSE
                && ! in_array($rule, array('required', 'isset', 'matches'), TRUE)
            )
            {
                continue;
            }

            // Call the function that corresponds to the rule
            if ($callback OR $callable !== FALSE)
            {
                if ($callback)
                {
                    if ( ! method_exists($this->CI, $rule))
                    {
                        log_message('debug', 'Unable to find callback validation rule: '.$rule);
                        $result = FALSE;
                    }
                    else
                    {
                        // Run the function and grab the result
                        $result = $this->CI->$rule($postdata, $param);
                    }
                }
                else
                {
                    $result = is_array($rule)
                        ? $rule[0]->{$rule[1]}($postdata, $param)
                        : $rule($postdata);

                    // Is $callable set to a rule name?
                    if ($callable !== FALSE)
                    {
                        $rule = $callable;
                    }
                }

                // Re-assign the result to the master data array
                if ($_in_array === TRUE)
                {
                    $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
                }
                else
                {
                    $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
                }
            }
            elseif ( ! method_exists($this, $rule))
            {
                // 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))
                {
                    // Native PHP functions issue warnings if you pass them more parameters than they use
                    $result = ($param !== FALSE) ? $rule($postdata, $param) : $rule($postdata);

                    if ($_in_array === TRUE)
                    {
                        $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
                    }
                    else
                    {
                        $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
                    }
                }
                else
                {
                    log_message('debug', 'Unable to find validation rule: '.$rule);
                    $result = FALSE;
                }
            }
            else
            {
                $result = $this->$rule($postdata, $param);

                if ($_in_array === TRUE)
                {
                    $this->_field_data[$row['field']]['postdata'][$cycles] = is_bool($result) ? $postdata : $result;
                }
                else
                {
                    $this->_field_data[$row['field']]['postdata'] = is_bool($result) ? $postdata : $result;
                }
            }

            // Did the rule test negatively? If so, grab the error.
            if ($result === FALSE)
            {
                // Callable rules might not have named error messages
                if ( ! is_string($rule))
                {
                    $line = $this->CI->lang->line('form_validation_error_message_not_set').'(Anonymous function)';
                }
                else
                {
                    $line = $this->_get_error_message($rule, $row['field']);
                }

                // Is the parameter we are inserting into the error message the name
                // of another field? If so we need to grab its "field label"
                if (isset($this->_field_data[$param], $this->_field_data[$param]['label']))
                {
                    $param = $this->_translate_fieldname($this->_field_data[$param]['label']);
                }

                // Build the error message
                $message = $this->_build_error_msg($line, $this->_translate_fieldname($row['label']), $param);

                // Save the error message
                $this->_field_data[$row['field']]['error'] = $message;

                if ( ! isset($this->_error_array[$row['field']]))
                {
                    $this->_error_array[$row['field']] = $message;
                }

                return;
            }
        }
    }

}