Magento – Are we forced to rewrite a template in Magento2 when rewritting a block

magento2overrides

This question is about following Magento2 best practices.

I had to rewrite \Magento\Theme\Block\Html\Topmenu::_addSubMenu() method in order to add some wrappers around elements. Now, because it's a protected method, my understanding is that I have to use preference feature:

<preference for="Magento\Theme\Block\Html\Topmenu" type="MyCompany\Theme\Block\Html\Topmenu" />

and add a class with my rewrites:

<?php

namespace MyCompany\Theme\Block\Html;

class Topmenu extends \Magento\Theme\Block\Html\Topmenu
{
    protected function _addSubMenu($child, $childLevel, $childrenWrapClass, $limit)
    {
        // my stuff
    }
}

Although the default class got rewritten, on the next page reload I got the following error:

main.CRITICAL: Invalid template file: 'html/topmenu.phtml' in module: 'MyCompany_Theme' block's name: 'catalog.topnav' [] []

Magento is trying to find html/topmenu.phtml under my extension and not under Magento_Theme. I do understand that this is correct behaviour, but I was thinking about practical aspects of this. Does this mean that whenever we rewrite a block, we need to rewrite its template too, even if we don't necessarily need to touch anything HTML related?

One way around this would be to rewrite _toHtml() method too, like this:

protected function _toHtml()
{
    $this->setModuleName($this->extractModuleName('Magento\Theme\Block\Html\Topmenu'));
    return parent::_toHtml();
}

Now, Magento is looking into Magento_Theme module for the template file again. But, this looks like a hack to me.

So, my question is: what is a recommendation in this situations? Should we always copy relevant template when rewritting block class, or the workaround is fine? Is there a better approach for this?

Best Answer

Since there are discussions around the proposed solution in pull request (https://github.com/magento/magento2/pull/1895), meanwhile you just need to "pin" original template when do change of original block class name:

<referenceBlock name="catalog.topnav" class="***" template="Magento_Theme::html/topmenu.phtml"/>