Magento 2 by default allows rendering child blocks via methods like getChildHtml()
and any block via getBlockHtml()
. Still, Loki has added various template variables to perform similar tasks: $blockRenderer
, $childRenderer
and $templateRenderer
. This ADR explains why.
$childRenderer
The regular $block->getChildHtml()
call allows you to render a block that is a child to the current block. This assumes that the child has been defined in the XML layout and that it has been given a proper name
or as
argument.
echo $block->getChildHtml('example');
However, if you want to pass data to the child block, it requires a bit more work:
echo $block->getChildBlock('example')->addData(['foo' => 'bar'])->toHtml();
With the $childRenderer
, this becomes a tiny bit shorter:
echo $childRenderer->html($block, 'example', ['foo' => 'bar']);
However, underneath some extra tricks are added. The first argument $block
is passed on to the child as a property ancestor_block
, so that within the child you can refer to the ancestor block as follows (which is actually identical to the Magento core its $block->getParent()
):
$block->getAncestorBlock();
There is also an uniq_id
property created on the child block, which serves as the base for a DOM ID:
$block->getUniqId();
The layout name of the child is automatically derived from the parent name, so that the hierarchy of XML layout names always makes sense. For instance, if the parent block is called loki.example-parent
and the child alias is example-child
, the XML layout name of the child becomes loki.example-parent.example-child
.
$block->getNameInLayout(); // loki.example-parent.example-child
If a child block is called upon in a loop, the XML layout name is usually suffixed with an incrementing counter.
$block->getNameInLayout(); // loki.example-parent.block2
If the parent block has a property view_model
, this ViewModel becomes available to the child as well:
$block->getViewModel(); // for example \Loki\Components\Component\ComponentViewModelInterface
Because of these tricks, the $childRenderer
is found frequently in the code. However, there are also cases where $block->getChildHtml()
is found to be a better option - mainly, if the data of the parent should not be added to the child to avoid confusion.
$blockRenderer
The $blockRenderer
is very similar to the $childRenderer
, except it is given a full block name instead:
echo $childRenderer->html($block, 'loki.example-block', ['foo' => 'bar']);
The same variables are added to the child block: view_model
, uniq_id
and ancestor_block
- as explained for the $childRenderer
above.
$templateRenderer
The $templateRenderer
is very similar to both the $blockRenderer
and the $childRenderer
, except it is given a template file instead:
echo $templateRenderer->html($block, 'Loki_Example::example.phtml', ['foo' => 'bar']);
The same variables are added to the child block: view_model
, uniq_id
and ancestor_block
- as explained for the $childRenderer
above. Just like with the $childRenderer
, an alias is generated on the fly as well.
$block
?In all of the renderer calls, the first argument $block
should refer to the block instance that you make the call from. It is cluttering the code a bit, so we tried to remove it and replace it with some other structure. Unfortunately, this failed.
The various renderer classes are added via an observer to each block. They are singletons and therefore should not retain state. We tried this and this caused issues with one child block overriding the parent block data, before a second child block was called. Obviously. Turning these classes into non-singletons (either with shared=true
or by using factories) worked perfectly fine. However, this led to 3 objects being created for every block instance and was not very good for performance.
Because of this, we settled for a more explicit syntax with the $block
being the first (required) argument for each call.