Home > Magento, Programming > Hiding “Out of Stock” items in Layered Navigation

Hiding “Out of Stock” items in Layered Navigation

July 14th, 2009

Updated 9/28/09

See the latest post in this series: Yet Another “Hiding Out-Of-Stock Products” Update, for an update that fixes advanced search, tags, up-sells, cross-sells, etc..

Updated 7/23/09

Updated once again to add support for filtering search results. (Added CatalogSearch/Model/Layer.php, moved Model/Layer.php to Catalog/Model/Layer.php, updated config.xml)

My previous post about “Hiding Out of Stock Items in Magento” had one pretty major downfall, in that it did not handle the product counts in the layered navigation. In response to a forum post regarding this I looked into fixing my original solution and have come up with a more concise and complete approach which I present now as a replacement to my original InStockOnly module.

Rather than observe an event we are going to simply overload the Mage_Catalog_Model_Layer->prepareProductCollection method which, as it turns out, serves as the basis for all of the catalog product collections including the product lists and the layered navigation product counts!

My module is called Lucky_InStockOnly for lack of a better name.

Here is the code for app/code/local/Lucky/InStockOnly/Catalog/Model/Layer.php:

<?php

class Lucky_InStockOnly_Catalog_Model_Layer extends Mage_Catalog_Model_Layer {

  /**
   * Add a statusInStock requirement for visibility
   */
  public function prepareProductCollection($collection){
    parent::prepareProductCollection($collection);
    $collection->joinField(
      'stock_status',
      'cataloginventory/stock_status',
      'stock_status',
      'product_id=entity_id', array(
        'stock_status' => Mage_CatalogInventory_Model_Stock_Status::STATUS_IN_STOCK,
        'website_id' => Mage::app()->getWebsite()->getWebsiteId(),
      )
    );
    return $this;
  }

}

We do the same thing for CatalogSearch so that search results also hide out of stock items. It turns out the API is consistent here and the code is identical except for the class names.

app/code/local/Lucky/InStockOnly/CatalogSearch/Model/Layer.php

<?php

class Lucky_InStockOnly_CatalogSearch_Model_Layer extends Mage_CatalogSearch_Model_Layer {

  /**
   * Add a statusInStock requirement for visibility
   */
  public function prepareProductCollection($collection){
    parent::prepareProductCollection($collection);
    $collection->joinField(
      'stock_status',
      'cataloginventory/stock_status',
      'stock_status',
      'product_id=entity_id', array(
        'stock_status' => Mage_CatalogInventory_Model_Stock_Status::STATUS_IN_STOCK,
        'website_id' => Mage::app()->getWebsite()->getWebsiteId(),
      )
    );
    return $this;
  }

}

Now we simply setup our module config file to overload the catalog classes of the same names:

app/code/local/Lucky/InStockOnly/etc/config.xml

<?xml version="1.0"?>
<config>

<global>
  <models>

    <catalog>
      <rewrite>
        <layer>Lucky_InStockOnly_Catalog_Model_Layer</layer>
      </rewrite>
    </catalog>
    <catalogsearch>
      <rewrite>
        <layer>Lucky_InStockOnly_CatalogSearch_Model_Layer</layer>
      </rewrite>
    </catalogsearch>


  </models>
</global>

</config>

And of course we need to enable the module (same as first version):

app/etc/modules/Lucky_InStockOnly.xml

<config>
  <modules>
    <Lucky_InStockOnly>
      <active>true</active>
      <codePool>local</codePool>
    </Lucky_InStockOnly>
  </modules>
</config>

You will need to refresh your cache and possibly rebuild the catalog and layered navigation indicies, then you are done! All out of stock products should be hidden, and the counts in the layered navigation should correctly indicate the number of items that are in-stock.

Magento, Programming

  • http://colin.mollenhour.com/2009/06/hiding-out-of-stock-items-in-magento/ Colin Mollenhour’s Technical Blog » Hiding “Out of Stock” Items in Magento

    […] This module has been replaced with one that correctly updates the layered navigation counts. See the new version here. […]

  • http://www.michael-lomas.co.uk Michael Lomas

    Hi Colin – just wanted to thank you for the changes you’ve made to this module. I’m launching a site over the weekend and was hoping to hide out of stock sizes to avoid the frustration of ‘out of stock’ messages for configurable products that say it’s in stock, with the simple product out of stock yet are still displayed as options. Good work on the timing – if it’d have launched last week I’d have missed this!

  • fluff

    Hi there; I think I found another neglected area in terms of inventory and layered navigation. My client’s store allows for layered navigation of apparel by size, color etc. If a shirt was once available in large, and is now sold out of large but is available in small, xl etc. it’s still showing up.

    This is happening after overwriting the new versions of your files above, removing Observer.php and refreshing my catalog and inventory. Any help would be very appreciated; thanks much!

  • http://colin.mollenhour.com colin

    @fluff

    This is an issue that will be much more difficult to fix. If you look at the queries here you can see that the very first join is on catalog_category_product_index which already filters out the simple products since it relies on a later join to catalogindex_eav to filter the attributes, but this table only contains the configurable products, not the simple products. Magento seems to have redundant data (once again) here because rather than retrieve the attributes of a configurable product via the simple products using catalog_product_entity_int, it does it through this catalogindex_eav table which keeps the relation between the parent product and the attribute value in-tact regardless of stock.

    In order to properly filter the query based on the stock in this case the query would have to be rewritten to join catalog_product_entity_int and filter the attributes based on that table, then filter the out of stock items, then join back to the parent product using catalog_product_super_link and group by the parent_id. However, I don’t see how you can do this without filtering out all simple products in the process since none of them would have a parent_id in this table. It could be done by possibly using a UNION, but given the OO design of Magento these kinds of complex queries are practically impossible.

    So, it appears to me that in order to hide out of stock products with layered navigation attribute filters using attributes as products, the problem would have to be tackled from an entirely different angle. Perhaps each time a simple product goes out of stock the attribute value could be removed from the catalogindex_eav table? Then you would have to add it back if the product comes back in stock, and I don’t know what other parts of Magento rely on this table so it could potentially break something else.

  • fluff

    Wow thanks for the thorough, timely response. That sounds like a mess.. why Magento wasn’t built with finite stock stores in mind is beyond me. If you should happen to come across any amazing solutions do let me know!

  • basejewellery

    @

    colin

    I am having this same issue with my store, for example I have a configurable item with 6 different sizes (and therefore 6 simple products associated with it), when one size sells out and a customer filters tries the products by size, the products in that size will still show up even if they are ‘out of stock’ and quantity is 0.

    I had an idea on a workaround that might be easily implemented, I noticed if the simple product for that size is set to disabled it doesn’t show up in the layered navigation. Therefore is there a way we can add a query somewhere along the line that will set the simple product to disabled when its quantity reaches 0?

    Therefore when a customer clicks on size 10 for example, it will only show the products in which size 10 is available.

  • http://colin.mollenhour.com colin

    @basejewellery
    That is a very doable idea. For my particular case we are using an inventory management system, so disabling a product would require it to be manually re-enabled when it came back in stock which would otherwise happen automatically. Besides the possible confusion stemming from your “active” products and “inactive” products both now being disabled your approach is probably the best chance of getting this bug-free. I think the event you are interested in is going to be cataloginventory_stock_item_save_before.

  • saho

    Awesome, I found this right before launching our site and works awesome.
    I notice however though advanced search does not filter out the out of stock products. Would this be easy to implement. I can pm you urls if you want to see it. Thanks again for all your hard work on this.

  • http://colin.mollenhour.com colin

    Ahh, right you are.. The advanced search uses yet another getProductsCollection function and does not use the prepareProductsCollection function like the others. Rather than copy that code a *third* time, I started to make a addInStockFilterToCollection function only to discover that one already exists in the cataloginventory/stock model which is where I was going to add it! I am working on a new version of the module which will use this function instead of my own functions and will also extend the advanced search model to filter out of stock items there as well (just as soon as netbeans starts responding again..). Thanks for the bug report!

  • João Gabriel

    Hi, the functionality sounds like perfect, however it didn’t work on version 1.7. Is there some updated package for download in order to make this work for 1.7?

  • http://colin.mollenhour.com Colin Mollenhour

    This functionality is already implemented in core (as of 1.4 I think). It can be enabled/disabled in the System > Configuration.

  • João Gabriel

    No, on 1.7 just there is a option to hide from frontend all out of stock products, however if you wanna show the out of stock products on frontend, the filters on layered navigation are shown even an one or more products for that attribute are out of stock. I wanna show the attributes which only have products in stock associated to them.

  • http://www.facebook.com/nowayyy Adrien Ferreira

    Same problem over here.. 1.7

  • SNH

    I am just wondering. Is this still an issue in Mage 1.8.2? Because we are still seeing this behavior with configurable products.

  • shilpa verma

    hi
    i want to know that how to add ‘show more’ link in layered navigation in magento so that if more than three attributes are there then there should be ‘show more’ link and after click on ‘show more’ link the rest attributes are also start to show.
    plz suggest
    thank u
    regards