5

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()
        );
    }
tread
  • 1,083
  • 2
  • 13
  • 36
  • Are you currently using an FPC/Varnish or particularly interested in the block caching layer? Also what version of Magento are you using? I would take a look at pricing blocks on category pages to help understand how it is implemented. Also AOE_TemplateHints https://github.com/AOEpeople/Aoe_TemplateHints will show you cache details of each block, even color coding states. – B00MER Oct 09 '16 at 20:35
  • I am only using standard magento caching. Aha I'll take a look at the pricing blocks. – tread Oct 09 '16 at 20:47
  • I will suggest you to follow up the article explained by alanstorm - http://alanstorm.com/magento_listener_lifecycle_block You will get you answer if you follow it carefully – rajatsaurastri Oct 10 '16 at 06:44
  • @rajatsaurastri The issue is accessing product from the block. I am not seeing a reference to that in the article. – tread Oct 10 '16 at 07:02

3 Answers3

6

$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());
}
Fabian Schmengler
  • 65,791
  • 25
  • 187
  • 421
  • Thanks, I have added my current block code. Please check and see if there are any red flags. – tread Oct 10 '16 at 06:46
  • I don't see anything product related. How do you access the product in the template? – Fabian Schmengler Oct 10 '16 at 06:54
  • I am doing it incorrectly. I tried using $this->getParent()->getProduct()->getId() but that also gave a null on getProduct. The parent class is Mage_Catalog_Block_Product_List – tread Oct 10 '16 at 07:00
  • Oh, 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() – tread Oct 10 '16 at 07:08
  • I see. Please see my update – Fabian Schmengler Oct 10 '16 at 08:55
  • I'm not too sure why you need the _beforeHtml() – tread Oct 11 '16 at 07:47
  • Optimization. To only execute this code if the block is not loaded from cache. – Fabian Schmengler Oct 11 '16 at 08:11
  • I thought it does that automatically. The whole blocks content has already been cached. When it has not been cached the setData will already have been called from the template – tread Oct 11 '16 at 08:23
  • So this was the template of the block itself? I thought it was from the parent block. If I misunderstood that, you are right and it is not necessary to change (but best practice nonetheless) – Fabian Schmengler Oct 11 '16 at 08:25
0
  1. after instance this block, setProduct($product)(block extends Varien_Object)
  2. setCacheKey(),please ensure its unique(ex. MY_BLOCK_NAME . $product--getId());or, you can rewrite the function 'getCacheKeyInfo()'
sorkl
  • 1
  • 1
0

I'm not sure if it's exactly what you're looking for but to cache custom block and then clean cache exactly for this block I use the next approach which works good to me:

  1. Implement \Magento\Framework\DataObject\IdentityInterface and set your custom cache tag like

    use Magento\Framework\DataObject\IdentityInterface;
    use Magento\Framework\View\Element\Template;
    use Magento\Framework\View\Element\Template\Context;
    ...
    

    class Block extends Template implements IdentityInterface { public const CACHE_TAG = 'custom_cache_tag';

    public function __construct (
        ...
        Context $context,
        array $data = []
    ) {
        parent::__construct($context, $data);
        ...
    }
    
    public function getIdentities(): array
    {
        return [self::CACHE_TAG];
    }
    
    ...
    
    

    }

  2. Use it to clean cache in a place you need

    use Magento\Framework\App\CacheInterface;
    use Magento\PageCache\Model\Cache\Type;
    

    ...

    public function __construct ( ... private readonly CacheInterface $cache, private readonly Type $fullPageCache ) { }

    public function cleanBlockCache (): void { $tags = [Block::CACHE_TAG]; //tag from our custom block $this->cache->clean($tags); $this->fullPageCache->clean(\Zend_Cache::CLEANING_MODE_MATCHING_ANY_TAG,$tags); }