Magento – How to Cache Custom Block in Category View

cache

I would like to add caching for a block on a per product basis.

I tried just adding:

protected function _construct()
{
    //cache for half a day
    $this->setCacheLifetime(43200);
}

The problem is a single cache entry is used for every product.
So if I load the first page with products that has specific html in it. Then on all other products the same html is shown.

So it is caching the entire block using a default block tag.

How would I cache the block for the page/url as well as per product?

Update:

$this->getProduct()->getId() to the getCacheKeyInfo() function I get the following error:

PHP Fatal error:  Call to a member function getId() on null

Update: Add Block Code

class Module_Sticker_Block_Badge extends Mage_Core_Block_Template
{

    protected function _construct()
    {
        //cache the stickers for half a day
        $this->setCacheLifetime(43200);

        // Add cache tags
        // cache tags are used as handles for clearing certain caches
        $this->setCacheTag(array(
            Mage_Core_Model_Store::CACHE_TAG,
            Mage_Cms_Model_Block::CACHE_TAG,
            Tengisa_Sticker_Model_Sticker::CACHE_TAG
        ));
    }

    // Cache key is unique per bit of HTML
    public function getCacheKeyInfo()
    {
        return array(
            Module_Sticker_Model_Sticker::CACHE_TAG,
            $this->getNameInLayout(),
            Mage::app()->getStore()->getId(),
            Mage::getDesign()->getPackageName(),
            Mage::getDesign()->getTheme('template'),
            //Product id
            // $this->getParentBlock()->getProduct()->getId()
            // Mage::registry('product')->getId()
            $this->getProduct()->getId()
        );
    }

Best Answer

$this->getProduct()->getId() to the getCacheKeyInfo() function I get the following error:

PHP Fatal error:  Call to a member function getId() on null

Adding the product ID to getCacheKeyInfo() is the right thing to do. But it looks like at least in some cases your block does not have the product available with getProduct(). Make sure that getProduct() is implemented and actually returns a product instance.


How would I cache the block for the page/url as well as per product?

You were talking about category pages, so I assume there are multiple different instances of your block on one page. In this case I don't see sense in using the URL for the cache key.

On product pages you could use Mage::registry('current_product') to refer the current product and on category pages, the same with "current_category".

I cannot tell, if any of these make sense to you. If you need more concrete help, please update the question with relevant code of your custom block.


Update based on comment

I just use setData to set the data I need in the template: <?php echo $this->getChild('badge')->setData('stickers', $_product->getStickers())->toHtml(); ?> and then access in the template with: $this->getStickers()

In this case, the block has no access to the product id. Now you have two possible ways to solve it:

  1. use the stickers data for the cache key

    \md5($this->getData('stickers')
    
  2. additionally pass the product id

    $this->getChild('badge')->setData('product_id', $_product->getId());
    

    and then use it for the cache key

    $this->getData('product_id')
    

Note that you pass generated HTML (product->getStickers()->toHtml()) from outside, so it is generated every time even if the block itself is cached.

To optimize this, you should move this HTML generation into the block so that is is only executed if the block is not already cached.

The parent template then should look like this:

$this->getChild('badge')->setData('product', $_product);

The additional data for the cache key:

$this->getData('product')->getId()

And the blocks _beforeToHtml() method (which is only called if the block was not cached):

protected function _beforeToHtml()
{
     parent::_beforeToHtml();
     $this->setData('stickers', $this->getData('product')->getStickers()->toHtml());
}