Codeigniter website screenshot

Codeigniter form with integrated data and file validation

Facebooktwittergoogle_pluspinterestlinkedintumblrmail

In one application built on Codeigniter framework I needed to create a special form. The form had to allow a user to both enter text data and upload an image. It was actually the first time I had any experience with programming forms that would include file upload. And I found out it is not a completely trivial task in Codeigniter. There are multiple reasons for this, but the main one is this: image validation and form data validation are done separately in Codeigniter. Not only the validation is done by two different classes, but the error messages are also independent.

So I went on to create a form that would integrate both types of validation (or rather, the error messages). On the way I had to decide on one major usability problem: what to do with image if the user submits the right image but the rest of the form does not validate (for example, forgets to enter name or email). I decided to go with the simplest option – the image is discarded, and the user is warned to not forget to attach the image again (since the file upload field cannot be repopulated, as other fields can be).

Admittedly, the image could be saved to save user the trouble of attaching it again, but in that case you have to solve another problem – what to do with the images that the user (a malicious user) submits and subsequently fails to submit the properly filled in form. I know, something could be done at the browser level using Javascript validation, but the server-side validation is still necessary…

So, anyway, here is the fruit of my work. The form has two fields – Username field and Userimage field. The Userimage field is required and should contain more than 5 symbols. The image field is not required (though you can make it required easily enough – see comments in the code). The Image should be a png, jpg or gif no larger than 100kb with max width of 2048 and height of 1536 pixels (of course, you can set your own requirements in the code).

I had to create three files to make this form and also one directory.

You have to create directory ./assets/uploads/ in Codeigniter base installation directory and to give the uploads directory full access permissions – 777. Otherwise the images will not be saved into this directory.

First file – ./application/libraries/My_Form_validation.php – extends the Codeigniters’s Form Validation library to allow adding custom error messages, that we need for integration of the image validation and form validation. We get a function setError() which allows to add validation errors even to fields that cannot be checked via the default Form Validation library:

<?php defined('BASEPATH') OR exit('No direct script access allowed');
 
class MY_Form_validation extends CI_Form_validation
{
   function __construct($config = array())
   {
      parent::__construct($config);
   }
 
   /**
   * sets the error message associated with a particular field
   *
   * @param   string  $field  Field name
   * @param   string  $error  Error message
   * used in controller like this: 
   * $this->form_validation->setError('username', 'Invalid login credentials');
   * added from: https://stackoverflow.com/a/35165483
   */
   public function setError($field, $error)
   {
      $this->_error_array[$field] = $error;
   }
}
/* end of file ./application/libraries/My_Form_validation.php */

Now every time we call $this->load->library('form_validation'); in the code, this extended library and not the default Codeigniter Form Validation library will be loaded.

Second file is the controller file – ./application/controllers/Upload.php – that does all the processing:

<?php defined('BASEPATH') OR exit('No direct script access allowed');
 
class Upload extends CI_Controller {
 
   public function __construct()
   {
      parent::__construct();
      $this->load->helper(array('form', 'url'));
   }
 
   public function index()
   {
      redirect('upload/do_upload');
   }
 
   public function do_upload()
   {
      $image = FALSE; //by default file is not uploaded
      $data = array();
 
      // here we set the image validation parameters
      // specify requirements for files: upload directory, allowed types, 
      // whether to convert extensions to lowercase, maximum dimensions and size
      $config['upload_path']          = './assets/uploads/';
      $config['allowed_types']        = 'gif|jpg|png';
      $config['file_ext_tolower']     = TRUE;
      $config['max_size']             = 100;
      $config['max_width']            = 2048;
      $config['max_height']           = 1536;
 
      // loading the required libraries
      $this->load->library('upload', $config);
      $this->load->library('form_validation');
 
      // setting validation rules for the username field
      // the userimage cannot be checked using form_validation library, so
      // we leave it for now
      $this->form_validation->set_rules('username', 'Username', 'required|min_length[5]');
 
      // checking if a file was attached
      // if you omitt this "if" check and go straight to the 
      // "if ( ! $this->upload->do_upload('userfile'))" check,
      // the image field will become required and produce an error message if empty
      if ( ! empty($_FILES['userfile']['name'])) 
      {
         // if it was attached, but does not conform to config requirements:
         if ( ! $this->upload->do_upload('userfile')) 
         {
            // save image error message to $image variable (replacing FALSE)
            // we strip tags (<p> tags in this case) to avoid double tags later, 
            // when we assign this variable to form validation errors array 
            // (form validation adds its own tags)
            $image = strip_tags($this->upload->display_errors()); 
         }
         // if attached file is ok:
         else 
         {
            // mark that there is a file and it is ok
            $image = TRUE; 
            // save the file data for later usage
            $data['upload_data'] = $this->upload->data(); 
         }
      }
 
      // case the form validation is not passed/nothing is submitted
      // or the image did not pass validation
      if ($this->form_validation->run() === FALSE || is_bool($image) === FALSE)
      {
         // deciding what to do with the file;
         // if there was a file error, $image variable will not be boolean (TRUE/FALSE)
         if (is_bool($image) === FALSE) 
         {
            $this->form_validation->setError('userfile', $image);
         }
         // if a proper image was submitted
         if ($image === TRUE) 
         {
            // since the form did not pass, we remove the image, and if removal is 
            // successful (unlink returns TRUE), we null the image-related variables, 
            // since we will not use the image
            if (unlink($data['upload_data']['full_path'])) $data['upload_data'] = NULL; 
            // since it is impossible to repopulate the file upload field due to browser
            // security, we remind the user to reattach image in a custom "error" message
            $message = 'Do not forget to <b>reattach image</b> it was a proper image.';
            $this->form_validation->setError('userfile', $message); 
         }
         // this is where the view would be loaded in normal form
         //$this->load->view('upload_form_view.php', $data); 
      }
      // case of successfully filled in form
      else 
      {
         // here we would normally save the form data to database, use $this->session->set_flashdata() 
         // to set success message and then use redirect() to send user to another page; in our case we
         // will simply set the success message to be displayed on the main form page. 
         // Since the file was uploaded successfully we already have the array $data['upload_data'] with
         // with the image properties that will also be printed, no need to set it
         $data['announce'] = '<p>You submitted Username: <b>' . $this->input->post('username') . '</b></p>';
      }
   $this->load->view('upload_form_view.php', $data);
   }
}
/* end of file ./application/controllers/Upload.php */

The third file is the view file – ./application/views/upload_form_view.php – that contains the display logic. There is only one view file for simplicity purposes, it displays both an empty form, a success message when the data was ok with the data entered and the image uploaded, and the error messages if the data was not ok:

<?php defined('BASEPATH') OR exit('No direct script access allowed'); ?>
<html>
<head>
<title>Upload Form</title>
<meta charset="utf-8">
</head>
<body>
<h3>Simple form with image upload</h3>
<div style="color:red;">
<?php echo validation_errors(); ?>
</div>
 
<?php echo form_open_multipart('upload/do_upload');?>
 
<h4>Username</h4>
<input type="text" name="username" size="20" value="<?php if(empty($announce)) echo set_value('username'); 
/*checking for value of $announce would not be needed if upon success the page got redirected*/ ?>"  />
<h4>Image</h4>
<input type="file" name="userfile" size="20" />
 
<br /><br />
<input type="submit" value="submit" />
 
<?php if (isset($announce)) { echo '<hr><h3>Form was successfully submitted!</h3>'; echo $announce; } ?>
 
<?php 
if (isset($upload_data))
{ ?>
<p><b>Data of the uploaded file: </b></p>
<ul>
<?php foreach ($upload_data as $item => $value):?>
<li><?php echo $item;?>: <?php echo $value;?></li>
<?php endforeach; ?>
</ul>
<img src="<?=site_url('assets/uploads/'. $upload_data['file_name'])?>" 
   alt="<?=$upload_data['file_name']?>" 
   border="0" <?=$upload_data['image_size_str']?> >
<?php } ?>
 
</form>
</body>
</html>
<?php  /* end of file ./application/views/upload_form_view.php */ ?>

Once you have saved these files, you can navigate to

http://yourwebsite/upload/

and test the form.

I hope the comments in the controller file explain clearly enough the way the code works. If you have suggestions on how to improve the code, leave them in comments. Thank you.

Facebooktwittergoogle_pluspinterestlinkedintumblrmail