Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

update layout programmatically in magento event observer

Tags:

magento

I am trying to change the template(view.phtml) of a block (product.info) for product detail page, to do this, I am observing an event (controller_action_layout_generate_blocks_before), in it after making necessary checks I am trying to change the template of the block (product.info) in following way:

$layout = $observer->getEvent()->getLayout();
$layout->getUpdate()->addUpdate('
        <reference name="product.info">
            <action method="setTemplate">
                <template>customlayout/product/view.phtml</template>
            </action>                                                          
        </reference>');
$layout->getUpdate()->load();
$layout->generateXml();

If I put "<remove name='product.info'/>" , it will be removed but when trying to do the above, its not working.
Edit:
Requirement is to switch the template (product detail) dynamically to the selected one (in CustomModule) against the current product.

like image 584
R T Avatar asked Sep 17 '12 11:09

R T


2 Answers

As Ben said, I don't know why you're going to put it on the observer but the problem in your case is the sequence of loadLayout.

You can check your loaded layout xml by using:

Mage::log(Mage::getSingleton('core/layout')->getUpdate()->asString());

Pretty sure your <action method="setTemplate"><template>customelayout/product/view.phtml</template> has been overridden by other setTemplate that's the reason your template is not shown.

Mage_Core_Controller_Varien_Action

public function loadLayout($handles=null, $generateBlocks=true, $generateXml=true)
{
    // if handles were specified in arguments load them first
    if (false!==$handles && ''!==$handles) {
        $this->getLayout()->getUpdate()->addHandle($handles ? $handles : 'default');
    }

    // add default layout handles for this action
    $this->addActionLayoutHandles();

    $this->loadLayoutUpdates(); //in here: $this->getLayout()->getUpdate()->load();

    if (!$generateXml) {
        return $this;
    }
    //event: controller_action_layout_generate_xml_before
    $this->generateLayoutXml(); //in here: $this->getLayout()->generateXml();

    if (!$generateBlocks) {
        return $this;
    }
    //event: controller_action_layout_generate_blocks_before, your observer is located here
    $this->generateLayoutBlocks(); //in here: $this->getLayout()->generateBlocks();
    $this->_isLayoutLoaded = true;

    return $this;
}

So, you're going to modify the xml using event: controller_action_layout_generate_blocks_before.

It means what you need to do is:

//add the update
$layout->getUpdate()->addUpdate('<reference name="product.info"><action method="setTemplate"><template>customelayout/product/view.phtml</template></action></reference>');
//then generate the xml
$layout->generateXml();

What cause your problem is:

$layout->getUpdate()->load();

was called again after

$layout->getUpdate()->addUpdate('<reference name="product.info"><action method="setTemplate"><template>customelayout/product/view.phtml</template></action></reference>');

Though it is better to use event: controller_action_layout_generate_xml_before. So that you don't need to generate your xml twice.

like image 59
ivantedja Avatar answered Sep 23 '22 13:09

ivantedja


I was going to comment on the fantastic answer by JBreton, but my particular use case which brought me to this thread is slightly different. (Also I'm an SO lurker and do not have adequate reputation to comment yet.)

The accepted answer and other suggestions for modifying the layout in PHP code did not work for me, even after trying to observe various events, so I figured I'd post a steal/support/example answer on JBreton's side. My use case was to REMOVE blocks (core and custom module blocks) from the checkout_cart_index layout programmatically based on certain conditions. The method of using a custom layout handle works for ADDING blocks as well since it simply "activates" a new handle that Magento will process from a standard layout XML file in a theme.

JBreton's method is the BEST from all of the ones that I tried. It makes more sense in the respect of current and future needs. Especially in the case where designers and template builders are not the same people who should be nosing around in the PHP code. Template people know XML and should be well familiar with Magento's layout XML system anyways. So using a custom handle to modify layouts on specific programmatic conditions is the superior method than adding XML through a string in PHP.

AGAIN ... this is not a solution I conjured on my own ... I stole this from JBreton's answer above and am supplying example code which my doppelganger could use in their situation as an additional starting point. Note that not all of my module code is included here (notably the app/modules XML file, model classes, etc).

My module's config file:

app/code/local/Blahblah/GroupCode/etc/config.xml

<config>
  ... other config XML too ...

  <frontend>
    <events>
        <controller_action_layout_load_before>
            <observers>
                <blahblah_groupcode_checkout_cart_index>
                    <type>singleton</type>
                    <class>Blahblah_Groupcode_Model_Ghost</class>
                    <method>checkout_cart_prepare</method>
                </blahblah_groupcode_checkout_cart_index>
            </observers>
        </controller_action_layout_load_before>
    </events>
  </frontend>
</config>

The observer's method in the class:

app/code/local/Blahblah/GroupCode/Model/Observer.php

<?php

    public function checkout_cart_prepare(Varien_Event_Observer $observer)
    {
        // this is the only action this function cares to work on
        $fullActionName = 'checkout_cart_index';

        ... some boring prerequiste code ...

        // find out if checkout is permitted
        $checkoutPermitted = $this->_ghost_checkoutPermitted();

        if(!$checkoutPermitted)
        {
            // add a custom handle used in our layout update xml file
            Mage::app()->getLayout()->getUpdate()->addHandle($fullActionName . '_disable_checkout');
        }

        return $this;
    }

The layout update inclusion in the theme file:

app/design/PACKAGE/THEME/etc/theme.xml

<?xml version="1.0"?>
<theme>
    <parent>...</parent>

    <layout>
        <updates>
            <!-- Adding references to updates in separate layout XML files. -->
            <blahblah_checkout_cart_index>
                <file>blahblah--checkout_cart_index.xml</file>
            </blahblah_checkout_cart_index>

            ... other update references too ...
        </updates>
    </layout>
</theme>

The layout update definition file:

app/design/PACKAGE/THEME/layout/blahblah--checkout_cart_index.xml

<layouts>
    <checkout_cart_index_disable_checkout>
        <reference name="content">
            <block type="core/template" name="checkout.disabled" as="checkout.disabled" before="-" template="checkout/disabled-message.phtml" />
            <remove name="checkout.cart.top_methods" />
            <remove name="checkout.cart.methods" />
        </reference>
    </checkout_cart_index_disable_checkout>

    ... other layout updates too ...
</layouts>

(Yes, there is other code in my module which watches the checkout process events to ensure that someone doesn't sneak in with a manual URL path. And other checks are in place to truly "disable" the checkout. I'm just showing my example of how to programmatically modify a layout through an observer.)

like image 43
Rodney Avatar answered Sep 26 '22 13:09

Rodney