Mini Tutorial:
Configurable Layouts in the Magento Backend

Suppose a client wanted the ability to alter the layouts of a given page without having to edit the xml layout file or specify a custom page design. The best way to achieve this functionality would be to create a select button in the System Configuration page of Magento’s backend.

For this example, I’ll demonstrate how to toggle between a 2columns-left catalog_category_layered page with layered navigation on the left column and a 1column page with the layered nav on top of that catalog list.

The steps required for this task are as follows:

1) Create a custom module

2) Add the relevant field in the backend config

3) Edit the layout xml to listen to that config setting

Create Your Module

Let’s get started by creating a custom module ( DavidF_LayoutConfig )

app/etc/modules/DavidF_LayoutConfig.xml:

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

app/code/local/DavidF/LayoutConfig/etc/config.xml:

<config>
    <modules>
        <DavidF_LayoutConfig>
            <version>0.0.1</version>
        </DavidF_LayoutConfig>
    </modules>
    <global>
        <models>
            <davidf_layoutconfig>
                <class>DavidF_LayoutConfig_Model</class>
            </davidf_layoutconfig>
        </models>
    </global>    
</config>

Note that the config.xml is only configuring the module’s version and declaring a model class. The model will come into play in the next step.

Adding the Field in the Backend

Magento backend forms can be created in the backend via a module’s system.xml file, and that’s what will be created here. Creating a separate tab in the config page is a good way to do this, as future layout configuration options can be specified there, too.

app/code/local/DavidF/LayoutConfig/etc/system.xml:

<?xml version="1.0"?>
<config>
    <tabs>
        <layout_config translate="label" module="davidf_layoutconfig">
            <label>Configurable Layout</label>
            <sort_order>150</sort_order>
        </layout_config>
    </tabs>
    <sections>
        <layout_config translate="label" module="davidf_layoutconfig">
            <label>Theme Settings</label>
            <tab>layout_config</tab>
            <frontend_type>text</frontend_type>
            <sort_order>10</sort_order>
            <show_in_default>1</show_in_default>
            <show_in_website>1</show_in_website>
            <show_in_store>1</show_in_store>
            <groups>
                <layouts translate="label">
                    <label>Custom Layouts</label>
                    <comment>In this section you can choose how you would like various Magento pages laid out.</comment>
                    <frontend_type>text</frontend_type>
                    <sort_order>400</sort_order>
                    <show_in_default>1</show_in_default>
                    <show_in_website>1</show_in_website>
                    <show_in_store>1</show_in_store>
                    <fields>
                        <catalog_page translate="label">
                            <label>Catalog Pages</label>
                            <frontend_type>select</frontend_type>
                            <source_model>davidf_layoutconfig/adminhtml_system_config_source_catalogpage</source_model>
                            <sort_order>1</sort_order>
                            <show_in_default>1</show_in_default>
                            <show_in_website>1</show_in_website>
                            <show_in_store>1</show_in_store>
                        </catalog_page>
                    </fields>
                </layouts>
            </groups>
        </layout_config>
    </sections>
</config>

In this file, we have created a new tab (line 3), associated a section called ‘layout_config’ with it (line 12), created a group called ‘layouts’ (line 18-19), and a field called ‘catalog_page'(line 28). The only step needed to complete the backend side of this configuration is to create the source_model specified in line 31 (davidf_layoutconfig/adminhtml_system_config_source_catalogpage).

app/code/local/DavidF/LayoutConfig/Model/Adminhtml/System/Config/Source/CatalogPage.php:

<?php
 
class DavidF_LayoutConfig_Model_Adminhtml_System_Config_Source_CatalogPage
{
    public function toOptionArray()
    {
        $toReturn = array();
        $toReturn[1]['value'] = '1';
        $toReturn[1]['label'] = '1 Column with Topnav';
        $toReturn[0]['value'] = '0';
        $toReturn[0]['label'] = '2 Columns with Leftnav';
 
        return $toReturn;
    } 
}

The toOptionArray() function is the magical bit that adds the corresponding values and labels to the backend select. In this case, only two options are necessary, so a boolean is the simplest way to set a config flag.

Now, if you navigate to the backend and checkout system configuration, you’ll see the new tab with the configurable layout select. The only problem, it still doesn’t do anything.

Configuring the Layout File

The only step that remains to be completed is setting up the layout to display the template specified by the configuration. To achieve this, we will implement the ifconfig parameter in the layout file. Note that the ifconfig function – which checks a boolean in the core_config_data table specified by a given path – only works on an action object in the xml. So first the block will be declared with the default template (in our case, 2columns-left because its value was set to ‘0’ in the source model), and have an action to add another template if the config returns true.

in app/design/frontend/[your_theme]/layout/local.xml:

...
<catalog_category_layered>
  <reference name="root">
      <action method="setTemplate"><template>page/2columns-left.phtml</template></action>
      <action method="setTemplate" ifconfig="layout_config/layouts/catalog_page"><template>page/1column.phtml</template></action>
  </reference>
</catalog_category_layered>
...

Now your layout file will respond to the configuration in the backend!

Note: that the path specified in the ifconfig parameter should match the [section]/[group]/[field] as specified in the system.xml.

Going Forward – Adding More than Two Options

Need to have an option for a 3-columns layout? Not a problem!  First, You need to change the source model to add another element to the array.

<?php
 
class DavidF_LayoutConfig_Model_Adminhtml_System_Config_Source_CatalogPage
{
    public function toOptionArray()
    {
        $toReturn = array();
        $toReturn[2]['value'] = '3columns';
        $toReturn[2]['label'] = '3 Columns with Polls';
        $toReturn[1]['value'] = '1column';
        $toReturn[1]['label'] = '1 Column with Topnav';
        $toReturn[0]['value'] = '2columns';
        $toReturn[0]['label'] = '2 Columns with Leftnav';
 
        return $toReturn;
    }
 
}

The issue is, however, that this is no longer a 0/1 boolean and a new config field has to be created. The best way to do this is to register an observer and have that set config values each time that config field is saved.

First, back to app/code/local/DavidF/LayoutConfig/etc/config.xml, add the lines in the event node:

<config>
    <modules>
        <DavidF_LayoutConfig>
            <version>0.0.1</version>
        </DavidF_LayoutConfig>
    </modules>
    <global>
        <models>
            <davidf_layoutconfig>
                <class>DavidF_LayoutConfig_Model</class>
            </davidf_layoutconfig>
        </models>
          <events>
            <admin_system_config_changed_section_layout_config>
                <observers>
                    <DavidF_LayoutConfig_Observer>
                        <type>singleton</type>
                        <class>davidf_layout/observer</class>
                        <method>admin_system_config_changed_section_layout_config</method>
                    </DavidF_LayoutConfig_Observer>
                </observers>
            </admin_system_config_changed_section_layout_config>
        </events>
    </global>    
</config>

Then we need to create and observer class – app/code/local/DavidF/LayoutConfig/Model/Observer.php:

<?php
 
class DavidF_LayoutConfig_Model_Observer
{
 
    protected function _findScope($data)
    {
        if (is_null($data['store']) && $data['website']) {
            return $data['website'];
        } elseif ($data['store']) {
            return $data['store'];
        }
 
        return 'default';
    }
 
    public function admin_system_config_changed_section_yeti($observer)
    {
        $postData = Mage::app()->getRequest()->getPost();
        $layoutFields = $postData['groups']['layouts']['fields'];
        $config = Mage::getModel('core/config');
        $scope = $this->_findScope($observer->getEvent()->getData());
        foreach ($layoutFields as $key => $layoutField) {
 
            // Catalog pages
            if ($key == 'catalog_page' && $layoutField['value'] == '1columns') {
                $config->saveConfig('layout_config/layouts/isUsing1Column', '1', $scope);
                $config->saveConfig('layout_config/layouts/isUsing2columns', '0', $scope);
                $config->saveConfig('layout_config/layouts/isUsing3columns', '0', $scope);
            } elseif ($key == 'catalog_page' && $layoutField['value'] == '2columns') {
                $config->saveConfig('layout_config/layouts/isUsing1Column', '0', $scope);
                $config->saveConfig('layout_config/layouts/isUsing2columns', '1', $scope);
                $config->saveConfig('layout_config/layouts/isUsing3columns', '0', $scope);
            } elseif ($key == 'catalog_page' && $layoutField['value'] == '3columns') {
                $config->saveConfig('layout_config/layouts/isUsing1Column', '0', $scope);
                $config->saveConfig('layout_config/layouts/isUsing2columns', '0', $scope);
                $config->saveConfig('layout_config/layouts/isUsing3columns', '1', $scope);
            }
        }
    }
}

Almost done!

This observer is saving three separate boolean fields in the config every time this section is saved. We need to edit the layout to look for which one of these fields is set to ‘1’:

...
<catalog_category_layered>
  <reference name="root">
      <action method="setTemplate" ifconfig="layout_config/layouts/isUsing2Columns><template>page/2columns-left.phtml</template></action>
      <action method="setTemplate" ifconfig="layout_config/layouts/isUsing1Column"><template>page/1column.phtml</template></action>
      <action method="setTemplate" ifconfig="layout_config/layouts/isUsing3Columns"><template>page/3columns.phtml</template></action>
 
  </reference>
</catalog_category_layered>
...

The layout is listening to the config that the observer set. Now your layouts should be easily configurable in the backend.

Icon made by Freepik from www.flaticon.com