Usuń blok z układu bez nazwy

12

Chcę usunąć blok z układu w Magento 2, który jest zadeklarowany w rozszerzeniu innej firmy, ale blok nie ma nazwy.
Czy mogę to zrobić?

Blok jest zadeklarowany w ten sposób

<referenceContainer name="before.body.end">
    <block class="Magento\Backend\Block\Template" template="[Vendor_Module]::template.phtml"/>
</referenceContainer>

Nie mogę użyć

<referenceBlock name="..." remove="true" /> 

ponieważ, jak widać, nie ma na nim nazwy.

Marius
źródło
marius, mam pomysł. jeśli używamy zdarzenia i usuwamy blok według nazwy szablonu dopasowania [Vendor_Module]::template.phtml
Amit Bera
Mam ten sam pomysł (patrz komentarze do odpowiedzi), ale wykorzystam go tylko jako desperacką miarę. Miałem nadzieję na proste rozwiązanie. Jeśli masz kod, opublikuj go jako odpowiedź.
Marius
ha ha, że ​​nie mamy prostego rozwiązania. napisz do mnie za pomocą zdarzenia
Amit Bera

Odpowiedzi:

5

Znalazłem ten problem w klasie Magento\Framework\View\Layout\ScheduledStructure\Helper

Istnieje funkcja _generateAnonymousName:

protected function _generateAnonymousName($class)
{
    $position = strpos($class, '\\Block\\');
    $key = $position !== false ? substr($class, $position + 7) : $class;
    $key = strtolower(trim($key, '_'));
    return $key . $this->counter++;
}

Wywołuje się z scheduleStructurefunkcji:

    public function scheduleStructure(
    Layout\ScheduledStructure $scheduledStructure,
    Layout\Element $currentNode,
    Layout\Element $parentNode
) {
    // if it hasn't a name it must be generated
    if (!(string)$currentNode->getAttribute('name')) {
        $name = $this->_generateAnonymousName($parentNode->getElementName() . '_schedule_block'); // CALL HERE
        $currentNode->setAttribute('name', $name);
    }
    $path = $name = (string)$currentNode->getAttribute('name');

    // Prepare scheduled element with default parameters [type, alias, parentName, siblingName, isAfter]
    $row = [
        self::SCHEDULED_STRUCTURE_INDEX_TYPE           => $currentNode->getName(),
        self::SCHEDULED_STRUCTURE_INDEX_ALIAS          => '',
        self::SCHEDULED_STRUCTURE_INDEX_PARENT_NAME    => '',
        self::SCHEDULED_STRUCTURE_INDEX_SIBLING_NAME   => null,
        self::SCHEDULED_STRUCTURE_INDEX_IS_AFTER       => true,
    ];

    $parentName = $parentNode->getElementName();
    //if this element has a parent element, there must be reset [alias, parentName, siblingName, isAfter]
    if ($parentName) {
        $row[self::SCHEDULED_STRUCTURE_INDEX_ALIAS] = (string)$currentNode->getAttribute('as');
        $row[self::SCHEDULED_STRUCTURE_INDEX_PARENT_NAME] = $parentName;

        list($row[self::SCHEDULED_STRUCTURE_INDEX_SIBLING_NAME],
            $row[self::SCHEDULED_STRUCTURE_INDEX_IS_AFTER]) = $this->_beforeAfterToSibling($currentNode);

        // materialized path for referencing nodes in the plain array of _scheduledStructure
        if ($scheduledStructure->hasPath($parentName)) {
            $path = $scheduledStructure->getPath($parentName) . '/' . $path;
        }
    }

    $this->_overrideElementWorkaround($scheduledStructure, $name, $path);
    $scheduledStructure->setPathElement($name, $path);
    $scheduledStructure->setStructureElement($name, $row);
    return $name;
}

W tym przypadku nazwą bloku może być:

  • before.body.end_schedule_block1
  • before.body.end_schedule_block2
  • ...

Myślę, że powinieneś zdefiniować blok sum bez nazwy na kontenerze, a nazwa bloku zamówienia musi zostać usunięta z kontenera.

Thao Pham
źródło
Nie sądzę, żeby to zadziałało. Nie można przewidzieć wygenerowanej nazwy, ponieważ na różnych stronach może być dodanych wiele bloków w body.before.endkontenerze w innej kolejności.
Marius
Ten przypadek dotyczy tylko bloku / kontenera bez nazwy. Jeśli wszystkie bez nazwy, tak trudno zdefiniować jakiś blok / kontener, trzeba je usunąć.
Thao Pham,
tak ... dokładnie mój problem
Marius
Powinniśmy przepisać $name = $this->_generateAnonymousName($parentNode->getElementName() . '_schedule_block');, czy należy przekazać klasę i szablon do parametru?
Thao Pham,
2
Wydaje się, że to przepisanie czegoś takiego. Szukam prostego rozwiązania (jeśli istnieje) lub odpowiedzi typu „niemożliwe bardzo łatwo”. Wydaje mi się, że mogę obserwować układ generujący zdarzenie blokowania lub coś, co może je tam usunąć, ale znów wydaje się, że jest to zbyt duże obciążenie. Trzymam to jako rozwiązanie zapasowe.
Marius
3

Naprawdę daję ci zły pomysł.

Tutaj pomysłem nie jest zatrzymanie wyjścia twojego bloku

Korzystanie z wydarzenia view_block_abstract_to_html_after

<?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_after">
        <observer name="myObserverName" instance="Stack\Work\Observer\MyObserver" />
    </event>
</config>

I używając tego obserwatora wyłącz wyjście swojego bloku

<?php
namespace Stack\Work\Observer;
use Magento\Framework\Event\ObserverInterface;

class MyObserver implements ObserverInterface
{
  public function __construct()
  {
    //Observer initialization code...
    //You can use dependency injection to get any class this observer may need.
  }

  public function execute(\Magento\Framework\Event\Observer $observer)
  {
    $block = $observer->getData('block');

    if('[Vendor_Module]::template.phtml' == $block->getTemplate()){
        $transport = $observer->getData('transport');
        $transport->setHtml('');

    }
  }
}
Amit Bera
źródło
to nie jest tak zły pomysł. Rzeczywiście istnieje nadmiar umiejętności obserwujący wszystkie bloki, ale jestem gotów użyć go w porównaniu z innymi opcjami. Spróbuję i dam ci znać.
Marius
coool. człowiek .... zobacz, co się stanie
Amit Bera
1
Działa, ale starałem się go trochę zoptymalizować, aby nie uruchamiać kodu dla każdego bloku. Więc skończyłem z moją odpowiedzią . Dzięki za pomysł.
Marius
Widzę odpowiedź, ten naprawdę dobry człowiek :)
Amit Bera
3

Wpadłem na pomysł z odpowiedzi Amita i skończyłem z działającym rozwiązaniem, które nie wygląda na bardzo nachalne i nie jest to przesada, ponieważ mój kod jest wykonywany tylko raz.

Utworzyłem obserwatora zdarzenia, layout_generate_blocks_afterktóre jest wykonywane po załadowaniu układów i wygenerowaniu bloków.

Może to mieć wadę, ponieważ blok, który próbuję usunąć, nadal jest tworzony w postaci instancji, ale w moim przypadku po prostu musiałem go usunąć ze strony.

Mam plik etc/adminhtml/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="layout_generate_blocks_after">
        <observer name="remove-the-block" instance="[MyVendor]\[MyModule]\Observer\RemoveBlock" />
    </event>
</config>

i moja klasa obserwatorów:

<?php
namespace [MyVendor]\[MyModule]\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class RemoveBlock implements ObserverInterface
{
    const TEMPLATE_TO_REMOVE = '[OtherVendor]_[OtherModule]::template.phtml';
    public function execute(Observer $observer)
    {
        /** @var \Magento\Framework\View\Layout $layout */
        $layout = $observer->getLayout();
        $blocks = $layout->getAllBlocks();
        foreach ($blocks as $key => $block) {
            /** @var \Magento\Framework\View\Element\Template $block */
            if ($block->getTemplate() == self::TEMPLATE_TO_REMOVE) {
                $layout->unsetElement($key);
            }
        }
    }
}
Marius
źródło