Coren
Coren

Reputation: 385

Override entire magento layout from observer

Problem Space

I'm trying to use an observer in magento to completely replace the entire layout for a given request based on a parameter in the request.

The issue I'm facing is that Magento is still trying to load a different root block template than the one I specified in my Observer (specifically "frontend/base/default/template/page/1column.phtml" for product pages which is the default root block template used by the template configured in the design tab of the backend). Because it isn't using the layout I specified, it dies in PHP trying to load templates that don't exist in the theme.

Any guidance is appreciated.

Note: I am not using a CMS page to test this concept since they load their own templates as specified in the backend. I created a test product and am using its product page. The test is made by requesting the following URL: http://mymagentosite.com/test-product?ajax=1

Possible Issues

Implementation

I've created a module with an observer and a very minimal layout as follows:

Module Folder Structure

app
├── code
│   └── local
│       └── MyCompany
│           └── MyModule
│               ├── etc
│               │   └── config.xml
│               └── Model
│                   └── Observer.php
├── design
│   └── frontend
│       └── myTheme
│           └── default
│               ├── layout
│               │   └── local.xml
│               └── template
│                   └── test.phtml
└── etc
    └── modules
        └── MyCompany_MyModule.xml

config.xml

<!-- language: lang-xml -->
<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <MyCompany_MyModule>
            <version>0.1.0.0</version>
        </MyCompany_MyModule>
    </modules>
    <global>
        <events>
            <controller_action_layout_generate_xml_before>
                <observers>
                    <myCompany_myModule_model_observer>
                        <type>singleton</type>
                        <class>MyCompany_MyModule_Model_Observer</class>
                        <method>changeRequestLayout</method>
                    </myCompany_myModule_model_observer>
                </observers>
            </controller_action_layout_generate_xml_before>
        </events>
    </global>
</config>

Observer.php

<!-- language: lang-php -->
<?php

class MyCompany_MyModule_Model_Observer
{
    public function changeRequestLayout($observer)
    {
        if ($observer->getAction()->getRequest()->isAjax()) {
            Mage::getDesign()->setArea('frontend')->setPackageName('myTheme')->setTheme('default');
        }
    }
}

local.xml

<!-- language: lang-xml -->
<?xml version="1.0" encoding="UTF-8"?>
<layout version="0.1.0.0">
    <default>
        <block type="page/html" name="root" output="toHtml" template="test.phtml" />
    </default>
</layout>

test.phtml

<!-- language: lang-html -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title>Testing, testing...1...2...3...</title>
    <style type="text/css">
        body {
            background-color:#f00;
        }
    </style>
</head>
<body>
    <h1>This is a test of the Magento layout system.  This is only a test.  If this were not a test, real content would follow.</h1>
</body>
</html>

MyCompany_MyModule.xml

<!-- language: lang-xml -->
<?xml version="1.0" encoding="UTF-8"?>
<config>
    <modules>
        <MyCompany_MyModule>
            <active>true</active>
            <codePool>local</codePool>
        </MyCompany_MyModule>
    </modules>
</config>

Upvotes: 6

Views: 2031

Answers (2)

Coren
Coren

Reputation: 385

The Real Problem

Magento's layered XML layout system is the source of the confusion in accomplishing what should be a trivial use case.

Because the base/default layout XML is combined with whatever layout I specify, I must not only override the root layout but also all of the other derived layouts defined in the base/default/page.xml. This is not practical as Magento defines a large number of customized layouts based on the type of page the client is browsing.

My Solution

The cleanest solution is to create symbolic links in the template/page directory to point to my test.phtml file for all of the following layouts. This guarantees that Magento will only use my layout template.

Note: you must enable template symlinks for this to work (in the admin backend, navigate to System>Configuration>Advanced>Developer, expand "Template Settings" and set "Allow Symlinks" to "Yes").

Files Linked to test.phtml

  • 1column.phtml
  • 2columns-left.phtml
  • 2columns-right.phtml
  • 3columns.phtml
  • empty.phtml
  • popup.phtml
  • print.phtml
  • redirect.phtml

Upvotes: 0

Michael Leiss
Michael Leiss

Reputation: 5670

This is a working solution i built that definitely does the job of switching the design package/theme via controller_front_init_before event.

Of course you could check some request params inside changeConfigNode() method with Mage::app()->getRequest()->getParam('switch') and just append the request param switch/1 to trigger the changes.

   <global>
    <events>
        <controller_front_init_before>
            <observers>
                <cartware_change_the_node>
                     <class>cartware_guest2customer/observer</class>
                     <method>changeConfigNode</method>
                </cartware_change_the_node>
            </observers>
        </controller_front_init_before>               
    </events>
   </global>

And the observer method:

public function changeConfigNode(){

    /** left for testing
    Mage::log( Mage::app()->getConfig()->getXmlString());
            **/

    Mage::app()->getConfig()->setNode('stores/default/design/package/name', 'enterprise');
    Mage::app()->getConfig()->setNode('stores/default/design/package/theme', 'enterprise');
    Mage::app()->getConfig()->setNode('stores/default/design/package/default_theme', 'enterprise');
    Mage::app()->getConfig()->setNode('stores/default/design/theme/default', 'enterprise');
    Mage::app()->getConfig()->setNode('stores/default/design/theme/layout', 'enterprise');
    Mage::app()->getConfig()->setNode('stores/default/design/theme/template', 'enterprise');



}

Its without additional checks but it might help you! Good luck!

Upvotes: 1

Related Questions