1

I am looking to create a custom product collection using specific product attributes as filters. The product collection I am getting has well over 100 products in which is fine but I need to find a way of choosing 5 products at random from the collection and then load them to display on a page. Has anyone developed anything similar to this or know the best way of implementing this?

  • If you want to show random products, take care of the performance. Most of the times, Magento optimize loading by caching objects and blocks. But if you explicitely want to show random products each time the page is seen, you'll force the whole pile of the framework to load. – Pilou Mar 17 '15 at 13:39
  • Hi Pilou, Thanks for the heads up on the performance side of it. Will definitely take this into consideration. – Ryan Copeland Mar 24 '15 at 11:16
0

I found out that ORDER BY RAND() is very unperformant on a Magento collection because all data gets copied to a temp table, assigned a random number and then sorted without index. If we reduce the data to be copied to just the ID, it gets a lot faster (still not perfect because the slow sorting remains but OK for a small catalog like yours).

For this, I made a modified version of getAllIds():

$numberOfItems = 5;

// Step 1: Preselect ids using ORDER BY RAND()
// Filters are applied here
$productCollection = Mage::getModel('catalog/product')
    ->getCollection();
$productCollection
    ->addStoreFilter()
    ->setVisibility(Mage::getSingleton('catalog/product_visibility')->getVisibleInCatalogIds());
$productCollection->getSelect()->order('RAND()');
$idsSelect = clone $productCollection->getSelect();
$idsSelect->reset(Zend_Db_Select::LIMIT_COUNT);
$idsSelect->reset(Zend_Db_Select::LIMIT_OFFSET);
$idsSelect->reset(Zend_Db_Select::COLUMNS);
$idsSelect->columns('e.' . $productCollection->getEntity()->getIdFieldName());
$idsSelect->limit($numberOfItems, null);
$idsSelect->resetJoinLeft();
$accessor = new ReflectionObject($productCollection);
$_bindParams = $accessor->getProperty('_bindParams');
$_bindParams->setAccessible(true);
$chosenIds = $productCollection->getConnection()
    ->fetchCol($idsSelect, $_bindParams->getValue($productCollection));

Then I could load the products by these random ids:

// Step 2: Load products
// Attributes and index data are joined here
$productCollection->addIdFilter($chosenIds);
$productCollection
    ->addMinimalPrice()
    ->addFinalPrice()
    ->addTaxPercents()
    ->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes())
    ->addUrlRewrite();

$productCollection->load();

Simpler alternative

An alternative that also works well on small catalogs (<10000 products) is to load all ids and select the random IDs with PHP:

$numberOfItems = 5;

$productCollection = Mage::getModel('catalog/product')->getCollection();
$productCollection
    ->addStoreFilter()
    ->setVisibility(Mage::getSingleton('catalog/product_visibility')->getVisibleInCatalogIds());
$candidateIds = $productCollection->getAllIds();
$numberOfProducts = count($candidateIds);
$chosenIds = [];
while ($numberOfItems) {
    $randomKey = mt_rand(0, $numberOfProducts - 1);
    if (!isset($chosenIds[$randomKey])) {
        chosenIds[$randomKey] = $candidateIds[$randomKey];
        --$numberOfItems;
    }
}
$productCollection->addIdFilter($chosenIds);
$productCollection
    ->addMinimalPrice()
    ->addFinalPrice()
    ->addTaxPercents()
    ->addAttributeToSelect(Mage::getSingleton('catalog/config')->getProductAttributes())
    ->addUrlRewrite();

$productCollection->load();

You can read a more in depth analysis and see some benchmarks in my blog: http://www.schmengler-se.de/en/2015/09/show-random-products-in-magento-you-are-doing-it-wrong/

1

try

$collection->getSelect()->order(new Zend_Db_Expr('RAND()'));
$collection->getSelect()->limit(5);

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.