I'm running on Magento 1.4, but also have verified the problem in 1.7.
Working with an instance of Varien_Data_Collection
provides the use of Varien_Data_Collection::removeItemByKey
. In my case, I'm removing items from the collection, and later trying to get the updated size of that collection, like so:
$body=$this->getTable()->getBody();
echo $body->getHeight(); // Outputs 25
$body->deleteRow(1);
echo $body->getHeight(); // Still outputs 25
...
My_Model_Body_Class extends Mage_Core_Model_Abstract {
/* @var $_rows Varien_Data_Collection */
protected $_rows;
public function deleteRow($index) {
$this->_rows->removeItemByKey($index);
return $this;
}
public function getHeight() {
return $this->_rows->getSize();
}
}
...
Code limited for brevity.
So if you call my deleteRow
method, the item will in fact be removed from the collection, but subsequent calls to get the size of that collection will always return the original count. Therefore, if I have 25 items in the collection, and remove 1, then a call to getSize
on the collection returns 25.
I traced this back to the parent class, in Varien_Data_Collection::getSize
:
/**
* Retrieve collection all items count
*
* @return int
*/
public function getSize()
{
$this->load();
if (is_null($this->_totalRecords)) {
$this->_totalRecords = count($this->getItems());
}
return intval($this->_totalRecords);
}
We see that the count hinges on the NULL status of the _totalRecords
property. So it looks like a bug in core code. Am I reading this correctly? Should I just rely on a call to count
on the items?
Whether to interpret said behaviour as bug or feature, lies in the eyes of the beholder.
This behaviour is not 1.4 specific, btw; it works the same way up to the current CE version (1.8.1).
Most people for sure expect a method named
getSize()
to always return the current size, so they may call it a bug, perhaps.But if you take a closer look at the
Varien_Data_Collection
class, you'll notice, thatgetSize()
is not the only method that looks somewhat.. "weird".Take the
addItem()
andremoveItemByKey()
methods, for example.Why don't they increment/decrement the
_totalRecords
property, whengetSize()
uses it?Lazy Loading
The reason for these "weird" behaviours is, that
Varien_Data_Collection
basically is designed for the usage of the Lazy Loading pattern. That means, it allows to delay loading of the collection, until the data is really needed.To accomplish this,
Varien_Data_Collection
implements theIteratorAggregate
andCountable
interfaces. Their implementation points are thegetIterator()
andcount()
methods:As you can see, both of these methods call
load()
first.The result of this is, that whenever you use
foreach
or the PHP functioncount()
on the collection, theload()
method automatically will be called first.Now, for a default
Varien_Data_Collection
nothing special will happen, whenload()
is called, becauseload()
only callsloadData()
andloadData()
only returns$this
.But when it comes to its heavily used child class
Varien_Data_Collection_Db
, then the call toload()
will result in loading the collection and setting_totalRecords
to the SQLcount
of loaded records.For performance reasons the collection usually will be loaded once only (if it hasn't been loaded yet).
So you see, depending on which context you use
Varien_Data_Collection
in, it perfectly makes sense, whygetSize()
behaves this way.If your collection is not bound to complex or slow I/O, I'd say: yes.
You can simply use:
Or even this way: