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

R T*_*R T 9 magento

我试图改变一个块的模板(view.phtml)( product.info)用于商品详细页,要做到这一点,我观察的事件(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();
Run Code Online (Sandbox Code Playgroud)

如果我放"<remove name='product.info'/>",它将被删除,但当试图做上述,它不工作.
编辑:
要求是将模板(产品详细信息)动态切换到当前产品中的选定模板(在CustomModule中).

iva*_*dja 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;
}
Run Code Online (Sandbox Code Playgroud)

所以,你将使用事件来修改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();
Run Code Online (Sandbox Code Playgroud)

你的问题是什么原因:

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

之后再次被召唤

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

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


Ala*_*orm 16

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

  1. 听取这个controller_action_layout_generate_blocks_after事件

  2. 使用PHP来操作布局

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

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

像这样的代码应该能得到你想要的东西(再次,来自after observer方法)

$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');
Run Code Online (Sandbox Code Playgroud)


ben*_*rks 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>
Run Code Online (Sandbox Code Playgroud)

<remove/>编辑块名时的FYI ,不会为包含该删除指令的任何渲染范围实例化具有该名称的块.