The document discusses Magento best practices for writing scalable and performant code. It provides examples of poorly written code that loads categories and products in loops, resulting in poor performance. The code is then refactored to load data more efficiently in one query instead of many sequential queries. Testing is also emphasized to safely refactor code and prevent regressions. The refactored code is 10x faster according to performance tests. In summary, the key messages are to avoid unnecessary database queries in loops, leverage Magento's collection functionality, and use testing to safely optimize code performance.
3. #m
agebp
3/16#magebp rules
● Use the best available tools
● Use Magento runtime
● Use alternatives to Mage::log()
● Extend native autoloading
● Write effective and solid MVC components
● Write scalable code
● Never touch the core
4. #m
agebp
4/16#magebp example
● Use the best available tools
● Use Magento runtime
● Use alternatives to Mage::log()
● Extend native autoloading
● Write effective and solid MVC components
● Write scalable code
● Never touch the core
5. #m
agebp
5/16#magebp example
● Use the best tools & habits
– to test code
– to profile code
● Use Magento runtime
– to validate code easily and rapidly
● Write scalable code
– to avoid performance traps
6. #m
agebp
6/16Based on a real story...
What you will be shown is real code
that run on production.
This code was written to display
a category navigation menu.
No animals were harmed in the making of
this code; a website was.
7. #m
agebp
7/16
First chunk
seek for category subcategories
$rootCatID = Mage::app()>getStore()>getRootCategoryId();
$cat = Mage::getModel('catalog/category')>load($rootCatID);
$subCats = $cat>getChildren();
foreach (explode(',', $subCats) as $subCatid) {
$category = Mage::getModel('catalog/category')
>load($subCatid);
$subcategories = $category>getChildren();
}
foreach (explode(',', $subcategories) as $subCategoryid) {
$subcategory = Mage::getModel('catalog/category')
>load($subCategoryid);
// Second chunk...
}
8. #m
agebp
8/16
First chunk
seek for category subcategories
$rootCatID = Mage::app()>getStore()>getRootCategoryId();
$cat = Mage::getModel('catalog/category')>load($rootCatID);
$subCats = $cat>getChildren();
foreach (explode(',', $subCats) as $subCatid) {
$category = Mage::getModel('catalog/category')
>load($subCatid);
$subcategories = $category>getChildren();
}
foreach (explode(',', $subcategories) as $subCategoryid) {
$subcategory = Mage::getModel('catalog/category')
>load($subCategoryid);
// Second chunk...
}
Assuming there is only one sub
category will break menu as soon as
a new one is added: and it happened!
Loading in loops
doesn't scale
9. #m
agebp
9/16
Second chunk
seek for product categories
foreach (explode(',', $subcategories) as $subCategoryid) {
$subcategory = Mage::getModel('catalog/category')>load($subCategoryid);
$category = Mage::getModel('catalog/category')
>load($subcategory>getId());
$subcatProducts = Mage::getModel('catalog/product')
>getCollection()
>addCategoryFilter($subcategory)>load();
foreach($subcatProducts as $product) {
foreach($product>getCategoryIds() as $categoryId) {
$myCategory = Mage::getModel('catalog/category')>load($categoryId);
$catUrl = $myCategory>getUrl();
$prodUrlKey = end($explode("/", $product>getProductUrl()));
}
}
}
10. #m
agebp
10/16
Second chunk
seek for product categories
foreach (explode(',', $subcategories) as $subCategoryid) {
$subcategory = Mage::getModel('catalog/category')>load($subCategoryid);
$category = Mage::getModel('catalog/category')
>load($subcategory>getId());
$subcatProducts = Mage::getModel('catalog/product')
>getCollection()
>addCategoryFilter($subcategory)>load();
foreach($subcatProducts as $product) {
foreach($product>getCategoryIds() as $categoryId) {
$myCategory = Mage::getModel('catalog/category')>load($categoryId);
$catUrl = $myCategory>getUrl();
$prodUrlKey = end($explode("/", $product>getProductUrl()));
}
}
}
$category is not used and
adds another load in a loop!
WHY?
11. #m
agebp
11/16
There is a name for this...
Code
Running
Ashamedly on
Production
Sadly enough a customer paid for that:
it's unethical other than unprofessional.
This is not technical debt.
This is building with sand.
12. #m
agebp
12/16
Refactoring first chunk
seek for category subcategories
$configuredCatId =
Mage::getStoreConfig('my/own/path');
$category = Mage::getModel('catalog/category')
>getCollection()
>addIdFilter($configuredCatId)
>setPageSize(1)
>getFirstItem();
$subcategories = $category>getChildrenCategories();
foreach ($subcategories as $subcategory) {
// Second chunk...
}
13. #m
agebp
13/16
Refactoring second chunk
seek for product categories
foreach ($subcategories as $subcategory) {
$subcatProducts = $subcategory>getProductCollection();
foreach($subcatProducts as $product) {
$productCategories = $product>getCategoryCollection();
foreach($productCategories as $myCategory) {
$catUrl = $myCategory>getUrl();
$prodUrl = $product>getProductUrl();
$prodUrlKey = substr(strrchr($prodUrl, '/'), 1);
}
}
}
15. #m
agebp
15/16
Refactoring
(using tests)
● Testing gives us the confidence to change
code without breaking functionality
● Testing doesn't require complex tools
– start from manual tests
– automate to scale
– start from common tools: diff
– use more sophisticated tools when needed