9

製品詳細ページのブロック()のテンプレート(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();

置く"<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;
}

したがって、イベントを使用して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を2回生成する必要はありません。

于 2012-09-17T14:03:29.813 に答える
16

オブザーバーからブロックのテンプレートを変更する場合は、

  1. controller_action_layout_generate_blocks_afterイベントを聞く

  2. PHPを使用してレイアウトを操作する

生成後のイベントをリッスンすることにより、ファイルベースのレイアウト更新XML文字列を介して指定されたすべてのアクションメソッドが最初に呼び出され、テンプレートの変更が「優先」されるようにします。

レイアウト更新XMLシステムはドメイン固有言語であるため、PHPコードを使用することをお勧めします。その目的は、PHPを1行も記述せずに、レイアウト更新に限定された機能セットを提供することでした。すでに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');
于 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

もう1つの解決策、つまり、私の観点からすると、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ファイルを作成します

完了すると、MY_HANDLE_というプレフィックスが付いたレイアウト更新ファイルの第2レベルノードとして使用できるfullActionNameができます。

これらの命令は、ハンドルが存在する場合にのみトリガーされるため、基本的に、オブザーバーで設定したすべての条件に対してトリガーされます。

<?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側にsteal / support/example回答を投稿すると思いました。私のユースケースは、特定の条件に基づいてプログラムでcheckout_cart_indexレイアウトからブロック(コアおよびカスタムモジュールブロック)を削除することでした。カスタムレイアウトハンドルを使用する方法は、Magentoがテーマ内の標準レイアウトXMLファイルから処理する新しいハンドルを単に「アクティブ化」するため、ADDINGブロックでも機能します。

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 に答える