问题描述
我希望主菜单不包括任何空白的类别.我已经通过使用
在相关的phtml文件中非常轻松地完成了分层导航$_category->getProductCount()
但是,对于导航菜单,我发现不可能如此轻松地做到这一点(我已经看过Prattski的示例,但看起来确实很像OTT).
主菜单似乎内置在Mage_Page_Block_Html_Topmenu.php
中,尤其是在功能_getHtml
中.这将使菜单中的所有子项都得到使用,如果我尝试使用$child->getId()
之类的东西,我将获得"category-node-36"
之类的东西.
似乎我离使用getProductCount()
尚有距离,因此请测试它是否大于零.
是否可以这样做?有人可以指出我怎么做吗?
如果可以,我将用我的版本扩展该类.
我终于破解了它,尽管我远不能相信这是一个最佳解决方案.无论如何,我将描述我在这里所做的事情,希望有人可以使其变得更有效率.由于我对很多方面都很熟悉,因此我将对其进行逐一介绍.抱歉,我要长度了.
正如我所说,至少在我的情况下,主菜单是通过Topmenu.php在app/code/core/Mage/Page/Block/Html中建立的,特别是_getHtml方法.我绝对不想修改核心文件,因此我发现了如何通过新模块扩展此方法. (如果您熟悉创建新模块,则可以跳过此位.)
配置新模块
我需要创建一个新模块(以下将其称为MYMOD).当我覆盖核心的magento页面块时,我必须创建新文件夹:app/code/local/MYMOD/Page,并在其中创建两个子文件夹,Block等(我相信它们区分大小写).在Block内还有另一个子文件夹Html.您可以看到,这恰好是app/code/core/Mage中文件夹结构的镜像.
etc文件夹在config.xml文件中包含新模块的规范.这是我的样子:
<?xml version="1.0" encoding="UTF-8"?>
<!-- The root node for Magento module configuration -->
<config>
<!--
The module's node contains basic
information about each Magento module
-->
<modules>
<!--
This must exactly match the namespace and module's folder
names, with directory separators replaced by underscores
-->
<MYMOD_Page>
<!-- The version of our module, starting at 0.0.1 -->
<version>0.0.1</version>
</MYMOD_Page>
</modules>
<global>
<blocks>
<page>
<rewrite>
<html_topmenu>MYMOD_Page_Block_Html_Topmenu</html_topmenu>
</rewrite>
</page>
</blocks>
</global>
</config>
您可以在其他地方找到有关此事的原因和理由.
不幸的是(我认为!),这不是在Magento中指定新模块所要做的全部.您还必须在app/etc/modules中创建一个名为"MYMOD_Page.xml"的文件.这只是告诉Magento您的模块以及在哪里寻找它.我的看起来像这样:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<MYMOD_Page>
<!-- Whether our module is active: true or false -->
<active>true</active>
<!-- Which code pool to use: core, community or local -->
<codePool>local</codePool>
</MYMOD_Page>
</modules>
</config>
好的,很抱歉,模块上的无关说明,但我确实喜欢独立的逐吹"说明.
覆盖方法
我现在可以在我的模块中创建一个带有子类的新文件,在该子类中,我可以使用将使用的方法(函数)代替核心的Magento方法.文件名必须与原始文件Topmenu.php相同,并且应位于app/code/local/MYMOD/Page/Block/Html中.
请记住,面向对象的结构意味着Topmenu.php原始核心版本中的所有功能都可供我使用,我不必将其复制到新版本中(出于可维护性的考虑).我的Topmenu.php版本仅包含我可能想要的任何新功能(在这种情况下,我不需要任何功能),并重新声明任何我想在自己的版本中覆盖的功能.就我而言,我需要修改两个函数:_getHtml和_getMenuItemClasses.
_getHtml需要额外的检查,因此不包括任何空类别.
_getMenuItemClasses需要进行其他检查,以使父"类不会添加到其子级为空的类别中.
这是我的操作方式(带有注释).我敢肯定还有更好的方法,但是我对Magento还是很陌生.
class MYMOD_Page_Block_Html_Topmenu extends Mage_Page_Block_Html_Topmenu
// Create my subclass, in accordance with how I've defined the new module
{
/**
* Recursively generates top menu html from data that is specified in $menuTree
*
* @param Varien_Data_Tree_Node $menuTree
* @param string $childrenWrapClass
* @return string
*/
protected function _getHtml(Varien_Data_Tree_Node $menuTree, $childrenWrapClass)
{
$html = '';
$children = $menuTree->getChildren();
$parentLevel = $menuTree->getLevel();
$childLevel = is_null($parentLevel) ? 0 : $parentLevel + 1;
$counter = 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);
}
/*
* Find out if this category has any products. I don't know an easier way.
* The id of every child returned by getID is of the form "category-node-nnn"
* where nnn is the id that can be used to select the category details.
* substr strips everything leaving just the nnn.
* Then use getModel-> getCollection with a filter on the id. Although this looks
* like it will return many, obviously category ids are unique so in fact it only
* returns the category we're currently looking at.
*/
$_gcategoryId = substr($child->getId(), 14, 6);
$_gcategories = Mage::getModel('catalog/category')->getCollection()->addFieldToFilter('entity_id', array('eq', $_gcategoryId));
foreach ($_gcategories as $_gcategory) {
$_gcategoryCount = $_gcategory->getProductCount();
}
/*
* Now only include those categories that have products.
* In my case I also wanted to include the top level categories come what may.
*/
if (($childLevel == 0) || ($_gcategoryCount > 0)) {
$html .= '<li ' . $this->_getRenderedMenuItemAttributes($child) . '>';
$html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>'
. $this->escapeHtml($child->getName()) . '</span></a>';
if ($child->hasChildren()) {
if (!empty($childrenWrapClass)) {
$html .= '<div class="' . $childrenWrapClass . '">';
}
$html .= '<ul class="level' . $childLevel . '">';
$html .= $this->_getHtml($child, $childrenWrapClass);
$html .= '</ul>';
if (!empty($childrenWrapClass)) {
$html .= '</div>';
}
}
$html .= '</li>';
}
$counter++;
}
return $html;
}
/**
* Returns array of menu item's classes
*
* @param Varien_Data_Tree_Node $item
* @return array
*/
protected function _getMenuItemClasses(Varien_Data_Tree_Node $item)
{
$classes = array();
$classes[] = 'level' . $item->getLevel();
$classes[] = $item->getPositionClass();
if ($item->getIsFirst()) {
$classes[] = 'first';
}
if ($item->getIsActive()) {
$classes[] = 'active';
}
if ($item->getIsLast()) {
$classes[] = 'last';
}
if ($item->getClass()) {
$classes[] = $item->getClass();
}
if ($item->hasChildren()) {
/*
* Don't just check if there are children but, if there are, are they all empty?
* If so, then the changes in _getHtml will mean none of them will be included
* and so this one has no children displayed and so the "parent" class is not appropriate.
*/
$children = $item->getChildren(); // Get all the children from this menu category
foreach ($children as $child) { // Loop over each child and find out how many products (see _getHtml)
$_gcategoryId = substr($child->getId(), 14, 6);
$_gcategories = Mage::getModel('catalog/category')->getCollection()->addFieldToFilter('entity_id', array('eq', $_gcategoryId));
foreach ($_gcategories as $_gcategory) { // Remember, there's actually only one category that will match the child's id
$_gcategoryCount = $_gcategory->getProductCount();
}
if ($_gcategoryCount > 0) { // As soon as one child has products, then we have a parent and can stop looking
$classes[] = 'parent';
break;
}
}
}
return $classes;
}
}
我希望这很清楚.它可以满足我的要求(我的商店很小),但欢迎提出任何改进建议.
I want my main menu to not include any categories that are empty. I've done this for the layered navigation very easily in the relevant phtml file by using
$_category->getProductCount()
However, for the navigation menu, I'm finding it impossible to do this as easily (I have seen the Prattski example but it does seem rather OTT).
The main menu seems to be built in Mage_Page_Block_Html_Topmenu.php
, specifically in the function _getHtml
. This gets all the children in the menu and if I try something like $child->getId()
, I get something like "category-node-36"
.
It doesn't seem like I'm too far from being able to use getProductCount()
and so test if it's more than zero.
Is it possible to do this? Can somebody point me to how?
If I can, I'll extend the class with my version.
I finally cracked it although I'm far from convinced it's an optimum solution. Anyway, I'll described what I did here and hopefully somebody can make it more efficient. I'll give a blow-by-blow description as I was ufamiliar with quite a few areas. So apologies for the length.
As I said, in my case at least, the main menu is built via Topmenu.php in app/code/core/Mage/Page/Block/Html, specifically the method _getHtml. I very definitely don't want to modify a core file so I found out how to extend this method via a new module. (You can skip this bit if you're familiar with creating new modules.)
Configuring a new module
I needed to create a new module (I'll call it MYMOD below). As I'm overwriting the core magento page block, I had to create new folders: app/code/local/MYMOD/Page and in there two sub-folders, Block and etc (I believe they are case sensitive). And within Block another subfolder Html. You can see this is exactly mirroring the folder structure from app/code/core/Mage.
The etc folder holds the specification for the new module in a config.xml file. This is what mine looks like:
<?xml version="1.0" encoding="UTF-8"?>
<!-- The root node for Magento module configuration -->
<config>
<!--
The module's node contains basic
information about each Magento module
-->
<modules>
<!--
This must exactly match the namespace and module's folder
names, with directory separators replaced by underscores
-->
<MYMOD_Page>
<!-- The version of our module, starting at 0.0.1 -->
<version>0.0.1</version>
</MYMOD_Page>
</modules>
<global>
<blocks>
<page>
<rewrite>
<html_topmenu>MYMOD_Page_Block_Html_Topmenu</html_topmenu>
</rewrite>
</page>
</blocks>
</global>
</config>
You can find out about the whys and wherefors of this elsewhere.
Unfortunately (in my opinion!), that's not all you have to do to specify a new module in Magento. You also have to create a file called "MYMOD_Page.xml" in app/etc/modules. This is just telling Magento about your module and where to look for it. Mine looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<config>
<modules>
<MYMOD_Page>
<!-- Whether our module is active: true or false -->
<active>true</active>
<!-- Which code pool to use: core, community or local -->
<codePool>local</codePool>
</MYMOD_Page>
</modules>
</config>
OK, sorry for the irrelevant instructions on modules but I do like self-contained blow-by-blow explanations.
Overwriting the method
I can now create a new file in my module with a subclass in which I can have methods (functions) that will be used instead of the core Magento ones. The file name has to be the same as the original file, Topmenu.php and it goes in app/code/local/MYMOD/Page/Block/Html.
Remember, the object oriented structure means that all of the functions in the original core version of Topmenu.php are available to me, I don't have to copy them in my new version (great for maintainability). My version of Topmenu.php only has to contain any new functions I might want (in this case I didn't need any) and redeclare any functions I want to overwite with my own version. In my case, I needed to modify two functions: _getHtml and _getMenuItemClasses.
_getHtml needs additional checks so any empty categories aren't included.
_getMenuItemClasses needs additional checks so that the class "parent" isn't added to categories whose children are empty.
Here's how I did it (with comments). I'm sure there are better ways but I'm still fairly new to Magento.
class MYMOD_Page_Block_Html_Topmenu extends Mage_Page_Block_Html_Topmenu
// Create my subclass, in accordance with how I've defined the new module
{
/**
* Recursively generates top menu html from data that is specified in $menuTree
*
* @param Varien_Data_Tree_Node $menuTree
* @param string $childrenWrapClass
* @return string
*/
protected function _getHtml(Varien_Data_Tree_Node $menuTree, $childrenWrapClass)
{
$html = '';
$children = $menuTree->getChildren();
$parentLevel = $menuTree->getLevel();
$childLevel = is_null($parentLevel) ? 0 : $parentLevel + 1;
$counter = 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);
}
/*
* Find out if this category has any products. I don't know an easier way.
* The id of every child returned by getID is of the form "category-node-nnn"
* where nnn is the id that can be used to select the category details.
* substr strips everything leaving just the nnn.
* Then use getModel-> getCollection with a filter on the id. Although this looks
* like it will return many, obviously category ids are unique so in fact it only
* returns the category we're currently looking at.
*/
$_gcategoryId = substr($child->getId(), 14, 6);
$_gcategories = Mage::getModel('catalog/category')->getCollection()->addFieldToFilter('entity_id', array('eq', $_gcategoryId));
foreach ($_gcategories as $_gcategory) {
$_gcategoryCount = $_gcategory->getProductCount();
}
/*
* Now only include those categories that have products.
* In my case I also wanted to include the top level categories come what may.
*/
if (($childLevel == 0) || ($_gcategoryCount > 0)) {
$html .= '<li ' . $this->_getRenderedMenuItemAttributes($child) . '>';
$html .= '<a href="' . $child->getUrl() . '" ' . $outermostClassCode . '><span>'
. $this->escapeHtml($child->getName()) . '</span></a>';
if ($child->hasChildren()) {
if (!empty($childrenWrapClass)) {
$html .= '<div class="' . $childrenWrapClass . '">';
}
$html .= '<ul class="level' . $childLevel . '">';
$html .= $this->_getHtml($child, $childrenWrapClass);
$html .= '</ul>';
if (!empty($childrenWrapClass)) {
$html .= '</div>';
}
}
$html .= '</li>';
}
$counter++;
}
return $html;
}
/**
* Returns array of menu item's classes
*
* @param Varien_Data_Tree_Node $item
* @return array
*/
protected function _getMenuItemClasses(Varien_Data_Tree_Node $item)
{
$classes = array();
$classes[] = 'level' . $item->getLevel();
$classes[] = $item->getPositionClass();
if ($item->getIsFirst()) {
$classes[] = 'first';
}
if ($item->getIsActive()) {
$classes[] = 'active';
}
if ($item->getIsLast()) {
$classes[] = 'last';
}
if ($item->getClass()) {
$classes[] = $item->getClass();
}
if ($item->hasChildren()) {
/*
* Don't just check if there are children but, if there are, are they all empty?
* If so, then the changes in _getHtml will mean none of them will be included
* and so this one has no children displayed and so the "parent" class is not appropriate.
*/
$children = $item->getChildren(); // Get all the children from this menu category
foreach ($children as $child) { // Loop over each child and find out how many products (see _getHtml)
$_gcategoryId = substr($child->getId(), 14, 6);
$_gcategories = Mage::getModel('catalog/category')->getCollection()->addFieldToFilter('entity_id', array('eq', $_gcategoryId));
foreach ($_gcategories as $_gcategory) { // Remember, there's actually only one category that will match the child's id
$_gcategoryCount = $_gcategory->getProductCount();
}
if ($_gcategoryCount > 0) { // As soon as one child has products, then we have a parent and can stop looking
$classes[] = 'parent';
break;
}
}
}
return $classes;
}
}
I hope this is clear. It does what I want (my store is small) but any suggestions for improvement welcome.
这篇关于从magento菜单中删除空类别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!