I'm trying to override the Topmenu block in Magento 2.1 but can't find any guide to do so. Everything I've found on here and elsewhere either seems to apply only to version 2.0 which appears to use a different folder structure or only has partial code examples which expects me to already know their proper context (which I don't).
My current folder structure for a custom theme is app/design/frontend/Vendor/theme_name
. Within this I have the registration, theme, and composer files as well as folders for the various modules, e.g. Magento_Theme
and Magento_Search
.
From what I understand I need to start with an etc/di.xml
file like below, edited from here:
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Theme\Block\Html\Topmenu" type="[Namespace]\[Module]\Block\Html\Topmenu" />
</config>
I also understand that the next step is to add a Block/Html/Topmenu.php
file like the below (again edited from the above source):
namespace [Namespace]\[Module]\Block\Html;
class Topmenu extends \Magento\Theme\Block\Html\Topmenu
{
protected function _addSubMenu($child, $childLevel, $childrenWrapClass, $limit)
{
}
}
However, it's not clear to me what I should use for [Namespace]
and [Module]
, or where to place these files. I've tried using the vendor and theme name, and placing the etc
and Block
folders in app/design/frontend/Vendor/theme_name
, as well as placing them in app/design/frontend/Vendor/theme_name/Magento_Theme
, amending the namespaces to Vendor\theme_name\Magento_Theme\Block\Html
, but neither have any effect.
If anyone could help explain exactly what I need to do to override the Topmenu block (and by inference any other block) in version 2.1 I would be much appreciated.
Addendum
I've attempted Khoa TruongDinh's answer but it has had no affect. I've used the following files:
app/code/Vendor/MagentoTheme/Block/Html/Topmenu.php
<?php
namespace Vendor\MagentoTheme\Block\Html;
class Topmenu extends \Magento\Theme\Block\Html\Topmenu
{
protected function _addSubMenu($child, $childLevel, $childrenWrapClass, $limit)
{
$html = '';
if (!$child->hasChildren())
{
return $html;
}
$colStops = null;
if ($childLevel == 0 && $limit)
{
$colStops = $this->_columnBrake($child->getChildren(), $limit);
}
// Added "test" class to test
$html .= '<ul class="level' . $childLevel . ' test submenu">';
$html .= $this->_getHtml($child, $childrenWrapClass, $limit, $colStops);
$html .= '</ul>';
return $html;
}
}
app/code/Vendor/MagentoTheme/composer.json
{
"name": "vendor/magento-theme",
"description": "",
"require": {
"php": "~5.5.0|~5.6.0|~7.0.0",
"magento/framework": "100.0.*"
},
"type": "magento2-module",
"version": "100.0.1",
"license": [
"OSL-3.0",
"AFL-3.0"
],
"autoload": {
"files": [ "registration.php" ],
"psr-4": {
"Vendor\\MagentoTheme\\": ""
}
}
}
app/code/Vendor/MagentoTheme/etc/di.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
<preference for="Magento\Theme\Block\Html\Topmenu" type="Vendor\MagentoTheme\Block\Html\Topmenu" />
</config>
app/code/Vendor/MagentoTheme/etc/module.xml
<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd">
<module name="Vendor_MagentoTheme" setup_version="1.0.0"></module>
</config>
app/code/Vendor/MagentoTheme/registration.php
<?php
\Magento\Framework\Component\ComponentRegistrar::register(
\Magento\Framework\Component\ComponentRegistrar::MODULE,
'Vendor_MagentoTheme',
__DIR__
);
I've then removed the contents of pub/static/frontend
, var/generation
, and var/view_preprocessed
, and flushed the Magento cache. The submenu doesn't have the intended "test" class added:
<ul class="level0 submenu ui-menu ui-widget ui-widget-content ui-corner-all" role="menu" aria-expanded="false" style="display: none; top: 52.6719px; left: 487.5px;" aria-hidden="true">...</ul>
Best Answer
Override block:
Create your own module under
app/code
folder.We can use
preference
to override the class in Magento 2.app/code/Vendor/Module/etc/di.xml
app/code/Vendor/Module/Block/Html/Topmenu.php
Temporary solution:
Currently, seem that above steps cannot override the the block completely. We need to create new custom theme. And then, create the
default.xml
file:app/design/frontend/Vendor/Theme/Magento_Theme/layout/default.xml
It's may be a Magento bug: Are we forced to rewrite a template in Magento2 when rewritting a block?
[EDIT]
1) We can set the template:
app/code/Vendor/Module/Block/Html/Topmenu.php
2) Set template via Xml:
For example:
app/code/Vendor/Module/view/frontend/layout/checkout_cart_index.xml
Remember to create
registration.php
andmodule.xml
.We create the new module because we're overriding the class of Magento. When we want to override any class, we have to create new module.
The custom theme under
app/design/frontend
contains:--layout
--templates
--js
--html templates (Knockout templates)
--less, css
--etc...
Read more here and here.
Autoloading standard and naming convention:
For
[Namespace]
and[Module]
, we should read more here:http://www.php-fig.org/psr/psr-0/
http://www.php-fig.org/psr/psr-4/
http://alanstorm.com/magento_2_autoloader_and_class_generation