在magento事件观察器中以编程方式更新布局

时间:2012-09-17 11:51:40

标签: magento

我正在尝试更改产品详细信息页面的块(product.info)的模板(view.phtml),为此,我正在观察事件(controller_action_layout_generate_blocks_before),之后在其中我正在尝试以下列方式更改块的模板(product.info):

$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();

如果我放"<remove name='product.info'/>",它会被删除,但在尝试执行上述操作时,它无法正常工作 修改:
要求是将模板(产品详细信息)动态切换到当前产品中的选定模板(在CustomModule中)。

5 个答案:

答案 0 :(得分:23)

正如Ben所说,我不知道你为什么要把它放在观察者身上,但你的案例中的问题是loadLayout的序列。

您可以使用以下方法检查加载的布局xml:

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

非常确定您的<action method="setTemplate"><template>customelayout/product/view.phtml</template>已被其他setTemplate覆盖,这就是您的模板未展示的原因。

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;
}

因此,您将使用事件修改xml:controller_action_layout_generate_blocks_before

这意味着你需要做的是:

//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();

导致问题的原因是:

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

之后再次调用

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

虽然最好使用事件:controller_action_layout_generate_xml_before。这样您就不需要两次生成xml了。

答案 1 :(得分:16)

如果要从观察者更改块的模板,则应

  1. 倾听controller_action_layout_generate_blocks_after事件

  2. 使用 PHP 来操作布局

  3. 通过侦听事件后生成,确保首先调用通过基于文件的布局更新XML字符串指定的每个操作方法,并且模板更改将“获胜”。

    我建议使用PHP代码,因为布局更新XML系统是一种特定于域的语言,其目的是为布局更新提供一组有限的功能,而无需编写单行PHP。如果您已经在使用PHP观察者,那么通过PHP操作布局是有意义的。

    类似这样的代码可以得到你想要的东西(再次,来自之后的观察者方法)

    $controller   = $observer->getAction();
    
    //limit to the product view page 
    if($controller->getFullActionName() != 'catalog_product_view')
    {
        return;
    }
    
    $layout       = $controller->getLayout();
    $product_info = $layout->getBlock('product.info');
    if(!$product_info)
    {
        Mage::log('Could not find product.info block');
        return;
    }
    
    $product_info->setTemplate('customelayout/product/view.phtml');
    

答案 2 :(得分:13)

为什么你这样做呢?

最好使用 local.xml 布局文件或为自定义模块声明的布局文件来执行此操作:

<?xml version="1.0" encoding="UTF-8"?>
<layout>
    <catalog_product_view>
        <reference name="product.info">
            <action method="setTemplate">
                <tpl>customelayout/product/view.phtml</tpl>
            </action>
        </reference>
    </catalog_product_view>
</layout>

仅当块名称为<remove/>时,对于包含该删除指令的任何渲染范围,不会实例化具有该名称的块。

答案 3 :(得分:4)

另一种解决方案,就是从我的角度来看,Magento精神中更多的是声明我们自己的手柄。

1。声明controller_action_layout_load_before

的观察者

在您的模块config.xml中,在节点config>frontend>events下面输入以下代码:

<controller_action_layout_load_before>
  <observers>
     <stackoverflow_set_handle>
        <class>stackoverflow_module/observer</class>
        <method>setHandle</method>
     </stackoverflow_set_handle>
  </observers>
</controller_action_layout_load_before>

2。定义你的观察者

class Stackoverflow_Module_Model_Observer
{
    public function setHandle(Varien_Event_Observer $observer)
    {
        $fullActionName = $observer->getEvent()->getAction()->getFullActionName();
        if (/* Any condition you may want to modify the layout */) {
            Mage::app()->getLayout()->getUpdate()->addHandle('MY_HANDLE_' . $fullActionName);
        }
    }

3。创建布局xml文件

完成后,您可以将任何fullActionName用作以MY_HANDLE _为前缀的布局更新文件中的第二级节点。

只有句柄存在时才会触发这些说明,因此基本上是您在观察者中设置的任何条件。

<?xml version="1.0"?>
<layout version="0.1.0">

    <MY_HANDLE_catalogsearch_result_index>
        <reference name="left">
            <remove name="catalogsearch.leftnav" />
        </reference>
    </MY_HANDLE_catalogsearch_result_index>

    <MY_HANDLE_catalog_product_view>
        <!-- Do anything you want -->
    </MY_HANDLE_catalog_product_view>

</layout>

最后的话

您当然可以测试观察者中的$fullActionName以更具体地添加句柄,并且可以基于fullActionName动态构建句柄。

有关信息,这是Magento管理大量布局变化的方式:

  • STORE_default &gt;使用当前商店动态构建
  • THEME_frontend_enterprise_enterprise &gt;使用当前主题动态构建
  • PRODUCT_TYPE_simple &gt;使用当前产品类型动态构建
  • PRODUCT_16 &gt;使用当前产品ID动态构建
  • customer_logged_out &gt;仅在客户登录时出现
  • 和其他人......

要查看它们,您可以暂时将其放在index.php的末尾:

var_dump(Mage::app()->getLayout()->getUpdate()->getHandles());

答案 4 :(得分:0)

我打算评论JBreton的精彩答案,但是我带到这个主题的特定用例略有不同。 (此外,我是一名SO潜伏者,尚未有足够的声誉发表评论。)

即使在尝试观察各种事件后,接受的答案和其他修改PHP代码布局的建议也不适合我,所以我想我会在JBreton&#39上发布窃取/支持/示例答案的一面我的用例是根据某些条件以编程方式从checkout_cart_index布局中删除块(核心和自定义模块块)。使用自定义布局句柄的方法也适用于ADDING块,因为它只是简单地激活&#34; Magento将从主题中的标准布局XML文件处理的新句柄。

JBreton的方法是我试过的所有方法中最好的。在当前和未来需求方面更有意义。特别是在设计师和模板构建者不是那些应该在PHP代码中引人注目的人的情况下。模板人员了解XML,并且应该熟悉Magento的布局XML系统。因此,使用自定义句柄修改特定编程条件下的布局是比通过PHP中的字符串添加XML更好的方法。

再次......这不是我自己想到的解决方案......我从上面的JBreton的答案中偷了这个,并提供我的分身可以在他们的情况下使用的示例代码作为一个额外的起点。请注意,此处并未包含所有模块代码(特别是app / modules XML文件,模型类等)。

我的模块的配置文件:

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>

班上观察者的方法:

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;
    }

主题文件中包含布局更新:

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>

布局更新定义文件:

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>

(是的,我的模块中还有其他代码可以监视结帐流程事件,以确保有人不会使用手动URL路径。并且还有其他检查以确保&#34;禁用&# 34;结帐。我只是展示了如何通过观察者以编程方式修改布局的示例。)