Magento Custom Product Listing – Sorting by Position

collection;product-collectionproduct-listsorting

First, I've searched Google, SO, and magento SE to no avail. I've seen many results, but none that seemed to work.

I've got a "custom" product listing on the front-end, which uses the below code:

$magentoProduct = Mage::getModel('catalog/product');
$_helper = $this->helper('catalog/output');

$collection = $magentoProduct->getCollection()
            ->addAttributeToSelect('*')
            ->addAttributeToFilter('status', 1)
            ->setStoreId(Mage_Core_Model_App::ADMIN_STORE_ID);
// This doesn't have any effect
//          ->setOrder('position', 'ASC');

$collectionIds = $collection->getAllIds();
foreach ($collectionIds as $prodId):
    $magentoProduct->reset();
    $magentoProduct = $magentoProduct->load($prodId);
    $cats = $magentoProduct->getCategoryIds();
    // ... do stuff ...
endforeach;

I want the sort order to respond to the Product Position (as set in the "Catalog"=>"Manage Categories" interface).

I've tried a variety of things, to no avail.

Any insights? (I'm also open to a better / different approach to this, so feel free to suggest best practices).

Best Answer

First, you should know that the position is saved in the relation table catalog_category_product, so without filtering by category you will not have this data. In theory you could join the relation table without actually filtering by category, but which row will you use? Usually products belong to multiple categories.

Your product collection is independent of any category, so it will contain products from different categories. Does it really makes sense to sort these products by position within an arbitrary category? You decide. But I would recommend adding a product attribute. If you call it "position" and add it as static attribute, i.e. as field to the main table1, you can use the code as it is. But to avoid conflicts with the position inside a category, I would choose a different code. Also, if you don't add it as static field, but as normal EAV attribute, you have to use addAttributeToSort('position') instead of setOrder('position').

A general recommendation: Instead of addAttributeToSelect('*') load only the attributes that you need. In product listing, the following is usually sufficient:

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

With this code, you should configure your new position attribute as "Used in product listing: Yes", so that it will be included in addAttributeToSelect() automatically.


1) Usually, you don't do that but if you use these custom collections a lot with many products, you should consider it, alongside with a MySQL index on the column, to improve the sorting performance.