One way to do this would be to get the collection sorted as you want, at load, rather than using code to manipulate the data. (which can be resource intensive, especially if you have many products) Loops are evil! Have mysql sort the data for you.
Thus I suggest the following process:
- Add a new field to the quote table.
Thus in your module's installer files you would add some code to append a new column to the
sales_flat_quote_item
table.
(untested)
$installer = $this;
$installer->startSetup();
$installer->getConnection()->addColumn($installer->getTable('sales/quote_item'),'order_by', 'int(1)');
$installer->endSetup();
be sure to flush your storage cache after this, as magento can sometimes not pick up database structure changes.
- Using observer events, populate that new field with the order_by value you require. In your case the category id.
You would be interested in the event 'sales_quote_item_save_before' Thsi will allow you to inject the order_by value into the quote item object for saving.
(untested)
public function sales_quote_item_save_before(Varien_Event_Observer $observer) {
try {
$event = $observer->getEvent();
$quoteItem = $event->getItem();
$product = $quoteItem->getProduct();
$productCatIds = $product->getCategoryIds();
sort($productCatIds);
$lowestProductCat = array_shift($productCatIds);
$quoteItem->setOrderBy((int)$lowestProductCat); // not sure if this is what you exatly wanted, so adjust to whatever order value you need
} catch (Exception $e) {
// log any issues, but allow system to continue.
Mage::logException($e);
if (Mage::getIsDeveloperMode()) {
die($e->getMessage());
}
}
return $this;
}
- Lastly, simply inject an order clause on the collection load. This will again be done via an observer. Unfortunately core magento does not define an eventPrefix for the quote_item_collection loading. Thus you have to listen to the generic (abstract) event, and check for the correct collection instance.
This can be a bit heavy on performance.
If you do go this way, I highly suggest to rewrite the core class Mage_Sales_Model_Resource_Quote_Item_Collection, and simply extend it and add an eventPrefix.
rewrite example to extend the core class
class YourModule_YourNameSpace_Model_Resource_Quote_Item_Collection extends Mage_Sales_Model_Resource_Quote_Item_Collection
{
protected $_eventPrefix = 'quote_item';
}
then you can target the event directly, which will be less performance issue.
untested event to order the collection on load
public function core_collection_abstract_load_before(Varien_Event_Observer $observer) {
try {
$collection = $observer->getCollection();
if($collection instanceof Mage_Sales_Model_Resource_Quote_Item_Collection){
$collection->getSelect()->order('order_by');
}
} catch (Exception $e) {
// log any issues, but allow system to continue.
Mage::logException($e);
if (Mage::getIsDeveloperMode()) {
die($e->getMessage());
}
}
return $this;
}
An alternative to the above (injecting the ->order directly into the select), is to make use of the Varien_Data_Collection_Db::_renderOrders()
method, which is called for any collection load via Varien_Data_Collection_Db::load()
method.
Hope that helps you to a solution....