Magento Module: Hide Empty Categories

There are a number of ways that you can hide empty categories: You can disable them manually, you can modify the template output to check the product count and if zero don’t display it, etc. Another way is to write a module and intercept the catalog collection output before it’s sent to the templates.

I could easily package this up and put it up on Magento Connect, but I’m more into trying to help people understand Magento modules, so below is a walkthrough of how to create a module that will provide this functionality.

Note: Update – Fixed for Flat Categories too. This has only been tested on 1.6.x.

Step 1

Create the file ‘/app/etc/modules/Prattski_HideEmptyCategories.xml’. This file tells Magento that your module exists, if it’s enabled or not, and where to find it.

<?xml version="1.0"?>
<!--
/**
 * Hide Empty Categories
 *
 * @category    Prattski
 * @package     Prattski_HideEmptyCategories
 * @copyright   Copyright (c) 2011 Prattski (http://prattski.com/)
 * @author      Josh Pratt (Prattski)
 */
-->
<config>
    <modules>
        <Prattski_HideEmptyCategories>
            <active>true</active>
            <codePool>local</codePool>
        </Prattski_HideEmptyCategories>
    </modules>
</config>

Step 2

Now we create our module directory. Create ‘/app/code/local/Prattski/HideEmptyCategories/’. This is where our module will reside. Inside that directory, create this file: ‘etc/config.xml’. This file does a number of things: It tells Magento which version your module is, defines your model namespace and directory, and creates and event observer to observe every time a category collection is loaded.

<?xml version="1.0"?>
<!--
/**
 * Hide Empty Categories
 *
 * @category    Prattski
 * @package     Prattski_HideEmptyCategories
 * @copyright   Copyright (c) 2011 Prattski (http://prattski.com/)
 * @author      Josh Pratt (Prattski)
 */
-->
<config>
    <modules>
        <Prattski_HideEmptyCategories>
            <version>1.0.0</version>
        </Prattski_HideEmptyCategories>
    </modules>
    <global>
        <models>
            <hideemptycategories>
                <class>Prattski_HideEmptyCategories_Model</class>
            </hideemptycategories>
            <catalog_resource>
                <rewrite>
                    <category_flat>Prattski_HideEmptyCategories_Model_Catalog_Resource_Category_Flat</category_flat>
                </rewrite>
            </catalog_resource>
        </models>
    </global>
    <frontend>
        <events>
            <catalog_category_collection_load_after>
                <observers>
                    <hideemptycategories>
                        <type>singleton</type>
                        <class>hideemptycategories/observer</class>
                        <method>catalogCategoryCollectionLoadAfter</method>
                    </hideemptycategories>
                </observers>
            </catalog_category_collection_load_after>
        </events>
    </frontend>
</config>

Step 3

Let’s create our observer model. As you can see in the file above, we are going to have the ‘catalogCategoryCollectionLoadAfter’ method run in this file when it observes the ‘catalog_category_collection_load_after’ event. Create this file: Model/Observer.php

<?php
/**
 * Hide Empty Categories
 *
 * @category    Prattski
 * @package     Prattski_HideEmptyCategories
 * @copyright   Copyright (c) 2011 Prattski (http://prattski.com/)
 * @author      Josh Pratt (Prattski)
 */
 
/**
 * Event Observer
 *
 * @category    Prattski
 * @package     Prattski_HideEmptyCategories
 */
class Prattski_HideEmptyCategories_Model_Observer extends Mage_Core_Model_Abstract
{
    /**
     * Remove hidden caegories from the collection
     *
     * @param Varien_Event_Observer $observer
     */
    public function catalogCategoryCollectionLoadAfter($observer)
    {
        if ($this->_isApiRequest()) return;
        $collection = $observer->getEvent()->getCategoryCollection();
        $this->_removeHiddenCollectionItems($collection);
    }
 
    /**
     * Remove hidden items from a product or category collection
     * 
     * @param Mage_Eav_Model_Entity_Collection_Abstract|Mage_Core_Model_Mysql4_Collection_Abstract $collection
     */
    public function _removeHiddenCollectionItems($collection)
    {
        // Loop through each category or product
        foreach ($collection as $key => $item)
        {
            // If it is a category
            if ($item->getEntityTypeId() == 3) {
 
                if ($item->getProductCount() < 1) {
                    $collection->removeItemByKey($key);
                }
            }
        }
    }
 
    /**
     * Return true if the reqest is made via the api
     *
     * @return boolean
     */
    protected function _isApiRequest()
    {
        return Mage::app()->getRequest()->getModuleName() === 'api';
    }
}

Step 4

Magento has the ability to turn on Flat Categories to speed up the performance whenever categories are loaded. The event that we are observing above does not work when Flat Categories are enabled. So, thanks to Vinai’s help, we need to rewrite a method in a core file to make this work. Create this file: Model/Catalog/Resource/Category/Flat.php

<?php
/**
 * Hide Empty Categories
 *
 * @category    Prattski
 * @package     Prattski_HideEmptyCategories
 * @copyright   Copyright (c) 2011 Prattski (http://prattski.com/)
 * @author      Josh Pratt (Prattski)
 */
 
/**
 * Event Observer
 *
 * @category    Prattski
 * @package     Prattski_HideEmptyCategories
 */
class Prattski_HideEmptyCategories_Model_Catalog_Resource_Category_Flat
    extends Mage_Catalog_Model_Resource_Category_Flat
{
    /**
     * Load nodes by parent id
     *
     * @param unknown_type $parentNode
     * @param integer $recursionLevel
     * @param integer $storeId
     * @return Mage_Catalog_Model_Resource_Category_Flat
     */
    protected function _loadNodes($parentNode = null, $recursionLevel = 0, $storeId = 0)
    {
        $nodes = parent::_loadNodes($parentNode, $recursionLevel, $storeId);
 
        foreach ($nodes as $node) {
            if ($node->getProductCount() == 0) {
                unset($nodes[$node->getId()]);
            }
        }
        return $nodes;
    }
}

Step 5

To recap all the files we have made:

  • /app/etc/modules/Prattski_HideEmptyCategories.xml
  • /app/code/local/Prattski/HideEmptyCategories/etc/config.xml
  • /app/code/local/Prattski/HideEmptyCategories/Model/Observer.php
  • /app/code/local/Prattski/HideEmptyCategories/Model/Catalog/Resource/Category/Flat.php

Test it out! You’ll be able to see if Magento recognizes your module by going to System > Configuration > Advanced. If you see the module in the list, Magento knows it’s there. You’ll have to have some categories in your system, some with products in them, some without, to see if it’s working properly on the frontend.

I hope you enjoyed learning how to build this module. If you find any issues or bugs, please let me know.

This entry was posted in Magento. Bookmark the permalink.

24 Responses to Magento Module: Hide Empty Categories

  1. Vinai says:

    Hi Josh,

    very cool post, thank you. Can you expand to make it work when the flat category tables are turned on?
    Thank you!

  2. imran says:

    its doesnt work for me. if i use flat categry yes then full menu become high even there is products!! and if i use flat category NO then all categories show. even there is no products, can you help? thanks

  3. imran says:

    its working now because i have enabled flat categories and re-added product! but after install it if there are some product already then all product need to be updated manually and save again. can u solve this bug? Thanks

    • Josh Pratt says:

      imran – I’m not quite sure there is even a bug. I’m not even quite sure what you are trying to explain, and i’ve tried to reproduce your issue, but it always works fine for me…

  4. amadex says:

    Hi! thank you for this extension! Nevertheless, the categories are hidden if they don’t directly have products, despite their subcategories having products… Mage 1.6, with no flat categories active.

    Tkx

  5. martin says:

    wow, this is simply brilliant!
    works perfect on 1.5.1

  6. Hey Josh, good sharing. It helped me a lot. Keep sharing such posts.

  7. martijn says:

    getproductcount() seems to count products, even if they are not set as visible. is there a way to fix that?

  8. Alias says:

    You can add items not visible?
    Thanks

  9. Ian says:

    i get this error

    Mage registry key “_singleton/hideemptycategories/observer” already exists

    Using 1.6.0.0

  10. Ian says:

    Dont worry, my mistake forgot to upload the Observer.php code, lol

  11. Hamid says:

    Hi Josh,

    Very useful module but it doesnt seem to work for me if the product belongs to a subsubcat or subsubsubcat. It seems to work OK if I assign the parent subcat to the actual product itself and then it picks it up otherwise it doesnt seem to recognise there are products lower down then subcats.

    I have checked anchor setttings etc, and am using magento CE 1.6.1

    Any ideas?

  12. gizmo says:

    I’m not having any luck with this at all. I’ve created the files as instructed. The module shows in ADMIN: System > Advanced and it is enabled…. but empty categories are still showing in my menus.

    I’ve turned flat files on and off, re-indexed caches, enabled/disabled/moved products.

    There is no effect, no matter what I do.

  13. vegetaaa says:

    Tried this module but i can’t seem to get it to work. I’ve tested it on 1.6.1.0 and 1.6.2.0 but no luck. When i try to open a category on the frontend i get this error: Mage registry key “_singleton/hideemptycategories/observer” already exists

    Also the system –> configuration –> catalog in the backend is not appearing anymore.

    Anybody have an idea what i’m doing wrong?

  14. Patrice Bois says:

    Hi Josh, if i have multiple stores in my magento, if one store have a product in category but not the other store, the category will be displayed…

  15. Patrick says:

    We have created your module as instructed but can’t seem to get it to work. The module appears in configuration/advanced but categories with no products are still appearing.

    We are using a custom theme so would this need to be put somewhere else within our theme?

    Can this also be modified to only count products that are enabled and have a stock qty?

    Any help would be greatly appreciated.

    Thanks

    Patrick

  16. Patrick Mackessy says:

    I left a previous comment but it seems to have been deleted. Could you let me know why?

    • Josh Pratt says:

      @Patrick – I can’t remember. I get loads of posts with tons of code, or advertisements, etc. If it was deleted, it was probably because of something like that.

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong> <pre lang="" line="" escaped="" highlight="">