Eliminar un bloque del diseño sin nombre

12

Quiero eliminar un bloque del diseño en magento 2 que se declara en una extensión de terceros, pero el bloque no tiene un nombre.
¿Puedo hacer eso?

El bloque se declara así

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

No puedo usar

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

porque, como puedes ver, no tiene nombre.

Marius
fuente
marius, tengo idea. si usamos evento y eliminamos el bloque por nombre de plantilla de partido [Vendor_Module]::template.phtml
Amit Bera
Tengo la misma idea (ver comentarios en la respuesta) pero la usaré solo como una medida desesperada. Esperaba una solución simple. Si tiene algún código, publíquelo como respuesta.
Marius
ja ja que no tenemos una solución simple. Permíteme darte una respuesta usando evento
Amit Bera

Respuestas:

5

Encontré este problema en clase Magento\Framework\View\Layout\ScheduledStructure\Helper

Hay función _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++;
}

Es llamada de la scheduleStructurefunción:

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

Con este caso, el nombre del bloque puede ser:

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

Creo que debe definir el bloque de totales sin el nombre en el contenedor y el nombre del bloque de pedido debe eliminarse en el contenedor.

Thao Pham
fuente
No creo que esto funcione. No hay forma de predecir el nombre generado, ya que en diferentes páginas puede haber múltiples bloques agregados en el body.before.endcontenedor en diferente orden.
Marius
Este caso solo se aplica al bloque / contenedor sin nombre. Si todos ellos sin el nombre, tan difícil de definir, es necesario eliminar algún bloque / contenedor.
Thao Pham
sí ... mi problema exactamente
Marius
Deberíamos reescribir $name = $this->_generateAnonymousName($parentNode->getElementName() . '_schedule_block');, ¿Debería pasar la clase y la plantilla al parámetro?
Thao Pham
2
parece una sobrecarga para reescribir algo así. Estoy buscando una solución simple (si existe) o una respuesta como 'no es posible con mucha facilidad'. Creo que puedo observar que el diseño genera un evento de bloques o algo para eliminarlo allí, pero nuevamente parece demasiada sobrecarga. Lo mantengo como una solución de respaldo.
Marius
3

Realmente te doy una mala idea.

Aquí la idea no es detener la salida de tu bloque

Usando evento 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>

Y usando este observador deshabilita la salida de tu bloque

<?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
fuente
Esto no es realmente una mala idea. De hecho, hay una exageración observando todos los bloques, pero estoy dispuesto a usarlo sobre otras opciones. Trataré de hacértelo saber.
Marius
coool hombre ... mira lo que sucede
Amit Bera
1
Funciona, pero intenté optimizarlo un poco, no ejecutar el código para cada bloque. Así que terminé con mi respuesta . Gracias por la idea
Marius
Veo la respuesta, ese hombre realmente bueno :)
Amit Bera
3

Tengo una idea de la respuesta de Amit y terminé con una solución de trabajo que no parece muy intrusiva y no es una exageración ya que mi código se ejecuta solo una vez.

He creado un observador en el evento layout_generate_blocks_afterque se ejecuta después de que se cargan los diseños y se generan los bloques.

Esto puede tener un inconveniente porque el bloque que estoy tratando de eliminar aún se instancia, pero en mi caso solo necesitaba eliminarlo de la página.

Entonces tengo el archivo 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>

y mi clase de observador:

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