Magento – Programmatically create and append a block to another block

blockscustom blockmagento2

I've declared a plugin that creates and adds a block programmatically if a condition is true. However, my code outputs nothing, it's like if the created block is not rendered at all.

etc/di.xml

<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="Vendor_Module" type="Vendor\Module\Plugin\AppendBlockPlugin"/>
    </type>
</config>

Plugins/AddBlockPlugin.php

public function beforeToHtml(AbstractBlock $block)
{
    if (($block instanceof Block\Product\View)
        && $block->getNameInLayout() === 'product.info'
    ) {
        $template = 'Vendor_Module::block.phtml';

        $block->addChild(
            'custom_block',
            \Vendor\Module\Block\Block::class,
            compact('template')
        );
    }
}

I've made some debugging to this code to ensue that the custom block renders without any errors and that the ->addChild method is actually being called, but still no output for the block I added.

Best Answer

You can do this by event.

use event:

layout_generate_blocks_after

On this event add your block to product.info.

Event:

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

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

class AddAChildBlock implements ObserverInterface
{
    const TEMPLATE_TO_ADD = '[OtherVendor]_[OtherModule]::template.phtml';
    const PARENT_BlOCK_NAME = 'product.info';
    public function execute(Observer $observer)
    {
        /** @var \Magento\Framework\View\Layout $layout */
        $layout = $observer->getLayout();
        $blocks = $layout->getAllBlocks();
        foreach ($blocks as $key => $block) {
             $template = 'Vendor_Module::block.phtml';
            /** @var \Magento\Framework\View\Element\Template $block */
            if ($block->getNameInLayout() == self:PARENT_BlOCK_NAME) {
                $blocks->addChild(
                'custom_block',
                 \Vendor\Module\Block\Block::class,
                    [
                        'template' => $template
                    ]
                );
            }
        }
    }
}

Also, your parent template file means product.info you need to add the code

echo $block->getChildHtml('custom_block);

to show the child block content