I want to remove a block from the layout in magento 2 that is declared in a third party extension, but the block does not have a name.
Can I do that?
The block is declared like this
<referenceContainer name="before.body.end">
<block class="Magento\Backend\Block\Template" template="[Vendor_Module]::template.phtml"/>
</referenceContainer>
I cannot use
<referenceBlock name="..." remove="true" />
because, as you can see there is no name on it.
3 Answers 3
I found this issue in class Magento\Framework\View\Layout\ScheduledStructure\Helper
There is function _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++;
}
It's call from scheduleStructure function:
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;
}
With this case, Block name can be:
before.body.end_schedule_block1before.body.end_schedule_block2- ...
I think you should define totals block without the name on container and order block name need remove on the container.
-
I don't think this will work. There is no way to predict the generated name since on different pages there can be multiple blocks added in the
body.before.endcontainer in different order.Marius– Marius2017年12月21日 10:37:53 +00:00Commented Dec 21, 2017 at 10:37 -
This case only applies to block/container without a name. If all of them without the name, so difficult to define some block/container need removed.LinoPham– LinoPham2017年12月21日 10:42:11 +00:00Commented Dec 21, 2017 at 10:42
-
yep...my problem exactlyMarius– Marius2017年12月21日 10:45:28 +00:00Commented Dec 21, 2017 at 10:45
-
We should rewrite
$name = $this->_generateAnonymousName($parentNode->getElementName() . '_schedule_block');, Should be pass class & template to parameter ?LinoPham– LinoPham2017年12月21日 10:58:19 +00:00Commented Dec 21, 2017 at 10:58 -
2seems like an overhead to rewrite something like that. I'm looking for a simple solution (if any) or an answer like 'not possible very easily'. I think I can observe the layout generate blocks event or something to remove it there, but again seems too much overhead. I'm keeping that as a backup solution.Marius– Marius2017年12月21日 11:01:44 +00:00Commented Dec 21, 2017 at 11:01
I got an idea from Amit's answer and ended up with a working solution that does not look very intrusive and it's not an overkill since my code is executed only once.
I've created an observer on the event layout_generate_blocks_after that is executed after the layouts are loaded and the blocks are generated.
This can have a drawback because the block I'm trying to remove still gets instantiated, but in my case I just needed to remove it from the page.
So I have the file 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>
and my observer class:
<?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);
}
}
}
}
I am really give u bad idea.
Here the idea is not stop output of your block
Using event 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>
And using this observer disable output of your block
<?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('');
}
}
}
-
this is not actually such a bad idea. Indeed there is an overkill observing all the blocks, but I'm willing to use it over other options. I will try and let you know.Marius– Marius2017年12月22日 07:46:48 +00:00Commented Dec 22, 2017 at 7:46
-
coool. man.... see what happen2017年12月22日 08:22:27 +00:00Commented Dec 22, 2017 at 8:22
-
1
-
I see answer, that really good man :)2017年12月22日 08:48:12 +00:00Commented Dec 22, 2017 at 8:48
[Vendor_Module]::template.phtml