Magento – Listing Random Products from all Subcategories of a Parent Category

blockscategorymagento-1.9product-listrandom

I need some help. I have the following category structure

category>subcagtegory>subsubcategory

The parent is an empty category that serves as a landing page. I need to display a list of random products from all the subcategories and subsubcategories on the parent landing category. Right now I have the following Random.php

class Mage_Catalog_Block_Product_List_Random extends Mage_Catalog_Block_Product_List
{
    protected function _getProductCollection()
    {
        if (is_null($this->_productCollection)) {
            $categoryID = $this->getCategoryId();
            if($categoryID)
            {
              $category = new Mage_Catalog_Model_Category();
              $category->load($categoryID);
              $collection = $category->getProductCollection();
            } else
            {
              $collection = Mage::getResourceModel('catalog/product_collection');
            }
            Mage::getModel('catalog/layer')->prepareProductCollection($collection);
            $collection->getSelect()->order('rand()');
            $collection->addStoreFilter();
            $numProducts = $this->getNumProducts() ? $this->getNumProducts() : 3;
            $collection->setPage(1, $numProducts)->load();

            $this->_productCollection = $collection;
        }
        return $this->_productCollection;
    }
}

and I call it on the parent's category "Custom Layout Update" like this:

<reference name="content">
     <block type="catalog/product_list_random" name="product.list.random" template="catalog/product/random.phtml">
        <action method="setCategoryId"><category_id>3</category_id></action>
        <action method="setColumnCount"><count>5</count></action>
        <action method="setNumProducts"><num_products>20</num_products></action>
    </block>
</reference>

What I need is to be able to specify a category ID in the XML just as I do now. But instead of listing products from one parent category (as it does now), I need to list products from all of its subcategories and subsubcategories. I believe I need an array of some sort but I am a PHP n00b so need help writing clean code.

Best Answer

Configure the parent category as anchor category. Anchor categories automatically include items from their subcategories.

screenshot

On an unrelated note, ORDER BY RAND() is unperformant because it results in a resource intensive temp table copy. It has to load all results into a temporary table, assign a random number to each row and then sort without any index. Instead we retrieve all ids (this is faster and the amount of data is managable even for large catalogs), pick some randomly and retrieve these rows directly.

To do so, replace:

Mage::getModel('catalog/layer')->prepareProductCollection($collection);
$collection->getSelect()->order('rand()');
$collection->addStoreFilter();
$numProducts = $this->getNumProducts() ? $this->getNumProducts() : 3;
$collection->setPage(1, $numProducts)->load();

with:

$collection
    ->addStoreFilter()
    ->setVisibility(Mage::getSingleton('catalog/product_visibility')->getVisibleInCatalogIds());

$numberOfItems = $this->getNumProducts() ? $this->getNumProducts() : 3;
$candidateIds = $productCollection->getAllIds();

$choosenIds = [];
$maxKey = count($candidateIds)-1;
while (count($choosenIds) < $numberOfItems)) {
  $randomKey = mt_rand(0, $maxKey);
  $choosenIds[$randomKey] = $candidateIds[$randomKey];
}

$collection->addIdFilter($choosenIds);
$collection
    ->addMinimalPrice()
    ->addFinalPrice()
    ->addTaxPercents()
    ->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes())
    ->addUrlRewrite();