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.