我试图覆盖Magento 2中的Topmenu块,以便我可以更改子菜单的HTML结构,但我似乎无法解决这个问题。
我做了什么
我创建了一个名为Ecommerce \ Topmenu的模块。
文件
应用程序/代码/电子商务/ TopMenu中
- etc /
--- di.xml
--- module.xml
- 插件/
--- Topmenu.php
- registration.php
Topmenu.php
<?php
/**
* Copyright © 2013-2017 Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
namespace Ecommerce\Topmenu\Plugin;
use \Magento\Framework\DataObject\IdentityInterface;
use \Magento\Framework\View\Element\Template;
use \Magento\Framework\Data\TreeFactory;
use \Magento\Framework\Data\Tree\Node;
use \Magento\Framework\Data\Tree\NodeFactory;
/**
* Html page top menu block
*/
class Topmenu extends \Magento\Theme\Block\Html\Topmenu
{
/**
* Get top menu html
*
* @param string $outermostClass
* @param string $childrenWrapClass
* @param int $limit
* @return string
*/
public function getHtml($outermostClass = '', $childrenWrapClass = '', $limit = 0)
{
$this->_eventManager->dispatch(
'page_block_html_topmenu_gethtml_before',
['menu' => $this->_menu, 'block' => $this]
);
$this->_menu->setOutermostClass($outermostClass);
$this->_menu->setChildrenWrapClass($childrenWrapClass);
$html = $this->_getHtml($this->_menu, $childrenWrapClass, $limit);
$transportObject = new \Magento\Framework\DataObject(['html' => $html]);
$this->_eventManager->dispatch(
'page_block_html_topmenu_gethtml_after',
['menu' => $this->_menu, 'transportObject' => $transportObject]
);
$html = $transportObject->getHtml();
return $html;
}
/**
* Count All Subnavigation Items
*
* @param \Magento\Backend\Model\Menu $items
* @return int
*/
protected function _countItems($items)
{
$total = $items->count();
foreach ($items as $item) {
/** @var $item \Magento\Backend\Model\Menu\Item */
if ($item->hasChildren()) {
$total += $this->_countItems($item->getChildren());
}
}
return $total;
}
/**
* Building Array with Column Brake Stops
*
* @param \Magento\Backend\Model\Menu $items
* @param int $limit
* @return array|void
*
* @todo: Add Depth Level limit, and better logic for columns
*/
protected function _columnBrake($items, $limit)
{
$total = $this->_countItems($items);
if ($total <= $limit) {
return;
}
$result[] = ['total' => $total, 'max' => (int)ceil($total / ceil($total / $limit))];
$count = 0;
$firstCol = true;
foreach ($items as $item) {
$place = $this->_countItems($item->getChildren()) + 1;
$count += $place;
if ($place >= $limit) {
$colbrake = !$firstCol;
$count = 0;
} elseif ($count >= $limit) {
$colbrake = !$firstCol;
$count = $place;
} else {
$colbrake = false;
}
$result[] = ['place' => $place, 'colbrake' => $colbrake];
$firstCol = false;
}
return $result;
}
/**
* Add sub menu HTML code for current menu item
*
* @param \Magento\Framework\Data\Tree\Node $child
* @param string $childLevel
* @param string $childrenWrapClass
* @param int $limit
* @return string HTML code
*/
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);
}
$html .= '<ul class="level' . $childLevel . ' megaSub">';
$html .= $this->_getHtml($child, $childrenWrapClass, $limit, $colStops);
$html .= '</ul>';
return $html;
}
/**
* Recursively generates top menu html from data that is specified in $menuTree
*
* @param \Magento\Framework\Data\Tree\Node $menuTree
* @param string $childrenWrapClass
* @param int $limit
* @param array $colBrakes
* @return string
*
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
* @SuppressWarnings(PHPMD.NPathComplexity)
*/
protected function _getHtml(
\Magento\Framework\Data\Tree\Node $menuTree,
$childrenWrapClass,
$limit,
$colBrakes = []
) {
$html = '';
$children = $menuTree->getChildren();
$parentLevel = $menuTree->getLevel();
$childLevel = $parentLevel === null ? 0 : $parentLevel + 1;
$counter = 1;
$itemPosition = 1;
$childrenCount = $children->count();
$parentPositionClass = $menuTree->getPositionClass();
$itemPositionClassPrefix = $parentPositionClass ? $parentPositionClass . '-' : 'nav-';
foreach ($children as $child) {
$child->setLevel($childLevel);
$child->setIsFirst($counter == 1);
$child->setIsLast($counter == $childrenCount);
$child->setPositionClass($itemPositionClassPrefix . $counter);
$outermostClassCode = '';
$outermostClass = $menuTree->getOutermostClass();
if ($childLevel == 0 && $outermostClass) {
$outermostClassCode = ' class="' . $outermostClass . '" ';
$child->setClass($outermostClass);
}
if (count($colBrakes) && $colBrakes[$counter]['colbrake']) {
// $html .= '</ul></li><li class="column"><ul>';
}
if($counter > 1 && $childLevel == 1){
continue;
}
$html .= '<li ' . $this->_getRenderedMenuItemAttributes($child) . '>';
$html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>' . $this->escapeHtml(
$child->getName()
) . '</span></a>' . $this->_addSubMenu(
$child,
$childLevel,
$childrenWrapClass,
$limit
) . '</li>';
$itemPosition++;
$counter++;
}
if (count($colBrakes) && $limit) {
$html = '<li class="column"><ul>' . $html . '</ul></li>';
}
return $html;
}
/**
* Generates string with all attributes that should be present in menu item element
*
* @param \Magento\Framework\Data\Tree\Node $item
* @return string
*/
protected function _getRenderedMenuItemAttributes(\Magento\Framework\Data\Tree\Node $item)
{
$html = '';
$attributes = $this->_getMenuItemAttributes($item);
foreach ($attributes as $attributeName => $attributeValue) {
$html .= ' ' . $attributeName . '="' . str_replace('"', '\"', $attributeValue) . '"';
}
return $html;
}
/**
* Returns array of menu item's attributes
*
* @param \Magento\Framework\Data\Tree\Node $item
* @return array
*/
protected function _getMenuItemAttributes(\Magento\Framework\Data\Tree\Node $item)
{
$menuItemClasses = $this->_getMenuItemClasses($item);
return ['class' => implode(' ', $menuItemClasses)];
}
/**
* Returns array of menu item's classes
*
* @param \Magento\Framework\Data\Tree\Node $item
* @return array
*/
protected function _getMenuItemClasses(\Magento\Framework\Data\Tree\Node $item)
{
$classes = [];
$classes[] = 'level' . $item->getLevel();
$classes[] = $item->getPositionClass();
if ($item->getIsFirst()) {
$classes[] = 'first';
}
if ($item->getIsActive()) {
$classes[] = 'active';
} elseif ($item->getHasActive()) {
$classes[] = 'has-active';
}
if ($item->getIsLast()) {
$classes[] = 'last';
}
if ($item->getClass()) {
$classes[] = $item->getClass();
}
if ($item->hasChildren()) {
$classes[] = 'parent';
}
return $classes;
}
/**
* Add identity
*
* @param array $identity
* @return void
*/
public function addIdentity($identity)
{
if (!in_array($identity, $this->identities)) {
$this->identities[] = $identity;
}
}
/**
* Get identities
*
* @return array
*/
public function getIdentities()
{
return $this->identities;
}
/**
* Get cache key informative items
*
* @return array
*/
public function getCacheKeyInfo()
{
$keyInfo = parent::getCacheKeyInfo();
$keyInfo[] = $this->getUrl('*/*/*', ['_current' => true, '_query' => '']);
return $keyInfo;
}
/**
* Get tags array for saving cache
*
* @return array
*/
protected function getCacheTags()
{
return array_merge(parent::getCacheTags(), $this->getIdentities());
}
/**
* Get menu object.
*
* @return Node
*/
public function getMenu()
{
return $this->_menu;
}
}
**预期的行为**
我的模块应该覆盖Magento Topmenu模块,同时继承它,允许我更改输出标记
问题
运行安装程序后:升级并重新编译,我只是出现以下错误:
Fatal error: Call to a member function setOutermostClass() on null in /var/www/myecom.co.uk/app/code/Ecommerce/Topmenu/Plugin/Topmenu.php on line 36
就像我的代码没有正确扩展Topmenu块一样。
有什么想法吗?
答案 0 :(得分:0)
这个问题是最近的Magento update的结果,这解释了为什么我使用之前在Magento早期版本上运行过的代码来解决这个问题。
最终解决方案
在Magento更新后修复此问题的最终解决方案是将$this->_menu
替换为$this->getMenu()
现在我的自定义模块的工作版本扩展了Magento顶级菜单类,只是覆盖了_getHtml函数以根据需要更改标记。
答案 1 :(得分:0)
要解决此问题,我的解决方案是如下插入结构:
public function __construct(Template\Context $context, NodeFactory $nodeFactory, TreeFactory $treeFactory, array $data = [])
{
parent::__construct($context, $nodeFactory, $treeFactory, $data);
$this->nodeFactory = $nodeFactory;
$this->treeFactory = $treeFactory;
}