2

I´m currently working om some modifications on my client´s Magento "One Page Checkout" page.

I have managed to get all the product items using this code:

$session= Mage::getSingleton('checkout/session');    
$items = $session->getQuote()->getAllItems();

So now I can loop through the products in the cart and for instance echo quantity and price by writing:

foreach($items as $item) {
$itemquantity = $item->getQty();
$itemPrice = $item->getPrice();
}

Now, what I need to do is to sort the $items array so that the products in the loop comes out sorted by the products 1st./mother/root category ID.

As far as I can tell the $items array is sorted according to the order of which the products are added to the card. So I need to change this sort order so that the products with the lowest mother-category ID comes first and the products with highest mother-category ID come last.

I hope someone out there can help me with this.

2 Answers 2

2

Here is what I ended up with, which is very simple and maybe also very inefficient coding wise, but it works:

1st off I fetched the items in the Cart:

$session= Mage::getSingleton('checkout/session');    
$items = $session->getQuote()->getAllItems();

Then I initiated an Array:

$ArrayOfItems = array();

Then I looped through the Cart items and stored a delimited string into the Array:

foreach($items as $item) {
$productID = $item->getId();
$_product = $item->getProduct();
$productName = $item->getName();
$itemquantity = $item->getQty();
$_priceIncludingTax = Mage::helper('tax')->getPrice($_product, $_product->getFinalPrice());
$productURL = $_product->getUrlInStore();
$itemsubtotal = $itemquantity * $_priceIncludingTax;


//Getting the Products Category IDs and fetching the name and ID of the mother-category and the last category
$categories_array = $item->getProduct()->getCategoryIds();
$categories_array_end = end($categories_array);    
$catscounter = 0;
foreach($categories_array as $categoryId) {
$catscounter = $catscounter + 1;
if ($catscounter > 1) { 
break; 
}
$category = Mage::getModel('catalog/category')->load($categoryId);
$catidindex = $categoryId;
$catname = $category->getName(); 
}

//Collecting Array
$ArrayOfItems[] = $catname.'|'.$productName.'|'.$productID.'|'.$itemquantity.'|'.$itemsubtotal.'|'.$catidindex.'|'.$categories_array_end.'|'.$productURL;
}

Now I sorted the Array:

sort($ArrayOfItems);

(This way the Array is sorted by the Category Name of the products mother-category and then by the product name and so on..)

Then finally I looped through the sorted array:

foreach($ArrayOfItems as $ArrayItem) {

$arrayString = explode('|', $ArrayItem);

$catname = $arrayString[0];
$productNameFinal = $arrayString[1];
$productIDfinal = $arrayString[2];
$itemquantity = $arrayString[3];
$itemsubtotal = $arrayString[4];
$categoryId = $arrayString[5];
$categoryIdEnd = $arrayString[6];
$ProductURL = $arrayString[7];
}

Feel free to comment on this, especially if I´m doing something very "stupid" or "Bad" here..

As a side note I must mention that I´m used to working in Joomla, calling out all the "facepalms" :), where I make most of my custom shopping scripts based on this kind of PHP loops and arrays. And I´m sure that this can be done much simpler in Magento as shown by ProxiBlue

2

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....

2
  • Though normally there will not be so many products in the cart, mostly 10-20 max. I ended up storing all the need product infos into an array with delimiter where I placed the products first category ID as the first value. Then I sorted the new array with sort(); and made a new loop through the new array. See the answer below Commented Dec 9, 2013 at 14:23
  • EDIT! I placed the products first category name as the first value (being that this is actually better according to the implementation). Commented Dec 9, 2013 at 15:33

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.