Magento 2: Block entfernen abhängig von einer Konfigurationseinstellung

13

Ich versuche, einen Block von einer bestimmten Seite zu entfernen (sei es Frontend oder Backend), aber nur, wenn ein bestimmtes Konfigurationsflag auf gesetzt ist true.
Nehmen wir ein Beispiel.
Ich möchte den Block mit dem Namen dashboardaus dem Admin-Dashboard entfernen .

Der Block ist in definiert adminhtml_dashboard_index.xml Datei aus dem Magento_BackendModul definiert:

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

Dank Adams Antwort kann ich das in deradminhtml_dashboard_index.xml

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

Ich möchte es aber noch etwas genauer betrachten und diesen Block nur entfernen, wenn die Konfigurationseinstellung mit dem Pfad dashboard/settings/removeden Wert hat1 .
Ein Layout-XML-Ansatz wäre fantastisch, aber ich werde auch einen Beobachteransatz wählen.

Marius
quelle
Marius, du weißt, dass das gleiche für events.xml verfügbar ist? Ich meine, ich möchte meinen Beobachter ausführen, wenn die Konfiguration aktiviert ist
Keyur Shah
Wenn Sie mit einer helperKlasse teilnehmen möchten,
lesen

Antworten:

17

Ich habe auch keinen Weg gefunden, dies mit Layout zu tun, aber hier ist ein Beispiel dafür, wie Sie es mit Beobachtern tun können (vorausgesetzt, sie erweitern den Template-Block) ...

Erstellen Sie Ihre events.xml in 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>

Erstellen Sie den Beobachter

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

Grundsätzlich prüft _toHtml, ob eine Vorlage vorhanden ist. Ist dies nicht der Fall, wird "" zurückgegeben.

BEARBEITEN

Nach einigem Nachgraben habe ich einen Weg gefunden, dies weiter die Kette hoch zu tun.

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

Und der Beobachter ...

<?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');
            }
        }
    }
}
Smartie
quelle
Dies funktioniert möglicherweise, aber nur für Blöcke, die Vorlagen verwenden. Dies gilt für das von mir bereitgestellte Beispiel. Wenn es jedoch Blöcke gibt, die den AbstractBlock erweitern, und nicht den Template-Block, funktioniert dies nicht. +1 für den guten Startpunkt.
Marius
Du hast Recht. Nach einigem mehr Graben fand ich, dass Sie dies früher im Prozess tun können. Antwort aktualisiert. Ich habe mein Original dort als Referenz gelassen.
Smartie
Danke, das ist eine nützliche Antwort. Das Problem ist, dass die Logik bei jedem Laden der Seite ausgelöst wird, da das Ereignis "layout_generate_blocks_after" verwendet wird. Wissen Sie, wie Sie es nur bei bestimmten Seitenladevorgängen ausführen können, z. B. beim Laden einer Kategorieseite (Ereignis ist "catalog_controller_category_init_after", aber auf das Layout kann nicht zugegriffen werden)?
Alex
2
Ja wirklich?! Müssen wir einen Beobachter beauftragen, einen Block zu entfernen oder nicht bedingt? das ist lächerlich, nur zu sagen.
slayerbleast
1
Beobachter sollten keine Daten manipulieren, denke ich ...
Alex
5

Normalerweise sollte es mit <action />tag gemacht werden:

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

EDIT:

Einziges Problem ist, dass unsetChild nur Alias ​​akzeptiert. Sie können keinen Blocknamen verwenden.

Andere Lösung: Magento Framework neu schreiben, um ifconfig mit remove = "true" verwenden zu können

1- Erstellen Sie Ihr eigenes Modul.

2- eine neue Datei hinzufügen Magento - Framework außer Kraft zu setzen: (zB: /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- Fügen Sie die Datei di.xml hinzu, um die Magento-Datei zu überschreiben:

<?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- Jetzt können Sie ifconfig im Layout in Kombination mit remove verwenden:

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

Dieses Beispiel gilt für Block, aber Sie können dasselbe für container tun, wenn Sie die Methode containerReference () von /Magento/Framework/View/Layout/Reader/Container.php überschreiben

eInyzant
quelle
Ich halte das Umschreiben des Frameworks für die beste Lösung. Weiß nicht, warum Magento dies nicht standardmäßig hat.
Slayerbleast
2

Mit dem Attribut "ifconfig" eines "Block" -Knotens im Layout können Sie einen Block mit einem Wert in der Geschäftskonfiguration verknüpfen.

"ifconfig" -Verarbeitung erfolgt in \Magento\Framework\View\Layout\GeneratorPool::buildStructure

Anton Kril
quelle
2

Aus den technischen Richtlinien :

14.1. Alle an ein Ereignis übergebenen Werte (einschließlich Objekte) DÜRFEN NICHT im Ereignisbeobachter geändert werden. Stattdessen sollten Plugins verwendet werden, um die Eingabe oder Ausgabe einer Funktion zu ändern.

14.3. Ereignisse DÜRFEN NICHT den Status von beobachtbaren Objekten ändern.

Also hier ist eine Plugin-Lösung dafür:

Deklarieren Sie das Plugin:

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

Definiere das Plugin:

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

Wie in der Antwort von Smartie habe ich versucht, die Kette \Magento\Framework\View\Layout\Builder::buildmit einer afterBuild()Methode weiter zu erweitern, aber dies führt zu einer endlosen Rekursion, da \Magento\Framework\View\Layout::getBlockund \Magento\Framework\View\Layout::unsetElementbeide \Magento\Framework\View\Layout\Builder::builderneut aufrufen .

Daniel
quelle