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 був би приголомшливим, але я також прийму спостерігач.


Маріусе, ти знаєш, що те ж саме доступне і для подій.xml? Я маю на увазі, що хочу запустити свого спостерігача, якщо конфігурація
включена

Якщо ви хочете піти з helperкласом, дивіться /programming/47237179/magento-2-i-want-to-add-ifconfig-in-override-block-xml?rq=1
Асрар

Відповіді:


17

Я не зміг знайти способу зробити це з компонуванням, але ось приклад того, як ви можете це зробити з спостерігачами (за умови, що вони розширюють блок «Шаблон») ...

Створіть свої події.xml у etc / 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 перевіряє, чи є шаблон. Якщо немає, він повертається ''.

EDIT

Після ще копання я знайшов спосіб зробити це далі по ланцюжку.

<?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, а не блок Шаблон, це не працюватиме. +1 для хорошої відправної точки.
Маріус

Ви праві. Після ще копання я знайшов, що ви можете це зробити раніше. Відповідь оновлено. Я залишив там свій оригінал для довідки.
Smartie

Дякую, це корисна відповідь. Проблема полягає в тому, що логіка буде запущена на кожному завантаженні сторінки, оскільки вона використовує подію "layout_generate_blocks_after". Чи знаєте ви, як запустити його лише на певних завантаженнях сторінки, наприклад, завантажуючи сторінку категорії (подія є "catalog_controller_category_init_after", але макет не може бути доступний)?
Олексій

2
Дійсно ?! Ми повинні зробити спостерігача, щоб видалити чи не умовно блок? це смішно, просто кажучи.
вбивство загибелі

1
Спостерігачі не повинні маніпулювати даними, я думаю ...
Алекс

5

Зазвичай це потрібно робити з <action />тегом:

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

Редагувати:

Єдина проблема - це unsetChild приймає лише псевдонім. Не можна використовувати ім'я блоку.

Інше рішення: переписати Magento Framework, щоб мати можливість ifconfig з delete = "true"

1- Створіть власний модуль.

2 Додати новий файл , щоб перевизначити Magento Framework (наприклад: /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 у макеті, поєднаному з видаленням:

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

Цей приклад призначений для Block, але ви можете зробити те ж саме для контейнера, якщо ви перекриєте метод containerReference () /Magento/Framework/View/Layout/Reader/Container.php


Я думаю, що переписання Framework - найкраще рішення, не знаю, чому у магенто цього немає за замовчуванням.
вбивство загибелі

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". Він працюватиме лише тоді, коли ви додасте новий блок.
Микита Абрашнєв
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.