Magento 2.x Overcoming FPC for Prices/Private Data

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));