Question

I wonder, if there is a best practice to use block caching for blocks that contain a form with the form_key input (CSRF token).

I am not talking about full page cache hole punching, but the standard block cache of Magento CE.

Generally spoken: How would you cache a block with a small portion of dynamic content, or a child block that is not cached?

Was it helpful?

Solution

This is how I solved it eventually, add the following methods to the block in question:

const FORM_KEY_PLACEHOLDER = '{{FORM_KEY}}';

protected function _toHtml()
{
    return $this->_insertFormKeyPlaceholder(parent::_toHtml());
}

protected function _afterToHtml($html)
{
    return $this->_restoreFormKey(parent::_afterToHtml($html));
}

protected function _insertFormKeyPlaceholder($html)
{
    /** @var $session Mage_Core_Model_Session */
    $session = Mage::getSingleton('core/session');
    return str_replace($session->getFormKey(), self::FORM_KEY_PLACEHOLDER, $html);
}

protected function _restoreFormKey($html)
{
    /** @var $session Mage_Core_Model_Session */
    $session = Mage::getSingleton('core/session');
    return str_replace(self::FORM_KEY_PLACEHOLDER, $session->getFormKey(), $html);
}

Explanation:

I replace the form key with a placeholder before the rendered block is cached and replace the placeholder with the current form key again after the block has been loaded from cache.

I use _toHtml to insert the placeholder because the return value of this method is immediately written to the cache.

I use _afterToHtml to restore the form key because this method is called immediately after the content has been loaded from cache (or after it has been saved to the cache if it was not cached yet). This method is guaranteed to be called, regardless of the block cache.


Further thougts

A generic solution does not seem to exist yet, but the method described above works for any dynamic content and if you need it in more than one block or with different content, it should be easy to extract the placeholder logic to a separate model.

For replacing the placeholders you could also use the core_block_abstract_to_html_after event, but unfortunately no event exists right before the rendered block gets cached, so an "observer only" solution is not possible without introducing new events in the core.

Licensed under: CC-BY-SA with attribution
Not affiliated with magento.stackexchange
scroll top