Magento 2: Hapus blok tergantung pada pengaturan konfigurasi

13

Saya mencoba untuk menghapus blok dari halaman tertentu (baik itu frontend atau backend) tetapi hanya jika flag konfigurasi tertentu disetel ke true.
Mari kita ambil contoh.
Saya ingin menghapus blokir dengan nama dashboarddari dasbor admin.

Blok didefinisikan dalam adminhtml_dashboard_index.xmlfile dari Magento_Backendmodul:

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

Berkat jawaban Adam saya bisa melakukan ini diadminhtml_dashboard_index.xml

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

Tapi saya ingin mengambilnya takik dan menghapus blok ini hanya jika pengaturan konfigurasi dengan path dashboard/settings/removememiliki nilai 1.
Pendekatan tata letak xml akan luar biasa, tapi saya akan mengambil pendekatan pengamat juga.

Marius
sumber
Marius, Anda tahu hal yang sama tersedia untuk events.xml? Maksudku, saya ingin mengeksekusi pengamat saya jika konfigurasi diaktifkan
Keyur Shah

Jawaban:

17

Saya tidak dapat menemukan cara untuk melakukan ini dengan tata letak baik tetapi di sini adalah contoh cara Anda dapat melakukannya dengan pengamat (asalkan mereka memperpanjang blok Templat) ...

Buat events.xml Anda di 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>

Buat pengamat

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

Pada dasarnya _toHtml memeriksa apakah ada templat. Jika tidak, ia mengembalikan ''.

EDIT

Setelah menggali lagi saya telah menemukan cara untuk melakukan ini lebih jauh ke atas rantai.

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

Dan pengamat ...

<?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
sumber
Ini mungkin berhasil, tetapi hanya untuk blok yang menggunakan templat. Ini berlaku untuk contoh yang saya berikan, tetapi tetap saja, jika ada blok yang memperpanjang AbstractBlock dan bukan blok Template ini tidak akan berfungsi. +1 untuk titik awal yang baik.
Marius
Anda benar. Setelah menggali lagi saya menemukan Anda dapat melakukan ini sebelumnya dalam proses. Jawaban diperbarui. Saya telah meninggalkan dokumen asli saya di sana untuk referensi.
Smartie
Terima kasih, ini jawaban yang bermanfaat. Masalahnya adalah itu berarti logika akan diaktifkan pada setiap halaman memuat karena menggunakan event "layout_generate_blocks_after". Apakah Anda tahu cara menjalankannya hanya pada pemuatan halaman tertentu, misalnya memuat halaman kategori (eventnya adalah "catalog_controller_category_init_after" tetapi tata letak tidak dapat diakses)?
Alex
2
Betulkah?! Kita harus melakukan pengamat untuk menghapus atau tidak memblokir secara kondisional? ini konyol, hanya mengatakan.
slayerbleast
1
Pengamat seharusnya tidak memanipulasi data saya pikir ...
Alex
5

Biasanya itu harus dilakukan dengan <action />tag:

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

EDIT:

Satu-satunya masalah adalah unsetChild hanya menerima alias. Anda tidak dapat menggunakan nama blokir.

Solusi lain: tulis ulang Magento Framework untuk dapat menggunakan ifconfig dengan remove = "true"

1- Buat modul Anda sendiri.

2- Menambahkan file baru untuk menimpa Magento Kerangka: (misalnya: /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- Tambahkan file di.xml untuk mengganti 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- Sekarang Anda dapat menggunakan ifconfig dalam tata letak dikombinasikan dengan hapus:

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

Contoh ini untuk Block, tetapi Anda bisa melakukan hal yang sama untuk kontainer jika Anda mengganti metode containerReference () dari /Magento/Framework/View/Layout/Reader/Container.php

eInyzant
sumber
Saya pikir menulis ulang Framework adalah solusi terbaik, tidak tahu mengapa magento tidak memiliki ini secara default.
slayerbleast
3

Dari pedoman teknis :

14.1. Semua nilai (termasuk objek) yang diteruskan ke suatu acara TIDAK HARUS dimodifikasi pada pengamat acara. Sebagai gantinya, plugins HARUS digunakan untuk memodifikasi input atau output dari suatu fungsi.

14.3. Acara TIDAK HARUS mengubah keadaan benda yang bisa diamati.

Jadi di sini adalah solusi plugin untuk ini:

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

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

Seperti dalam jawaban dari Smartie, saya mencoba plugin lebih jauh ke rantai \Magento\Framework\View\Layout\Builder::builddengan afterBuild()metode tetapi ini akan menyebabkan rekursi yang tak ada habisnya \Magento\Framework\View\Layout::getBlockdan karena \Magento\Framework\View\Layout::unsetElementkeduanya menelepon \Magento\Framework\View\Layout\Builder::buildlagi

Daniel
sumber
2

Atribut "ifconfig" dari simpul "blok" dalam tata letak memungkinkan Anda untuk menautkan blok ke nilai dalam konfigurasi toko.

pemrosesan "ifconfig" terjadi di \Magento\Framework\View\Layout\GeneratorPool::buildStructure

Anton Kril
sumber
Itu tidak akan bekerja dengan "referenceBlock". Ini hanya akan berfungsi ketika Anda menambahkan blok baru.
Nikita Abrashnev