REAL USE CASES OF PERFORMANCE OPTIMISATION
MAGENTO 2
by Max Pronko
ABOUT ME
➤ former Magento core member
➤ CTO at TheIrishStore and
GiftsDirect
➤ Founder of Pronko Consulting
➤ 7+ years of Magento Experience
➤ Magento 2 Blog
AGENDA
➤ Performance Optimisation
➤ eCommerce Website Data
➤ Real Improvements
➤ Varnish
➤ Q&A
www.maxpronko.com
PERFORMANCE
www.maxpronko.com
PERFORMANCE
Refers to the speed in which pages are
downloaded and displayed on the user’s browser
www.maxpronko.com
IS PERFORMANCE IMPORTANT?
➤ Customer Experience
➤ Conversion Rate
➤ Organic Search
www.maxpronko.com
PERFORMANCE OPTIMISATION
www.maxpronko.com Image: Fast and Furious
STEPS OF PERFORMANCE TUNING
➤ Assess the problem
➤ Measure performance
➤ Identify part of the system critical for performance
improvement
➤ Modify part of system to remove the bottleneck
➤ Measure the performance after modification
➤ If better, adopt it or revert
https://en.wikipedia.org/wiki/Performance_tuningwww.maxpronko.com
ECOMMERCE WEBSITE PROFILE
➤ Seasonal business
➤ 4k products
➤ 500 Configurable Products
➤ 2k Configurable Variations
➤ 1.5k Simple Products
➤ 500 Categories
➤ Average Order Value: $100
➤ 2-3 products per Order
www.maxpronko.com
BEFORE VS AFTER OPTIMISATION
Before
Category Page
Product Page
Shopping Cart
Checkout Index
0 500 1000 1500 2000
Magento 2 Application
www.maxpronko.com
BEFORE VS AFTER OPTIMISATION
After
Category Page
Product Page
Shopping Cart
Checkout Index
0 500 1000 1500 2000
Magento 2 Application
www.maxpronko.com
MAGENTO 2 APPLICATION
www.maxpronko.com
MAGENTO 2 APPLICATION
Optimisations
www.maxpronko.com
MAGENTO 2 APPLICATION
914 318ms ms
Before After
www.maxpronko.com
LET’S START
Image: mezzotint.dewww.maxpronko.com
DISABLE UNUSED
CORE MODULES
www.maxpronko.com
FOR EXISTING MAGENTO 2 INSTALLATION
> bin/magento module:disable Magento_Downloadable
> bin/magento setup:upgrade
> bin/magento setup:di:compile
> bin/magento setup:static-content:deploy
www.maxpronko.com
COMPOSER.JSON
{
"repositories": [

{

"type": "composer",

"url": "https://repo.magento.com/"

}

],
"require": {
"magento/composer": "~1.0.0",

"magento/framework": "100.1.1",

"magento/language-en_us": "100.1.0",

"magento/magento-composer-installer": "*",

"magento/magento2-base": “2.1.1”,
...
},
}
www.maxpronko.com
WHAT TO DISABLE?
➤ Extra Languages
➤ Offline shipping and payments
➤ Backup, Captcha, Persistent, Rss
➤ Msrp, Send Friend, Weee
➤ Multishipping, Checkout Agreements
➤ Product types?
➤ More… depends on project
www.maxpronko.com
DISABLE UNUSED FEATURES
www.maxpronko.com
HOW TO DISABLE
FEATURES?
www.maxpronko.com
DISABLING FEATURES
➤ Observer
➤ Plugin
➤ Layout
www.maxpronko.com
MAGENTO REPORTS
➤ vendor/magento/module-reports/etc/frontend/events.xml
<config>

<event name="catalog_product_compare_remove_product">

<observer name="reports" instance="MagentoReportsObserverCatalogProductCompareClearObserver" />

</event>

<event name="customer_login">

<observer name="reports" instance="MagentoReportsObserverCustomerLoginObserver" />

</event>

<event name="customer_logout">

<observer name="reports" instance="MagentoReportsObserverCustomerLogoutObserver" />

</event>

<event name="catalog_controller_product_view">

<observer name="reports" instance="MagentoReportsObserverCatalogProductViewObserver" />

</event>

<event name="sendfriend_product">

<observer name="reports" instance="MagentoReportsObserverSendfriendProductObserver" />

</event>

<event name="catalog_product_compare_add_product">

<observer name="reports" instance="MagentoReportsObserverCatalogProductCompareAddProductObserver" />

</event>

<event name="catalog_product_compare_item_collection_clear">

<observer name="reports" instance="MagentoReportsObserverCatalogProductCompareClearObserver" />

</event>

<event name="sales_quote_item_save_before">

<observer name="reports" instance="MagentoReportsObserverCheckoutCartAddProductObserver" />

</event>

<event name="wishlist_add_product">

<observer name="reports" instance="MagentoReportsObserverWishlistAddProductObserver" />

</event>

<event name="wishlist_share">

<observer name="reports" instance="MagentoReportsObserverWishlistShareObserver" />

</event>

</config>

www.maxpronko.com
DISABLE OBSERVERS
➤ app/code/Pronko/Performance/etc/frontend/events.xml


<config>

<event name="catalog_product_compare_remove_product">

<observer name="reports" disabled="true" />

</event>

<event name="catalog_controller_product_view">

<observer name="reports" disabled="true" />

</event>

<event name="sendfriend_product">

<observer name="reports" disabled="true" />

</event>

<event name="catalog_product_compare_add_product">

<observer name="reports" disabled="true" />

</event>

<event name="catalog_product_compare_item_collection_clear">

<observer name="reports" disabled="true" />

</event>

<event name="wishlist_add_product">

<observer name="reports" disabled="true" />

</event>

<event name="wishlist_share">

<observer name="reports" disabled="true" />

</event>

</config>

www.maxpronko.com
DISABLE VIA PLUGIN
➤ app/code/Pronko/Performance/etc/di.xml


<config>

<type name="MagentoBackendModelMenu">

<arguments>

<argument name="logger" xsi:type="object">PsrLogNullLogger</argument>

</arguments>

</type>

<type name="MagentoFrameworkCacheInvalidateLogger">

<plugin name="DisableCacheInvalidateLog" type="PronkoPerformancePluginInvalidateLoggerPlugin" />

</type>

</config>
www.maxpronko.com
REMOVE BLOCKS FROM LAYOUT
➤ Debug Hints
➤ Find non-used Blocks
➤ Remove it
www.maxpronko.com
REMOVE BLOCKS FROM LAYOUT
➤ default.xml


<referenceBlock name="copyright" remove="true" />

<referenceBlock name="store_switcher" remove="true" />

<referenceBlock name="store_language" remove="true" />

<referenceBlock name="store.settings.language" remove="true" />

<referenceBlock name="translate" remove="true" />
➤ catalog_product_view.xml


<referenceBlock name="product.price.tier" remove="true"/>

<referenceBlock name="product.info.upsell" remove="true"/>

<referenceBlock name="product.info.extrahint.qtyincrements" remove="true"/>

<referenceBlock name="product.tooltip" remove="true"/>

<referenceBlock name="product.info.mailto" remove="true"/>
www.maxpronko.com
HOW ABOUT BLOCK
CACHE?
www.maxpronko.com
BLOCK CACHE
➤ Reduce number of Database queries
➤ Collection Load
➤ Template processing
➤ Eliminate Inline Translation
www.maxpronko.com
ABSTRACT BLOCK CLASS
public function toHtml()

{

$html = $this->_loadCache();

if ($html === false) {



$this->_beforeToHtml();

$html = $this->_toHtml();

$this->_saveCache($html);



}

$html = $this->_afterToHtml($html);



return $html;

}
www.maxpronko.com
ABSTRACT BLOCK CLASS
public function toHtml()

{

$html = $this->_loadCache();

if ($html === false) {



$this->_beforeToHtml();

$html = $this->_toHtml();

$this->_saveCache($html);



}

$html = $this->_afterToHtml($html);



return $html;

}
no more final :)
www.maxpronko.com
ABSTRACT BLOCK CLASS
protected function _loadCache()

{

if ($this->getCacheLifetime() === null || !$this->_cacheState-
>isEnabled(self::CACHE_GROUP)) {

return false;

}

$cacheKey = $this->getCacheKey();

$cacheData = $this->_cache->load($cacheKey);

if ($cacheData) {

$cacheData = str_replace(//placeholder, //value,

$cacheData

);

}

return $cacheData;

}
www.maxpronko.com
CATEGORY PAGE
www.maxpronko.com
CACHED BLOCKS
Navigation
Product
Price
www.maxpronko.com
Breadcrumb
Layered
Navigation
Product
Product List
NON-CACHED BLOCKS
www.maxpronko.com
LETS USE BLOCK CACHE
www.maxpronko.com
BREADCRUMBS BLOCK
default.xml
<referenceBlock name="breadcrumbs">

<arguments>

<argument name="cache_lifetime" xsi:type="string">604800</argument>

</arguments>

</referenceBlock>
www.maxpronko.com
LAYERED NAVIGATION BLOCK
➤ Layer State Key
➤ Category
➤ Customer Group
➤ Store
➤ Filters
➤ Currency
www.maxpronko.com
LAYERED NAVIGATION BLOCK
<referenceBlock name="catalog.leftnav">

<arguments>

<argument name="cache_lifetime" xsi:type="number">604800</argument>

</arguments>

</referenceBlock>
catalog_category_view.xml
www.maxpronko.com
LAYERED NAVIGATION PLUGIN DECLARATION
di.xml
<config>

<type name="MagentoLayeredNavigationBlockNavigation">

<plugin name="LayeredNavigationCacheKey" type="PronkoPerformancePlugin
LayeredNavigationPlugin"/>

</type>

<type name="MagentoLayeredNavigationBlockNavigationCategory">

<plugin name="LayeredNavigationCacheKey" type="PronkoPerformancePlugin
LayeredNavigationPlugin"/>

</type>

</config>
www.maxpronko.com
LAYERED NAVIGATION PLUGIN
class LayeredNavigationPlugin

{

public function afterGetCacheKey(Navigation $block, $cacheKey)

{

$cacheKey .= $this->priceCurrency->getCurrency()->getCurrencyCode();



$category = $block->getLayer()->getCurrentCategory();

if ($category) {

$cacheKey .= $category->getId();

}



$stateKey = $block->getLayer()->getStateKey();

if ($stateKey) {

$cacheKey .= $stateKey;

}



$cacheKey .= $this->toolbarModel->getCurrentPage();

$cacheKey .= $this->toolbarModel->getDirection();

$cacheKey .= $this->toolbarModel->getLimit();

$cacheKey .= $this->toolbarModel->getMode();

$cacheKey .= $this->toolbarModel->getOrder();



return $cacheKey;

}

}
www.maxpronko.com
PRODUCT LIST
<?php

$_productCollection = $block->getLoadedProductCollection();

$_helper = $this->helper('MagentoCatalogHelperOutput');

?>

<?php if (!$_productCollection->count()): ?>

<div class="message info empty"><div><?php /* @escapeNotVerified */ echo __('We can't find products matching the selection.') ?></div></div>

<?php else: ?>

<?php echo $block->getToolbarHtml() ?>

<?php echo $block->getAdditionalHtml() ?>

<?php

if ($block->getMode() == 'grid') {

$viewMode = 'grid';

$image = 'category_page_grid';

$showDescription = false;

$templateType = MagentoCatalogBlockProductReviewRendererInterface::SHORT_VIEW;

} else {

$viewMode = 'list';

$image = 'category_page_list';

$showDescription = true;

$templateType = MagentoCatalogBlockProductReviewRendererInterface::FULL_VIEW;

}

/**

* Position for actions regarding image size changing in vde if needed

*/

$pos = $block->getPositioned();

?>

<div class="products wrapper <?php /* @escapeNotVerified */ echo $viewMode; ?> products-<?php /* @escapeNotVerified */ echo $viewMode; ?>">

<?php $iterator = 1; ?>

<ol class="products list items product-items">

<?php /** @var $_product MagentoCatalogModelProduct */ ?>

<?php foreach ($_productCollection as $_product): ?>

<?php /* @escapeNotVerified */ echo($iterator++ == 1) ? '<li class="item product product-item">' : '</li><li class="item product product-item">' ?>

<div class="product-item-info" data-container="product-grid">

<?php

$productImage = $block->getImage($_product, $image);

if ($pos != null) {

$position = ' style="left:' . $productImage->getWidth() . 'px;'

. 'top:' . $productImage->getHeight() . 'px;"';

}

?>

<?php // Product Image ?>

<a href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>" class="product photo product-item-photo" tabindex="-1">

<?php echo $productImage->toHtml(); ?>

</a>

<div class="product details product-item-details">

<?php

$_productNameStripped = $block->stripTags($_product->getName(), null, true);

?>

<strong class="product name product-item-name">

<a class="product-item-link"

href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>">

<?php /* @escapeNotVerified */ echo $_helper->productAttribute($_product, $_product->getName(), 'name'); ?>

</a>

</strong>

<?php echo $block->getReviewsSummaryHtml($_product, $templateType); ?>

<?php /* @escapeNotVerified */ echo $block->getProductPrice($_product) ?>

<?php echo $block->getProductDetailsHtml($_product); ?>



<div class="product-item-inner">

<div class="product actions product-item-actions"<?php echo strpos($pos, $viewMode . '-actions') ? $position : ''; ?>>

<div class="actions-primary"<?php echo strpos($pos, $viewMode . '-primary') ? $position : ''; ?>>

<?php if ($_product->isSaleable()): ?>

<?php $postParams = $block->getAddToCartPostParams($_product); ?>

<form data-role="tocart-form" action="<?php /* @escapeNotVerified */ echo $postParams['action']; ?>" method="post">

<input type="hidden" name="product" value="<?php /* @escapeNotVerified */ echo $postParams['data']['product']; ?>">

<input type="hidden" name="<?php /* @escapeNotVerified */ echo Action::PARAM_NAME_URL_ENCODED; ?>" value="<?php /* @escapeNotVerified */ echo $postParams['data'][Action::PARAM_NAME_URL_ENCODED]; ?>">

<?php echo $block->getBlockHtml('formkey')?>

<button type="submit"

title="<?php echo $block->escapeHtml(__('Add to Cart')); ?>"

class="action tocart primary">

<span><?php /* @escapeNotVerified */ echo __('Add to Cart') ?></span>

</button>

</form>

<?php else: ?>

<?php if ($_product->getIsSalable()): ?>

<div class="stock available"><span><?php /* @escapeNotVerified */ echo __('In stock') ?></span></div>

<?php else: ?>

<div class="stock unavailable"><span><?php /* @escapeNotVerified */ echo __('Out of stock') ?></span></div>

<?php endif; ?>

<?php endif; ?>

</div>

<div data-role="add-to-links" class="actions-secondary"<?php echo strpos($pos, $viewMode . '-secondary') ? $position : ''; ?>>

<?php if ($this->helper('MagentoWishlistHelperData')->isAllow()): ?>

<a href="#"

class="action towishlist"

title="<?php echo $block->escapeHtml(__('Add to Wish List')); ?>"

aria-label="<?php echo $block->escapeHtml(__('Add to Wish List')); ?>"

data-post='<?php /* @escapeNotVerified */ echo $block->getAddToWishlistParams($_product); ?>'

data-action="add-to-wishlist"

role="button">

<span><?php /* @escapeNotVerified */ echo __('Add to Wish List') ?></span>

</a>

<?php endif; ?>

<?php

$compareHelper = $this->helper('MagentoCatalogHelperProductCompare');

?>

<a href="#"

class="action tocompare"

title="<?php echo $block->escapeHtml(__('Add to Compare')); ?>"

aria-label="<?php echo $block->escapeHtml(__('Add to Compare')); ?>"

data-post='<?php /* @escapeNotVerified */ echo $compareHelper->getPostDataParams($_product); ?>'

role="button">

<span><?php /* @escapeNotVerified */ echo __('Add to Compare') ?></span>

</a>

</div>

PRODUCT LIST
➤ Copy list.phtml and move foreach from the template
➤ Create new ProductList block class
➤ Create new Product/Item block class and item.phtml template
➤ Don’t forget about unique cacheKeyInfo
➤ Layered navigation, filter, toolbar, currency
➤ Set cache_lifetime value for both block classes
www.maxpronko.com
NEW PRODUCT LIST TEMPLATE
<?php if (!$block->getLoadedProductCollection()->count()): ?>

<div class="message info empty"><div><?php /* @escapeNotVerified */ echo __('We can't find products matching the selection.') ?></
div></div>

<?php else: ?>

<?php echo $block->getToolbarHtml() ?>

<?php echo $block->getAdditionalHtml() ?>

<div class="products wrapper <?php /* @escapeNotVerified */ echo $block->getViewMode(); ?> products-<?php /* @escapeNotVerified */
$block->getViewMode(); ?>">

<ol class="products list items product-items" itemscope itemtype="http://schema.org/ItemList">

<?php echo $block->getProductsListHtml(); ?>

</ol>

</div>

<?php echo $block->getToolbarHtml() ?>

<?php if (!$block->isRedirectToCartEnabled()) : ?>

<script type="text/x-magento-init">

{

"[data-role=tocart-form], .form.map.checkout": {

"catalogAddToCart": {}

}

}

</script>

<?php endif; ?>

<?php endif; ?>
app/design/Pronko/default/Magento_Catalog/templates/product/list.phtml
www.maxpronko.com
CATEGORY PAGE - CACHED
www.maxpronko.com
RELATED PRODUCTS
Product Page
www.maxpronko.com
PRODUCT PAGE
www.maxpronko.com
33 Attributes EAV Tables
ACTIONS
➤ Don’t use getAttributesUsedInProductListing() method
➤ Decrease number of attributes loaded
➤ Decrease number of EAV Tables usage on product page
➤ Enable block cache for each related product
ATTRIBUTES CONFIG
namespace MagentoCatalogModel;



class Config extends MagentoEavModelConfig

{
public function getProductAttributes()

{

if (is_null($this->_productAttributes)) {

$this->_productAttributes = array_keys($this->getAttributesUsedInProductListing());

}

return $this->_productAttributes;

}
}
ATTRIBUTES CONFIG
namespace PronkoPerformanceModelConfig;



class Related extends MagentoCatalogModelConfig

{

const XML_PATH_PRODUCT_RELATED_ATTRIBUTES = 'catalog/related_product_attributes';



public function getProductAttributes()

{

return array_keys($this->_scopeConfig->getValue(self::XML_PATH_PRODUCT_RELATED_ATTRIBUTES));

}

}

CONFIGURATION
<virtualType name="PronkoPerformanceProductRelatedContext" type="MagentoCatalogBlockProductContext">

<arguments>

<argument name="catalogConfig" xsi:type="object">PronkoPerformanceModelConfigRelated</argument>

</arguments>

</virtualType>



<type name="PronkoPerformanceBlockProductListRelated">

<arguments>

<argument name="context" xsi:type="object">PronkoPerformanceProductRelatedContext</argument>

</arguments>

</type>
di.xml
<config>

<default>

<catalog>

<related_product_attributes>

<name />

<tax_class_id />

<small_image />

<image_label />

<special_price />

</related_product_attributes>

</catalog>

</default>

</config>

config.xml
CATALOG_PRODUCT_VIEW.XML
<referenceBlock name="catalog.product.related" remove="true" />
<referenceContainer name="content.aside">

<block class="PronkoPerformanceBlockProductListRelated" name="pronko.catalog.product.related"
template="Magento_Catalog::product/list/items.phtml">

<arguments>

<argument name="type" xsi:type="string">related</argument>

</arguments>

</block>

</referenceContainer>
3RD PARTY MODULES
www.maxpronko.com
DOUBLE CHECK
➤ HTTP POST requests
➤ Collections usage
➤ Slow file/database operations
➤ Performance Tests on staging server
www.maxpronko.com
VARNISH
www.maxpronko.com
X-MAGENTO-VARY
➤ Customer Logged In
➤ Customer Group
➤ Customer Segment (EE only)
➤ Store
➤ Currency
➤ Tax Rates
www.maxpronko.com
X-MAGENTO-VARY
X-Magento-Vary = MD5(context data)
sub vcl_hash {

if (req.http.cookie ~ "X-Magento-Vary=") {

hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "1"));

}



if (req.http.host) {

hash_data(req.http.host);

} else {

hash_data(server.ip);

}

}
www.maxpronko.com
VARNISH HASH DATA
➤ 1 Website, no customer groups, same content for guest and logged in
➤ GeoIP functionality
sub vcl_hash {

if (req.url ~ "(?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid)=") {
set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content|
gclid)=([^&]+)", "");
set req.url = regsuball(req.url, "?(utm_source|utm_medium|utm_campaign|utm_content|
gclid)=([^&]+)", "?");
set req.url = regsub(req.url, "?&", "?");
set req.url = regsub(req.url, "?$", "");
}
hash_data(req.http.X-Currency);

}
www.maxpronko.com
VARNISH PERFORMANCE BEST PRACTICES
➤ Warmup pages after website content update
➤ Plan content changes ahead
➤ Run cron to clean up cache, reindex and warmup
www.maxpronko.com
WHAT ELSE?
www.maxpronko.com
MORE PERFORMANCE OPTIMISATIONS
➤ Remove Compare Products feature
➤ Minify and merge JavaScript and CSS
➤ Enable Async Order Grid
➤ Enable Async Transactional Emails
www.maxpronko.com
TRACKING AND MARKETING
➤ Asynchronous script loading for everything
➤ Google Tag Manager is good for async
➤ Avoid additional collection loads for Facebook, adWords, etc.
www.maxpronko.com
PERFORMANCE TOOLS
www.maxpronko.com
SUMMARY
➤ Disable functionality
➤ Use block cache
➤ Optimise/reduce database queries
➤ Remove unused blocks, observers
➤ Warmup all pages
➤ Never stop improving your website
www.maxpronko.com
THANK YOU
max_pronko
Q&A Time
www.maxpronko.com

Real use cases of performance optimization in magento 2

  • 1.
    REAL USE CASESOF PERFORMANCE OPTIMISATION MAGENTO 2 by Max Pronko
  • 2.
    ABOUT ME ➤ formerMagento core member ➤ CTO at TheIrishStore and GiftsDirect ➤ Founder of Pronko Consulting ➤ 7+ years of Magento Experience ➤ Magento 2 Blog
  • 3.
    AGENDA ➤ Performance Optimisation ➤eCommerce Website Data ➤ Real Improvements ➤ Varnish ➤ Q&A www.maxpronko.com
  • 4.
  • 5.
    PERFORMANCE Refers to thespeed in which pages are downloaded and displayed on the user’s browser www.maxpronko.com
  • 6.
    IS PERFORMANCE IMPORTANT? ➤Customer Experience ➤ Conversion Rate ➤ Organic Search www.maxpronko.com
  • 7.
  • 8.
    STEPS OF PERFORMANCETUNING ➤ Assess the problem ➤ Measure performance ➤ Identify part of the system critical for performance improvement ➤ Modify part of system to remove the bottleneck ➤ Measure the performance after modification ➤ If better, adopt it or revert https://en.wikipedia.org/wiki/Performance_tuningwww.maxpronko.com
  • 9.
    ECOMMERCE WEBSITE PROFILE ➤Seasonal business ➤ 4k products ➤ 500 Configurable Products ➤ 2k Configurable Variations ➤ 1.5k Simple Products ➤ 500 Categories ➤ Average Order Value: $100 ➤ 2-3 products per Order www.maxpronko.com
  • 10.
    BEFORE VS AFTEROPTIMISATION Before Category Page Product Page Shopping Cart Checkout Index 0 500 1000 1500 2000 Magento 2 Application www.maxpronko.com
  • 11.
    BEFORE VS AFTEROPTIMISATION After Category Page Product Page Shopping Cart Checkout Index 0 500 1000 1500 2000 Magento 2 Application www.maxpronko.com
  • 12.
  • 13.
  • 14.
    MAGENTO 2 APPLICATION 914318ms ms Before After www.maxpronko.com
  • 15.
  • 16.
  • 17.
    FOR EXISTING MAGENTO2 INSTALLATION > bin/magento module:disable Magento_Downloadable > bin/magento setup:upgrade > bin/magento setup:di:compile > bin/magento setup:static-content:deploy www.maxpronko.com
  • 18.
    COMPOSER.JSON { "repositories": [
 {
 "type": "composer",
 "url":"https://repo.magento.com/"
 }
 ], "require": { "magento/composer": "~1.0.0",
 "magento/framework": "100.1.1",
 "magento/language-en_us": "100.1.0",
 "magento/magento-composer-installer": "*",
 "magento/magento2-base": “2.1.1”, ... }, } www.maxpronko.com
  • 19.
    WHAT TO DISABLE? ➤Extra Languages ➤ Offline shipping and payments ➤ Backup, Captcha, Persistent, Rss ➤ Msrp, Send Friend, Weee ➤ Multishipping, Checkout Agreements ➤ Product types? ➤ More… depends on project www.maxpronko.com
  • 20.
  • 21.
  • 22.
    DISABLING FEATURES ➤ Observer ➤Plugin ➤ Layout www.maxpronko.com
  • 23.
    MAGENTO REPORTS ➤ vendor/magento/module-reports/etc/frontend/events.xml <config>
 <eventname="catalog_product_compare_remove_product">
 <observer name="reports" instance="MagentoReportsObserverCatalogProductCompareClearObserver" />
 </event>
 <event name="customer_login">
 <observer name="reports" instance="MagentoReportsObserverCustomerLoginObserver" />
 </event>
 <event name="customer_logout">
 <observer name="reports" instance="MagentoReportsObserverCustomerLogoutObserver" />
 </event>
 <event name="catalog_controller_product_view">
 <observer name="reports" instance="MagentoReportsObserverCatalogProductViewObserver" />
 </event>
 <event name="sendfriend_product">
 <observer name="reports" instance="MagentoReportsObserverSendfriendProductObserver" />
 </event>
 <event name="catalog_product_compare_add_product">
 <observer name="reports" instance="MagentoReportsObserverCatalogProductCompareAddProductObserver" />
 </event>
 <event name="catalog_product_compare_item_collection_clear">
 <observer name="reports" instance="MagentoReportsObserverCatalogProductCompareClearObserver" />
 </event>
 <event name="sales_quote_item_save_before">
 <observer name="reports" instance="MagentoReportsObserverCheckoutCartAddProductObserver" />
 </event>
 <event name="wishlist_add_product">
 <observer name="reports" instance="MagentoReportsObserverWishlistAddProductObserver" />
 </event>
 <event name="wishlist_share">
 <observer name="reports" instance="MagentoReportsObserverWishlistShareObserver" />
 </event>
 </config>
 www.maxpronko.com
  • 24.
    DISABLE OBSERVERS ➤ app/code/Pronko/Performance/etc/frontend/events.xml 
 <config>
 <eventname="catalog_product_compare_remove_product">
 <observer name="reports" disabled="true" />
 </event>
 <event name="catalog_controller_product_view">
 <observer name="reports" disabled="true" />
 </event>
 <event name="sendfriend_product">
 <observer name="reports" disabled="true" />
 </event>
 <event name="catalog_product_compare_add_product">
 <observer name="reports" disabled="true" />
 </event>
 <event name="catalog_product_compare_item_collection_clear">
 <observer name="reports" disabled="true" />
 </event>
 <event name="wishlist_add_product">
 <observer name="reports" disabled="true" />
 </event>
 <event name="wishlist_share">
 <observer name="reports" disabled="true" />
 </event>
 </config>
 www.maxpronko.com
  • 25.
    DISABLE VIA PLUGIN ➤app/code/Pronko/Performance/etc/di.xml 
 <config>
 <type name="MagentoBackendModelMenu">
 <arguments>
 <argument name="logger" xsi:type="object">PsrLogNullLogger</argument>
 </arguments>
 </type>
 <type name="MagentoFrameworkCacheInvalidateLogger">
 <plugin name="DisableCacheInvalidateLog" type="PronkoPerformancePluginInvalidateLoggerPlugin" />
 </type>
 </config> www.maxpronko.com
  • 26.
    REMOVE BLOCKS FROMLAYOUT ➤ Debug Hints ➤ Find non-used Blocks ➤ Remove it www.maxpronko.com
  • 27.
    REMOVE BLOCKS FROMLAYOUT ➤ default.xml 
 <referenceBlock name="copyright" remove="true" />
 <referenceBlock name="store_switcher" remove="true" />
 <referenceBlock name="store_language" remove="true" />
 <referenceBlock name="store.settings.language" remove="true" />
 <referenceBlock name="translate" remove="true" /> ➤ catalog_product_view.xml 
 <referenceBlock name="product.price.tier" remove="true"/>
 <referenceBlock name="product.info.upsell" remove="true"/>
 <referenceBlock name="product.info.extrahint.qtyincrements" remove="true"/>
 <referenceBlock name="product.tooltip" remove="true"/>
 <referenceBlock name="product.info.mailto" remove="true"/> www.maxpronko.com
  • 28.
  • 29.
    BLOCK CACHE ➤ Reducenumber of Database queries ➤ Collection Load ➤ Template processing ➤ Eliminate Inline Translation www.maxpronko.com
  • 30.
    ABSTRACT BLOCK CLASS publicfunction toHtml()
 {
 $html = $this->_loadCache();
 if ($html === false) {
 
 $this->_beforeToHtml();
 $html = $this->_toHtml();
 $this->_saveCache($html);
 
 }
 $html = $this->_afterToHtml($html);
 
 return $html;
 } www.maxpronko.com
  • 31.
    ABSTRACT BLOCK CLASS publicfunction toHtml()
 {
 $html = $this->_loadCache();
 if ($html === false) {
 
 $this->_beforeToHtml();
 $html = $this->_toHtml();
 $this->_saveCache($html);
 
 }
 $html = $this->_afterToHtml($html);
 
 return $html;
 } no more final :) www.maxpronko.com
  • 32.
    ABSTRACT BLOCK CLASS protectedfunction _loadCache()
 {
 if ($this->getCacheLifetime() === null || !$this->_cacheState- >isEnabled(self::CACHE_GROUP)) {
 return false;
 }
 $cacheKey = $this->getCacheKey();
 $cacheData = $this->_cache->load($cacheKey);
 if ($cacheData) {
 $cacheData = str_replace(//placeholder, //value,
 $cacheData
 );
 }
 return $cacheData;
 } www.maxpronko.com
  • 33.
  • 34.
  • 35.
  • 36.
    LETS USE BLOCKCACHE www.maxpronko.com
  • 37.
    BREADCRUMBS BLOCK default.xml <referenceBlock name="breadcrumbs">
 <arguments>
 <argumentname="cache_lifetime" xsi:type="string">604800</argument>
 </arguments>
 </referenceBlock> www.maxpronko.com
  • 38.
    LAYERED NAVIGATION BLOCK ➤Layer State Key ➤ Category ➤ Customer Group ➤ Store ➤ Filters ➤ Currency www.maxpronko.com
  • 39.
    LAYERED NAVIGATION BLOCK <referenceBlockname="catalog.leftnav">
 <arguments>
 <argument name="cache_lifetime" xsi:type="number">604800</argument>
 </arguments>
 </referenceBlock> catalog_category_view.xml www.maxpronko.com
  • 40.
    LAYERED NAVIGATION PLUGINDECLARATION di.xml <config>
 <type name="MagentoLayeredNavigationBlockNavigation">
 <plugin name="LayeredNavigationCacheKey" type="PronkoPerformancePlugin LayeredNavigationPlugin"/>
 </type>
 <type name="MagentoLayeredNavigationBlockNavigationCategory">
 <plugin name="LayeredNavigationCacheKey" type="PronkoPerformancePlugin LayeredNavigationPlugin"/>
 </type>
 </config> www.maxpronko.com
  • 41.
    LAYERED NAVIGATION PLUGIN classLayeredNavigationPlugin
 {
 public function afterGetCacheKey(Navigation $block, $cacheKey)
 {
 $cacheKey .= $this->priceCurrency->getCurrency()->getCurrencyCode();
 
 $category = $block->getLayer()->getCurrentCategory();
 if ($category) {
 $cacheKey .= $category->getId();
 }
 
 $stateKey = $block->getLayer()->getStateKey();
 if ($stateKey) {
 $cacheKey .= $stateKey;
 }
 
 $cacheKey .= $this->toolbarModel->getCurrentPage();
 $cacheKey .= $this->toolbarModel->getDirection();
 $cacheKey .= $this->toolbarModel->getLimit();
 $cacheKey .= $this->toolbarModel->getMode();
 $cacheKey .= $this->toolbarModel->getOrder();
 
 return $cacheKey;
 }
 } www.maxpronko.com
  • 42.
    PRODUCT LIST <?php
 $_productCollection =$block->getLoadedProductCollection();
 $_helper = $this->helper('MagentoCatalogHelperOutput');
 ?>
 <?php if (!$_productCollection->count()): ?>
 <div class="message info empty"><div><?php /* @escapeNotVerified */ echo __('We can't find products matching the selection.') ?></div></div>
 <?php else: ?>
 <?php echo $block->getToolbarHtml() ?>
 <?php echo $block->getAdditionalHtml() ?>
 <?php
 if ($block->getMode() == 'grid') {
 $viewMode = 'grid';
 $image = 'category_page_grid';
 $showDescription = false;
 $templateType = MagentoCatalogBlockProductReviewRendererInterface::SHORT_VIEW;
 } else {
 $viewMode = 'list';
 $image = 'category_page_list';
 $showDescription = true;
 $templateType = MagentoCatalogBlockProductReviewRendererInterface::FULL_VIEW;
 }
 /**
 * Position for actions regarding image size changing in vde if needed
 */
 $pos = $block->getPositioned();
 ?>
 <div class="products wrapper <?php /* @escapeNotVerified */ echo $viewMode; ?> products-<?php /* @escapeNotVerified */ echo $viewMode; ?>">
 <?php $iterator = 1; ?>
 <ol class="products list items product-items">
 <?php /** @var $_product MagentoCatalogModelProduct */ ?>
 <?php foreach ($_productCollection as $_product): ?>
 <?php /* @escapeNotVerified */ echo($iterator++ == 1) ? '<li class="item product product-item">' : '</li><li class="item product product-item">' ?>
 <div class="product-item-info" data-container="product-grid">
 <?php
 $productImage = $block->getImage($_product, $image);
 if ($pos != null) {
 $position = ' style="left:' . $productImage->getWidth() . 'px;'
 . 'top:' . $productImage->getHeight() . 'px;"';
 }
 ?>
 <?php // Product Image ?>
 <a href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>" class="product photo product-item-photo" tabindex="-1">
 <?php echo $productImage->toHtml(); ?>
 </a>
 <div class="product details product-item-details">
 <?php
 $_productNameStripped = $block->stripTags($_product->getName(), null, true);
 ?>
 <strong class="product name product-item-name">
 <a class="product-item-link"
 href="<?php /* @escapeNotVerified */ echo $_product->getProductUrl() ?>">
 <?php /* @escapeNotVerified */ echo $_helper->productAttribute($_product, $_product->getName(), 'name'); ?>
 </a>
 </strong>
 <?php echo $block->getReviewsSummaryHtml($_product, $templateType); ?>
 <?php /* @escapeNotVerified */ echo $block->getProductPrice($_product) ?>
 <?php echo $block->getProductDetailsHtml($_product); ?>
 
 <div class="product-item-inner">
 <div class="product actions product-item-actions"<?php echo strpos($pos, $viewMode . '-actions') ? $position : ''; ?>>
 <div class="actions-primary"<?php echo strpos($pos, $viewMode . '-primary') ? $position : ''; ?>>
 <?php if ($_product->isSaleable()): ?>
 <?php $postParams = $block->getAddToCartPostParams($_product); ?>
 <form data-role="tocart-form" action="<?php /* @escapeNotVerified */ echo $postParams['action']; ?>" method="post">
 <input type="hidden" name="product" value="<?php /* @escapeNotVerified */ echo $postParams['data']['product']; ?>">
 <input type="hidden" name="<?php /* @escapeNotVerified */ echo Action::PARAM_NAME_URL_ENCODED; ?>" value="<?php /* @escapeNotVerified */ echo $postParams['data'][Action::PARAM_NAME_URL_ENCODED]; ?>">
 <?php echo $block->getBlockHtml('formkey')?>
 <button type="submit"
 title="<?php echo $block->escapeHtml(__('Add to Cart')); ?>"
 class="action tocart primary">
 <span><?php /* @escapeNotVerified */ echo __('Add to Cart') ?></span>
 </button>
 </form>
 <?php else: ?>
 <?php if ($_product->getIsSalable()): ?>
 <div class="stock available"><span><?php /* @escapeNotVerified */ echo __('In stock') ?></span></div>
 <?php else: ?>
 <div class="stock unavailable"><span><?php /* @escapeNotVerified */ echo __('Out of stock') ?></span></div>
 <?php endif; ?>
 <?php endif; ?>
 </div>
 <div data-role="add-to-links" class="actions-secondary"<?php echo strpos($pos, $viewMode . '-secondary') ? $position : ''; ?>>
 <?php if ($this->helper('MagentoWishlistHelperData')->isAllow()): ?>
 <a href="#"
 class="action towishlist"
 title="<?php echo $block->escapeHtml(__('Add to Wish List')); ?>"
 aria-label="<?php echo $block->escapeHtml(__('Add to Wish List')); ?>"
 data-post='<?php /* @escapeNotVerified */ echo $block->getAddToWishlistParams($_product); ?>'
 data-action="add-to-wishlist"
 role="button">
 <span><?php /* @escapeNotVerified */ echo __('Add to Wish List') ?></span>
 </a>
 <?php endif; ?>
 <?php
 $compareHelper = $this->helper('MagentoCatalogHelperProductCompare');
 ?>
 <a href="#"
 class="action tocompare"
 title="<?php echo $block->escapeHtml(__('Add to Compare')); ?>"
 aria-label="<?php echo $block->escapeHtml(__('Add to Compare')); ?>"
 data-post='<?php /* @escapeNotVerified */ echo $compareHelper->getPostDataParams($_product); ?>'
 role="button">
 <span><?php /* @escapeNotVerified */ echo __('Add to Compare') ?></span>
 </a>
 </div>

  • 43.
    PRODUCT LIST ➤ Copylist.phtml and move foreach from the template ➤ Create new ProductList block class ➤ Create new Product/Item block class and item.phtml template ➤ Don’t forget about unique cacheKeyInfo ➤ Layered navigation, filter, toolbar, currency ➤ Set cache_lifetime value for both block classes www.maxpronko.com
  • 44.
    NEW PRODUCT LISTTEMPLATE <?php if (!$block->getLoadedProductCollection()->count()): ?>
 <div class="message info empty"><div><?php /* @escapeNotVerified */ echo __('We can't find products matching the selection.') ?></ div></div>
 <?php else: ?>
 <?php echo $block->getToolbarHtml() ?>
 <?php echo $block->getAdditionalHtml() ?>
 <div class="products wrapper <?php /* @escapeNotVerified */ echo $block->getViewMode(); ?> products-<?php /* @escapeNotVerified */ $block->getViewMode(); ?>">
 <ol class="products list items product-items" itemscope itemtype="http://schema.org/ItemList">
 <?php echo $block->getProductsListHtml(); ?>
 </ol>
 </div>
 <?php echo $block->getToolbarHtml() ?>
 <?php if (!$block->isRedirectToCartEnabled()) : ?>
 <script type="text/x-magento-init">
 {
 "[data-role=tocart-form], .form.map.checkout": {
 "catalogAddToCart": {}
 }
 }
 </script>
 <?php endif; ?>
 <?php endif; ?> app/design/Pronko/default/Magento_Catalog/templates/product/list.phtml www.maxpronko.com
  • 45.
    CATEGORY PAGE -CACHED www.maxpronko.com
  • 46.
  • 47.
  • 48.
    ACTIONS ➤ Don’t usegetAttributesUsedInProductListing() method ➤ Decrease number of attributes loaded ➤ Decrease number of EAV Tables usage on product page ➤ Enable block cache for each related product
  • 49.
    ATTRIBUTES CONFIG namespace MagentoCatalogModel;
 
 classConfig extends MagentoEavModelConfig
 { public function getProductAttributes()
 {
 if (is_null($this->_productAttributes)) {
 $this->_productAttributes = array_keys($this->getAttributesUsedInProductListing());
 }
 return $this->_productAttributes;
 } }
  • 50.
    ATTRIBUTES CONFIG namespace PronkoPerformanceModelConfig;
 
 classRelated extends MagentoCatalogModelConfig
 {
 const XML_PATH_PRODUCT_RELATED_ATTRIBUTES = 'catalog/related_product_attributes';
 
 public function getProductAttributes()
 {
 return array_keys($this->_scopeConfig->getValue(self::XML_PATH_PRODUCT_RELATED_ATTRIBUTES));
 }
 }

  • 51.
    CONFIGURATION <virtualType name="PronkoPerformanceProductRelatedContext" type="MagentoCatalogBlockProductContext">
 <arguments>
 <argumentname="catalogConfig" xsi:type="object">PronkoPerformanceModelConfigRelated</argument>
 </arguments>
 </virtualType>
 
 <type name="PronkoPerformanceBlockProductListRelated">
 <arguments>
 <argument name="context" xsi:type="object">PronkoPerformanceProductRelatedContext</argument>
 </arguments>
 </type> di.xml <config>
 <default>
 <catalog>
 <related_product_attributes>
 <name />
 <tax_class_id />
 <small_image />
 <image_label />
 <special_price />
 </related_product_attributes>
 </catalog>
 </default>
 </config>
 config.xml
  • 52.
    CATALOG_PRODUCT_VIEW.XML <referenceBlock name="catalog.product.related" remove="true"/> <referenceContainer name="content.aside">
 <block class="PronkoPerformanceBlockProductListRelated" name="pronko.catalog.product.related" template="Magento_Catalog::product/list/items.phtml">
 <arguments>
 <argument name="type" xsi:type="string">related</argument>
 </arguments>
 </block>
 </referenceContainer>
  • 53.
  • 54.
    DOUBLE CHECK ➤ HTTPPOST requests ➤ Collections usage ➤ Slow file/database operations ➤ Performance Tests on staging server www.maxpronko.com
  • 55.
  • 56.
    X-MAGENTO-VARY ➤ Customer LoggedIn ➤ Customer Group ➤ Customer Segment (EE only) ➤ Store ➤ Currency ➤ Tax Rates www.maxpronko.com
  • 57.
    X-MAGENTO-VARY X-Magento-Vary = MD5(contextdata) sub vcl_hash {
 if (req.http.cookie ~ "X-Magento-Vary=") {
 hash_data(regsub(req.http.cookie, "^.*?X-Magento-Vary=([^;]+);*.*$", "1"));
 }
 
 if (req.http.host) {
 hash_data(req.http.host);
 } else {
 hash_data(server.ip);
 }
 } www.maxpronko.com
  • 58.
    VARNISH HASH DATA ➤1 Website, no customer groups, same content for guest and logged in ➤ GeoIP functionality sub vcl_hash {
 if (req.url ~ "(?|&)(utm_source|utm_medium|utm_campaign|utm_content|gclid)=") { set req.url = regsuball(req.url, "&(utm_source|utm_medium|utm_campaign|utm_content| gclid)=([^&]+)", ""); set req.url = regsuball(req.url, "?(utm_source|utm_medium|utm_campaign|utm_content| gclid)=([^&]+)", "?"); set req.url = regsub(req.url, "?&", "?"); set req.url = regsub(req.url, "?$", ""); } hash_data(req.http.X-Currency);
 } www.maxpronko.com
  • 59.
    VARNISH PERFORMANCE BESTPRACTICES ➤ Warmup pages after website content update ➤ Plan content changes ahead ➤ Run cron to clean up cache, reindex and warmup www.maxpronko.com
  • 60.
  • 61.
    MORE PERFORMANCE OPTIMISATIONS ➤Remove Compare Products feature ➤ Minify and merge JavaScript and CSS ➤ Enable Async Order Grid ➤ Enable Async Transactional Emails www.maxpronko.com
  • 62.
    TRACKING AND MARKETING ➤Asynchronous script loading for everything ➤ Google Tag Manager is good for async ➤ Avoid additional collection loads for Facebook, adWords, etc. www.maxpronko.com
  • 63.
  • 64.
    SUMMARY ➤ Disable functionality ➤Use block cache ➤ Optimise/reduce database queries ➤ Remove unused blocks, observers ➤ Warmup all pages ➤ Never stop improving your website www.maxpronko.com
  • 65.