7

There may be a simple answer to this, but I can't find it. I'm trying to retrieve a product collection, excluding products that are enabled, but out of stock.

I've tried the following approach, but this kicks out an error:

$productIds = array('3','4','9');

$productCollection = Mage::getResourceModel('catalog/product_collection')
    ->addAttributeToSelect(
        array('name', 'image', 'price')
    )
    ->addIdFilter($productIds)
;

$this->_productCollection = Mage::getSingleton('cataloginventory/stock')
    ->addInStockFilterToCollection( $productCollection )
;

The following error occurs:

PHP Fatal error: Call to a member function getTypeInstance() on a non-object in app/code/core/Mage/Catalog/Block/Product/Abstract.php on line 117

app/code/core/Mage/Catalog/Block/Product/Abstract.php:117 has the following method:

/**
 * Retrieve url for add product to cart
 * Will return product view page URL if product has required options
 *
 * @param Mage_Catalog_Model_Product $product
 * @param array $additional
 * @return string
 */
public function getAddToCartUrl($product, $additional = array())
{
    // line 117:
    if (!$product->getTypeInstance(true)->hasRequiredOptions($product)) {
        return $this->helper('checkout/cart')->getAddUrl($product, $additional);
    }
    $additional = array_merge(
        $additional,
        array(Mage_Core_Model_Url::FORM_KEY => $this->_getSingletonModel('core/session')->getFormKey())
    );
    if (!isset($additional['_escape'])) {
        $additional['_escape'] = true;
    }
    if (!isset($additional['_query'])) {
        $additional['_query'] = array();
    }
    $additional['_query']['options'] = 'cart';
    return $this->getProductUrl($product, $additional);
}

Is there a simple way to filter a collection to get only products that are in stock? Obviously I know about isSaleable() inside the loop. Thanks.

  • 1
    You're quoting the wrong method, fixing... – Melvyn Apr 9 '14 at 20:37
  • 1
    And judging from that, this goes wrong in your template, not in the code you pasted. So how do you loop through your collection and are you sure the call is failing inside your loop? Not some stray $this->getAddToCartUrl($product) outside of it. – Melvyn Apr 9 '14 at 20:45
23

To answer the question. You would use this (not very different than your version):

$productCollection = Mage::getResourceModel('catalog/product_collection')
                        ->addAttributeToSelect(array('name', 'image', 'price'))
                        ->addAttributeToFilter('status', array('eq' => Mage_Catalog_Model_Product_Status::STATUS_ENABLED));

Mage::getSingleton('cataloginventory/stock')
    ->addInStockFilterToCollection($productCollection);

$this->_productCollection = $productCollection;

But I don't know exactly what causes your error, because it seems related to the fact that the getSubmitUrl/getAddToCartUrl product param is boolean false, and not to the collection per se.

  • Yes, this is the answer to the original question, thx newermind. – Jongosi Apr 9 '14 at 20:55
  • 3
    I'm afraid that it doesn't help for configurable products, since it will join cataloginventory_stock_item using the configurable product stock info, witch is incorrect, considering that Magento uses simple products to say if it is available or not. – Ricardo Martins Aug 28 '15 at 17:28
  • Great that solve my problem – Sourav Feb 8 '18 at 5:22
3

Perhaps a day late and a dollar short, but instead of doing all this second-step work, you can simply join the cataloginventorystock_stock_status table to your collection select():

$collection = Mage::getModel('catalog/product')->getCollection();
$collection->getSelect()->join('cataloginventory_stock_status', 'cataloginventory_stock_status.product_id = e.entity_id', array('stock_status'));
$collection->getSelect()->where("`cataloginventory_stock_status`.`stock_status` = 1");

You can do this in conjunction with additional join() or where() clauses. Hope it helps you in the right direction.

2

Newermind answered the original question correctly; my thanks to him. For future adventurers, here's what was wrong with my code. Special thanks to Melvyn for his help.

Problem

From my code in the question, $this->_productCollection contains an object. When we iterate over that object in the view, the $_product variable is an array.

$_products = $this->_productCollection;

foreach ( $_products as $_product ) {
    echo gettype( $_product ); // <-- returns type [array]
}

The method at app/code/core/Mage/Catalog/Block/Product/Abstract.php:117 (ultimately) calls getTypeInstance(). That method is expecting an object, specifically an instance of Mage_Catalog_Model_Product.

In the view, I was calling $this->getSubmitUrl($_product) inside the foreach loop:

$_products = $this->_productCollection;

foreach ( $_products as $_product ) {
    echo $this->getSubmitUrl( $_product );
}

getSubmitUrl() is a method of Mage_Catalog_Block_Product_Abstract, line 115. On line 123 it calls getAddToCartUrl(), which calls getTypeInstance() and that is expecting an object. Problem was, I was handing it an array.

Simple OOP principle, but I guess I was too tired to see it. Thx all for the help.

The Solution

So, unfortunately the addInStockFilterToCollection method returns an inventory stock object - not what I was after. I need a product collection of in-stock products.

Also, the addInStockFilterToCollection method only works with a Mage_Catalog_Model_Resource_Eav_Mysql4_Product_Link_Product_Collection collection.

So, how to get the collection? My approach was as follows:

  1. Get a collection of in-stock products.
  2. Create an ID array from the result set.
  3. Get the product collection, using the ID's from the in-stock results.

Here's the code:

$inStockProductIds = array();

$inStockCollection = Mage::getModel('cataloginventory/stock')
    ->getItemCollection()
    ->addFieldToFilter('is_in_stock', 
        array( 'eq' => 1 )
    )
    ->addFieldToFilter( 'entity_id', 
        array( 'in' => array ('1', '2') )
    )
;

if ( $inStockCollection) {
    foreach ($inStockCollection as $stockProduct) {
        $inStockProductIds[] = $stockProduct->getId();
    }
}

$this->_productCollection = Mage::getModel('catalog/product')
    ->getCollection()
    ->addAttributeToSelect(
        array('name', 'image', 'price')
    )
    ->addIdFilter( $inStockProductIds )
    ->setPageSize( 2 )
    ->load();
;

return $this->_productCollection;

That returns a product collection, only containing in-stock products. It's a bit heavy, but it works. I'd appreciate any improvements on this. Thanks.

  • why not just do something like: $collection = Mage::getModel("catalog/product")->getCollection() ... ; Mage::getSingleton('cataloginventory/stock')->addInStockFilterToCollection($col); notice I don't do anything with the return value, but the collection gets updated to have instock filters on it. – sbditto85 Jun 10 '15 at 15:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.