Magento 2: rimuovere il blocco in base a un'impostazione di configurazione


13

Sto cercando di rimuovere un blocco da una determinata pagina (sia esso frontend o backend) ma solo se è impostato un determinato flag di configurazione true.
Facciamo un esempio.
Voglio rimuovere il blocco con il nome dashboarddalla dashboard di amministrazione.

Il blocco è definito nel adminhtml_dashboard_index.xmlfile dal Magento_Backendmodulo:

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

Grazie alla risposta di Adam posso farlo neladminhtml_dashboard_index.xml

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

Ma voglio prendere un livello e rimuovere questo blocco solo se l'impostazione di configurazione con il percorso dashboard/settings/removeha il valore 1.
Un approccio xml di layout sarebbe fantastico, ma prenderò anche un approccio osservatore.


Marius, sai che è disponibile la stessa cosa per events.xml? Voglio dire, voglio eseguire il mio osservatore se la configurazione è abilitata
Keyur Shah,

Risposte:


17

Non sono riuscito a trovare un modo per farlo con il layout, ma ecco un esempio di come puoi farlo con gli osservatori (a condizione che estendano il blocco Template) ...

Crea il tuo 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>

Crea l'osservatore

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

Fondamentalmente _toHtml verifica se esiste un modello. Altrimenti restituisce ''.

MODIFICARE

Dopo qualche altro scavo ho trovato il modo di farlo più in alto nella catena.

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

E l'osservatore ...

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

Questo potrebbe funzionare, ma solo per i blocchi che utilizzano modelli. Si applica all'esempio che ho fornito, ma comunque, se ci sono blocchi che estendono il blocco astratto e non il blocco modello, questo non funzionerà. +1 per il buon punto di partenza.
Marius

Hai ragione. Dopo aver scavato un po 'di più ho scoperto che puoi farlo prima nel processo. Risposta aggiornata Ho lasciato il mio originale lì per riferimento.
Smartie,

Grazie questa è una risposta utile. Il problema è che la logica verrà attivata a ogni caricamento della pagina poiché utilizza l'evento "layout_generate_blocks_after". Sai come eseguirlo solo su determinati caricamenti di pagina, ad esempio caricando una pagina di categoria (l'evento è "catalog_controller_category_init_after" ma non è possibile accedere al layout)?
Alex,

2
Veramente?! Dobbiamo fare un osservatore per rimuovere o non condizionatamente un blocco? questo è ridicolo, sto solo dicendo.
slayerbleast

1
Gli osservatori non dovrebbero manipolare i dati credo ...
Alex

5

Normalmente dovrebbe essere fatto con il <action />tag:

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

MODIFICARE :

L'unico problema è unset: il bambino accetta solo l'alias. Non è possibile utilizzare il nome del blocco.

Altra soluzione: riscrivere Magento Framework per poter usare ifconfig con remove = "true"

1- Crea il tuo modulo.

2- Aggiungere un nuovo file per ignorare Magento quadro: (es /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- Aggiungi il file di.xml per sovrascrivere il file 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- Ora puoi usare ifconfig nel layout combinato con remove:

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

Questo esempio è per Block, ma puoi fare lo stesso per container se sostituisci il metodo containerReference () di /Magento/Framework/View/Layout/Reader/Container.php


Penso che riscrivere il Framework sia la soluzione migliore, non so perché magento non lo abbia di default.
slayerbleast,

3

Dalle linee guida tecniche :

14.1. Tutti i valori (inclusi gli oggetti) passati a un evento NON DEVONO essere modificati nell'osservatore di eventi. Al contrario, i plugin DOVREBBERO essere utilizzati per modificare l'input o l'output di una funzione.

14.3. Gli eventi NON DOVREBBERO cambiare uno stato di oggetti osservabili.

Quindi ecco una soluzione plugin per questo:

Dichiara il 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>

Definire il plug-in:

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

Come nella risposta di Smartie, ho provato a collegare ulteriormente la catena \Magento\Framework\View\Layout\Builder::buildcon un afterBuild()metodo, ma questo porterà a una ricorsione infinita perché \Magento\Framework\View\Layout::getBlocked \Magento\Framework\View\Layout::unsetElemententrambi chiamano di \Magento\Framework\View\Layout\Builder::buildnuovo.


2

L'attributo "ifconfig" di un nodo "blocco" nel layout consente di collegare il blocco al valore nella configurazione dell'archivio.

L'elaborazione "ifconfig" avviene in \Magento\Framework\View\Layout\GeneratorPool::buildStructure


Tuttavia, non funzionerà con "referenceBlock". Funzionerà solo quando aggiungi un nuovo blocco.
Nikita Abrashnev il
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.