Get 5 Random Product IDs From Product Collection
-
12-12-2019 - |
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?
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);