Adding a step to the Onepage Checkout – Magento
The default Magento onepage checkout includes six steps for the customer to complete. However, sometimes you may have a requirement to create an extra checkout step. An example of this might be an option for your customer to choose a free gift as part of their order, or an extra step to collect special delivery instructions. Using delivery instructions as an example, we’ll demonstrate how this can be achieved.
The first file we need to modify is app/code/core/Mage/Checkout/Block/Onepage.php. Obviously we don’t want to modify the code in the core context, so copy the file to app/code/local/Mage/Checkout/Block/Onepage.php. Magento will use this file automatically.
In the getSteps() method of this class there is an array of the step codes, in order of viewing. We need to add our own step code in this array, in the relevant place. For this example, the code will be deliveryinstructions so we will change the line to be this:
$stepCodes = array('billing', 'shipping', 'shipping_method', 'deliveryinstructions', 'payment', 'review');
Next, we need to create a new file – app/code/local/Mage/Checkout/Block/Onepage/Deliveryinstructions.php. As you can see from the name, this is the block file that runs our new step. You can use this class to do any special setup work for your block, but more than likely all you’ll need is this:
<?php class Mage_Checkout_Block_Onepage_Deliveryinstructions extends Mage_Checkout_Block_Onepage_Abstract { protected function _construct() { $this->getCheckout()->setStepData('deliveryinstructions', array( 'label' => Mage::helper('checkout')->__('Delivery Instructions'), 'is_show' => $this->isShow() )); parent::_construct(); } }
Now we need to make the actual template. The file we need is:
With the template created, we need to tell Magento about it by editing the
These are all inside the content reference area. At the position between all these files that you want your step to show, you need to add a reference to your new template. It should look something like this:
<block type="checkout/onepage_deliveryinstructions" name="checkout.onepage.deliveryinstructions" as="deliveryinstructions" template="checkout/onepage/deliveryinstructions.phtml"/>
Following on from that, we need to override the onepage controller to put in our extra step. How to actually override a controller is beyond the scope of this post, however the Magento wiki article on overriding controllers should tell you everything you need to know.
There are two parts we need to work with in our overridden controller, and the first one is the save
$result['goto_section'] = 'deliveryinstructions';
We also need to remove the following code, as we don’t need to update our delivery instructions section. We will need the code again for the next part, so keep it handy:
$result['update_section'] = array( 'name' => 'payment-method', 'html' => $this->_getPaymentMethodsHtml() );
The next change to this controller should be to add a new method, called saveDeliveryinstructionsAction. This method must perform two functions; it should call the method on the Onepage Model, and also pass any data back to the browser. The following code is an example of what we need, copied and modified from the saveShippingMethodAction method:
public function saveDeliveryinstructionsAction() { $this->_expireAjax(); if ($this->getRequest()->isPost()) { $data = $this->getRequest()->getPost('deliveryinstructions', ''); $result = $this->getOnepage()->saveDeliveryinstructions($data); /* $result will have error data if shipping method is empty */ if(!$result) { Mage::dispatchEvent('checkout_controller_onepage_save_deliveryinstructions', array('request'=>$this->getRequest(), 'quote'=>$this->getOnepage()->getQuote())); $this->getResponse()->setBody(Zend_Json::encode($result)); $result['goto_section'] = 'payment'; $result['update_section'] = array( 'name' => 'payment-method', 'html' => $this->_getPaymentMethodsHtml() ); } $this->getResponse()->setBody(Zend_Json::encode($result)); } }
In the last step we called a method that we haven’t defined yet, so we’ll do that now. This method is in the app/code/core/Mage/Checkout/Model/Type/Onepage.php file. Like the past few files, we are going to modify the saveShippingMethod method and also copy it to a new method. The saveShippingMethod code should be modified to look like this:
public function saveShippingMethod($shippingMethod) { if (empty($shippingMethod)) { $res = array( 'error' => -1, 'message' => Mage::helper('checkout')->__('Invalid shipping method.') ); return $res; } $rate = $this->getQuote()->getShippingAddress()->getShippingRateByCode($shippingMethod); if (!$rate) { $res = array( 'error' => -1, 'message' => Mage::helper('checkout')->__('Invalid shipping method.') ); return $res; } $this->getQuote()->getShippingAddress()->setShippingMethod($shippingMethod); $this->getQuote()->collectTotals()->save(); $this->getCheckout() ->setStepData('shipping_method', 'complete', true) ->setStepData('deliveryinstructions', 'allow', true); return array(); }
Unfortunately, there is no simple way to save the data that we collect. For example, information about a customer will be saved completely differently to shipping information like our delivery instructions; the delivery instructions will be saved differently again to how we would add a free gift to the order.
Because of this I won’t show you any code for actually saving the data you’ve captured, though we may address this in a follow up post at a later date. The following code is the new method that does not save anything:
public function saveDeliveryinstructions($deliveryinstructions) { // Save the data here $this->getCheckout() ->setStepData('deliveryinstructions', 'complete', true) ->setStepData('payment', 'allow', true); return array(); }
Next, we need to make a couple of JavaScript changes. These changes will be made in the
There is a class further down in the file called ShippingMethod, inside of which is a method called nextStep. We need to change it as per the above method so that the step goes to the delivery instructions step rather than the payment step. Then, again like before, we need to copy the whole class and modify it so that it’s now a DeliveryInstructions class, making sure to set the next step to be the payment step.
There is now one final requirement, which is to add in our step in the progress meter on the right side of the checkout. To do this we need to edit
<?php if ($this->getCheckout()->getStepData('deliveryinstructions', 'is_show')): ?> <?php if ($this->getCheckout()->getStepData('deliveryinstructions', 'complete')): ?> <li> <h4 class="complete"><?php echo $this->__('Delivery Instructions') ?> <span class="separator">|</span> <a href="#deliveryinstructions" onclick="checkout.accordion.openSection('opc-deliveryinstructions');return false;"><?php echo $this->__('Change') ?></a></h4> <div class="content"> <!-- Here write code to display the data the customer enters --> </div> </li> <?php else: ?> <li> <h4><?php echo $this->__('Delivery Instructions') ?></h4> </li> <?php endif; ?> <?php endif; ?>
We have now added a new step to our checkout and made it accessible in the same way as the rest of the checkout steps.
Enjoy coding…..:)
Your cranium must be proiecttng some very valuable brains.
ZIUMM0 Thanks again for the blog article.Really thank you! Cool.
UfTs8P Im thankful for the blog.Really thank you! Awesome.