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_Collectionclass, 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
_totalRecordsproperty, whengetSize()uses it?Lazy Loading
The reason for these "weird" behaviours is, that
Varien_Data_Collectionbasically 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_Collectionimplements theIteratorAggregateandCountableinterfaces. 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
foreachor the PHP functioncount()on the collection, theload()method automatically will be called first.Now, for a default
Varien_Data_Collectionnothing 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_totalRecordsto the SQLcountof 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_Collectionin, 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: