Filter For Popular Products By Category in Magento

What we are try to do seems simple enough, we want to show the popular products from each category within our menu. We’re looking to get 5 of those products, since that’s what the UI is designed to handle. We would like them sorted by popularity, which I believe is number of views on the product in Magento.

It many cases, the menu category we’re requesting has no products for itself. So, it has to support pulling in products for it’s children as well. Example of one branch of our category layout is:

  • Mens, #2 (no items)
    • Shirts, #5 (has items)
    • Accessories, #6 (has items)
    • Pants, #7 (has items)
    • Etc, #8 (has items)

What this code currently does, is return the same popular products list regardless of the Category we pass it to filter. Removing the ->addCategoryFilter(…) effectively returns the same results. I suspect if we can solve how to filter by Categories the rest will fall into place.

$storeId = 1;
$category; // Category Object for id = 2 passed to this code
$productCount = 5;

$products = Mage::getResourceModel('reports/product_collection')
->addOrderedQty()
->addAttributeToSelect('*')
->addAttributeToSelect(array('name','small_image'))
->setStoreId($storeId)
->addStoreFilter($storeId)
->addCategoryFilter($category)
->addViewsCount()
->setPageSize($productCount);

We’ve tried a few variations on this. I’m not sure if the addCategoryFilter(…) method takes into account children categories or not. If not, that should be easy enough to query out and solve. Of course, as it stands right now it just always returns the same products with no filtering on category… first things first as they say.

Running Magento 1.4.0.1

A quick look at the data for products shows these keys in the $products->getFirstItem()->getData() to be:

Array
(
[0] => entity_id
[1] => entity_type_id
[2] => attribute_set_id
[3] => type_id
[4] => sku
[5] => has_options
[6] => required_options
[7] => created_at
[8] => updated_at
[9] => name
[10] => url_key
[11] => gift_message_available
[12] => meta_title
[13] => meta_description
[14] => image
[15] => small_image
[16] => thumbnail
[17] => custom_design
[18] => page_layout
[19] => options_container
[20] => url_path
[21] => image_label
[22] => thumbnail_label
[23] => small_image_label
[24] => description
[25] => short_description
[26] => meta_keyword
[27] => custom_layout_update
[28] => weight
[29] => price
[30] => special_price
[31] => cost
[32] => news_from_date
[33] => news_to_date
[34] => special_from_date
[35] => special_to_date
[36] => custom_design_from
[37] => custom_design_to
[38] => exclusive
[39] => size
[40] => color
[41] => status
[42] => visibility
[43] => is_imported
[44] => tax_class_id
[45] => enable_googlecheckout
[46] => is_recurring
[47] => is_salable
[48] => stock_item
) 

Sadly no category_ids

2 thoughts on “Filter For Popular Products By Category in Magento”

  1. Alright, well I found an answer that basically works through the lack of working and/or multiple category filtering. It’s dirty, but works. It’s based off a blog post from here: http://asia-connect.com.vn/2009/07/magento-filter-by-multiple-categories/

    I can’t say I’m happy with this solution. I can’t figure out why Magento would remove the ability to do filtering on Categories in a useful manner. The fact that it didn’t appear to filter anything at all just compounded my issues. Feel free to chime in with a more sensible solution. I’d gladly replace this solution with it.

    $products = Mage::getResourceModel('reports/product_collection')
            ->addOrderedQty()
            ->addAttributeToSelect('*')
            ->addAttributeToSelect(array('name','small_image'))
            ->setStoreId($storeId)
            ->addStoreFilter($storeId)
    //        ->addCategoryFilter($category)
            ->addViewsCount()
            ->setPageSize($productCount);
    
    $alias = 'cat_index';
    $categoryCondition = $products->getConnection()->quoteInto(
            $alias.'.product_id=e.entity_id AND '.$alias.'.store_id=? AND ',
            $storeId
    );
    $cats = array( $category->getId() );
    foreach($category->getChildren() as $catChild) {
            $cats[] = $catChild->getId();
    }
    
    
    $categoryCondition.= $alias.'.category_id IN ('.implode(',',$cats).')';
    
    $products->getSelect()->joinInner(
            array($alias => $products->getTable('catalog/category_product_index')),
            $categoryCondition,
            array('position'=>'position')
    );
    
    $products->_categoryIndexJoined = true;
    

  2. You can use addCategoryFilter if you have only one category. Category ids are stored for product in a comma separated way. So, to filter product collection by more than one category, you have to use:

    addAttributeToFilter('category_ids',array('finset'=>$categoryIds));
    

    $categoryIds can be a single category id or comma separated category ids.

    The following code filters product collection by two category ids (10 and 15).

    $_productCollection = Mage::getResourceModel('reports/product_collection')
       ->addAttributeToSelect('*')
       ->addAttributeToFilter('category_ids',array('finset'=>'10,15'));
    

    when we use finset, the mysql function find_in_set is used in the sql query by Magento.mysql function find_in_set:

    FIND_IN_SET() looks for the first occurrence of a string within another string containing comma-separated values.

    SELECT FIND_IN_SET('b','a,b,c,d'); // result = 2
    

Leave a Reply

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