【问题标题】:Remove empty categories from magento menu从 magento 菜单中删除空类别
【发布时间】:2014-09-29 20:19:48
【问题描述】:

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

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

但是,对于导航菜单,我发现它不可能那么容易地做到这一点(我看过 Prattski 的例子,但它看起来确实很OTT)。

主菜单似乎内置在Mage_Page_Block_Html_Topmenu.php 中,具体是在函数_getHtml 中。这会让所有的孩子都出现在菜单中,如果我尝试$child->getId() 之类的东西,我会得到"category-node-36" 之类的东西。

我似乎距离使用getProductCount() 并不太远,所以测试它是否大于零。

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

如果可以的话,我会用我的版本扩展这个类。

【问题讨论】:

  • 抱歉,查看 Magento 1.8.1 版,现在似乎在 app/code/core/Mage/Page/Block/Html/Topmenu.php 中。

标签: php magento


【解决方案1】:

为此,请转到:

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;
}

希望我的代码对你有所帮助。

【讨论】:

  • 我尝试直接编辑核心文件只是为了尝试,但没有成功。我认为这个文件根本没有被使用,Topmenu.php 是被使用的那个。
  • 到目前为止我还没有尝试过 magento 1.8.1。如果您得到了解决方案,请在此处更新为答案。
  • 我已经发布了一个(非常!:()关于我是如何做到的冗长的回复!
【解决方案2】:

我终于破解了它,尽管我并不相信这是一个最佳解决方案。无论如何,我将描述我在这里所做的事情,希望有人可以让它更有效率。因为我对很多领域都很熟悉,所以我将给出一个详细的描述。抱歉,篇幅较长。

正如我所说,至少在我的情况下,主菜单是通过 app/code/core/Mage/Page/Block/Html 中的 Topmenu.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 中指定一个新模块所要做的全部。您还必须在 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 需要额外的检查,以便“父级”类不会添加到子级为空的类别中。

这就是我的做法(使用 cmets)。我确信有更好的方法,但我对 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;
    }

}

我希望这很清楚。它可以满足我的需求(我的商店很小),但欢迎提出任何改进建议。

【讨论】:

    【解决方案3】:

    路径: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++;
    }
    }
    

    这是一种控制产品数量的非常快速且安全的方法。

    【讨论】:

    • 这个答案包含三个非常糟糕的 Magento 做法:1)直接编辑核心文件 2)直接进行 db 查询而不是使用 magento dsl 3)从模板文件中执行此操作。
    猜你喜欢
    • 1970-01-01
    • 2014-12-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-08-11
    相关资源
    最近更新 更多