Yet Another “Hiding Out-Of-Stock Products” Update
Update: Bug fix
It turns out that when layered navigation indexes are refreshed the website_id is 0 which causes the filtered results to be empty which makes layered navigation disappear. The fix is to simply place an if statement around the filter dependent on the website id no being 0. The Magento Connect module has been updated with this fix (0.3.1).
Ok, I think this will finally be the last update to this module… I’ve learned a lot about Magento since the first version of this module, and think I finally did it right this time. This update adds support for advanced search (thanks saho for the bug report) and also every other aspect of the store I could find.
I ended up doing a search like so:
app/code/core/Mage $ grep -rl addVisibleIn.\*FilterToCollection . ./Checkout/Block/Cart/Crosssell.php ./Tag/Block/Product/Result.php ./Tag/Block/Customer/View.php ./Adminhtml/Block/Sales/Order/Create/Search/Grid.php ./Adminhtml/Model/Sales/Order/Random.php ./Bundle/Block/Catalog/Product/List/Partof.php ./Bundle/Model/Observer.php ./Reports/Block/Product/Abstract.php ./Wishlist/Block/Share/Email/Items.php ./Wishlist/Block/Share/Wishlist.php ./Wishlist/Block/Customer/Wishlist.php ./Wishlist/Block/Customer/Sidebar.php ./Wishlist/Model/Mysql4/Wishlist.php ./CatalogSearch/Model/Layer.php ./CatalogSearch/Model/Advanced.php ./Rss/Block/Catalog/New.php ./Rss/Block/Catalog/Special.php ./CatalogIndex/Model/Indexer.php ./Sales/Model/Order.php ./Catalog/Block/Seo/Sitemap/Product.php ./Catalog/Block/Product/Compare/List.php ./Catalog/Block/Product/New.php ./Catalog/Block/Product/List/Related.php ./Catalog/Block/Product/List/Crosssell.php ./Catalog/Block/Product/List/Upsell.php ./Catalog/Block/Product/Bestsellers.php ./Catalog/Model/Layer.php ./Catalog/Model/Product/Visibility.php ./Catalog/Helper/Product/Compare.php
and determined (obviously) that it would be best to simply override the addVisibleIn*FilterToCollection functions rather than to try to override all of the above. I actually discovered the following code which turned out to not be what I wanted, but I think that is due to it not being updated when Varien added the stock_status table:
Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($collection);
However, I liked the idea of having such a function available that actually works as intended (the above does not work on configurable products) so I did just that.
First, the function that can be used on any product collection to filter out of stock products:
app/code/local/Lucky/InStockOnly/Model/Stock.php
<?php
class Lucky_InStockOnly_Model_Stock {
/**
* Add a statusInStock requirement for visibility
*/
public function addInStockFilterToCollection($collection)
{
if($websiteId = Mage::app()->getWebsite()->getWebsiteId()) {
$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' => $websiteId
)
);
}
}
}
Next we add some functions that override the addVisibleIn*FilterToCollection functions that are used heavily throughout Magento:
app/code/local/Lucky/InStockOnly/Model/Visibility.php
<?php
class Lucky_InStockOnly_Model_Visibility extends Mage_Catalog_Model_Product_Visibility {
public function addVisibleInCatalogFilterToCollection(Mage_Eav_Model_Entity_Collection_Abstract $collection)
{
parent::addVisibleInCatalogFilterToCollection($collection);
Mage::getSingleton('cataloginventory/instockonly')->addInStockFilterToCollection($collection);
return $this;
}
public function addVisibleInSearchFilterToCollection(Mage_Eav_Model_Entity_Collection_Abstract $collection)
{
parent::addVisibleInSearchFilterToCollection($collection);
Mage::getSingleton('cataloginventory/instockonly')->addInStockFilterToCollection($collection);
return $this;
}
public function addVisibleInSiteFilterToCollection(Mage_Eav_Model_Entity_Collection_Abstract $collection)
{
parent::addVisibleInSiteFilterToCollection($collection);
Mage::getSingleton('cataloginventory/instockonly')->addInStockFilterToCollection($collection);
return $this;
}
}
Now the model rewrites:
app/code/local/Lucky/InStockOnly/etc/config.xml
<?xml version="1.0"?>
<config>
<global>
<models>
<catalog>
<rewrite>
<product_visibility>Lucky_InStockOnly_Model_Visibility</product_visibility>
</rewrite>
</catalog>
<cataloginventory>
<rewrite>
<instockonly>Lucky_InStockOnly_Model_Stock</instockonly>
</rewrite>
</cataloginventory>
</models>
</global>
</config>
And of course we need to enable the module (same as before):
app/etc/modules/Lucky_InStockOnly.xml
<config>
<modules>
<Lucky_InStockOnly>
<active>true</active>
<codePool>local</codePool>
</Lucky_InStockOnly>
</modules>
</config>
Known Bugs
If you have attributes that are used to create configurable products and are also used in layered navigation, out of stock products will still show up when layered navigation is in use for an attribute where sibling products are in stock. I don't think there is a good fix for this other than having an observer disable products entirely when they go out of stock, but this has some side-effects which might negatively affect some people.
Download
This version has been packaged as a proper Magento Connect extension and is available here: In Stock Only