Magento 2:構成設定に応じてブロックを削除


13

特定の構成フラグがに設定されてtrueいる場合にのみ、特定のページ(フロントエンドまたはバックエンド)からブロックを削除しようとしています。
例を見てみましょう。管理ダッシュボードから
名前dashboardを持つブロックを削除したい。

ブロックはモジュールのadminhtml_dashboard_index.xmlファイルで定義されますMagento_Backend

<referenceContainer name="content">
    <block class="Magento\Backend\Block\Dashboard" name="dashboard"/>
</referenceContainer>

アダムの答えのおかげで、私はこれをadminhtml_dashboard_index.xml

<body>
    <referenceBlock name="dashboard" remove="true"  />
</body>

しかし、パスdashboard/settings/removeを使用した構成設定に値がある場合にのみ、ノッチを取り、このブロックを削除します1
レイアウトxmlアプローチは素晴らしいでしょうが、オブザーバーアプローチも取ります。


?マリウスは、あなたが知っている同じことは、コンフィギュレーションが有効である場合、私は私が私のオブザーバーを実行したいわけevents.xmlに利用可能です
Keyurシャー

回答:


17

レイアウトでもこれを行う方法は見つかりませんでしたが、オブザーバー(テンプレートブロックを拡張する場合)でそれを行う方法の例を次に示します...

etc / events.xmlにevents.xmlを作成します

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="view_block_abstract_to_html_before">
        <observer name="remove_block" instance="[Vendor]\[ModuleName]\Model\Observer\RemoveBlock" />
    </event>
</config>

オブザーバーを作成する

<?php

namespace [Vendor]\[ModuleName]\Model\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class RemoveBlock implements ObserverInterface
{
    protected $_scopeConfig;

    public function __construct(
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    ) {
        $this->_scopeConfig = $scopeConfig;
    }

    public function execute(Observer $observer)
    {
        /** @var \Magento\Framework\View\Element\Template $block */
        $block = $observer->getBlock();
        if ($block->getType() == 'Magento\Backend\Block\Dashboard') {
            $remove = $this->_scopeConfig->getValue(
                'dashboard/settings/remove',
                \Magento\Store\Model\ScopeInterface::SCOPE_STORE
            );

            if ($remove) {
                $block->setTemplate(false);
            }
        }
    }
}

基本的に、_toHtmlはテンプレートがあるかどうかを確認します。ない場合は、 ''を返します。

編集

さらに掘り下げた後、これをチェーンのさらに上で行う方法を見つけました。

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="layout_generate_blocks_after">
        <observer name="remove_block" instance="[Vendor]\[ModuleName]\Model\Observer\RemoveBlock" />
    </event>
</config>

そしてオブザーバー...

<?php

namespace [Vendor]\[ModuleName]\Model\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class RemoveBlock implements ObserverInterface
{
    protected $_scopeConfig;

    public function __construct(
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
    ) {
        $this->_scopeConfig = $scopeConfig;
    }

    public function execute(Observer $observer)
    {
        /** @var \Magento\Framework\View\Layout $layout */
        $layout = $observer->getLayout();
        $block = $layout->getBlock('dashboard');
        if ($block) {
            $remove = $this->_scopeConfig->getValue(
                'dashboard/settings/remove',
                \Magento\Store\Model\ScopeInterface::SCOPE_STORE
            );

            if ($remove) {
                $layout->unsetElement('dashboard');
            }
        }
    }
}

これは機能する可能性がありますが、テンプレートを使用するブロックに対してのみです。これは、私が提供した例に適用されますが、AbstractBlockを拡張するブロックがあり、Templateブロックではない場合、これは機能しません。良い出発点に+1。
マリウス

あなたは正しいです。さらに掘り下げた後、プロセスの早い段階でこれを実行できることがわかりました。回答が更新されました。参考のためにオリジナルをそこに残しました。
スマーティー

ありがとう、これは役に立つ答えです。問題は、「layout_generate_blocks_after」イベントを使用しているため、ページがロードされるたびにロジックが起動されることを意味します。特定のページの読み込み(カテゴリページの読み込みなど)でのみ実行する方法を知っていますか(イベントは "catalog_controller_category_init_after"ですが、レイアウトにアクセスできません)。
アレックス

2
本当に?!ブロックを削除するか、条件付きでブロックしないようにオブザーバーを実行する必要がありますか?これはとんでもないです。
slayerbleast

1
オブザーバーは、私が思うデータを操作してはいけない
アレックス

5

通常は<action />tag を使用して実行する必要があります。

<referenceContainer name="content">
    <action method="unsetChild" ifconfig="dashboard/settings/remove">
        <argument xsi:type="string">dashboard</argument>
    </action>
</referenceContainer>

編集:

唯一の問題は、unsetChildがエイリアスのみを受け入れることです。ブロック名は使用できません。

その他の解決策:remove = "true"でifconfigを使用できるようにMagentoフレームワークを書き直します

1-独自のモジュールを作成します。

新しいファイルを追加します。2 - Magentoのフレームワークを上書きします(例:/Vendor/Module/Override/Magento/Framework/View/Layout/Reader/Block.php

namespace Vendor\Module\Override\Magento\Framework\View\Layout\Reader;

use Magento\Framework\App;
use Magento\Framework\Data\Argument\InterpreterInterface;
use Magento\Framework\View\Layout;

/**
 * Block structure reader
 */
class Block extends \Magento\Framework\View\Layout\Reader\Block
{
    /**
     * @var \Magento\Framework\App\ScopeResolverInterface
     */
    protected $scopeResolver;

    /**
     * @var \Magento\Framework\App\Config\ScopeConfigInterface
     */
    protected $scopeConfig;

    /**
     * Constructor
     *
     * @param Layout\ScheduledStructure\Helper $helper
     * @param Layout\Argument\Parser $argumentParser
     * @param Layout\ReaderPool $readerPool
     * @param InterpreterInterface $argumentInterpreter
     * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
     * @param \Magento\Framework\App\ScopeResolverInterface $scopeResolver
     * @param string|null $scopeType
     */
    public function __construct(
        Layout\ScheduledStructure\Helper $helper,
        Layout\Argument\Parser $argumentParser,
        Layout\ReaderPool $readerPool,
        InterpreterInterface $argumentInterpreter,
        \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig,
        \Magento\Framework\App\ScopeResolverInterface $scopeResolver,
        $scopeType = null
    ) {
        parent::__construct($helper,
            $argumentParser,
            $readerPool,
            $argumentInterpreter,
            $scopeType
        );
        $this->scopeConfig = $scopeConfig;
        $this->scopeResolver = $scopeResolver;
    }

    protected function scheduleReference(
        Layout\ScheduledStructure $scheduledStructure,
        Layout\Element $currentElement
    ) {
        $elementName = $currentElement->getAttribute('name');
        $elementRemove = filter_var($currentElement->getAttribute('remove'), FILTER_VALIDATE_BOOLEAN);
        if ($elementRemove) {
            $configPath = (string)$currentElement->getAttribute('ifconfig');
            if (empty($configPath)
                || $this->scopeConfig->isSetFlag($configPath, $this->scopeType, $this->scopeResolver->getScope())
            ) {
                $scheduledStructure->setElementToRemoveList($elementName);
            }
        } else {
            $data = $scheduledStructure->getStructureElementData($elementName, []);
            $data['attributes'] = $this->mergeBlockAttributes($data, $currentElement);
            $this->updateScheduledData($currentElement, $data);
            $this->evaluateArguments($currentElement, $data);
            $scheduledStructure->setStructureElementData($elementName, $data);
        }
    }
}

3- di.xmlファイルを追加して、magentoファイルを上書きします。

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Framework\View\Layout\Reader\Block"
       type="Vendor\Module\Override\Magento\Framework\View\Layout\Reader\Block" />    
</config>

4-レイアウトでifconfigをremoveと組み合わせて使用​​できるようになりました:

<referenceBlock name="content" remove="true" ifconfig="path/to/myconfig" />

この例はブロック用ですが、/ Magento / Framework / View / Layout / Reader / Container.phpのcontainerReference()メソッドをオーバーライドすると、コンテナに対しても同じことができます


フレームワークを書き換えることが最善の解決策だと思いますが、デフォルトでmagentoにこれがないのはなぜかわかりません。
slayerbleast

3

技術ガイドラインから:

14.1。イベントに渡されるすべての値(オブジェクトを含む)は、イベントオブザーバで変更してはなりません(MUST NOT)。代わりに、関数の入力または出力を変更するためにプラグインを使用する必要があります。

14.3。イベントは、オブザーバブルオブジェクトの状態を変更すべきではありません。

そのため、ここにプラグインソリューションがあります。

プラグインを宣言します。

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Framework\View\Element\AbstractBlock">
        <plugin name="remove_block" type="[Vendor]\[Module]\Plugin\RemoveBlock" />
    </type>
</config>

プラグインを定義します。

<?php

namespace Vendor\Module\Plugin;


use Magento\Framework\App\Config\ScopeConfigInterface;
use Magento\Framework\View\Element\AbstractBlock;

class RemoveBlock
{
    const BLOCK_NAME = 'block_to_be_removed';

    const CONFIG_PATH = 'your/path';

    private $_scopeConfig;

    public function __construct(ScopeConfigInterface $scopeConfig) {
        $this->_scopeConfig = $scopeConfig;
    }

    public function afterToHtml(AbstractBlock $subject, $result)
    {
        if ($subject->getNameInLayout() === self::BLOCK_NAME && $this->_scopeConfig->getValue(self::class)) {
            return '';
        }

        return $result;
    }
}

以下のようSmartieからの回答私にチェーンまで、さらにプラグインしようとした\Magento\Framework\View\Layout\Builder::buildとのafterBuild()方法が、これは、無限再帰につながる\Magento\Framework\View\Layout::getBlock\Magento\Framework\View\Layout::unsetElementの両方のコール\Magento\Framework\View\Layout\Builder::build再び。


2

レイアウトの「ブロック」ノードの「ifconfig」属性を使用すると、ブロックをストア構成の値にリンクできます。

「ifconfig」処理は \Magento\Framework\View\Layout\GeneratorPool::buildStructure


ただし、「referenceBlock」では機能しません。新しいブロックを追加するときにのみ機能します。
ニキータ・アブラシュネフ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.