Magento got popularity among developers long time ago. There are thousands of out-of-the-box modules, themes and language packs available for Magento. About one-thirds of magento store owners has 30 to 50 custom extensions installed.
To be honest, not all of these extensions are optimized for high load, multi stores or, even, multilingual stores. That`s why I would like to provide a list of things you should do as Magento Developer.
Top 8 Magento coding issues
- Use iterators
The common way working with collections is:
$collection = Mage::getModel(‘sales/order’)->getCollection();
foreach($collection as $order) {
…
}
But the problem is collection is an array of objects. The more orders we have and the more attributes we’ll add to the collection, the bigger resulting array will be. Even more, method load() called right before looping and each attribute added to collection select will be loaded for every item in the collection. It causes memory leaks.
Solution is simple and already implemented in Magento Core – Mage_Core_Model_Resource_Iterator. Example of usage:
public function updateOrderStatus() {
$collection = Mage::getModel(‘sales/order’)->getCollection();
Mage::getSingleton(‘core/resource_iterator’)->walk($collection>getSelect(), array(array($this, ‘orderCallback’)));
}
public function orderCallback($args) {
$order = Mage::getModel(‘sales/order);
$order->setData($args[‘row’]);
$order->setOrderAttribute($value);
//save only one attribute instead of whole order
$order->getResource()->saveAttribute($order, ‘order_attribute’);
}
- Don’t select all attributes
Because of reasons described in first hint, avoid using $collection->addAttributeToSelect(‘*’) since you really need this. You can list all the needed attributes just in one row: $collection->addAttributeToSelect(array(‘attr_1’,’attr_2’,’attr_3’,’attr_4’));
- Avoid loading collections
Especially in loops. Often you could meet something like this:
$products = $this->getLoadedProductCollection();
…
foreach($products as $product) {
$product = Mage::getModel(‘catalog/product’)->load($product->getId());
..
$someCustomAttribute = $product->getSomeCustomAttribute();
}
OMG! Loading whole list of dozens or, even, hundreds of attributes in the loop to get just one value? Nope. That’s the point.
…
foreach($products as $product) {
$_resource = $product->getResource();
$optionValue = $_resource->getAttributeRawValue($product->getId(), some_custom_attribute’, Mage::app()->getStore());
}
- Debug collections
Sometimes there is a need to debug collection’s sql query. You can print it like here:
echo $collection->getSelect()->__toString();
getSelect() is a powerful method because it lets you switch between Magento SQL API and Zend Framework’s. So after describing magento’s getCollection() you can call getSelect() and add ZF’s where().
- Use getSize() method
There are 2 methods for calculating collection length: getSize() and count(). Let’s take a look on them:
public function getSize()
{
if (is_null($this->_totalRecords)) {
$sql = $this->getSelectCountSql();
$this->_totalRecords = $this->getConnection()->fetchOne($sql, $this->_bindParams);
}
return intval($this->_totalRecords);
}
public function count()
{
$this->load();
return count($this->_items);
}
The main difference is getSize() method doesn’t load whole data from the database table. This method is significantly faster than count() collection method or count php function. Use it 🙂
- Limitate your query results
It’s often needed to get only one item from collection by calling $collection->getFirstItem(). If no restrictions are applied before it is called, it will load all the items of the collection. Always remember to apply the limitation in the case where the result of the query is a set of more than one item, in order to improve code performance and scalability:
$collection->setOrder(‘some_attribute’,’some_order’)
->setPageSize(1);
$collection->getFirstItem();
Also you can use setCurPage() method to set offset.
- Don’t use fetchAll()
Using of this method on large result sets may run out of memory just because you’re trying to grab all the data at once. But in the code below each result row is fetched separately using fetch() method to reduce resource consumption:
$query = $this->_getReadAdapter()->query($select);
while ($row = $query->fetch()) {
…
}
The database server will execute only one query and the database buffer will be used for retrieving records one by one.