Magento 2:删除块取决于配置设置


13

我试图从某个页面(前端或后端)中删除一个块,但前提是某个配置标志设置为true
让我们举个例子。
我想dashboard从管理控制台中删除名称相同的块。

该块adminhtml_dashboard_index.xmlMagento_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方法很棒,但是我也将采用观察者方法。


Marius,您知道event.xml可以使用相同的功能吗?我的意思是,如果配置启用,我想执行我的观察者
Keyur Shah,

Answers:


17

我也找不到使用布局执行此操作的方法,但这是您可以与观察者执行此操作的示例(假设他们扩展了Template块)...

在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是一个很好的起点。
马里乌斯

你是对的。经过更多的挖掘,我发现您可以在此过程的早期进行此操作。答案已更新。我把原件留在那里供参考。
Smartie

谢谢,这是一个有用的答案。问题在于这意味着使用事件“ layout_generate_blocks_after”时,每次加载页面时都会触发该逻辑。您是否知道如何仅在某些页面加载(例如加载类别页面)上运行它(事件为“ catalog_controller_category_init_after”,但无法访问布局)?
亚历克斯

2
真?!我们必须做一个观察者来移除或不条件地阻止一个块吗?只是说这太荒谬了。
slayerbleast

1
观察者不应该操纵我认为的数据……
Alex

5

通常应该使用<action />tag 来完成:

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

编辑:

唯一的问题是unsetChild只接受别名。您不能使用块名称。

其他解决方案:重写Magento Framework,使其能够将ifconfig与remove =“ true”一起使用

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" />

此示例适用于Block,但是如果您覆盖/Magento/Framework/View/Layout/Reader/Container.php的方法containerReference(),则可以对容器执行相同的操作


我认为重写框架是最好的解决方案,不知道为什么magento默认不具有此功能。
slayerbleast

3

根据技术准则

14.1。传递给事件的所有值(包括对象)都不得在事件观察器中进行修改。相反,应该使用插件来修改函数的输入或输出。

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”。它仅在添加新块时有效。
Nikita Abrashnev
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.