Author Archive

Tutorial: Introducing The Magento Layout


  

In this tutorial, we introduce the Magento layout by creating a simple module that will add some custom HTML content to the bottom of every customer-facing page, in a non-intrusive manner. In other words, we will do so without actually modifying any Magento templates or core files. This kind of functionality is a common requirement for many things such as affiliate referral programs, customer tracking analytics, adding custom JavaScript functionality, etc.

We will be covering a number of interesting topics, including:

  • Magento layout handles
  • Layout XML files
  • Blocks and templates
  • An alternative to Widgets

Once you have understood this article, you will have all the information you need to integrate popular third-party tools such as:

  • Google Analytics
  • Reinvigorate
  • CrazyEgg

Third-Party Tools Google Analytics, Reinvigorate and CrazyEgg

Before We Start

This tutorial assumes that you are already familiar with creating your own Magento module, so if you haven’t already done so, I recommend reading our previous article on Creating a Magento Module.

Okay, lets get started. The file structure of our new module should look like this:

app
  - code
      - local
          - SmashingMagazine
              - Layout
                  - etc
                      - config.xml

  - etc
      - modules
          - SmashingMagazine_Layout.xml

We can leave config.xml empty for now, as we will be populating it later on in the tutorial, but we can already complete the content of SmashingMagazine_Layout.xml, with:

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

Introducing The Magento Layout

The content that is displayed on each Magento page is largely determined by the layout XML files, which can be found in app/design/frontend/base/default/layout. In these XML files, you will see a number of snippets of XML enclosed in layout handle parent nodes, which are used to determine the type of page being displayed. For example, the handle catalog_product_view is used to add content to the product detail page, and checkout_cart_index the basket page.

Child nodes are then created inside these layout handles to determine which content should appear on any particular page. These child nodes are called blocks, which may in turn contain child blocks of their own. For example, in a layout XML file for the product page (see app/design/frontend/base/default/layout/catalog.xml), under the catalog_product_view layout handle, we might find a block for displaying the product wrapper template. Then as children of that block, we would find a block for the product image template, a block for displaying the price template and another for displaying the add to basket template.

Each of these blocks has a template associated with it. In Model–View–Controller (MVC) terms, the block acts as a mini controller and the template acts as the view. All of the logic for displaying dynamic content is found in the block, which is requested by the template and displayed in HTML form.

The Magento layout is quite a complex, yet very powerful, beast, and as a result we will only cover the parts that are relevant to this tutorial. There is a layout handle named default which is included on every page, and since we want our module’s HTML content to appear at the bottom of every page, this is the layout handle we will use.

Adding A New Layout File

We need to define a new layout file to contain our updates, so first we need to modify our module’s config.xml to cater to layout updates:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <SmashingMagazine_Layout>
            <version>0.0.1</version>
        </SmashingMagazine_Layout>
    </modules>
    
    <!-- we are making changes to the frontend -->
    <frontend>
        
        <!-- we are making changes to the layout -->
        <layout>
            
            <!-- we are adding a new update file -->
            <updates>
                
                <!-- 
                    this child node name must be
                    unique throughout Magento
                -->
                <smashingmagazine_layout 
                         module="SmashingMagazine_Layout">
                    
                    <!-- the name of the layout file we are adding -->
                    <file>smashingmagazine_layout.xml</file>
                    
                </smashingmagazine_layout>
                
            </updates>
            
        </layout>
        
    </frontend>

</config>

Now let’s create this layout XML file here:

app
  - design
      - frontend
          - base
              - default
                  - layout
                      - smashingmagazine_layout.xml

Now those of you who have a little Magento experience, or have read any more noteworthy Magento tutorials, may be gasping at the fact we are making changes in base/default since this is where Magento core files are located. However, we are not modifying any files here, we are creating new ones, and furthermore we are prefixing our file name with “smashingmagazine,” so there is very little chance of this conflicting with other modules or causing any issues with upgrading Magento in the future.

The content of our new layout file will be as follows:

<?xml version="1.0" encoding="UTF-8"?>

<!-- all layout files begin with this node -->
<layout>
    
    <!-- this is the layout handle we are interested in -->
    <default>
        
        <!-- this is the name of the block we want to add to -->
        <reference name="before_body_end">
            
            <!-- 
                here we define our new block and template 
                to be added to 'before_body_end' 
            -->
            <block type="core/template" 
                   name="smashingmagazine_layout_footer" 
                   template="smashingmagazine_layout/footer.phtml" />
            
        </reference>
        
    </default>
    
</layout>

Here we have referenced an existing block, before_body_end, in order to add our own block, smashingmagazine_layout_footer, as its child. before_body_end is the name of a block that has its content output just before the </body> tag of the page HTML. You can find the definition of this parent block by looking in app/design/frontend/base/default/layout/page.xml, and the content of this block is output in the .phtml templates within app/design/frontend/base/default/template/page.

By using reference, we are able to add content to existing blocks without needing to modify core Magento files. For example, we could achieve the same result as the above snippet by modifying app/design/frontend/base/default/layout/page.xml directly and adding our block code, but this is not good practice:

<default translate="label" module="page">
    <label>All Pages</label>
    <block type="page/html" name="root" output="toHtml" 
           template="page/3columns.phtml">

        ...

        <block type="core/text_list" name="before_body_end" 
               as="before_body_end" translate="label">
            
            <label>Page Bottom</label>
            
            <block type="core/template" 
                   name="smashingmagazine_layout_footer"
                   template="smashingmagazine_layout/footer.phtml" />
            
        </block>

        ...

    </block>
</default>

Adding A New Template File

We have now defined a new block, smashingmagazine_layout_footer, and assigned the template smashingmagazine_layout/footer.phtml to contain our HTML content. Let’s create that template file now:

app
  - design
      - frontend
          - base
              - default
                  - template
                      - smashingmagazine_layout
                          - footer.phtml

The content of footer.phtml can be whatever we like, but for this tutorial we will create a very simple template containing an image, functionality which is often required by affiliate tracking integrations:

<img 
    src="http://media.smashingmagazine.com/themes/smashingv4/images/logo.png?date=YYYY-MM-DD"
    width="459"
    height="120"
/>

Now it’s time to take a look at the front end. Upon viewing any customer-facing Magento page, you should see an image displayed at the bottom, and upon viewing the source of the page, you will see it has been included just before the <body/> tag.

Creating A New Custom Block

Now we want to tie in some simple logic to our template. The source of the image in our template includes a parameter “date” which currently contains a static value of “YYYY-MM-DD.” We want to be able to dynamically populate this parameter with the current date, for which we require some logic from our associated block.

At the moment, our template is associated with the default block type core/template, which only allows us some basic abstract block functionality. Therefore we must create our own custom block.

First let’s modify the block type definition in smashingmagazine_layout.xml:

...

<block type="smashingmagazine_layout/footer" 
       name="smashingmagazine_layout_footer"
       template="smashingmagazine_layout/footer.phtml" />

...

Next let’s update our module’s config.xml to cater to custom blocks:

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

        <!-- we are adding a new block definition -->
        <blocks>
            
            <!-- a unique short name for our block files -->
            <smashingmagazine_layout>
                
                <!-- the location of our module's blocks -->
                <class>SmashingMagazine_Layout_Block</class>

            </smashingmagazine_layout>

        </blocks>

    </global>
    <frontend>
        <layout>
            <updates>
                <smashingmagazine_layout
                         module="SmashingMagazine_Layout">
                    <file>smashingmagazine_layout.xml</file>
                </smashingmagazine_layout>
            </updates>
        </layout>
    </frontend>
</config>

Now let’s create this custom block PHP file:

app
  - code
      - community
          - SmashingMagazine
              - Layout
                  - Block
                      - Footer.php

Finally, let’s define our block class and add a simple method for retrieving the current date:

<?php
class SmashingMagazine_Layout_Block_Footer
    extends Mage_Core_Block_Template
{
    public function getDate()
    {
        $date = date('Y-m-d');
        return urlencode($date);
    }
}

Retrieving Dynamic Content From A Block

Inside a Magento template, PHP’s $this keyword contains a reference to the associated block object, so we can call the method $this->getDate() directly from our template:

<img 
    src="http://media.smashingmagazine.com/themes/smashingv4/images/logo.png?date=<?php echo $this->getDate() ?>"
    width="459"
    height="120"
/>

Other Handles And Parent Blocks

Try experimenting by changing the layout handle from default to catalog_product_view or cms_index_index to see the HTML content appear only on the product page or the homepage respectively.

You can also try changing the location the content appears in our page’s HTML by modifying the parent block from body_before_end to after_body_start or content.

Summary

This is a very simple example aimed at showing you the basics of how to make use of the Magento layout without needing to modify existing templates, which would potentially cause you problems when upgrading your Magento instance.

Using the techniques outlined in this tutorial, you could easily add something like Google Analytics tracking to every page, without needing to modify a single line in Magento’s core templates. What’s more, if you no longer wanted the custom HTML content on your website, you could simply disable the module and hey presto, it’s gone!

The Magento layout is quite complex, and new developers might be tempted to simply “hack” the changes into the appropriate templates for the time being. However, the Magento layout is written in such a way that once we are familiar with it, we can unobtrusively add our custom content while not jeopardizing the ability to install community modules or to upgrade Magento without worrying, “will this work on my hacked-up Magento?”

Feel free to download the source code (ZIP).

Try It Yourself

Now that we are all experts on the Magento layout, here are some very simple examples which follow the very same pattern, but demonstrate how easy it is to integrate third party software with Magento.

Since all of the information you need is detailed in the above article, I will provide some pointers on how you should approach each integration.

Google Analytics

Magento actually ships with Google Analytics, so it is in fact as simple as entering your tracking code in the admin panel; however, this does not make it any less useful as a try-it-yourself example.

The first step is to visit the Google website to retrieve the snippet of code that you need to include on your website. You will notice the only dynamic element of the standard tracking code is the account number (e.g. UA-12345678-90), so for the purpose of this example I would suggest making this number the dynamic content to be retrieved from the block. In a future article, we will cover the topic of making this kind of content admin panel configurable, so abstracting it to the block for now makes good sense should you wish to revisit this example.

Now on to creating your module. I would suggest keeping the same namespace for all of these examples, “SmashingMagazine,” and a fitting module name would be “GoogleAnalytics” in this case. You will need all of the elements of the main article above, so make your way through the article once more, adjusting as required. Since you want your code to appear on every page, you should now know the best layout handle to choose.

If you encounter any problems, check and double check that you have followed the main article exactly, as most beginner issues with Magento are typos and syntax-related mistakes.

Reinvigorate, CrazyEgg and Everything Else…

You should now see a pattern emerging! You can simply follow exactly the same steps again to integrate other popular third-party software with your Magento website, all without modifying a single core Magento file or having to copy and paste content directly into the admin panel, where somebody could accidentally delete it one day.

I welcome any questions and would love to hear any feedback in the comments area below.

(cp)


© Joseph McDermott for Smashing Magazine, 2012.


The Basics Of Creating A Magento Module


  

A lot of community extensions (or modules) are available for the feature-rich open-source e-commerce solution Magento, but what if they don’t quite work as you want them to? What if you could understand the structure of a Magento module a little better, to the point that you could modify it to suit your needs or, better yet, write your own module from scratch?

The Basics Of Creating A Magento Module

In this tutorial, we will introduce the coding of Magento in the form of a “Hello World�-style module. The goal of the module will be simply to write some information to a log file every time a product is saved. This very basic module will allow us to cover a number of interesting topics, including:

  • The app/code directories,
  • The structure and creation of a Magento module,
  • Event observers,
  • Logging.

Before We Begin

This tutorial assumes that you already have an installation of Magento up and running, either locally or on a development server, that you can add new files to. The version of Magento that you use doesn’t really matter, because we will be covering fundamental aspects that exist across all versions and editions: Community, Professional and Enterprise.

Disable the Cache

This is one of the first lessons a Magento developer should learn: disable the cache! You can do this by going to Admin Panel → System → Cache Management → Select All → Actions: Disable → Submit.

While very good at boosting performance in a production environment, the cache is a developer’s enemy. Leave it enabled at your peril! Every Magento developer I have met has on more than one occasion spent an hour or so wondering why their latest update is not showing up, only to find that Magento is still displaying the version of the website that it conveniently cached earlier that day.

The app/code Directory

The brains of Magento can be found in individual modules inside the app/code directory, which is split in to three areas: core, community and local.

Core

The app/code/core directory contains all of the functionality for products, categories, customers, payments, etc. Until you know what you are doing (and even afterwards), keep app/code/core off limits because these files should not be modified.

Magento is structured in such a way that you can alter the functionality of any of these core files without modifying them directly, which ensures that your application remains upgrade-proof. By all means, look in order to better understand how Magento works, but do not touch.

Community

As the name suggests, app/code/community is where you will find modules that have been provided by third parties (i.e. not Magento’s core team). Hundreds of modules are available through Magento Connect, and when you install them through the built-in “Package Manager,� this is where they end up.

Local

Magento ships with an empty app/code/local directory, ready for you to add bespoke modules for your own Magento installation. This is where we will be working for the duration of this tutorial.

Structuring Our Directory

Open your favorite editor, and navigate to app/code/local to add some new directories and files.

Module Namespace

The first directory we will create is a “namespace.� This can be called anything you like, but the convention is some form of the name of the company or module’s author. Magento uses “Mage� as its namespace. Here at Ampersand Commerce, we use “Ampersand.� For this tutorial, we will use “SmashingMagazine� as our namespace. So, create the directory app/code/local/SmashingMagazine.

Module Name

For the next directory, we will give our module a descriptive name. The module we are creating will write log entries each time a product is saved, so a logical name would be LogProductUpdate. Create the directory app/code/local/SmashingMagazine/LogProductUpdate.

We should now have the following directory structure for our module. These directory and file names are case-sensitive, so capitalize where appropriate.

app
   -code
      --local
         ---SmashingMagazine
            ----LogProductUpdate

Configuring Our Module

Next, we will begin to configure our module. The configuration files belong inside our module in a directory named etc, so let’s create that along with a new XML file: app/code/local/SmashingMagazine/LogProductUpdate/etc/config.xml. This XML file will inform Magento of the location of the files in our module, as well as many other things, such as version number and events to observe. For now, we will create a simple config.xml file, which contains comments that explain the meaning of each section.

<?xml version="1.0" encoding="UTF-8"?>

<!-- The root node for Magento module configuration -->
<config> 

    <!-- The module's node contains basic information about each Magento module -->
    <modules>

        <!--
            This must exactly match the namespace and module’s folder names,
            with directory separators replaced by underscores
        -->
        <SmashingMagazine_LogProductUpdate>

            <!-- This is the version of our module, starting at 0.0.1 -->
            <version>0.0.1</version>

        </SmashingMagazine_LogProductUpdate>

    </modules>

</config>

Activating Our Module

The next step is to inform our Magento installation that our module exists, which we do by creating a new XML file in app/etc/modules. The name of this XML file can be anything you like, since Magento will read all XML files in this directory and will be interested only in the content. However, by convention we should give the file and module the same name. Let’s create app/etc/modules/SmashingMagazine_LogProductUpdate.xml with the following content:

<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <SmashingMagazine_LogProductUpdate>

            <!-- Whether our module is active: true or false -->
            <active>true</active>

            <!-- Which module code pool to use: core, community or local -->
            <codePool>local</codePool>

        </SmashingMagazine_LogProductUpdate>
    </modules>
</config>

Sanity Check: Is The Module Enabled?

We now have a fully functional module that is enabled in Magento. It doesn’t do anything, but it is a valid module. This is our first opportunity to see whether we have correctly configured everything so far. If we log into the Magento admin panel and navigate to System → Configuration → Advanced → Advanced and view the “Disable Modules Output� listing, we should see our SmashingMagazine_LogProductUpdate module listed as enabled. If it is not listed, then something has gone wrong, so carefully run through the steps up to this point again. This is usually when new Magento developers discover the cache!

Our module’s structure now looks like this:

app
   -code
      --local
         ---SmashingMagazine
            ----LogProductUpdate
               -----etc
                  ------config.xml

   -etc
      --modules
         ---SmashingMagazine_LogProductUpdate.xml

Defining An Event Observer

Event observers are extremely powerful and are one of the cleanest ways to extend Magento’s functionality without having to rewrite or override any core methods or classes. We want to observe the event that Magento dispatches just after a product is saved, so the code for the event we are interested in is catalog_product_save_after. Determining which event code to use when defining a new observer requires a basic understanding of Magento’s model layer, which is beyond the scope of this tutorial. Don’t worry, though: we’ll cover it another time!

We now need to modify our config.xml to include the event observer definition:

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

    <!-- Configure our module's behavior in the global scope -->
    <global>

        <!-- Defining an event observer -->
        <events>

            <!-- The code of the event we want to observe -->
            <catalog_product_save_after>

                <!-- Defining an observer for this event -->
                <observers>

                    <!--
                        Unique identifier in the catalog_product_save_after node.
                        By convention, we put the module's name in lowercase.
                    -->
                    <smashingmagazine_logproductupdate>

                        <!-- The model to be instantiated -->
                        <class>smashingmagazine_logproductupdate/observer</class>

                        <!-- The method of the above model to be called -->
                        <method>logUpdate</method>

                        <!-- We generally use the singleton type for observers -->
                        <type>singleton</type>

                    </smashingmagazine_logproductupdate >

                </observers>

            </catalog_product_save_after>

        </events>

    </global>

</config>

Configuring Our Model’s Directory

In the event observer defined above, we made reference to a model that we have not yet created. We need to inform Magento where to find models in our module by updating config.xml with the following:

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

    <!-- Configure our module's behavior in the global scope -->
    <global>

        <!-- Defining models -->
        <models>

            <!--
                Unique identifier in the model’s node.
                By convention, we put the module’s name in lowercase.
            -->
            <smashingmagazine_logproductupdate>

                <!--
                    The path to our models directory, with directory
                    separators replaced by underscores
                -->
                <class>SmashingMagazine_LogProductUpdate_Model</class>

            </smashingmagazine_logproductupdate>

        </models>

        <events>
            <catalog_product_save_after>
                <observers>
                    <smashingmagazine_logproductupdate>
                        <class>smashingmagazine_logproductupdate/observer</class>
                        <method>logUpdate</method>
                        <type>singleton</type>
                    </smashingmagazine_logproductupdate >
                </observers>
            </catalog_product_save_after>
        </events>

    </global>

</config>

Creating An Observer Model

We will now create the model to be instantiated when the event is dispatched. Create a new PHP file in app/code/local/SmashingMagazine/LogProductUpdate/Model/Observer.php with the following content:

<?php
/**
 * Our class name should follow the directory structure of
 * our Observer.php model, starting from the namespace,
 * replacing directory separators with underscores.
 * i.e. app/code/local/SmashingMagazine/LogProductUpdate/Model/Observer.php
 */
class SmashingMagazine_LogProductUpdate_Model_Observer
{
    /**
     * Magento passes a Varien_Event_Observer object as
     * the first parameter of dispatched events.
     */
    public function logUpdate(Varien_Event_Observer $observer)
    {
        // Retrieve the product being updated from the event observer
        $product = $observer->getEvent()->getProduct();

        // Write a new line to var/log/product-updates.log
        $name = $product->getName();
        $sku = $product->getSku();
        Mage::log("{$name} ({$sku}) updated", null, 'product-updates.log');
    }
}

We’re done! Try it out.

The directory structure for our completed module should now look like this:

app
   -code
      --local
         ---SmashingMagazine
            ----LogProductUpdate
               -----Model
                  ------Observer.php

               -----etc
                  ------config.xml

   -etc
      --modules
         ---SmashingMagazine_LogProductUpdate.xml

Now that our module is complete, it’s time to try it out! Log into the Magento admin panel, create or update a product in your catalog, and then check the var/log folder to see your product-updates.log file populated.

If nothing appears or the directory does not exist, ensure that the correct permissions are set to allow Magento to write to this directory, and that logging is enabled in Admin Panel → System → Configuration → Developer → Log Settings → Enabled.

This basic tutorial is meant to give you an overall understanding of how Magento modules work. After completing this tutorial, spend some time exploring the Magento modules in app/code/core and see if you now have a better idea of how it all works.

We welcome any questions and would love to hear any feedback in the comments area below.

(al)


© Joseph McDermott for Smashing Magazine, 2012.


  •   
  • Copyright © 1996-2010 BlogmyQuery - BMQ. All rights reserved.
    iDream theme by Templates Next | Powered by WordPress