1 / 57

Making Magento flying like a rocket! A set of valuable tips for developers

Making Magento flying like a rocket! A set of valuable tips for developers. Ivan Chepurnyi. Co Owner / Trainer EcomDev BV. About me. Technical Consultant, Co-owner at EcomDev B.V. Was one of the first five developers in original Magento core team

mosborne
Download Presentation

Making Magento flying like a rocket! A set of valuable tips for developers

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Making Magento flying like a rocket!A set of valuable tips for developers Ivan Chepurnyi Co Owner / Trainer EcomDev BV

  2. About me • Technical Consultant, Co-owner at EcomDev B.V. • Was one of the first five developers in original Magento core team • Providing trainings and coaching Magento Developers in Europe • Main areas of my expertise: • System Architecture • Performance Optimization • Test Driven Development • Complex Customizations Developers Paradise

  3. Lecture Overview • Common Developer Mistakes • Tips for regular Magento Stores • Dealing with high loaded projects Developers Paradise

  4. Mistake #0 Developing a store without turning on flat version of products and categories Developers Paradise

  5. Mistake #1 Using of model load in the template to access property that is not available in the collection Developers Paradise

  6. Mistake #1 public getMySuperProductAttribute($product) { $myProduct = Mage::getModel(‘catalog/product’)->load($product->getId()); return $myProduct->getMySuperProductAttribute(); } Some Helper Developers Paradise

  7. Mistake #1 <p class=“my-attribute”> <?php echo $this->helper(‘my_helper’) ->getMySuperProductAttribute($product);?> </p> Product List Template Developers Paradise

  8. Mistake #1 • Usually Affected Models • Product Model • Caused by need to access some custom values from product in lists • Category Model • Caused by need to retrieve some custom text attribute in menu or layered navigation • Stock Item • Need to access available stock qty in product listings Developers Paradise

  9. Solution for Product Listing // In case of new attribute $this->addAttribute(‘catalog_product’, ‘custom_attribute_code’, array( // …. your attribute options “used_in_product_listing” => true )); // In case of updating existing attribute $this->updateAttribute( ‘catalog_product’, ‘customer_attribute_code’, ‘used_in_product_listing’, 1 ); Your install script Developers Paradise

  10. Solution for Category Collection <frontend> <category> <collection> <your_custom_attribute_code /> </collection> </category> </frontend> Your config.xml file Developers Paradise

  11. Solution for Category Tree (Flat) <frontend> <events> <catalog_category_flat_loadnodes_before> <class>your_module/observer</class> <method>addCustomAttribute</method> </catalog_category_flat_loadnodes_before> </events> </frontend> In your config.xml Developers Paradise

  12. Solution for Category Tree (Flat) public function addCustomAttribute($observer) { $select = $observer->getSelect(); $attributes = array(‘attribute_code1’, ‘attribute_code2’, ‘attribute_code3’); Mage::getResourceSingleton(‘your_module/observer) ->addAttributesToSelect($select, $attributes); } In your Observer Developers Paradise

  13. Solution for Category Tree (Flat) public function addAttributesToSelect($select, $attributes) { $select->columns($attributes, ‘main_table’); } In your Observer Resource Developers Paradise

  14. Solution for Stock Item <frontend> <events> <catalog_product_collection_load_after> <class>your_module/observer</class> <method>addStockItemData</method> </catalog_product_collection_load_after> </events> </frontend> In your config.xml Developers Paradise

  15. Solution for Stock Item public function addStockItemData($observer) { $collection = $observer->getCollection(); Mage::getSingleton('cataloginventory/stock') ->addItemsToProducts($collection); } In your Observer Developers Paradise

  16. Mistake #2 Using of full collection load where it is not needed. Developers Paradise

  17. Mistake #2 public function isCategoryHasSaleableProducts($category) { $collection = Mage::getResourceModel('catalog/product_collection'); $collection ->setStoreId(Mage::app()->getStore()->getId()) ->addCategoryFilter($category); Mage::getSingleton('catalog/product_status’) ->addVisibleFilterToCollection($collection); Mage::getSingleton('catalog/product_visibility') ->addVisibleInCatalogFilterToCollection($collection); return $collection->getFirstItem()->isSaleable(); } Code in block Developers Paradise

  18. Solutions • Write a resource model for making query to find this data in stock and product status tables. • Write an indexer that will contain information about salable categories and add this as column to all categories select. Developers Paradise

  19. Summary • Always develop with flat version of catalog turned on • Never use model load on every collection item, there is always an option to add your custom attribute • Never use collection to check for something, they are created to load subset of the data for loading, not for checking. Developers Paradise

  20. If you write your code correctly… It is time to get your hands dirty in core optimizations! Developers Paradise

  21. Regular Store Bottlenecks • Overall Problems • Category Pages • Product Pages • Checkout Pages Developers Paradise

  22. Overall Problems Developers Paradise

  23. Problem #1 Non optimized layout handles usage Magento adds on every category and product page a layout handle with its entity id, so you have layout cache unique for each product or category! Developers Paradise

  24. Solution Add an observer to controller_action_layout_load_before,check loaded handles and remove items that match starts with CATEGORY_ and PRODUCT_, except PRODUCT_TYPE. Developers Paradise

  25. Solution public function optimizeLayout($observer) { $update = $observer->getLayout()->getUpdate(); foreach ($update->getHandles() as $handle) { if (strpos($handle, 'CATEGORY_') === 0 || (strpos($handle, 'PRODUCT_') === 0 && strpos($handle, 'PRODUCT_TYPE_') === false)) { $update->removeHandle($handle); } } } Example of Observer Developers Paradise

  26. Problem #2 No cache for CMS Blocks Magento doesn’t cache them by default. On average projects you have up to 10-20 CMS blocks that can be edited by customer on every page. Developers Paradise

  27. Solution Add an observer to core_block_abstract_to_html_before,and specify required cache information for the block, like cache key (that is a block identifier) and cache tags (Mage_Cms_Model_Block::CACHE_TAG). Developers Paradise

  28. Solution public function optimizeCmsBlocksCache($observer) { if ($block instanceofMage_Cms_Block_Widget_Block || $block instanceofMage_Cms_Block_Block) { $cacheKeyData = array( Mage_Cms_Model_Block::CACHE_TAG, $block->getBlockId(), Mage::app()->getStore()->getId() ); $block->setCacheKey(implode('_', $cacheKeyData)); $block->setCacheTags( array(Mage_Cms_Model_Block::CACHE_TAG) ); $block->setCacheLifetime(false); } } Example of Observer Developers Paradise

  29. Problem #3 Magento top menu Since CE 1.7 version of Magento, they moved menu to another block, but forget to add caching to it. So now each page load spends from 100-300mson building top menu! And the more categories you have, the more time is spent! Developers Paradise

  30. Solution • Add a cache tags via observer similar to optimization of CMS blocks. The difference only, that you will need to add category cache tag instead: Mage_Catalog_Model_Category::CACHE_TAG • Rewrite Mage_Page_Block_Html_Topmenu to add category id into the menu node class name. Yes, there is no event for this :( • Add JS on the page that will use current category id for highlighting top menu item. Developers Paradise

  31. Solution public function optimizeNavigationCache($observer) { if ($block instanceofMage_Page_Block_Html_Topmenu) { $cacheKeyData = array( Mage_Catalog_Model_Category::CACHE_TAG, 'NAVIGATION', Mage::app()->getStore()->getCode() ); $block->setCacheKey(implode('_', $cacheKeyData)); $block->setCacheTags( array(Mage_Catalog_Model_Category::CACHE_TAG) ); $block->setCacheLifetime(false); } } Example of Observer Developers Paradise

  32. Solution protected function _getMenuItemClasses(Varien_Data_Tree_Node $item) { $classes = parent::_getMenuItemClasses($item); // Will be something like category-node-#id $classes[] = $item->getId(); return $classes; } Overridden Block Method Developers Paradise

  33. Solution JS implementation is up to you. I believe you can do that yourself :) Developers Paradise

  34. Category Pages & Product Pages Developers Paradise

  35. Problem #1 Layered Navigation For each attribute you have in Magento and that is marked as filtrable, it will make a call to getAllOptions() of attribute source model. Even if there is no filter results for it, it will invoke attribute option collection load. For merchants who have a lot of attributes, it is a huge performance bottleneck. Developers Paradise

  36. Solution • Remove collection usage usage for each item by rewriting Mage_Eav_Model_Entity_Attribute_Source_Tableclass. • Implement a static method, that will load values for all attributes at once. If you will not use objects, it won’t take too much memory. • Override getAllOptions() method to use your static method. • You can find class by this url: http://bit.ly/mageoption Developers Paradise

  37. What it does? // This one will use collection getData() method // to retrieve array items instead of collection of objects. Mage::getResourceModel('eav/entity_attribute_option_collection') ->setPositionOrder('asc') ->setStoreFilter($storeId) ->getData(); On the first call to getAllOptions() it will preload all attribute values for current store in array format. Developers Paradise

  38. Problem #2 Dropdown Options on Product Page The problem is the same as with layered navigation, but not that much visible if you don’t have too much dropdown attributes to show. Product getOptionText() uses the same getAllOptions() method call. This one is automatically fixed by fixing previous one. Developers Paradise

  39. Problem #3 Configurable Products Magento is not using flat version of products for configurable products children retrieval. So every configurable product page is a bottleneck, especially for fashion retailers. Developers Paradise

  40. Solution • Rewrite the class with this long name: Mage_Catalog_Model_Resource_Product_Type_Configurable_Product_Collection • OverridenisEnabledFlat() method that should return the real information about flat availability. • Make sure that attributes, that your customer is using, are included into the flat version of catalog. Mark them as “used_in_product_listing”. Developers Paradise

  41. Solution public function isEnabledFlat() { return Mage_Catalog_Model_Resource_Product_Collection::isEnabledFlat(); } Overriden Collection Method Developers Paradise

  42. Checkout Pages Developers Paradise

  43. Problem #1 Catalog Price Rules Each time when Magento calls collectTotals() method on quote object, it walks though all items in the quote and invoked getFinalPrice() method on your product. This method dispatches catalog_product_get_final_price event,that is observed by Mage_CatalogRule module. Developers Paradise

  44. Solution • Rewrite Mage_CatalogRule_Model_Observer class and create a new method that will preload rule prices for quote. • Define $_preloadedPricesproperty to cache results per quote object. • Add an observer in configuration for sales_quote_collect_totals_before event, for original core class alias, but with the method you’ve created. Full observer class can be found at this url: http://bit.ly/magerule Developers Paradise

  45. Solution public function beforeCollectTotals(Varien_Event_Observer $observer) { // … Omited retrieval of product ids and customer group with website $cacheKey = spl_object_hash($quote); if (!isset($this->_preloadedPrices[$cacheKey])) { $this->_preloadedPrices[$cacheKey] = Mage::getResourceSingleton('catalogrule/rule') ->getRulePrices($date, $websiteId, $groupId, $productIds); } foreach ($this->_preloadedPrices[$cacheKey] as $productId => $price) { $key = implode('|', array($date, $websiteId, $groupId, $productId)); $this->_rulePrices[$key] = $price; } } Created method code Developers Paradise

  46. There are more issues… But it is too much for one presentation :) Let’s better talk about high loaded projects! Developers Paradise

  47. Are you using Varnish for your projects? Developers Paradise

  48. Varnish Issues Common issues caused by developers, when they use Varnish on the project • Developers usually hide poor code quality behind front cache server • Doing backend callbacks for functionality that can be fully done by modern browser in JavaScript. • Caching static files by Varnish Developers Paradise

  49. ESI include is an evil for Magento Only when the content for ESI include can be cached by Varnish. It doesn’t make to make ESI includes for shopping cart, compare and recently viewed reports. Developers Paradise

  50. AJAX Callbacks Issue AJAX call should be done only when needed. Do not perform a bunch of calls just because the data should be loaded from the server. It is always possible to decrease amount of calls by using your frontend skills. Developers Paradise

More Related