Magento 2: Usuń blok w zależności od ustawienia konfiguracji


13

Próbuję usunąć blok z określonej strony (czy to frontend, czy backend), ale tylko wtedy, gdy ustawiona jest pewna flaga konfiguracji true.
Weźmy przykład.
Chcę usunąć blok o nazwie dashboardz pulpitu administratora.

Blok jest zdefiniowany w adminhtml_dashboard_index.xmlpliku z Magento_Backendmodułu:

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

Dzięki odpowiedzi Adama mogę to zrobić wadminhtml_dashboard_index.xml

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

Ale chcę go podnieść i usunąć ten blok tylko wtedy, gdy ustawienie konfiguracji ze ścieżką dashboard/settings/removema wartość 1.
Podejście XML do układu byłoby niesamowite, ale przyjmuję również podejście obserwatora.


Marius, wiesz, że to samo jest dostępne dla events.xml? Mam na myśli, że chcę uruchomić mojego obserwatora, jeśli konfiguracja jest włączona
Keyur Shah

Odpowiedzi:


17

Nie mogłem też znaleźć sposobu, aby to zrobić za pomocą układu, ale oto przykład sposobu, w jaki można to zrobić za pomocą obserwatorów (pod warunkiem, że rozszerzają blok szablonu) ...

Utwórz plik events.xml w pliku 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>

Utwórz obserwatora

<?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);
            }
        }
    }
}

Zasadniczo _toHtml sprawdza, czy istnieje szablon. Jeśli nie, zwraca ''.

EDYTOWAĆ

Po dalszych kopaniach znalazłem sposób na zrobienie tego dalej.

<?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>

A obserwator ...

<?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');
            }
        }
    }
}

Może to działać, ale tylko w przypadku bloków korzystających z szablonów. Dotyczy to podanego przeze mnie przykładu, ale jeśli istnieją bloki, które rozszerzają blok AbstractBlock, a nie blok szablonu, to nie zadziała. +1 za dobry punkt początkowy.
Marius

Masz rację. Po kilku kopaniach odkryłem, że możesz to zrobić wcześniej. Odpowiedź zaktualizowana. Zostawiłem tam swój oryginał w celach informacyjnych.
Smartie

Dzięki, to przydatna odpowiedź. Problem polega na tym, że logika będzie uruchamiana przy każdym ładowaniu strony, ponieważ używa zdarzenia „layout_generate_blocks_after”. Czy wiesz, jak uruchamiać go tylko przy niektórych ładowaniach strony, np. Podczas ładowania strony kategorii (zdarzenie to „katalog_kontroler_kategoria_inicjacja_na później”, ale układ nie jest dostępny)?
Alex

2
Naprawdę?! Musimy zrobić obserwatora, aby usunąć lub nie warunkowo blok? to niedorzeczne, mówiąc tylko.
slayerbleast

1
Myślę, że obserwatorzy nie powinni manipulować danymi ...
Alex

5

Zwykle należy to zrobić za pomocą <action />tagu:

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

EDYTOWAĆ :

Jedyny problem jest nierozwiązany Dziecko akceptuje tylko alias. Nie możesz użyć nazwy bloku.

Inne rozwiązanie: przepisz Magento Framework, aby móc używać ifconfig z remove = "true"

1- Utwórz własny moduł.

2- Dodaj nowy plik nadpisać Magento ramowej: (np /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- Dodaj plik di.xml, aby zastąpić plik 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- Teraz możesz używać ifconfig w układzie w połączeniu z usuwaniem:

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

Ten przykład dotyczy Block, ale możesz zrobić to samo dla kontenera, jeśli przesłonisz metodę containerReference () w /Magento/Framework/View/Layout/Reader/Container.php


Myślę, że przepisanie frameworka jest najlepszym rozwiązaniem, nie wiem, dlaczego Magento nie ma tego domyślnie.
slayerbleast

3

Z wytycznych technicznych :

14.1 Wszystkie wartości (w tym obiekty) przekazane do zdarzenia NIE MOGĄ być modyfikowane w obserwatorze zdarzeń. Zamiast tego wtyczki POWINNY BYĆ używane do modyfikowania wejścia lub wyjścia funkcji.

14,3 Zdarzenia NIE POWINNY zmieniać stanu obserwowalnych obiektów.

Oto rozwiązanie wtyczki do tego:

Zadeklaruj wtyczkę:

<?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>

Zdefiniuj wtyczkę:

<?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;
    }
}

Podobnie jak w odpowiedzi z Smartie próbowałem plugin dalej w górę łańcucha \Magento\Framework\View\Layout\Builder::buildz afterBuild()metody, ale doprowadzi to do nieskończonej rekurencji bo \Magento\Framework\View\Layout::getBlocki \Magento\Framework\View\Layout::unsetElementzarówno połączeń \Magento\Framework\View\Layout\Builder::buildponownie.


2

Atrybut „ifconfig” węzła „blok” w układzie umożliwia powiązanie bloku z wartością w konfiguracji sklepu.

Przetwarzanie „ifconfig” odbywa się w \Magento\Framework\View\Layout\GeneratorPool::buildStructure


Jednak nie będzie działać z „referenceBlock”. Działa tylko wtedy, gdy dodajesz nowy blok.
Nikita Abrashnev
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.