In this article, I’m going to be covering what I believe to be a very effective way of modifying the layout of any Magento theme.

For several of the first Magento themes I built, I copied the layout files from the default or blank theme into the custom theme layout folder. I would then modify the layout files directly, editing or commenting out content in files like: catalog.xml, page.xml, checkout.xml, etc… I never liked editing these files directly, as I knew that when it came time to upgrade to a newer version of Magento that had upgraded the layout files, I’d have to merge the changes into the new layout files.

One day, I was digging through the Magento code relating to layout files and discovered a bit of code that made me realize that it was possible to just place a local.xml file in my custom theme’s layout folder and have it loaded automatically by Magento. (this code is on line 283 in /app/code/core/Mage/Core/Model/Layout/Update.php in the fetchFileLayoutUpdates() method).

Due to Magento’s brilliant tags, it’s possible to do just about anything you want without having to edit any of the default layout files.

Before delving into the code, let’s look at the advantages/disadvantages of this method:

Advantages

Allows you to upgrade themes without having to merge in changes

All custom layout changes are centralized, allowing developers to more easily make changes to custom theme elements

Disadvantages

At first, it’s slower to use this method than hacking up the standard layout files directly

You will have one more place to look where the site might be pulling code (template phtmls, standard layout files, admin layout updates, AND local.xml)

Here is the slimmed down, commented local.xml from one of our recent projects:

<?xml version="1.0"?> <layout version="0.1.0"> <default> <reference name="head"> <!-- Magento looks in /skin/frontend/<INTERFACE>/<THEME>/js/buyprinting.js for this file --> <action method="addItem"><type>skin_js</type><name>js/buyprinting.js</name></action> <!-- This removes the item that was set in the page.xml file --> <action method="removeItem"><type>skin_js</type><name>js/iehover-fix.js</name></action> <!-- Magento looks in /js/prototype/element.storage.js for this file --> <action method="addJs"><name>prototype/element.storage.js</name></action> <action method="addCss"><stylesheet>css/buyprinting.css</stylesheet></action> </reference> <reference name="header"> <!-- This adds a CMS block that can be called from the template file associated with the header block. --> <block type="cms/block" name="cms_quick_help"> <action method="setBlockId"><block_id>quick_help</block_id></action> </block> <!-- The remove tag removes the blocks with the specified name from the layout --> <remove name="top.menu"/> <remove name="store_language"/> <remove name="breadcrumbs"/> </reference> <reference name="top.nav"> <remove name="catalog.topnav"/> </reference> <reference name="left"> <remove name="left.newsletter"/> <remove name="left.permanent.callout"/> <remove name="catalogsearch.leftnav"/> <!-- When you use the remove tag, it removes any blocks with the specified name from the entire layout, regardless of the context. So, if I remove right.newsletter in the <default> context and that name is used in say the <catalog_product_view> context, then both blocks will be removed. Because remove operates on the global context, you can only remove an element once. Since <remove name="right.newsletter" /> is being called in catalogsearch.xml, we have to unset it, or else we'll get an error. The line below only unsets the block from the parent's context, not the global layout context --> <action method="unsetChild"><name>right.newsletter</name></action> </reference> <reference name="right"> <!-- Some blocks have to be removed using remove, others via unsetChild. I've not spent the time digging into the code to figure out why --> <remove name="right.permanent.callout"/> <remove name="catalog.compare.sidebar"/> <remove name="left.reports.product.viewed"/> <action method="unsetChild"><name>sale.reorder.sidebar</name></action> <action method="unsetChild"><name>wishlist_sidebar</name></action> <action method="unsetChild"><name>right.reports.product.viewed</name></action> <remove name="cart_sidebar"/> </reference> </default> <!-- CATALOG PAGES --> <catalog_product_view><!-- 2columns-right --> <reference name="root"> <action method="setTemplate"><template>page/2columns-left.phtml</template></action> </reference> <reference name="content"> <reference name="product.info"> <block type="cms/block" name="cms_product_info_tabs"> <action method="setBlockId"><block_id>product_info_tabs</block_id></action> </block> <block type="catalog/product_view" name="product.clone_prices" as="prices" template="catalog/product/view/price_clone.phtml"/> <action method="unsetChild"><name>tierprices</name></action> <action method="unsetChild"><name>addto</name></action> <remove name="addto"/> <reference name="product.info.options.wrapper.bottom"> <action method="unsetChild"><name>product.tierprices</name></action> </reference> </reference> </reference> </catalog_product_view> </layout> 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 <? xml version = "1.0" ?> <layout version = "0.1.0" > <default> <reference name = "head" > <!-- Magento looks in /skin/frontend/<INTERFACE>/<THEME>/js/buyprinting.js for this file --> <action method = "addItem" > <type> skin_js </type> <name> js/buyprinting.js </name> </action> <!-- This removes the item that was set in the page.xml file --> <action method = "removeItem" > <type> skin_js </type> <name> js/iehover-fix.js </name> </action> <!-- Magento looks in /js/prototype/element.storage.js for this file --> <action method = "addJs" > <name> prototype/element.storage.js </name> </action> <action method = "addCss" > <stylesheet> css/buyprinting.css </stylesheet> </action> </reference> <reference name = "header" > <!-- This adds a CMS block that can be called from the template file associated with the header block. --> <block type = "cms/block" name = "cms_quick_help" > <action method = "setBlockId" > <block_id> quick_help </block_id> </action> </block> <!-- The remove tag removes the blocks with the specified name from the layout --> <remove name = "top.menu" /> <remove name = "store_language" /> <remove name = "breadcrumbs" /> </reference> <reference name = "top.nav" > <remove name = "catalog.topnav" /> </reference> <reference name = "left" > <remove name = "left.newsletter" /> <remove name = "left.permanent.callout" /> <remove name = "catalogsearch.leftnav" /> <!-- When you use the remove tag, it removes any blocks with the specified name from the entire layout, regardless of the context. So, if I remove right.newsletter in the <default> context and that name is used in say the <catalog_product_view> context, then both blocks will be removed. Because remove operates on the global context, you can only remove an element once. Since <remove name="right.newsletter" /> is being called in catalogsearch.xml, we have to unset it, or else we'll get an error. The line below only unsets the block from the parent's context, not the global layout context --> <action method = "unsetChild" > <name> right.newsletter </name> </action> </reference> <reference name = "right" > <!-- Some blocks have to be removed using remove, others via unsetChild. I've not spent the time digging into the code to figure out why --> <remove name = "right.permanent.callout" /> <remove name = "catalog.compare.sidebar" /> <remove name = "left.reports.product.viewed" /> <action method = "unsetChild" > <name> sale.reorder.sidebar </name> </action> <action method = "unsetChild" > <name> wishlist_sidebar </name> </action> <action method = "unsetChild" > <name> right.reports.product.viewed </name> </action> <remove name = "cart_sidebar" /> </reference> </default> <!-- CATALOG PAGES --> <catalog_product_view> <!-- 2columns-right --> <reference name = "root" > <action method = "setTemplate" > <template> page/2columns-left.phtml </template> </action> </reference> <reference name = "content" > <reference name = "product.info" > <block type = "cms/block" name = "cms_product_info_tabs" > <action method = "setBlockId" > <block_id> product_info_tabs </block_id> </action> </block> <block type = "catalog/product_view" name = "product.clone_prices" as = "prices" template = "catalog/product/view/price_clone.phtml" /> <action method = "unsetChild" > <name> tierprices </name> </action> <action method = "unsetChild" > <name> addto </name> </action> <remove name = "addto" /> <reference name = "product.info.options.wrapper.bottom" > <action method = "unsetChild" > <name> product.tierprices </name> </action> </reference> </reference> </reference> </catalog_product_view> </layout>

I hope with article has given you some direction as to how you can improve you Magento theming skills. If you have any additional tips/comments on coding layouts, please suggest them in the comments section.