Magento Checkout – How to customize the design

Customize checkout page!One of the most important parts of any e-commerce solution, including Magento, is checkout process. The number of orders is higly dependent on the simplicity, usability and even look of the checkout pages. That's why shop owners often decide to create custom design of those pages in their Magento website. With Magento templates system you can easily customize the look of any page, but in case of checkout, you have to keep some default constructions.

You can find the default template of checkout stages in app/design/frontend/.../.../templates/checkout/onepage.phtml. Here's the fragment of html output of that template:

<ol id="checkoutSteps" class="one-page-checkout">
    <li id="opc-login" class="section">
        <div class="head">
            ...<!-- head of  login section -->
        </div>
        <div id="checkout-step-login" class="box a-item" style="display:none;">
            ...<!-- body of login section -->
        </div>
    </li>
    ... <!-- next sections here -->
</ol>

As we can see, the checkout process is enclosed in an ordered list (<ol>...</ol>). It is quite a good idea, as the order of steps is very important here. Moreover, the default javaScript scripts are strongly related to the html structure (especially to the names of classes – we will look at them later). So it is suggested to preserve this default structure of an ordered list.

To improve the usability of the process, it is a good habit to give a different look to checkout steps headers, depending on the currently active stage. It is an easy task in Magento, because the included script dynamicaly adds or removes class names to the <li class="section"> elements of <ol id="checkoutSteps">. Those class names depend on the currently active step of checkout:

  • no additional class for all next steps after the current step
  • class="allow" for all previous steps
  • class="allow active" for the current step

A similar mechanism is applied in checkout progress - an additional column containing data of already passed checkout steps (billing address, shipping address, shipping method and payment method). The content of this column is changed dynamically, too, according to the current step. The example below show the checkout progress block's html structure after the "billing information" checkout step:

<div class="box one-page-checkout-progress">
    <h3>Your Checkout Progress</h3>
    <ol>
        <li>
            <h4 class="complete">Billing Address                  
                <span class="separator">|</span>                  
                <a href="#billing" onclick="checkout.accordion.openSection('opc-billing'); return false;">Change</a>
            </h4>
            <div class="content">
                ... <!-- billing address here -->
            </div>
        </li>
        <li>
            <h4>Shipping Address</h4>
        </li>
        <li>
            <h4>Shipping Method</h4>
        </li>
        <li>
            <h4>Payment Method</h4>
        </li>
    </ol>
</div>

The checkout progress is an ordered list, too. The steps that are not yet passed contain only <h4> title. In the other steps there is a class="complete" added to <h4> so we can make the titles of previous steps look different then the titles of next steps.

Those are the basis of the Magento checkout page structure. Understanding and remembering that strucutre can be very helpful during custom Magento shop development.

Magento Presentation (Magento Comics)

Magento comics by Baobaz

Translation by Anne Blot

How to use Magento Shipping Table Rates

With Magento you can set few kinds of shipping methods: flat rate, table rates or even real-time carrier rates from UPS, FedEx and DHL. You can also use free shipping discounts, that can be created for order amounts, or as part of specific marketing promotions.

To set shipping methods in backoffice go to System -> Configuration and choose from the left navigation "Shipping methods". When you want to use Table rates you can choose one of three conditions avalaible:

  • Weight vs. Destination
  • Price vs. Destination
  • Number of Items vs. Destination

You also need to create csv file for your table rates. You can first export one from magento to have a template. To do that you will need to change scope for your website in "Current Configuration Scope" (top left select box). Choose "Main website" for example. Then in Table rates you will be able to see "Export CSV" button.

Export table rates

Export and save tablerates.csv on your computer. The CSV file should looks like:

"Country","Region/State","Zip/Postal Code","Weight (and above)","Shipping Price"
"FRA","*","*","0.0000","11.0000
"FRA","*","*","10.0000","13.000"
"FRA","*","*","20.0000","15.0000"

Above lines define shipping rates for all regions in France. As you see weight condition is set as "from and above". So when order wieght is 0 and above (0-10 kg) shipping wil cost 11 euros, when its 10 and above (10 - 20 kg) shipping is 13 euros. When order weight is above 20 kg you will pay 15 euros for shipping. Even when it's 100 kg or 1000 kg you will still pay 15 euros! The problem with condition "from and above" is that you are unable to set maximum weight. So lets make these conditions work in "up to" way.
In "up to" way tablerates.csv like:

"Country","Region/State","Zip/Postal Code","Weight (and above)","Shipping Price"
"FRA","*","*","10.0000","13.000"
"FRA","*","*","20.0000","15.0000"

will define that for order which weight is up to 10 kg (0-10 kg) shipping cost is 13 euros. For orders up to 20 kg (10 - 20 kg) it's 15 euros. So the maximum weight for table rate is 20kg. Above 20kg you will have no table rate available.

To make table rates work that way function getRate() from Mage_Shipping_Model_Mysql4_Carrier_Tablerate needs to be overwriten. First we need to create new module under /code/local directory and configure it:

Magento module

<?xml version="1.0"?>
<config>
    <global>
        <models>
            <shipping_mysql4>
                <rewrite>
                    <carrier_tablerate>Baobaz_Shipping_Model_Mysql4_Carrier_Tablerate</carrier_tablerate>
                </rewrite>
            </shipping_mysql4>
        </models>
    </global>
</config>

 

Our module needs to be activated by creating file Boabaz_Shipping.xml in app/etc/modules directory:

<?xml version="1.0"?>
<config>
    <modules>
        <Baobaz_Shipping>
            <active>true</active>
            <codePool>local</codePool>
        </Baobaz_Shipping>
    </modules>
</config>

In new module create class Baobaz_Shipping_Model_Mysql4_Carrier_Tablerate that will extend core magento class Mage_Shipping_Model_Mysql4_Carrier_Tablerate.

class Baobaz_Shipping_Model_Mysql4_Carrier_Tablerate extends Mage_Shipping_Model_Mysql4_Carrier_Tablerate
{}

Then lets copy/paste getRate(Mage_Shipping_Model_Rate_Request $request) function from core class and change few last lines at the end of the function:

$select->where('condition_value<=?', $request->getData($conditionName));

$select->order('condition_value DESC');

to:

$select->where('condition_value>=?', $request->getData($conditionName));

$select->order('condition_value ASC');

That will make table rates conditions work in "up to" way.

Custom contact forms with eZ-Gento

Magento contains built in simple contact form module, that allows to send a query by customer to web shop owner. Although it's not easy to customize it and fit into client's needs. As eZ Gento allows to load any content from eZ Publish, it also allows to use contact forms created by eZ Publish, which are much more flexible and gives a lot of possibilities that can be used on Magento site.

First of all, it is possible to add much more fields of different type, not only string or text area, but also email, selection, checkbox, date/time fields. All of this of course with data validation. Even more, thanks to eZ Publish extensibility, it's possible to develop our own specific data type. No limitations here!

Custom contact form

Next, it is possible to create as much different contact forms as client wants. Each of form can have different fields and fit to different part of website. When submitted, email will be sent to different recipients, for example depending on selected subject. Besides, everything can have many translations so fields will be displayed in proper language, exactly as it is presented in "eZ Gento - content translation" post.

Last important thing is that collected informations can be easily accessed from backoffice. It is also possible to export it or schedule for sending exports everyday. All of that thanks to eZ Gento that brings great content management functions to Magento!

http://www.ezgento.org/

Magento Taxes

Whether you are retailer or wholesaler you might need to manage taxes in your online store. Magento allows to add tax classes for products and customers in Sales > Tax tab. In here you are also able to set Tax rates for products that will be exported to other countries (Import/Export Tax Rates) .

Magento Tax menu

When you have set products tax classes, you may choose to display prices in catalog  to include TVA or not. For that you need to set value for "Catalog prices include tax" in System > Configuration > Sales > Tax > Calculation.

Tax calculation - Magento

When you define promotions in your store remember to define which prices it will be applied to: prices with or without TVA by setting "Apply Discount On Prices Including Tax". This is important because in some place your customers may end up with discount prices but TVA still calculated from the original price.

eZ Gento demo at Paris e-Commerce conference 2009

Paris e-Commerce convention 2009 is also the opportunity to demonstrate new technologic innovations.

Olivier Ouin showed a demo to Yoav Kutner, Varien CTO - the Magento Company, of our eZ Publish / Magento mashup, eZ Gento.

eZ Gento demo to Yoav Kutner by Olivier Ouin

eZ Gento demo to Yoav Kutner by Olivier Ouin

eZ Gento demo to Yoav Kutner by Olivier Ouin

Magento Dataflow - Optimized Product Import [Part 3]

Magento Dataflow module comes with standard product adapter (see Magento Dataflow - Default Adapters [Part 2]). Sometimes though, default solution is not enough and you may want to create your own adapter processing products.

Creating own adapter is not hard, but if you forget two lines of code, you may be very surprised with its performance. These two lines you should add before calling $product->save():

$product->setIsMassupdate(true);
$product->setExcludeUrlRewrite(true);

First line sets $data variable 'is_massupdate', which can be later checked to save some postprocessing actions time. Some observers watching for catalog_product_save_after event check this value (i.e. CatalogRule module's Observer, which skips action of applying catalog rules on products if  $product->getIsMassupdate() returns  true )

Second line also sets $data variable 'exclude_url_rewrite', which is used by afterSave method of Mage_Catalog_Model_Product_Attribute_Backend_Urlkey to check if catalog url rewrite cache should be refreshed.

Those two lines allow you to save few seconds per each row of processed products, so keep in mind to not forget about them

Magento 1.3.2.4 : Remember to update your Magento installation

Like all softwares, e-commerce solution Magento is not bug free. These bugs are quickly noticed and fixed by Varien.

Wednesday 23 Septembre, version 1.3.2.4 of Magento was released. It is strongly advised to update your Magento installation because this version fixes a possible XSS issue on customer registration page.

Here are different ways for updating Magento:

1. Update with diff file

For each new version, Magento provides a .diff file to download at this url: http://www.magentocommerce.com/download/diff. All that we must do then is download this file in our Magaento main directory and type command:

patch -p0 < 1.3.2.3-1.3.2.4.diff

2. Update with Magento Connect Manager

In admin part of your site, go to System > Magento Connect > Magento Connect Manager. Update then extension  Mage_Core_Modules. If this extension is not installed, key for installing it is  magento-core/Mage_Core_Modules.

More information:

Magento Translations Module

One of best things in magento is you can easily add new language versions to the site. It's all done using CSV files - 1 file for 1 module per language. For most popular languages translations are available as free Magento modules on magento connect.

What if we want to change the translations and don't lose changes after updating magento and translations? Or if we want to have translation common independently of module? Or just list all the texts that were added by us and are not original magento texts? We can create our own very simple translations module.

You need to edit only 2 files:

  • etc/config.xml
    <?xml version="1.0"?>
    <config>
            <modules>
                    <Baobaz_Translations>
                            <version>0.1.0</version>
                    </Baobaz_Translations>
            </modules>
           
            <global>
                    <helpers>
                            <translations>
                                    <class>Baobaz_Translations_Helper</class>
                            </translations>
                    </helpers>
            </global>
           
            <frontend>
                    <translate>
                            <modules>
                                    <Baobaz_Translations>
                                            <files>
                                                    <default>Baobaz_Translations.csv</default>
                                            </files>
                                    </Baobaz_Translations>
                            </modules>
                    </translate>
            </frontend>

    </config>
  • and helper/data.php:
    class Baobaz_Translations_Helper_Data extends Mage_Core_Helper_Abstract
    {

    }

and that's it... Module must be turned on (in etc/modules) and it will work fine.

in app/locale/fr_fr/ we create file Baobaz_Translations.csv and in it we put all the new translations we need.

Then, anywhere in magento - doesn't matter if it's template, block or controller - we can use:

echo Mage::helper('translations')->__('text to translate');

Yes. It is that simple.

Magento module: Create your own controller

Respect my authoritahMagento is based on MVC model. This model helps for defining models, view (layout + templates) and controllers. Despite big amount of modules available by default in Magento and on Magento Connect, you may want to create your own module and define your controller for you Magento website. No problem, this tutorial will explain you how to create your own controller and how to make it respect its authoritah to layouts and templates.

Purpose of this example controller will be to give result of two integers multiplication (very useful if you lost your calculator). Integers will be provided through a basic form. Result will be displayed in a Magento notification.

Before starting creation of your module, please turn off the cache management in order to see immediately every change.

Creating your module

Our extension will be named arithmetic. Folders needed for this extension are created.

$ mkdir -p app/code/local/Baobaz/Arithmetic/controllers
$ mkdir -p app/code/local/Baobaz/Arithmetic/etc

We create file app/code/local/Baobaz/Arithmetic/etc/config.xml, in order to register this extension

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <baobaz_arithmetic>
            <version>0.0.1</version>
        </baobaz_arithmetic>
    </modules>
</config>

And a file app/etc/modules/Baobaz_Arithmetic.xml for its activation:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <Baobaz_Arithmetic>
            <active>true</active>
            <codePool>local</codePool>
        </Baobaz_Arithmetic>
    </modules>
</config>

For more informations about creation of a new extension, please check Wojtek's post "Developing module for Magento Tutorial - Where to Begin [Part 1]".

Creating a controller

You need now to create file app/code/local/Baobaz/Arithmetic/controllers/IntegerController.php and write method that will be used for multiplication.

Controller files must always follow pattern xxxxxController.php (xxxxx will be used after in url for calling this controller) and put in controllers folder.
Controllers methods names must follow pattern yyyyyAction (yyyyy will also be used in url for calling this controller). For the moment content of our file is:

class Baobaz_Arithmetic_IntegerController extends Mage_Core_Controller_Front_Action
{
    public function multiplyAction(){
    }
}

We need to indicate now that some controllers are available in Arithmetic modules. For doing that, we add the following content in app/code/local/Baobaz/Arithmetic/etc/config.xml file:

<config>
    ...
    <frontend>
        <routers>
            <arithmetic>
                <use>standard</use>
                <args>
                    <module>Baobaz_Arithmetic</module>
                    <frontName>arithmetic</frontName>
                </args>
            </arithmetic>
        </routers>  
    </frontend>
</config>

Let see how this router declaration works:

  • <frontend> indicates that router will be use in front part of website
  • <routers> is where you declare all your routers
  • <arithmetic> is identifier of this router
  • <use>standard</use> can take value standard (for front part) or admin (for admin part).
  • <module>Baobaz_Arithmetic</module> indicates which module contain controller that handles this router
  • <frontName>arithmetic</frontName> is router name that will be used in url

We can now modify multiplyAction method for making it displaying a message:

public function multiplyAction(){
    echo "Respect my authoritah";
}

When you call now url http://monsitemagento/arithmetic/integer/multiply message "Respect my authoritah" will be displayed. Let dissect this url:

  • arithmetic tells that controller is in Baobaz_Arithmetic module
  • integer tells that controllers/integerController.php file must be cehcked
  • multiply tells that multiplyAction method must be chosen in this file

Displaying a template

We define which layout file will be used in the module:

<config>
    ...
    <frontend>
        ...
        <layout>
            <updates>
                <arithmetic>
                    <file>arithmetic.xml</file>
                </arithmetic>
            </updates>
        </layout>
    </frontend>
</config>

We create app/design/frontend/default/default/layout/arithmetic.xml file in order to define which blocks will be used for controller that was just made.

<?xml version="1.0" encoding="UTF-8"?>
<layout version="0.1.0">
    <arithmetic_integer_multiply>
        <reference name="root">
            <action method="setTemplate">
                <template>page/1column.phtml</template>
            </action>
        </reference>
        <reference name="content">
            <block type="core/template" name="arithmetic_integer_multiply" template="arithmetic/integer/multiply.phtml"></block>
        </reference>
    </arithmetic_integer_multiply>
</layout>

Main template used by arithmetic/integer/multiply is page/1column.phtml. For "content" part of this template, only arithmetic_integer_multiply block will be displayed. This block does not need any particular management. It is then set with core/template type that is default type. Template file used will be arithmetic/integer/multiply.phtml.

Our template is defined, app/design/frontend/default/default/template/arithmetic/integer/multiply.phtml must then be created. This file will be empty for the moment..

For displaying correctly layout, it must be loaded in controller

public function multiplyAction(){
    $this->loadLayout();
    $this->renderLayout();
}

Interaction between template and controller

Our template will just have a basic form for providing integers to multiply

<form action="<?php echo Mage::getUrl('arithmetic/integer/multiply') ?>" method="post">
    <fieldset>
        <ul>
            <li>
                <label for="int1">Integer 1</label>
                <input type="text" id="int1" name="int1" />
            </li>
            <li>
                <label for="int2">Integer 2</label>
                <input type="text" id="int2" name="int2" />
            </li>
            <li><input type="submit" value="Multiply" /></li>
        </ul>
    </fieldset>
</form>

Action form url is again arithmetic/integer/multiply. Controller action must then be modified in order to manage data from form and to give result.

public function multiplyAction(){
    if ($this->getRequest()->isPost()){
        $int1 = $this->getRequest()->getPost('int1');
        $int2 = $this->getRequest()->getPost('int2');
        $result = $int1 * $int2;
    Mage::getSingleton('customer/session')->addSuccess("$int1 * $int2 = $result");
    }
    $this->loadLayout();
    $this->_initLayoutMessages('customer/session');
    $this->renderLayout();
}

In order to know if controller is called after using form, following instruction is used:

$this->getRequest()->isPost()

Result is put in 'customer/session' session. For being able to display this result in template, message template must be loaded in multiplyAction method:

$this->_initLayoutMessages('customer/session');

Add then in you template the following line where you want to display the result

echo $this->getMessagesBlock()->getGroupedHtml();

And here we are: we have now a new controller that displays result of a multiplication.

All files used in this tutorial are available in baobaz_arithmetic.tar.gz archive.

This post was inspired by Alan Storm's post Magento Front Controller. Take a look at his very interesting blog.