4

I have a block class My\Extension\Block\MyClass which I want to insert into the layout. But I would not be knowing which page to insert in (will be specified by the admin), so I'm observing the layout_render_before event and in my observer class' execute method I'm doing the following

 $allowedBlock = $this->_configModel->getAllowedBlock(); // get the block name where to insert
 $allowedAction = $this->_configModel->getAllowedAction(); // get the full action name of the controller where to insert
 $layout = $this->_layout;
 $action = $this->_request->getFullActionName();
 if($allowedAction !== $action) return false; // if current page is not where to insert block, stop further processing
 $parentBlock = $layout->getBlock($allowedBlock); // get the block from the layout where to insert
 if ($parentBlock) { // if parent block exists
 $blockName = 'My\Extension\Block\MyClass';
 $blockToInsert = $layout->createBlock($blockName,'my_extensions_unique_block_name'); // create block to insert
 $parentBlock->append($blockToInsert); // insert block to parent
 }

And in my block I am setting the template like so,

public function _prepareLayout(){
 $this->setTemplate('My_Extension::subdirectory/my_template.phtml');
 parent::_prepareLayout();
}

And in my template file, I have a very simple string displayed

<h1>Hello World From Template</h1>

This exact code was working in Magento 1.9.x. The only difference was that I used the controller_action_layout_render_before event instead of layout_render_before. But the controller_action_layout_render_before was not working in Magento 2.1.x, so I used the layout_render_before event instead.

But now, my block is not visible on the frontend, even though if I write some code in my block's constructor (like echo "Hello World From Block's constructor";) it comes up on the page, meaning the block is being called, but the template is not visible on the frontend.

Considering the fact that the exact same code which was working on Magento 1.9.x, is not working in Magento 2.1.x, what am I doing wrong?

asked Jul 14, 2017 at 7:47

2 Answers 2

5
+50

Use plugins. Scroll down for the working solution.

Two issues with your approach:

  1. layout_render_before is called after layout is rendered. Check vendor/magento/framework/View/Result/Layout.php:

    160 \Magento\Framework\Profiler::start('LAYOUT');
    161 \Magento\Framework\Profiler::start('layout_render');
    162 
    163 $this->applyHttpHeaders($response);
    164 $this->render($response);
    165 
    166 $this->eventManager->dispatch('layout_render_before');
    167 $this->eventManager->dispatch('layout_render_before_' . $this->request->getFullActionName());
    168 \Magento\Framework\Profiler::stop('layout_render');
    169 \Magento\Framework\Profiler::stop('LAYOUT');
    

    So you append your block after the block has been rendered.

  2. Magento does not render all block's children. At least not for vendor/magento/framework/View/Element/Template.php which is used for almost (correct me if I am wrong here) all blocks. You would have to put echo $this->getChildHtml('name_in_layout') in all templates.

Working solution. Tested in Magento 2.1.4

Use this plugin in your etc/di.xml:

<type name="Magento\Framework\View\Element\AbstractBlock">
 <plugin name="Goivvy_Inject" type="Goivvy\Custom\Plugin\InjectPlugin" /> 
</type>

app/code/Goivvy/Custom/Plugin/InjectPlugin.php:

 <?php
 namespace Goivvy\Custom\Plugin;
 class InjectPlugin {
 protected $_request;
 public function __construct(\Magento\Framework\App\RequestInterface $request){
 $this->_request = $request;
 }
 public function aftertoHtml(\Magento\Framework\View\Element\AbstractBlock $block, $result){
 if($block->getNameInLayout() == 'copyright' && $this->_request->getFullActionName() == 'catalog_product_view'){
 $bl = $block->getLayout()->createBlock('Goivvy\Custom\Block\Inject','goivvy_block_inject');
 return $result.$bl->toHtml();
 } 
 return $result;
 }
}

Instead of copyright and catalog_product_view use your dynamic block name and full action name.

answered Jul 23, 2017 at 5:43
6
  • I don't know why I was stupid enough to not think of using plugins. Anyway, it works now. Thank you for your help Commented Jul 23, 2017 at 10:57
  • Also, don't you think it's a little misleading to call it layout_render_before if it's dispatched after layout is rendered? Commented Jul 23, 2017 at 11:00
  • yeah it is probably a bug Commented Jul 23, 2017 at 14:32
  • It is worth emphasizing the plugin class name must be suffixed with word "Plugin" as per Magento 2 plugin name convention. Commented Nov 9, 2017 at 8:07
  • If you want to load dynamic content in the block then it wont work. It will always show the cached content if full_page cache is enabled. Commented May 30, 2018 at 10:41
0

The problem not came from your observer, it might came from your block class.

public function _prepareLayout(){
 $this->setTemplate('My_Extension::subdirectory/my_template.phtml');
 parent::_prepareLayout();
}

Don't set template inside _prepareLayout, try to use the core field instead. Something like

$this->_template = 'My_Extension::subdirectory/my_template.phtml';

Here is how it's working in M2

layout_template_construct

answered Jul 17, 2017 at 4:50
1
  • I tried that as well. Still it's not showing up. One more interesting phenomenon which is happening is, if I call the toHtml() method on the block's prepareLayout or constructor, the block is being rendered correctly, which means the template is set correctly Commented Jul 17, 2017 at 6:28

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.