从magento菜单中删除空类别

时间:2014-04-02 21:04:13

标签: php magento

我希望我的主菜单不包含任何空的类别。我已经使用

在相关的phtml文件中轻松完成了分层导航
$_category->getProductCount()

然而,对于导航菜单,我发现不可能轻易做到这一点(我已经看过Prattski的例子,但看起来确实相当于OTT)。

主菜单似乎是在Mage_Page_Block_Html_Topmenu.php中构建的,特别是在函数_getHtml中。这会让所有孩子都进入菜单,如果我尝试$child->getId()之类的内容,我会得到像"category-node-36"这样的内容。

我似乎没有能够使用getProductCount(),所以测试它是否超过零。

有可能这样做吗?有人可以指点我怎么做?

如果可以的话,我会用我的版本扩展课程。

3 个答案:

答案 0 :(得分:0)

要执行此操作,请转到:

app/code/core/Mage/Catalog/Block文件夹并复制 Navigation.php 并在您的本地包中覆盖它。

打开您的软件包的 Navigation.php 并将以下代码粘贴到此文件中:

if ($category->getIsActive()) {
    $cat = Mage::getModel('catalog/category')->load($category->getId());
    $products = Mage::getResourceModel('catalog/product_collection')->addCategoryFilter($cat);
    Mage::getSingleton('catalog/product_status')->addVisibleFilterToCollection($products);
    Mage::getSingleton('catalog/product_visibility')->addVisibleInCatalogFilterToCollection($products);
    Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($products);
    if(count($products)==0)
       return;
}

我希望我的代码可以帮到你。

答案 1 :(得分:0)

我终于破解了它,尽管我并不相信它是一个最佳解决方案。无论如何,我将描述我在这里所做的事情,并希望有人能够提高效率。因为我不熟悉很多方面,所以我会逐一介绍一下。所以道歉。

正如我所说,至少在我的情况下,主菜单是通过app / code / core / Mage / Page / Block / Html中的T​​opmenu.php构建的,特别是方法_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中指定一个新模块。您还必须创建一个名为&#34; MYMOD_Page.xml&#34;的文件。在app / etc / modules中。这只是告诉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需要额外的检查,以便类&#34; parent&#34;没有添加到子项为空的类别中。

以下是我的做法(附评论)。我确信有更好的方法,但我对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;
    }

}

我希望这很清楚。它做我想要的(我的商店很小),但欢迎任何改进建议。

答案 2 :(得分:0)

路径:app / design / frontend / rwd / default / template / page / html / topmenu / renderer.phtml

在foreach ($children as $child) {

下进行此查询( 0.0004 秒)
$mageconnection = Mage::getSingleton("core/resource")->getConnection("core_read");

$query="select count(cataloginventory_stock_item.is_in_stock) as subcount,catalog_category_flat_store_1.`name` from catalog_category_flat_store_1 INNER JOIN
    catalog_category_product_index on catalog_category_product_index.category_id=catalog_category_flat_store_1.entity_id INNER JOIN
    cataloginventory_stock_item on cataloginventory_stock_item.product_id=catalog_category_product_index.product_id
    where cataloginventory_stock_item.is_in_stock=1 and catalog_category_product_index.category_id=";

$subCatqueryId =  str_replace('category-node-', '', $child->getId());
$prodCollection = $mageconnection->fetchAll("$query'{$subCatqueryId}'");

if($prodCollection[0]["subcount"] > 0) {

$child->setLevel($childLevel);
$child->setIsFirst($counter == 1);
// these are the existing code ..
...
...
  $counter++;
}
}

控制产品数量是非常快速和安全的方法。