마 젠토 2 : 구성 설정에 따라 블록 제거


13

특정 구성 플래그가로 설정되어있는 경우에만 특정 페이지 (프론트 엔드 또는 백엔드)에서 블록을 제거하려고합니다 true.
예를 들어 봅시다.
이름을 가진 블록을 제거하고 싶습니다dashboard관리 대시 보드에서 .

블록은 모듈 에서 adminhtml_dashboard_index.xml파일 로 정의됩니다 Magento_Backend.

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

아담의 대답 덕분에 나는이 작업을 수행 할 수 있습니다adminhtml_dashboard_index.xml

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

그러나 경로 dashboard/settings/remove를 사용하여 구성 설정 에 값이있는 경우에만 한 단계 높이고이 블록을 제거하고 싶습니다 1.
레이아웃 xml 접근 방식은 훌륭하지만 관찰자 접근 방식도 취하겠습니다.


마리우스, 당신은 events.xml에 같은 것을 사용할 수 있다는 것을 알고 있습니까? 구성이 활성화되어 있으면 관찰자를 실행하고 싶습니다.
Keyur Shah

당신이 함께 가고 싶은 경우 helper클래스를 참조 /programming/47237179/magento-2-i-want-to-add-ifconfig-in-override-block-xml?rq=1을
Asrar

답변:


17

레이아웃을 사용 하여이 작업을 수행 할 수있는 방법을 찾을 수는 없지만 관찰자 (템플릿 블록을 확장하면 제공)를 사용하여 수행 할 수있는 방법의 예입니다 ...

etc / events.xml에 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>

관찰자 만들기

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

기본적으로 _toHtml은 템플릿이 있는지 확인합니다. 없으면 ''를 반환합니다.

편집하다

좀 더 파고 난 후에 체인을 더 위로 할 수있는 방법을 찾았습니다.

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

그리고 관찰자는 ...

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

이것은 작동하지만 템플릿을 사용하는 블록에서만 작동합니다. 그것은 내가 제공 한 예에 적용되지만 여전히 템플릿 블록이 아닌 AbstractBlock을 확장하는 블록이 있으면 작동하지 않습니다. 좋은 시작점 +1
Marius

당신이 올바른지. 좀 더 파고 나면 프로세스 초기 에이 작업을 수행 할 수 있다는 것을 알았습니다. 답변이 업데이트되었습니다. 나는 원본을 참조 용으로 남겨 두었다.
Smartie

감사합니다. 유용한 답변입니다. 문제는 "layout_generate_blocks_after"이벤트를 사용하므로 모든 페이지로드에서 로직이 시작된다는 것입니다. 카테고리 페이지로드와 같은 특정 페이지로드에서만 실행하는 방법을 알고 있습니까 (이벤트는 "catalog_controller_category_init_after"이지만 레이아웃에 액세스 할 수 없음)?
Alex

2
정말?! 조건부 블록을 제거하거나 제거하지 않으려면 관찰자를해야합니까? 이것은 말도 안되는 말입니다.
slayerbleast

1
관찰자는 내가 생각하는 데이터를 조작해서는 안됩니다.
Alex

5

일반적으로 <action />태그 로 수행해야합니다 .

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

편집하다 :

문제는 unsetChild 만 별칭을 허용하는 것입니다. 블록 이름을 사용할 수 없습니다.

다른 솔루션 : remove = "true"와 함께 ifconfig를 사용할 수 있도록 Magento Framework를 다시 작성하십시오.

1- 자신의 모듈을 만듭니다.

새 파일을 추가 2 젠토 프레임 워크를 오버라이드 (override) : (예 : /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- magento 파일을 덮어 쓰려면 di.xml 파일을 추가하십시오.

<?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- 이제 레이아웃에서 ifconfig를 remove와 결합하여 사용할 수 있습니다.

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

이 예제는 Block에 대한 것이지만 /Magento/Framework/View/Layout/Reader/Container.php의 containerReference () 메소드를 재정의하는 경우 컨테이너에 대해서도 동일한 작업을 수행 할 수 있습니다.


프레임 워크를 다시 작성하는 것이 가장 좋은 해결책이라고 생각합니다. 왜 magento에 기본적으로 이것이 없는지 모르겠습니다.
slayerbleast

3

로부터 기술 지침 :

14.1. 이벤트에 전달 된 모든 값 (객체 포함)은 이벤트 옵저버에서 수정해서는 안됩니다 (MUST NOT). 대신, 플러그인은 함수의 입력 또는 출력을 수정하는 데 사용해야합니다.

14.3. 이벤트는 관찰 가능한 객체의 상태를 변경해서는 안됩니다 (SHOULD NOT).

이에 대한 플러그인 솔루션은 다음과 같습니다.

플러그인을 선언하십시오 :

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

플러그인을 정의하십시오.

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

에서와 마찬가지로 Smartie에서 대답 I에 체인까지 추가로 플러그인을 시도 \Magento\Framework\View\Layout\Builder::buildafterBuild()방법하지만이 때문에 무한 재귀으로 이어질 것입니다 \Magento\Framework\View\Layout::getBlock\Magento\Framework\View\Layout::unsetElement모두 호출 \Magento\Framework\View\Layout\Builder::build을 다시.


2

레이아웃에서 "블록"노드의 "ifconfig"속성을 사용하면 상점 구성에서 블록을 값에 연결할 수 있습니다.

"ifconfig"처리는 \Magento\Framework\View\Layout\GeneratorPool::buildStructure


"referenceBlock"에서는 작동하지 않습니다. 새 블록을 추가 할 때만 작동합니다.
Nikita Abrashnev
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.