Blog

Archives

Make your customizations compatible with Magento full page cache

Magento Enterprise comes with number of useful features and improvements out of the box comparing with it’s younger sister. Let’s talk about the one which is very important, since it may increase your store performance significantly. It’s a full page cache feature (FPC), available under admin panel System / Configuration / Cache management.

It fully caches static pages and the most part of pages with dynamic content. But as always, when your shop needs special customizations – you should take care about extending some of Magento features. Full page caching is not an exception in this case.

Let’s see the example from the real life. Consider that we need a block which should represent some products and these products are different for each customer. This block should be shown on the homepage. So we go to the admin panel, choose the homepage from CMS and insert something like this:

   {{block type="mymodule/products" template="mymodule/products.phtml"}}

We set up special logic for the block class which generates specific products collection for each customer (let’s say using id of currently logged in user). While full page cache is turned off everything is alright. But once you turn it on you will see things a bit different from what you expected. Once first customer comes to the homepage Magento caches the whole content including our special products block and all other customers will see the same items.

How are we going to solve this problem? Fortunately Magento caching mechanism is flexible enough and there is a feature which we may call a “hole” or a “placeholder”. It provides an ability to define blocks on the page which will have special caching logic, different from the rest of the page.
Back to our example, let’s create an xml file called cache.xml under the module etc folder (near to the config.xml):

  <?xml version="1.0" encoding="UTF-8"?>
  <config>
    <placeholders>
        <homepage_products>
        <block>mymodule/products</block>
        <placeholder>HOMEPAGE_PRODUCTS</placeholder>
        <container>Oggetto_Mymodule_Model_PageCache_Container_Homepageproducts</container>
        <cache_lifetime>86400</cache_lifetime>
        </homepage_products>
    </placeholders>
  </config>

You may see that we defined a placeholder which will catch our block mymodule/products and cache it separately using our own container class:

<?php
/**
* Placeholder container for homepage special products block
*
* @category    Mymodule
* @package     Oggetto_Mymodule
*/
class Oggetto_Mymodule_Model_PageCache_Container_Homepageproducts extends Enterprise_PageCache_Model_Container_Abstract
{
/**
* Get customer identifier from cookies
*
* @return string
*/
protected function _getIdentifier()
{
return $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '');
}

/**
* Get cache identifier
*
* @return string
*/
protected function _getCacheId()
{
return 'HOMEPAGE_PRODUCTS' . md5($this->_placeholder->getAttribute('cache_id') . $this->_getIdentifier());
}

/**
* Render block content
*
* @return string
*/
protected function _renderBlock()
{
$blockClass = $this->_placeholder->getAttribute('block');
$template = $this->_placeholder->getAttribute('template');

$block = new $blockClass;
$block->setTemplate($template);
return $block->toHtml();
}
}

Let’s explore this class more detailed:

protected function _renderBlock
This method defines the logic of our block rendering in context of caching. We say that it should work as we defined originally: using that defined block class and template.

protected function _getCacheId
This is more interesting method which defines the cache id which will be used for our special page piece. This is key moment here, because we should use different cache id for each customer. So we set unique prefix HOMEPAGE_PRODUCTS and then call method which will give us a customer identifier:

protected function _geIdentifier
Here we simply retrieve customer id from the cookie.

That’s all. Now each customer will get his own version of cached products list and the rest of cached homepage which is the same for everybody. One important note here is that our product list is actually being cached, this means that this solution will not require any additional queries each time user visits the site. Only first time for each customer, so it won’t kill the performance which is so important for you valuable enterprise clients.

*** Enjoy caching ***

  • Very useful post. I tried it with my own block and it works perfectly. Thank you Daniel!

  • spuff

    Thank you for this very handy post! Works great with Magento 1.9.1 and we’ve been having so many caching issues lately.

    What are your thoughts on having a specific block that never caches (with FPC turned on)? We’ve sort of hacked this out by doing the above but adding in the applyWithoutApp function and changing it to return false always. Would this be the best method?

    • Hello,
      yes, we used similar solution to prevent caching of specific blocks. We used the code above but with _getCacheId always returning a string constant and rewrited _saveCache method from abstract class to always return $this.

  • See here if you want to learn full details of how Caching works with Magento Enterprise:

    http://magentophp.blogspot.com/2011/02/magento-enterprise-full-page-caching.html

  • cache_lifetime node in cache.xml is ignored (because it’s deprecated since 1.8). Specify a block cache lifetime in the container’s _saveCache() method if needed.

    • @suhskov
      You are entirely right.
      The method you are talking about tries to set up the $lifetime recieving param with the one set up in the config cache.xml file, and if it is not there it just defaults to false.
      So It is not needed, but not ignored xD

    • Sorry I meant that @sushkov is NOT entirely right
      Sorry about that ;-)

  • Nithin Chacko Ninan

    I have searched a lot,but i found here…….

    Block caching rocks here…

    Thanks,

  • Yash K

    I am following the same instructions, but my _renderBlock() function is not being called??
    It is not saving cache because of the _saveCache() function, it is fine but I want some other thing to be render.
    Do I missing something??