【问题标题】:update layout programmatically in magento event observer在 magento 事件观察器中以编程方式更新布局
【发布时间】:2019-01-21 17:28:20
【问题描述】:

我正在尝试更改产品详细信息页面的块 (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();

如果我输入 "&lt;remove name='product.info'/&gt;" ,它将被删除,但在尝试执行上述操作时,它不起作用。
编辑:
要求是针对当前产品将模板(产品详细信息)动态切换到选定的模板(在 CustomModule 中)。

【问题讨论】:

  • 短版:“删除”功能与“引用”和“阻止”标签的操作方式不同。您正在尝试以一种并非旨在使用的方式使用系统 - 根据以下答案 1. 使用 local.xml 2. 通过您的模块添加自定义布局 XML 更新文件并使用该文件 3. 使用纯PHP 通过事件观察者操作布局对象。

标签: magento


【解决方案1】:

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

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

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

很确定您的 &lt;action method="setTemplate"&gt;&lt;template&gt;customelayout/product/view.phtml&lt;/template&gt; 已被其他 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-&gt;getUpdate()-&gt;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 两次了。

【讨论】:

  • 成功了!!!就像你说的 $layout->getUpdate()->load(); (东西在错误的地方):D,谢谢,谢谢大家,我从stackoverflow学到了很多东西。
【解决方案2】:

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

  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');

【讨论】:

  • 谢谢!这对我来说是根据 URL 参数更改模板的诀窍。
【解决方案3】:

你到底为什么要这样做?

最好使用 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>

仅供参考,当块名称为 &lt;remove/&gt;ed 时,将不会为任何包含该删除指令的渲染范围实例化具有该名称的块。

【讨论】:

  • 本马克是对的。它是最简单的方法。但是,如果您的模板取决于系统配置等条件,则使用&lt;ifconfig&gt; tagin xml。如果它取决于产品或类别,则使用该产品或类别的后端布局更新。
  • +1 @benmarks 恰到好处。如果您正在编写布局更新 XML,您应该将其放入布局更新 XML 文件中。如果您想避开布局更新 XML 文件,请使用 after 事件侦听器并直接通过 PHP 操作布局对象。
  • 实际上,我必须为详细信息页面提供 4 种(选项)不同的布局。这是我的任务,即根据我的自定义模块中的任何产品(选择的布局)中的后端布局选择切换到布局。我必须在不覆盖任何核心模块的情况下做到这一点。
  • 我希望这最后的评论是原始问题的一部分。
【解决方案4】:

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

1。声明controller_action_layout_load_before 的观察者

在您的模块 config.xml 中,在节点 config&gt;frontend&gt;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());

【讨论】:

    【解决方案5】:

    我打算对 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 路径潜入。并且其他检查已到位以真正“禁用”结帐。我'我只是展示了如何通过观察者以编程方式修改布局的示例。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-12-08
      • 2015-03-06
      • 2012-12-31
      • 2018-11-28
      • 2020-06-04
      • 2021-02-13
      • 1970-01-01
      • 2015-07-06
      相关资源
      最近更新 更多