Question

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?

Was it helpful?

Solution

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/

OTHER TIPS

try

$collection->getSelect()->order(new Zend_Db_Expr('RAND()'));
$collection->getSelect()->limit(5);
Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top