12

I have one collection in a method where I want to perform two different operations on this collection. So, I want two separate copies of same collection, and then assign one of the two collections to original collection again and return it.

To make this simple, suppose I have an object collection called $collection.

Now, I am trying it with PHP cloning as I don't know if there is any Magento collection cloning inbuilt or not.

$coll1 = clone $collection;
$coll2 = clone $collection;

Now I am trying to perform different operations on these two separate clones of the original collection, something like.

$coll1->getSelect()->where('some where condition');
$coll2->getSelect()->where('some different where condition');
if($coll1->count() == 0) {
    $collection = $coll2;
} else {
    $collection = $coll1;
}

But the weird thing is, both these cloned collections have both the where conditions assigned! $coll1 condition is applied to $coll2 along with $coll2's condition, and vice versa.

Does anyone know how to achieve this?

Thanks!

2 Answers 2

15

Use of the PHP clone operator, where deep cloning is desired, requires classes which store objects on properties implement a __clone method to copy the objects. If they don't define it, the properties on both instances will reference the same object.

Magento does not implement __clone on it's collection abstracts, and therefore does not support deep cloning as you want it to.

My suggestion is to look for other ways to accomplish what you are wanting to do, as cloning a collection could be pretty expensive.

The example you gave (for instance) could be changed to clone the select, modify it to select a count of the records it would have loaded and then based on that result modify the collection. This would also perform better since you would not be loading a collection and counting it just to determine which one to use.

EDIT: The following demonstrates how to grab a count without loading or actually modifying the collection.

$collection = Mage::getModel(...)->getCollection();

$count = $collection->getSelectCountSql();
$count->where('some where condition');
if ($count->query()->fetchColumn() == 0) {
    ...
} else {
    ...
}
5
  • Just a small detail: The information of the where is saved in $collection->getSelect() and not in the collection itself. May 23, 2013 at 6:43
  • Thanks for answer. But after applying where condition only I want to know the collection count, and based on that count only I want to decide whether to use different where condition or not. Can you post some code snippet to better understand how to do it?
    – MagExt
    May 23, 2013 at 9:09
  • Updated answer with a code example. As @FabianBlechschmidt pointed out, the where is in the select, which is where your specific trouble comes from since it's not cloned when the collection object is cloned and both end up referring to the same select object instance.
    – davidalger
    May 23, 2013 at 13:31
  • Thanks for update. I have not tried this as I already got the solution something like that only.
    – MagExt
    May 23, 2013 at 15:43
  • Actually if there is an issue cloning the collection, serialize and unserialize can be helpful in the process. There are also other alternatives to clone in PHP that are pretty decent. But all in all David is correct... basically when you're cloning the object you're cloning the pointers to the nested Objects attached to it as well, although his answer didn't properly state the underlying issue.
    – mprototype
    Dec 24, 2013 at 21:03
1

To expand on @davidalger's answer, you can reset the select if you want to do a different operation than a count - like so:

$select= $collection->getSelectCountSql()->reset();

$select
    ->from('newsletter_subscriber', array('some_column'))
    ->distinct();

Be careful though, this could have detrimental effects later in the process since this modifies the collection.

A better way would be to clone the select somehow, but a shallow copy wouldn't cut it since the object contains complex types (Varien_Db_Select nor Zend_Db_Select have a __clone method).

One way to get around this is to save the select data, modify it, run your query, then put back the original select data.

See here for an example: https://ka.lpe.sh/2013/05/23/magento-clone-collection-how-to-clone-collection-in-magento/

Your Answer

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

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