Onepage Checkout – Add File Upload Field to any step

In this tutorial we are going to see how to add a file upload field to any of the magento checkout steps.

Adding file upload is a bit tricky in onepage checkout since all steps use ajax to save data. I am going to use jquery based ajax upload tool : Uplodify to setup the file uploading.
In the current module i have added the file upload field to billing step and payment method setup but following the same logic field can be added to any step. In the payment method section the file upload field is added to the purchase order payment method. Here are screenshot of the module

Admin Order View

Admin Order View

My Account Order View

My Account Order View

Onepage Checkout Billing Step

Onepage Checkout Billing Step

Onepage Checkout Payment Step

Onepage Checkout Payment Step

Attached is the source code of the module developed
[dm]28[/dm]
Lets see step by step explanation of the module source.

Step1: Add File Upload

First i have created a module name Excellence_File. The first step is to add the file upload field to the checkout billing step. For this first we need to override, the default checkout/onepage/billing.phtml file and put in our custom module. To this the following code is added to the layout xml of the module.

<checkout_onepage_index>
    	<reference name='head'>
    		<action method="addItem"><type>skin_js</type><name>uploadify/jquery-1.4.2.min.js</name></action>
    		<action method="addItem"><type>skin_js</type><name>uploadify/noconflict.js</name></action>
    		<action method="addItem"><type>skin_js</type><name>uploadify/jquery.uploadify.v2.1.4.js</name></action>
    		<action method="addItem"><type>skin_js</type><name>uploadify/swfobject.js</name></action>
    	</reference>
    	<reference name='checkout.onepage.billing'>
    		<action method='setTemplate'><template>file/checkout/onepage/billing.phtml</template></action>
    	</reference>
    </checkout_onepage_index>

As you can see in the above code the relevant uploadify java script files have been added to the checkout and the default billing.phtml file has been override to file/checkout/onepage/billing.phtml. In the new billing.phtml we add the upload field. Below code is added where you want to show the file upload field.

<ul class="form-list">
    	<li>
    		<label class="required"><em>*</em><?php echo $this->__('Please Upload Your Address Proof: ') ?><span id='file_upload_text'></span></label>
	        <div class="input-box">
				<input id="file_upload" type="file" name="file_upload" />
			</div>
			<input id="file_upload_path" type="hidden" name="file_upload_path" class='required-entry'  />
			<input type="hidden" value='billing_file' name="file_upload_type" class='required-entry'  />
    	</li>
    </ul>

Here important thing to notice is we have added two fields with name file_upload_path and file_upload_type which we will use later.
Next we need to initialize the uploadify java script

<script type="text/javascript">
// <![CDATA[
jQuery(document).ready(function() {
	jQuery('#file_upload').uploadify({
    'uploader'  : '<?php echo $this->getSkinUrl('uploadify/uploadify.swf') ?>',
    'cancelImg' : '<?php echo $this->getSkinUrl('uploadify/cancel.png') ?>',
    'script'    : '<?php echo $this->getUrl('file/index/upload') ?>',
    'auto'       : true,
    'fileDataName' : 'file',
    'fileExt'     : '*.jpg;*.gif;*.png',
    'fileDesc'    : 'Image Files',
    'onComplete' : function(event, ID, fileObj, response, data){
    	jQuery('#file_upload_path').val(response);
    	jQuery('#file_upload_text').html('File Uploaded: '+fileObj.name);
     }
    
  });
});
// ]]>
</script>

After doing the above step the file upload field should start showing up in the billing checkout step. Similarly we can add the upload field to the payment step as well, for details please check the source code.

Step2: Saving File

Now we need to create a controller where the actual file upload takes place. In the uploadify script we have set

    'script'    : '<?php echo $this->getUrl('file/index/upload') ?>'

as the file uploading url. So we next create the IndexController and uploadAction() as shown.

<?php
require_once 'Mage/Checkout/controllers/OnepageController.php';
class Excellence_File_IndexController extends Mage_Checkout_OnepageController
{
	public function uploadAction()
	{
		Mage::log($_FILES);
		if ($data = $this->getRequest()->getPost()) {
				
			$type = 'file';
			if(isset($_FILES[$type]['name']) && $_FILES[$type]['name'] != '') {
				try {
					$uploader = new Varien_File_Uploader($type);
					//$uploader->setAllowedExtensions(array('jpg','jpeg','gif','png'));
					$uploader->setAllowRenameFiles(true);
					$uploader->setFilesDispersion(true);
					$path = Mage::getBaseDir('media') . DS . 'uploads' . DS;
					$uploader->save($path, $_FILES[$type]['name'] );
					$filename = $uploader->getUploadedFileName();
						
				} catch (Exception $e) {
				}

			}
			echo $filename;
		}
	}
}

Here we are only using magento uploader to save file to hard disk and return the file path.

Step3: Saving File To Database

Now we need to save the file path of the uploaded file to the database. For each quote object, we need to save the file path and file type. Here file type is the custom input type added by me and not the media type. This file type field has been added just to identify later which file was uploaded in which step. To save the file to database, i have added an event observer for sales quote save after.

<sales_quote_save_after>
    			<observers>
    				<sales_quote_save_after>
    					<type>singleton</type>
	                    <class>Excellence_File_Model_Observer</class> <!-- Over Model Class -->
	                    <method>saveQuoteAfter</method> <!-- name of function -->
    				</sales_quote_save_after>
    			</observers>
    		</sales_quote_save_after>

and the code in the saveQuoteAfter() function is

public function saveQuoteAfter($evt){
		$quote = $evt->getQuote();

		$post = Mage::app()->getRequest()->getPost();

		if(isset($post['file_upload_path'])){
			$quote_id = $quote->getId();
			$filename = $post['file_upload_path'];
			$type = $post['file_upload_type'];
			Mage::log($quote_id.'xx'.$filename);
			Mage::getModel('file/file')->saveFile($quote_id,$filename,$type);
		}

	}

Here first we check if file_upload_path variable is found in $_POST. If it’s found then it gets saved to a custom database table created. The code to create the custom database is

DROP TABLE IF EXISTS {$this->getTable('quote_file')};
CREATE TABLE {$this->getTable('quote_file')} (
  `id` int(11) unsigned NOT NULL auto_increment,
  `quote_id` varchar(255) NOT NULL default '',
  `filename` varchar(255) NOT NULL default '',
  `type`  varchar(255) NOT NULL default '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
Step4: Transfer Files To Order

This is the final step where we need to associate the files uploaded to the order placed. For this i have added observer to the checkout_submit_all_after event.

<checkout_submit_all_after>
    			<observers>
                   <save_after> <!-- Any Unique Identifier -->
	                    <type>singleton</type>
	                    <class>Excellence_File_Model_Observer</class> <!-- Over Model Class -->
	                    <method>placeOrderAfter</method> <!-- name of function -->
                   </save_after>
		        </observers>
    		</checkout_submit_all_after>

and in the placeOrderAfter() function add the code

public function placeOrderAfter($evt)
	{
		$order = $evt->getOrder();
		$quote = $evt->getQuote();

		$quote_id = $quote->getId();
		$order_id = $order->getId();

		$collection = Mage::getModel('file/file')->getCollection();
		$collection->addFieldToFilter('quote_id',$quote_id);

		Mage::log('Observer Place Order After Quote ID:' . $quote_id);

		foreach($collection as $object){
			Mage::getModel('file/order')->saveFile($order_id,$object->getFilename(),$object->getType());
			$object->delete();
		}
// 		Mage::getModel('file/file')->resetUniqId();
	}

What this does is simply read all files from the quote table and add it a new order table. The structure of the order table is

DROP TABLE IF EXISTS {$this->getTable('order_file')};
CREATE TABLE {$this->getTable('order_file')} (
  `id` int(11) unsigned NOT NULL auto_increment,
  `order_id` varchar(255) NOT NULL default '',
  `filename` varchar(255) NOT NULL default '',
  `type`  varchar(255) NOT NULL default '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
This is a basic explanation of the module there are many more files in the source code attached. This module can be extended to support multiple file uploads as well.