Question

So, i've a requirement to write some functionality for a B2B store which requires some aspect of dynamic pricing - or at least the price not to be cached.

The basic requirement is to write up a VAT Switcher which allows the user to toggle between tax displays.

Now, I've already wrote a block using a custom UI Component for the dropdown itself to make sure that block is not cached. This works great, it shows the selected display or default display if untouched and is invalidated via a section declaration.

However, to move the whole core pricing blocks/templates to be not cached would be a bit of work. More than anything i'm after suggestions of how to best go about the task in hand.

I don't want any answers telling me to simply add cacheable="false" into the product price blocks, this would render any page showing price uncachable and is simply not a solution. Also, $_isScopePrivate is deprecated so again, not an option.

I've definitely toyed with the idea of rewriting the price blocks so that they are managed via UI Components, defining sections that would be invalidated upon certain requests my extension would make to trigger a change in pricing.

Any Ideas?

Was it helpful?

Solution

Ok, for anybody else facing a similar issue, i think i've found the best way around it. As Nicholas Miller commented, my first port of call was to look into the Block Caching system.

I looked into how Magento uses the Cache Keys for each block to understand which blocks need to be cached. From here i tried writing a few Plugin/Interceptors to change the Cache Key of the price blocks as and when i needed to.

However, this opened up the real issue. It was on the Full Page Cache layer that my problem lied.

This lead me to look into how Magento's FPC Identifys pages as cached or uncached.

Inside Magento\Framework\App\PageCache\Identifier is the getValue() method. As below:

/**
 * Return unique page identifier
 *
 * @return string
 */
public function getValue()
{
    $data = [
        $this->request->isSecure(),
        $this->request->getUriString(),
        $this->request->get(\Magento\Framework\App\Response\Http::COOKIE_VARY_STRING)
            ?: $this->context->getVaryString()
    ];
    return md5(serialize($data));
}

This is where Magento gets the Identifier for each page and then attempts to load the cached version of that page if existent.

My target was to alter this so that I would add an extra value to the Identifier, one which would indicate which display of VAT the customer had chosen. To achieve this i simply added a preference for my own class to override the getValue() function shown above.

Di.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <preference for="Magento\Framework\App\PageCache\Identifier" type="Interjar\VatSwitcher\PageCache\Identifier" />
</config>

Interjar\VatSwitcher\PageCache\Identifier::getValue():

/**
 * Return Hashed Page Identifier for PageCache Processing
 *
 * @return string
 */
public function getValue()
{
    if($this->switcherHelper->getIsEnabled()) {
        $data = [
            $this->request->isSecure(),
            $this->request->getUriString(),
            $this->request->get(\Magento\Framework\App\Response\Http::COOKIE_VARY_STRING)
                ?: $this->context->getVaryString(),
            $this->switcherHelper->getVatDisplay()
        ];
        return md5(serialize($data));
    }
    return parent::getValue();
}

This then allowed me to include a value from my own Helper which indicates the VAT Display the user has selected. It then creates a cached version of the page for each VAT Display type.

Conclusion:

Im interested in learning whether theres any other way to achieve this. I originally looked into the possibility of using a plugin for this but couldn't see a way of this working because if i was to use a before plugin and set the $data variable, it would have been overriten by the original method. An after plugin wouldn't be possible as the array is encoded when returned.

2.2 Notes:

With the imminent release of version 2.2 there are a couple of minor notes.

In the Magento\Framework\App\PageCache\Identifier Magento change the serialization and encoding methods to use sha1() for encoding and also a new serialization class Magento\Framework\Serialize\Serializer\Json included in 2.2

So the return value for the getValue() method looks like this:

return sha1($this->serializer->serialize($data));

OTHER TIPS

The recommended approach I believe is to use a plugin for \Magento\Framework\App\Http\Context e.g.

public function beforeGetVaryString(\Magento\Framework\App\Http\Context $subject)
{
        $subject->setValue(\Taxer\Switch\Helper\Data::CONTEXT_TAX_DISPLAY, $this->getCurrentTaxDisplay(), $this->getDefaultTaxDisplay());
}

This should allow you to modify the getVaryString() and in effect add your own stuff to the value returned by getValue()

While this works on theory I was unable to get a switchable price display, the page is not getting refreshed. Do you do any cache refresh manually or send some headers?

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