7

It is possible to add element to the collection as the first element, similar to this method which adds element to the end?

$product = Mage::getModel('catalog/product')->load(15879);
$productCollection->addItem($product);
10

DISCLAIMER: Use the database for sorting purposes wherever possible. However, in some cases you might want to arrange items in a collection that are not stored in the database (yet), for example after dynamically adding items to the quote during collectTotals(). Then this approach will be useful.

Unfortunately not, Varien_Data_Collection is not very flexible in this respect. To reorder items in a collection you need to remove them and add them again in the desired order.

In your specific case:

// remove previous items, keep them in $items
$items = $productCollection->getItems();
foreach ($productCollection as $key => $item) {
    $collection->removeItemByKey($key);
}

// add new item
$productCollection->addItem($product);

// re-add $items
foreach ($items as $item) {
    $productCollection->addItem($item);
}

Update: Performance

Since there are objections regarding performance, I did a little benchmark on a real product database. Note that loading a collection with 20K products is something you should never actually do but this is just to prove the point that iterating over the loaded arrays twice does not add a significant overhead:

Test Script

$startTime = microtime(true);
$productCollection = Mage::getResourceModel('catalog/product_collection')->load();

printf("Loaded %d products. Time: %.2fs Memory: %s\n",
    $productCollection->count(), microtime(true)-$startTime, number_format(memory_get_usage(true)));

// remove previous items, keep them in $items
$items = $productCollection->getItems();
foreach ($productCollection as $key => $item) {
    $productCollection->removeItemByKey($key);
}

printf("Removed items. Time %.2fs Memory: %s\n",
    microtime(true)-$startTime, number_format(memory_get_usage(true)));

// add new item
$productCollection->addItem(Mage::getModel('catalog/product'));

// re-add $items
foreach ($items as $item) {
    $productCollection->addItem($item);
}

printf("Added items. Time %.2fs Memory: %s\n",
    microtime(true)-$startTime, number_format(memory_get_usage(true)));

Output

Loaded 20761 products. Time: 3.70s Memory: 81,264,640

Removed items. Time 3.92 Memory: 81,788,928

Added items. Time 4.31 Memory: 81,788,928

And with a more realistic amount of 100 loaded products:

Loaded 100 products. Time: 0.11s Memory: 10,223,616

Removed items. Time 0.11s Memory: 10,223,616

Added items. Time 0.12s Memory: 11,272,192

  • This is a terrible answer. You are doing not one but two foreachs over a collection before loading it. This will take forever for large collections and will decrease performance a lot. – mbalparda Feb 10 '15 at 16:55
  • I assumed, the collection is already loaded. Otherwise the question does not make much sense to me. – Fabian Schmengler Feb 10 '15 at 16:56
  • It clearly is and still, this idea adds TWO foreachs over a collection. This is not the case of loading inside a foreach, but going trough the entire collection twice before doing anything else will destroy any environment. – mbalparda Feb 10 '15 at 16:57
  • 1
    @mbalparda is right, manipulating collections using PHP is really very slow, always make the database server do the work using PHP only if there is absolutely no other option - large collections would absolutely grind to a halt with PHP. – Jonathan Hussey Feb 10 '15 at 17:10
  • 1
    @JonathanHussey I wholeheartily agree that you should let the database do the sorting gruntwork if possible. But since the question was without context and I see scenarios where this is a legit desire, I gave a solution. – Fabian Schmengler Feb 10 '15 at 17:17
3

A collection object has a standard array called _items which contains all the resulting models retrieved by the database query. The addItem() method exists in class Varien_Data_Collection and if you look at it all it does is push an entry onto the end of the array and is subject to exactly the same behaviour as array_push:

public function addItem(Varien_Object $item)
{
    $itemId = $this->_getItemId($item);

    if (!is_null($itemId)) {
        if (isset($this->_items[$itemId])) {
            throw new Exception('Item ('.get_class($item).') with the same id "'.$item->getId().'" already exist');
        }
        $this->_items[$itemId] = $item;
    } else {
        $this->_addItem($item);
    }
    return $this;
}

And the _addItem() method is as follows:

protected function _addItem($item)
{
    $this->_items[] = $item;
    return $this;
}

Thus all that happens when calling the addItem() method is that a new entry is added to the end of the array using the id of the item you are adding if it exists as the key, otherwise using the next available auto increment key value (by not defining a key).

If you need to have the items ordered in a specific way, you should instead ensure your database query orders the results correctly, or pull all of the item from the collection object using getItems() and then order then using PHP. By FAR the most efficient is to order them correctly in your database query, manipulating the results once pulled from the database via PHP is countless times slower.

1

I do get any proper solution.Below solution may work:

Create new varien collection object:

 $collection- = new Varien_Data_Collection();


 $product = Mage::getModel('catalog/product')->load(15879);

Assign the product add first element:

 $collection->addItem($product);

using loop assign rest of product:

foreach($productCollection as $eachProd):
    $collection->addItem($eachProd); 
    endforeach;

$productCollection=$collection;
  • 1
    This method does not exist. There is only getFirstItem() – Fabian Schmengler Feb 10 '15 at 16:54
  • Although ok for small collections, iterating over an entire production collection using PHP is always going to be slow... – Jonathan Hussey Feb 10 '15 at 17:13
  • yes. i am agree with you 100% – Amit Bera Feb 10 '15 at 17:16
1

I had the same task to add element to the collection as the first element.Below is the code i implemented and works perfectly for me.

$product = Mage::Model('catalog/product')->load(product_id);// product/element to add as first element

$collection = Mage::Model('catalog/product')->getCollection();

$collection->addAttributeToSelect.... // general code to select attributes

$collection->addAttributeToFilter.... // general code to apply filter

if($product && $product->getId()){ **// product to add as first element**

   $collection->addItem($product); // added product to collection

   $expr = new Zend_Db_Expr("`e`.`entity_id` = {$product->getId()} desc"); 

   $collection->getSelect()->order($expr); **// $product will be order by     DESC to set as first element**  

}

$collection->addAttributeToSort( 'price', 'asc'); // this sorting will apply on remaining elements in the collection.

Explanation

In the above code, $product is needed to add as first element, created a product collection $collection, added $product to that collection.

Using $collection->getSelect()->order($expr); forcefully sort position of $product to set as first position and rest of the elements will be order by $collection->addAttributeToSort( 'price', 'asc');

Note - It is must to call $collection->getSelect()->order($expr); before any sort by code.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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