Magento 2: Xóa khối tùy theo cài đặt cấu hình


13

Tôi đang cố gắng xóa một khối khỏi một trang nhất định (có thể là frontend hoặc backend) nhưng chỉ khi một cờ cấu hình nhất định được đặt thành true.
Hãy lấy một ví dụ.
Tôi muốn xóa khối có tên dashboardkhỏi bảng điều khiển quản trị.

Khối được xác định trong adminhtml_dashboard_index.xmltệp từ Magento_Backendmô-đun:

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

Nhờ câu trả lời của Adam mà tôi có thể làm điều này trongadminhtml_dashboard_index.xml

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

Nhưng tôi muốn nâng nó lên và loại bỏ khối này chỉ khi cài đặt cấu hình với đường dẫn dashboard/settings/removecó giá trị 1.
Một cách tiếp cận xml bố cục sẽ là tuyệt vời, nhưng tôi cũng sẽ có một cách tiếp cận quan sát viên.


Marius, bạn có biết điều tương tự có sẵn cho sự kiện không? Ý tôi là tôi muốn thực thi trình quan sát của mình nếu cấu hình được bật
Keyur Shah

Nếu bạn muốn đi cùng một helperlớp, hãy xem /programming/47237179/magento-2-i-want-to-add-ifconfig-in-override-block-xml?rq=1
Asrar

Câu trả lời:


17

Tôi cũng không thể tìm ra cách để làm điều này với bố cục nhưng đây là một ví dụ về cách bạn có thể làm điều đó với người quan sát (miễn là họ mở rộng khối Mẫu) ...

Tạo sự kiện của bạn trong etc / event.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>

Tạo người quan sát

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

Về cơ bản, _toHtml kiểm tra xem có mẫu nào không. Nếu không, nó sẽ trả về ''.

BIÊN TẬP

Sau khi đào thêm tôi đã tìm ra cách để làm điều này hơn nữa trong chuỗi.

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

Và người quan sát ...

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

Điều này có thể hoạt động, nhưng chỉ đối với các khối sử dụng mẫu. Nó áp dụng cho ví dụ tôi đã cung cấp, nhưng vẫn có, nếu có các khối mở rộng AbstractBlock và không phải khối Mẫu này sẽ không hoạt động. +1 cho điểm khởi đầu tốt.
Marius

Bạn nói đúng. Sau khi đào thêm tôi thấy bạn có thể làm điều này sớm hơn trong quá trình. Trả lời cập nhật. Tôi đã để lại bản gốc của mình ở đó để tham khảo.
Smartie

Cảm ơn đây là một câu trả lời hữu ích. Vấn đề là nó có nghĩa là logic sẽ được kích hoạt trên mỗi lần tải trang vì nó sử dụng sự kiện "layout_generate_blocks_after". Bạn có biết làm thế nào để chỉ chạy nó trên các tải trang nhất định, ví dụ như tải một trang danh mục (sự kiện là "catalog_controll_carget_init_after" nhưng bố cục không thể truy cập được)?
Alex

2
Có thật không?! Chúng ta phải làm một người quan sát để loại bỏ hoặc không có điều kiện một khối? Điều này là vô lý, chỉ cần nói.
slayerbleast

1
Người quan sát không nên thao túng dữ liệu tôi nghĩ ...
Alex

5

Thông thường nên được thực hiện với <action />thẻ:

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

BIÊN TẬP :

Chỉ có vấn đề là unsetChild chỉ chấp nhận bí danh. Bạn không thể sử dụng tên khối.

Giải pháp khác: viết lại Magento Framework để có thể sử dụng ifconfig với remove = "true"

1- Tạo mô-đun của riêng bạn.

2- Thêm một tệp mới để ghi đè Magento Framework: (ví dụ /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- Thêm tệp di.xml để ghi đè tệp 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- Bây giờ bạn có thể sử dụng ifconfig trong bố cục kết hợp với remove:

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

Ví dụ này dành cho Block, nhưng bạn có thể làm tương tự cho container nếu bạn ghi đè phương thức containerReference () của /Magento/Framework/View/Layout/Reader/Container.php


Tôi nghĩ rằng viết lại Framework là giải pháp tốt nhất, không biết tại sao magento không có cái này theo mặc định.
slayerbleast

3

Từ hướng dẫn kỹ thuật :

14.1. Tất cả các giá trị (bao gồm các đối tượng) được truyền cho một sự kiện PHẢI KHÔNG được sửa đổi trong trình quan sát sự kiện. Thay vào đó, các plugin NÊN được sử dụng để sửa đổi đầu vào hoặc đầu ra của một chức năng.

14.3. Sự kiện KHÔNG NÊN thay đổi trạng thái của các đối tượng quan sát được.

Vì vậy, đây là một giải pháp plugin cho việc này:

Khai báo 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>

Xác định 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;
    }
}

Giống như trong câu trả lời từ Smartie, tôi đã cố gắng bổ sung thêm chuỗi \Magento\Framework\View\Layout\Builder::buildbằng một afterBuild()phương thức nhưng điều này sẽ dẫn đến một đệ quy vô tận vì \Magento\Framework\View\Layout::getBlock\Magento\Framework\View\Layout::unsetElementcả hai gọi \Magento\Framework\View\Layout\Builder::buildlại.


2

Thuộc tính "ifconfig" của nút "chặn" trong bố cục cho phép bạn liên kết khối với giá trị trong cấu hình cửa hàng.

Quá trình xử lý "ifconfig" xảy ra trong \Magento\Framework\View\Layout\GeneratorPool::buildStructure


Nó sẽ không hoạt động với "ReferenceBlock". Nó sẽ chỉ hoạt động khi bạn thêm một khối mới.
Nikita Abrashnev
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.