Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom layouts for catalog_category_view on Magento

I need to be able to use different layouts for different categories, selected on the Custom Design tab on the category and Page Layout field.

I'm using Magento 1.9.1.0.

In my config.xml I have:

<global>
    <page>
        <layouts>
            <module_home module="page" translate="label">
                <label>Module Home</label>
                <template>page/base.phtml</template>
                <layout_handle>module_home</layout_handle>
            </module_home>

            <module_categories module="page" translate="label">
                <label>Module Categories</label>
                <template>page/base.phtml</template>
                <layout_handle>module_categories</layout_handle>
            </module_categories>
        </layouts>
    </page>
    ...

And in my layouts.xml file I have:

<default>
    <reference name="root">...</reference>
</default>
<module_home translate="label">...</module_home>
<module_categories translate="label">...</module_categories>

When I select Module Categories layout from the admin of the category I don't get the changes for module_categories handler, only the ones set on <default>.

If I force it like this:

<catalog_category_view>
    <update handle="module_categories" />
</catalog_category_view>

I do get the changes, but I want multiple handlers, selected as Layouts on the admin.

Maybe I'm doing something wrong? Could not find an example for how to do this, maybe you can point somewhere? Thanks!

like image 362
PabloRosales Avatar asked Sep 29 '22 21:09

PabloRosales


1 Answers

You've got the right idea with the <update /> directive. Just put it in your category's Custom Layout Update field in the admin, and that category should apply that layout handle. You can still set the page template with the Page Layout field.

The reason you need to explicitly specify the layout handle with the <update /> directive is because Magento's category controller does not use the layout_handle node, whereas other parts of Magento, like Magento's CMS page controller, do use it.

For example, let's look at Mage_Cms_PageController, which is responsible for rendering a CMS page:

public function viewAction()
{
    $pageId = $this->getRequest()
        ->getParam('page_id', $this->getRequest()->getParam('id', false));
    if (!Mage::helper('cms/page')->renderPage($this, $pageId)) {
        $this->_forward('noRoute');
    }
}

Let's dig deeper and look at Mage_Cms_Helper_Page::renderPage(), which calls Mage_Cms_Helper_Page::_renderPage():

protected function _renderPage(Mage_Core_Controller_Varien_Action  $action, $pageId = null, $renderLayout = true)
{

    $page = Mage::getSingleton('cms/page');

    /* ... */

    if ($page->getRootTemplate()) {
        $handle = ($page->getCustomRootTemplate()
                    && $page->getCustomRootTemplate() != 'empty'
                    && $inRange) ? $page->getCustomRootTemplate() : $page->getRootTemplate();
        $action->getLayout()->helper('page/layout')->applyHandle($handle);
    }

    /* ... */

    if ($page->getRootTemplate()) {
        $action->getLayout()->helper('page/layout')
            ->applyTemplate($page->getRootTemplate());
    }

    /* ... */
}

We see two important pieces of logic here.

First, _renderPage() calls $action->getLayout()->helper('page/layout')->applyHandle($handle). If you dig even deeper, you'll see that Mage_Page_Helper_Layout::applyHandle() is responsible for applying the appropriate layout_handle as defined by the configuration XML:

public function applyHandle($pageLayout)
{
    $pageLayout = $this->_getConfig()->getPageLayout($pageLayout);

    if (!$pageLayout) {
        return $this;
    }

    $this->getLayout()
        ->getUpdate()
        ->addHandle($pageLayout->getLayoutHandle());

    return $this;
}

Second, _renderPage() calls $action->getLayout()->helper('page/layout')->applyTemplate($page->getRootTemplate()). Similar to applyHandle(), applyTemplate() applies the actual page template.

So, this explains why you can depend on the layout_handle as defined in the configuration XML when it comes to CMS pages. Now, let's find out why it's not dependable for categories.

Let's look at Mage_Catalog_CategoryController::viewAction(), which is responsible for displaying a category page:

public function viewAction()
{
    if ($category = $this->_initCatagory()) {
        $design = Mage::getSingleton('catalog/design');
        $settings = $design->getDesignSettings($category);

        /* ... */

        // apply custom layout update once layout is loaded
        if ($layoutUpdates = $settings->getLayoutUpdates()) {
            if (is_array($layoutUpdates)) {
                foreach($layoutUpdates as $layoutUpdate) {
                    $update->addUpdate($layoutUpdate);
                }
            }
        }

        /* ... */

        // apply custom layout (page) template once the blocks are generated
        if ($settings->getPageLayout()) {
            $this->getLayout()->helper('page/layout')->applyTemplate($settings->getPageLayout());
        }

        /* ... */
    }
    elseif (!$this->getResponse()->isRedirect()) {
        $this->_forward('noRoute');
    }
}

Stripping out all of the default layout logic, we're left with two pieces:

        // apply custom layout update once layout is loaded
        if ($layoutUpdates = $settings->getLayoutUpdates()) {
            if (is_array($layoutUpdates)) {
                foreach($layoutUpdates as $layoutUpdate) {
                    $update->addUpdate($layoutUpdate);
                }
            }
        }

This goes through the category's layout updates (as defined in the Custom Layout Update field in the admin) and applies them. This is why using <update handle="some_handle" /> works.

And...

        // apply custom layout (page) template once the blocks are generated
        if ($settings->getPageLayout()) {
            $this->getLayout()->helper('page/layout')->applyTemplate($settings->getPageLayout());
        }

This applies the custom page template, similar to how the CMS page logic did it, using Mage_Page_Helper_Layout::applyTemplate().

Now, notice something missing?

Yup, the category controller does not call Mage_Page_Helper_Layout::applyHandle() to apply the layout_handle as defined in the configuration XML. This means that you can use the Page Layout field to give the category a particular page template, but your layout_update that accompanies the template won't get applied!

Hope this clears up why your layout_update node isn't getting used on the category page the way you would expect it to. Magento is full of odd behavior and inconsistencies like this :)

like image 165
Agop Avatar answered Oct 01 '22 10:10

Agop