Pitfalls to avoid during Magento development

Posted on 10 May, 2016

In an e-Commerce implementation, system performance is a major concern where customers demand instant fulfillment. Delayed loading time and memory consumption beyond the certain limit can drag your customers to rival vendors. The research statistics has shown a conclusive link between website performance and customer satisfaction; every second that customers have to wait longer than 3 seconds reduces their satisfaction by 16%.

Besides losing out a sale, indirectly you’ve also lost the money you spent marketing your company!

The noticeable scalability degradation can be exposed to some extent after debugging how often the code is executed and on what amounts of data by the developers.

One bad programmer can easily create two new job opportunities a year!

Well, programmers are not great by birth; they just implant great habits. Of Course, no company welcomes such a "bad" programmer who adversely affects its revenue and goodwill.

Among a lot of pitfalls that developers might get into while building a Magento store, I have spotlight those points that Magento newbies can use to get rid of any bottlenecks that might exist.

1. Modifying core files

Why hit the hammer on your own leg?! This worst habit can cause all the harm and result into hanging up of the particular core module. Just imagine what happens when you have variety of customization in the same file, and you need to disable one of them for the time being?..or you have to upgrade Magento..?

Magento provides a list of events that are dispatched at different times. Following Observer pattern, you can allow multiple custom modules to be plugged in and out without modifying the core code.

Another way could be using <rewrite> to extend the core file. But, there’s is no other way to rewrite the same core file more than once without creating extends chain- and the chain could result in errors when the middle module is deactivated. By using event observers multiple modules can exist at the same time without conflicting as many modules can observe the same event.

2. Excess memory usage-fetching large result set

Using the database adapter method fetchAll() to fetch large result sets will cause a heavy demand on system and possibly network resources:

1
2
3
4
5
 $rowSet = $this->_getReadAdapter()->fetchAll($select);
            foreach ($rowSet as $row) {
                //process row
            }
                                

Better, keep an approach to fetch each database row separately using the fetch() method to reduce resource consumption:

1
2
3
4
5
$query = $this->_getReadAdapter()->query($select);
            while ($row = $query->fetch()) {
                //process row
           
                                

3. Implementing count on each iteration

Count() is undoubtedly a fast function but, if used in the looping condition, it calculates the size of the array on each iteration of the loop. If the array contains a lot of items, this will result in longer execution time and more memory needs to be allocated.

1
2
3
4
for ($product = 0; $product < count($rows); $product++) {
        //process product
                                

 Counting size should be done outside the loop, not on each iteration.

1
2
3
4
5
$number = count($rows);
for ($product = 0; $product < $number; $product++) {
        //process product
                                

 When developers need to retrieve the number of items in a particular collection, it's far efficient to use $collection->getSize() instead of $collection->count() or count($collection) because this way, all of the items will be loaded from the database and then iterated.

COUNT TYPE TIME MEMORY
count() ~0.966 sec  ~25 MB
getSize()  ~0.081 sec  ~1.8 MB

4. Inefficient data set utilization

Collections are often used to retrieve only one item by calling the $collection->getFirstItem() method or returning the first item on the first iteration of the loop. A common mistake here is not applying a limitation on the collection’s query results. This will load all the items from collections and then return first one.

1
2
3
4
5
public function getSelectedItem() {
           $collection = Mage::getResourceModel('mymodule/custom_collection');
           return $collection->getFirstItem();
    }
                                

When you are sure you need only the first item, avoid loading rest of the items from database. As a better approach, Use the $collection->setPageSize() and $collection->setCurPage() methods to specify the limitation and offset respectively, or modify the collection query directly: $collection->getSelect()->limit()

1
2
3
4
5
6
public function getSelectedItem() {
   $collection = Mage::getResourceModel('mymodule/custom_collection')->setPageSize(1);
         return $collection->getFirstItem();
    }
                                

5. SQL queries inside a loop

Running SQL query is very resource expensive operation and doing it in a loop tends to make it even worse. Loading an entity attribute value (EAV) model requires several heavy queries to execute. As the number of executed queries is multiplied by the number of products, we get extremely inefficient and slow code.

1
2
3
4
5
 foreach ($this->getProductIds() as $productId){
        $product = Mage::getModel('catalog/product')->load($productId);
        //$this->processProduct($product);
   
                                

An important benefit of Magento data collection is that it provides control over the loaded data fields. While loading an EAV model will request multiple tables and select all the attributes of the entity.

1
2
3
4
5
6
7
$collection = Mage:getResourceModel('catalog/product_collection')
            ->addFieldsToFilter('entity_id', array($this->getProductIds()))
            ->addAttributeToSelect(array('name'));
              foreach ($collection as $product){
                  //$this->processProduct($product);
              }
                                
LOAD TYPE # QUERIES # JOINS TIME MEMORY
Inside loop 875 1072 ~4 sec ~7 MB
Outside loop 8 1 ~0.4 sec ~2.5 MB

6. Loading the same model multiple times

Developers need to consider that model load operation is not internally cached,and each time the load() method is called, several queries are dispatched against the database. Loading the same model several times in turn invites scalability issues:

1
2
3
4
$name = Mage::getModel(‘catalog/product’)->load($productId)->getName();
$sku  = Mage::getModel(‘catalog/product’)->load($productId)->getSku();
$price = Mage::getModel(‘catalog/product’)->load($productId)->getPrice();
                                

Each model should be loaded only once (and that too, only if needed) to optimize performance:

1
2
3
4
5
$product = Mage::getModel(‘catalog/product’)->load($productId);
$name    = $product->getName();
$sku     = $product->getSku();
$price    = $product->getPrice();
                                

In some cases, it is not even necessary to load the model because you don’t need to work with model entity itself. Just get your work done by loading only attributes which are needed.

1
2
Mage::getResourceModel('catalog/product')->getAttributeRawValue($productId, 'attribute_code', $storeId); 
                                

Or

1
2
$productId = Mage::getModel(‘catalog/product’)->getIdBySku($sku); 
                                

7. Iterating collections

Fetching item on each iteration will prove as time and memory optimizer rather than fetch entire collection on each iteration.

1
2
3
4
5
$products = Mage::getModel(‘catalog/product’)->getCollection();
foreach($products as $product){
    //do something
                                

A scalable approach:

1
2
3
4
5
$products = Mage::getModel(‘catalog/product’)->getCollection();
foreach($product->getData() as $productData){
    //do something
                                

Alternative preferable approach:

1
2
3
4
5
$products = Mage::getModel(‘catalog/product’)->getCollection();
    while($object=$products->fetchItem()){
        //do something
   
                                
Fetch collection Items Time Memory
Standard ~1.913 sec ~25 MB
getData() ~0.017 sec ~1.3 MB
fetchItem() ~0.026 sec ~1.1 MB

8. save() unnecessarily on entire product

If you have a batch operation for thousands of products, categories, customers or any other entity that uses EAV-structure, speed and memory usage are important factors to consider. Especially when you only need to change one or two attributes of a product or category there’s no point to save() the entire product. Things just get too heavy too soon.

$product=Mage::getModel('catalog/product')->load(<product_id>);
            $product->setName('Trendy Black purse')->save();  
								

Instead, saving only the specific attribute will execute in less time and keep more memory free.

  $product=Mage::getModel('catalog/product')->load(<product_id>);
            $product->setName('Trendy Black purse')
            ->getResource()
            ->saveAttribute($product,'name');  
								
Save method Time Memory
save() ~1.5 sec ~5 MB
saveAttribute() ~0.02 sec a few KB

9. Misleading routers

Routers can also misroute your module..! Firstly, if your module doesn't need admin router then don’t leave an admin router there, it will lead to some random issue like auto redirect HTTP to HTTPS on your module, or redirect to frontend page not found. Secondly, a preferable approach to avoid routers conflict is not to keep the same name for admin and frontend routers.

10. Not checking module's status

Magento serves us with a very handy facility of disabling module's output from admin panel without going deep into the directory structure.
Do keep in mind that your custom extension also has to be a part of this core functionality.
Never ever miss out to check the module's status at every point where output is rendered on the front end.
The module should not render any functionality on frontend when it is disabled from
System -> Configuration -> Advanced tab.

11. Not using prefix

Well, it's not a bad habit; but it is a precaution to avoid any kind of namespace conflicts. Especially if you are not wary about the core naming convention, there are no worries for conflictions if you add your (more likely,company)prefix before it. Likewise, adding the same prefix to database/table name, layout and template folders/files and CSS class will make the naming convention for your module distinct.

12. Skipping out try-catch block

Since ever Magento presented the errors on the front-end for everyone to see and it was good for the developers but once the site has gone live that couldn’t be an option anymore.
Ideally, using error handling mechanism is definitely going to prevent your module from throwing an unexpected error and thereby ceasing the module's process. How exceptions are used is going to vary project by project, team by team.
Every time you can’t predict all possible error so it is best to put a try/catch in important function/handler in production.

13. Overlooking security patches

Magento announces security patches as and when any vulnerability is reported. They are the base that strengthens your store's security against various hacking techniques. Always install latest security patches from Magento. Ignoring security patches can result in serious security threats. You can download latest security patches from Magento official website.

14. Copying entire theme to new folder

If you are planning out to make your custom theme, don't follow the same traditional way of copying the default theme folder to your custom folder.

Magento has a great theme "fallback system". It allows you to edit your theme without duplicating unchanged files.
Basically, what Magento does is this:

It looks for the specific file in your theme folder

  • if found – outputs it
  • if not found, it searches for “default” theme in the custom design package
  • if “default” theme exists in a custom package, it searches for the file there
  • if “default” theme does not exist in a custom package or file is not found in “default theme”, it fallbacks to “base” package and searches  for file in “default” theme
  • if the file is not found, Magento outputs rendering error

Copy only the files you need to re-design from the “app/design/frontend/default/default/” directory to the “app/design/frontend/default/your_theme” directory. Do the same thing with the “skin/frontend/default/default” directory. All that left is to apply the new theme from the Design Settings (System > Configuration > Design).

This was just a glance into the ocean of Magento development. We'll keep you updated as we experience other loopholes, but you don't miss to comment any Magento bad practice that you know or have overcome. We would love to hear from you. Have fun Magentoing..!


Bhavika Khetani , eCommerce Engineer
Magento Blog

Post Your Review

X

Your Review has been posted

1 Comment(s)

iFlair

Posted on 19 November, 2016

The blog is really nice, Well Magento coding based on PHP framework, so good knowledge of PHP web technology can work on Magento. Magento is multi-layered development task. A developer should avoid the navigation problem. and Website should be SEO friendly, Search engine can crawl easily.