2

Why is that form validation still return an error even though the input file is not empty

Here's my controller together with my callback function

public function add_post()
    {
            $validation = array (
                        array(
                            'field' => 'post_title',
                            'label' => 'Post title',
                            'rules' => 'trim|required|alpha_numeric_spaces'
                            ),
                        array(
                            'field' => 'post_desc',
                            'label' => 'Post Description',
                            'rules' => 'trim|required|alpha_numeric_spaces'
                            ),
                        array(
                            'field' => 'post_content',
                            'label' => 'Post content',
                            'rules' =>  'trim|required'
                            ),
                        array(
                            'field' => 'headerimage',
                            'label' => 'File',
                            'rules' => 'required|callback_file_check'
                            )

                    );

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

            if($this->form_validation->run()===FALSE)
            {
                $info['errors'] = validation_errors();
                $info['success'] = false;
            }
            else
            {
                $this->save_image();
                $datetime = $this->get_time();
                $data = array(
                            "post_title" => $this->input->post('post_title'),
                            "post_content" => $this->input->post('post_content'),
                            "post_image" => $this->uploadFileName,
                            "post_created" => $datetime
                        );

                $this->Blog_model->add_post($data);

                $info['success'] = true;
                $info['message'] = "Successfully added blog post";
           }
        $this->output->set_content_type('application/json')->set_output(json_encode($info));
    }

Here's the form mark-up

 <?php echo form_open_multipart('#',array("class"=>"form-horizontal","id"=>"blogform")); ?>
      <div class="row">
                <div class="col-md-6">
            <input type="text" placeholder="Enter your post title" name="post_title" class="form-control">
        </div>
          <div class="col-md-6 form-group">
            <label for="file" class="control-label col-md-4">Select header image:</label>
              <div class="col-md-8">
              <input type="file" id="header" name="headerimage" accept="image/*" />
            </div>
        </div>
      </div>
        <input type="text" placeholder="Enter your post description" name="post_desc" class="form-control">

        <label class="control-label text-muted">Post Body:</label>
           <div>
             <textarea id="post_content"></textarea>
           </div>
            <button class="btn btn-default pull-right" type="button" id="save-post"><i class="fa fa-save"></i> Save Post</button>
            <div class="clearfix"></div>
       <?php echo form_close(); ?>

I call the add_post controller function through AJAX.

$(document).on('click','#save-post',function(){
        $post_content = $('#post_content').summernote('code');
        $.ajax({
            url:site_url('Blog/add_post'),
            data: $('#blogform').serialize() + "&post_content=" + $post_content,
            type: "POST",
            dataType: 'json',
            encode: true,
            success: function(data){
                if(!data.success){
                    if(data.errors){
                        $('#blog-message').html(data.errors).addClass('alert alert-danger');
                    }
                }else{
                    alert(data.message);
                }
            }
        });
});

I don't understand why validation is not working properly or I think the controller doesn't get the input file.

2
  • Where you have this form_open_multipart('#') instead of hashtag should be a controller name codeigniter.com/user_guide/helpers/… Commented Mar 12, 2017 at 8:58
  • i call the controller through ajax Commented Mar 12, 2017 at 9:09

2 Answers 2

1

File upload data is not stored in the $_POST array, so cannot be validated using CodeIgniter's form_validation library. File uploads are available to PHP using the $_FILES array.

if (empty($_FILES['headerimage']['name']))
{
     $this->form_validation->set_rules('headerimage', 'file', 'required');

     // OR

     $this->form_validation->set_rules('headerimage', 'file', 'trim|required');
}

OR you can use below system/application/libraries/MY_form_validation.php

Example :

$this->form_validation->set_rules(

          // Field Name
          $file_field_name ,

          // Label
          "YOUR FILE LAEBL",

          // Rules
          "file_required|file_min_size[10KB]|file_max_size[500KB]|file_allowed_type[jpg,jpeg]|file_image_mindim[50,50]|file_image_maxdim[400,300]"
);

MY_form_validation.php

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

/*
* Rules supported:
* file_required
* file_allowed_type[type]
* file_disallowed_type[type]
* file_size_min[size]
* file_size_max[size]
* file_image_mindim[x,y]
* file_image_maxdim[x,y]
*/

class MY_Form_validation extends CI_Form_validation {

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

    function set_rules($field, $label = '', $rules = '')
    {
        if(count($_POST)===0 AND count($_FILES) > 0)//it will prevent the form_validation from working
        {
            //add a dummy $_POST
            $_POST['DUMMY_ITEM'] = '';
            parent::set_rules($field,$label,$rules);
            unset($_POST['DUMMY_ITEM']);
        }
        else
        {
            //we are safe just run as is
            parent::set_rules($field,$label,$rules);
        }    
    }

    function run($group='')
    {
        $rc = FALSE;
        log_message('DEBUG','called MY_form_validation:run()');
        if(count($_POST)===0 AND count($_FILES)>0)//does it have a file only form?
        {
            //add a dummy $_POST
            $_POST['DUMMY_ITEM'] = '';
            $rc = parent::run($group);
            unset($_POST['DUMMY_ITEM']);
        }
        else
        {
            //we are safe just run as is
            $rc = parent::run($group);
        }

        return $rc;
    }

    function file_upload_error_message($error_code)
    {
        switch ($error_code)
        {
            case UPLOAD_ERR_INI_SIZE:
                return 'The uploaded file exceeds the upload_max_filesize directive in php.ini';
            case UPLOAD_ERR_FORM_SIZE:
                return 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form';
            case UPLOAD_ERR_PARTIAL:
                return 'The uploaded file was only partially uploaded';
            case UPLOAD_ERR_NO_FILE:
                return 'No file was uploaded';
            case UPLOAD_ERR_NO_TMP_DIR:
                return 'Missing a temporary folder';
            case UPLOAD_ERR_CANT_WRITE:
                return 'Failed to write file to disk';
            case UPLOAD_ERR_EXTENSION:
                return 'File upload stopped by extension';
            default:
                return 'Unknown upload error';
        }
    }     

    function _execute($row, $rules, $postdata = NULL, $cycles = 0)
    {
        log_message('DEBUG','called MY_form_validation::_execute ' . $row['field']);
        //changed based on
        //http://codeigniter.com/forums/viewthread/123816/P10/#619868
        if(isset($_FILES[$row['field']]))
        {// it is a file so process as a file
            log_message('DEBUG','processing as a file');
            $postdata = $_FILES[$row['field']];


            //before doing anything check for errors
            if($postdata['error'] !== UPLOAD_ERR_OK)
            {
                $this->_error_array[$row['field']] = $this->file_upload_error_message($postdata['error']);
                return FALSE;
            }


            $_in_array = FALSE;        

            // If the field is blank, but NOT required, no further tests are necessary
            $callback = FALSE;
            if ( ! in_array('file_required', $rules) AND $postdata['size']==0)
            {
                // Before we bail out, does the rule contain a callback?
                if (preg_match("/(callback_\w+)/", implode(' ', $rules), $match))
                {
                    $callback = TRUE;
                    $rules = (array('1' => $match[1]));
                }
                else
                {
                    return;
                }
            }        

            foreach($rules as $rule)
            {
                /// COPIED FROM the original class

                // 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;
                    }

                    // Run the function and grab the result
                    $result = $this->CI->$rule($postdata, $param);

                    // 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;
                    }

                    // If the field isn't required and we just processed a callback we'll move on...
                    if ( ! in_array('file_required', $rules, TRUE) AND $result !== FALSE)
                    {
                        return;
                    }
                }
                else
                {                
                    if ( ! 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))
                        {
                            $result = $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;
                            }
                        }

                        continue;
                    }

                    $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;
                    }
                }

                //this line needs testing !!!!!!!!!!!!! not sure if it will work
                //it basically puts back the tested values back into $_FILES
                //$_FILES[$row['field']] = $this->_field_data[$row['field']]['postdata'];

                // 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];
                    }

                    // 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]) AND isset($this->_field_data[$param]['label']))
                    {
                        $param = $this->_field_data[$param]['label'];
                    }

                    // Build the error message
                    $message = sprintf($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;
                }                
            }        
        }
        else
        {
            log_message('DEBUG','Called parent _execute');
            parent::_execute($row, $rules, $postdata,$cycles);
        }
    }


    /**
    * Future function. To return error message of choice.
    * It will use $msg if it cannot find one in the lang files
    * 
    * @param string $msg the error message
    */
    function set_error($msg)
    {
        $CI =& get_instance();    
        $CI->lang->load('upload');
        return ($CI->lang->line($msg) == FALSE) ? $msg : $CI->lang->line($msg);
    }

    /**
    * tests to see if a required file is uploaded
    * 
    * @param mixed $file
    */
    function file_required($file)
    {
        if($file['size']===0)
        {
            $this->set_message('file_required','Uploading a file for %s is required.');
            return FALSE;
        }

        return TRUE;
    }

    /**
    * tests to see if a file is within expected file size limit
    * 
    * @param mixed $file
    * @param mixed $max_size
    */
    function file_size_max($file,$max_size)
    {
        $max_size_bit = $this->let_to_bit($max_size);
        if($file['size']>$max_size_bit)
        {
            $this->set_message('file_size_max',"%s is too big. (max allowed is $max_size)");
            return FALSE;
        }
        return true;
    }

    /**
    * tests to see if a file is bigger than minimum size
    * 
    * @param mixed $file
    * @param mixed $min_size
    */
    function file_size_min($file,$min_size)
    {
        $max_size_bit = $this->let_to_bit($max_size);
        if($file['size']<$min_size_bit)
        {
            $this->set_message('file_size_min',"%s is too small. (Min allowed is $max_size)");
            return FALSE;
        }
        return true;
    }    

    /**
    * tests the file extension for valid file types
    * 
    * @param mixed $file
    * @param mixed $type
    */
    function file_allowed_type($file,$type)
    {
        //is type of format a,b,c,d? -> convert to array
        $exts = explode(',',$type);

        //is $type array? run self recursively
        if(count($exts)>1)
        {
            foreach($exts as $v)
            {
                $rc = $this->file_allowed_type($file,$v);
                if($rc===TRUE)
                {
                    return TRUE;
                }
            }
        }

        //is type a group type? image, application, word_document, code, zip .... -> load proper array
        $ext_groups = array();
        $ext_groups['image'] = array('jpg','jpeg','gif','png');
        $ext_groups['application'] = array('exe','dll','so','cgi');
        $ext_groups['php_code'] = array('php','php4','php5','inc','phtml');
        $ext_groups['word_document'] = array('rtf','doc','docx');
        $ext_groups['compressed'] = array('zip','gzip','tar','gz');

        if(array_key_exists($exts[0],$ext_groups))
        {
            $exts = $ext_groups[$exts[0]];
        }

        //get file ext
        $file_ext = strtolower(strrchr($file['name'],'.'));
        $file_ext = substr($file_ext,1);

        if(!in_array($file_ext,$exts))
        {
            $this->set_message('file_allowed_type',"%s should be $type.");
            return false;        
        }
        else
        {
            return TRUE;
        }
    }

    function file_disallowed_type($file,$type)
    {
        $rc = $this->file_allowed_type($file,$type);
        if(!$rc)
        {
            $this->set_message('file_disallowed_type',"%s cannot be $type.");
        }

        return $rc;
    }




    //http://codeigniter.com/forums/viewthread/123816/P20/
    /**
    * given an string in format of ###AA converts to number of bits it is assignin
    * 
    * @param string $sValue
    * @return integer number of bits
    */
    function let_to_bit($sValue)
    {
        // Split value from name
        if(!preg_match('/([0-9]+)([ptgmkb]{1,2}|)/ui',$sValue,$aMatches))
        { // Invalid input
            return FALSE;
        }

        if(empty($aMatches[2]))
        { // No name -> Enter default value
            $aMatches[2] = 'KB';
        }

        if(strlen($aMatches[2]) == 1)
        { // Shorted name -> full name
        $aMatches[2] .= 'B';
        }

        $iBit   = (substr($aMatches[2], -1) == 'B') ? 1024 : 1000;
        // Calculate bits:

        switch(strtoupper(substr($aMatches[2],0,1)))
        {
            case 'P':
                $aMatches[1] *= $iBit;
            case 'T':
                $aMatches[1] *= $iBit;
            case 'G':
                $aMatches[1] *= $iBit;
            case 'M':
                $aMatches[1] *= $iBit;
            case 'K':
                $aMatches[1] *= $iBit;
            break;
        }

        // Return the value in bits
        return $aMatches[1];
    }    

    /**
    * returns false if image is bigger than the dimensions given
    * 
    * @param mixed $file
    * @param array $dim
    */
    function file_image_maxdim($file,$dim)
    {
        log_message('debug','MY_form_validation:file_image_maxdim ' . $dim);
        $dim = explode(',',$dim);

        if(count($dim)!==2)
        {
            //bad size given
            $this->set_message('file_image_maxdim','%s has invalid rule expected similar to 150,300 .');
            return FALSE;
        }

        log_message('debug','MY_form_validation:file_image_maxdim ' . $dim[0] . ' ' . $dim[1]);

        //get image size
        $d = $this->get_image_dimension($file['tmp_name']);

        log_message('debug',$d[0] . ' ' . $d[1]);

        if(!$d)
        {
            $this->set_message('file_image_maxdim','%s dimensions was not detected.');
            return FALSE;        
        }

        if($d[0] < $dim[0] && $d[1] < $dim[1])
        {
            return TRUE;
        }

        $this->set_message('file_image_maxdim','%s image size is too big.');
        return FALSE;
    }

    /**
    * returns false is the image is smaller than given dimension
    * 
    * @param mixed $file
    * @param array $dim
    */
    function file_image_mindim($file,$dim)
    {
        $dim = explode(',',$dim);

        if(count($dim)!==2)
        {
            //bad size given
            $this->set_message('file_image_mindim','%s has invalid rule expected similar to 150,300 .');
            return FALSE;
        }

        //get image size
        $d = $this->get_image_dimension($file['tmp_name']);

        if(!$d)
        {
            $this->set_message('file_image_mindim','%s dimensions was not detected.');
            return FALSE;        
        }

        log_message('debug',$d[0] . ' ' . $d[1]);

        if($d[0] > $dim[0] && $d[1] > $dim[1])
        {
            return TRUE;
        }

        $this->set_message('file_image_mindim','%s image size is too big.');
        return FALSE;
    }

    /**
    * attempts to determine the image dimension
    * 
    * @param mixed $file_name path to the image file
    * @return array
    */
    function get_image_dimension($file_name)
    {
        log_message('debug',$file_name);
        if (function_exists('getimagesize'))
        {
            $D = @getimagesize($file_name);

            return $D;
        }

        return FALSE;
    }
}



/* End of file MY_form_validation.php */
/* Location: ./system/application/libraries/MY_form_validation.php */
Sign up to request clarification or add additional context in comments.

Comments

0

For required file validation you can set validation as follow:

if (empty($_FILES['headerimage']['name']))
{
    $this->form_validation->set_rules('headerimage', 'File', 'required');
}

Remove:

array(
                            'field' => 'headerimage',
                            'label' => 'File',
                            'rules' => 'required|callback_file_check'
                            )

1 Comment

its not working still returns a required field error even though there is a file field is not empty

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.