9

我正在尝试更改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 中)。

4

5 回答 5

24

正如本所说,我不知道你为什么要把它放在观察者身上,但你的问题是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;
}

因此,您将使用 event: 修改 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>');

虽然最好使用 event: controller_action_layout_generate_xml_before。这样你就不需要生成你的 xml 两次。

于 2012-09-17T14:03:29.813 回答
16

如果你想从观察者那里改变一个块的模板,你应该

  1. 监听controller_action_layout_generate_blocks_after事件

  2. 使用PHP操作布局

通过监听 generate after 事件,您可以确保首先调用基于文件的布局更新 XML 字符串指定的每个操作方法,并且您的模板更改将“获胜”。

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

像这样的代码应该可以得到你想要的(同样,来自after观察者方法)

$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');
于 2012-09-17T19:22:17.400 回答
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/>编辑时,不会为任何包含该删除指令的渲染范围实例化具有该名称的块。

于 2012-09-17T13:01:34.687 回答
4

另一种解决方案,也就是在我看来,Magento's Spirit 中更多的是声明我们自己的句柄。

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 > 使用当前商店动态构建
  • THEME_frontend_enterprise_enterprise > 使用当前主题动态构建
  • PRODUCT_TYPE_simple > 使用当前产品类型动态构建
  • PRODUCT_16 > 使用当前产品 ID 动态构建
  • customer_logged_out > 仅在客户登录时出现
  • 和别的...

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

var_dump(Mage::app()->getLayout()->getUpdate()->getHandles());
于 2014-01-09T13:18:55.433 回答
1

我打算对 JBreton 的精彩回答发表评论,但是将我带到这个线程的特定用例略有不同。(而且我是一个潜伏者,还没有足够的声誉发表评论。)

即使在尝试观察各种事件之后,接受的答案和修改 PHP 代码布局的其他建议对我也不起作用,所以我想我会在 JBreton 方面发布一个窃取/支持/示例答案。我的用例是根据某些条件以编程方式从 checkout_cart_index 布局中删除块(核心和自定义模块块)。使用自定义布局句柄的方法也适用于 ADDING 块,因为它只是“激活”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 路径潜入。并且其他检查已到位以真正“禁用”结帐。我只是展示我如何通过观察者以编程方式修改布局的示例。)

于 2016-02-24T18:40:01.660 回答