magento 1 fpc 动态内容

magento 1 企业版的一些页面使用了 fpc ,如果我们调用一个产生随机字符串的方法,那么在 fpc 开着的情况下,我们每次会得到相同的字符串,而不是随机的字符串。那么我们怎样才能每次都得到随机的字符串呢?

我们以 Magento 1.14.1.0 为例,创建一个模块 VendorName_TestModule 做实验。

File: app\etc\modules\VendorName_TestModule.xml

<?xml version="1.0"?>
<config>
    <modules>
        <VendorName_TestModule>
            <active>true</active>
            <codePool>local</codePool>
        </VendorName_TestModule>
    </modules>
</config>

File: app\code\local\VendorName\TestModule\etc\config.xml

<?xml version="1.0"?>
<config>
    <modules>
        <VendorName_TestModule>
            <version>0.1.0</version>
        </VendorName_TestModule>
    </modules>
    <global>
        <blocks>
            <testmodule>
                <class>VendorName_TestModule_Block</class>
            </testmodule>
        </blocks>
        <helpers>
            <testmodule>
                <class>VendorName_TestModule_Helper</class>
            </testmodule>
        </helpers>
    </global>
    <frontend>
        <layout>
            <updates>
                <testmodule>
                    <file>testmodule.xml</file>
                </testmodule>
            </updates>
        </layout>
    </frontend>
</config>

File:app\design\frontend\rwd\default\layout\testmodule.xml

<?xml version="1.0"?>
<layout version="0.1.0">
    <default>
        <reference name="content">
            <block type="testmodule/fpctest" name="fpctest" template="testmodule/fpctest.phtml" after="-"/>
        </reference>
    </default>
</layout>

File: app\code\local\VendorName\TestModule\Block\Fpctest.php

<?php
class VendorName_TestModule_Block_Fpctest extends Mage_Core_Block_Template
{
    protected function _construct()
    {
        $this->setTemplate('testmodule/fpctest.phtml');
    }

    public function getRandString($length = 4)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
        $str   = '';
        for ($i = 0; $i < $length; ++$i) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }
}

File: app\design\frontend\rwd\default\template\testmodule\fpctest.phtml

This is from block
<br/>
<?php echo $this->getRandString() ?>
<br/>

缓存原因,每次刷新页面得到的都是相同字符串。

每次刷新获得随机字符串

File: app\code\local\VendorName\TestModule\etc\cache.xml

<?xml version="1.0"?>
<config>
    <placeholders>
        <testmodule>
            <block>testmodule/fpctest</block>
            <placeholder>TESTMODULE_CACHE</placeholder>
            <container>VendorName_TestModule_Model_Container_Fpctest</container>
            <cache_life>5</cache_life>
        </testmodule>
    </placeholders>
</config>

File: app\code\local\VendorName\TestModule\Model\Container\Fpctest.php

<?php
class VendorName_TestModule_Model_Container_Fpctest extends Enterprise_PageCache_Model_Container_Abstract{

    protected function _renderBlock()
    {
        $block = $this->_getPlaceHolderBlock();
        return $block->toHtml();
    }

    protected function _getCacheId()
    {
        return 'TESTMODULE_CACHE' . md5($this->_placeholder->getAttribute('cache_id')).'_'.$this->_getIdentifier();
    }

    protected function _getIdentifier()
    {
        return $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '');
    }

    public function applyWithoutApp(&$content)
    {
        // by default will attempt to load block from cache.
        return false; // always dynamic
    }
}

记得刷新缓存

fpc 页面有 4 种状态

  1. Page in cache, no dynamic blocks
  2. Page in cache, dynamic blocks cached
  3. Page in cache, dynamic blocks not cached
  4. Page not in cache

当我们刷新缓存后,再请求页面,我们的页面处于状态 4 ,执行过程就是正常的过程。但是 magento 会根据 cache.xml ,用 <!--{MY_PLACEHOLDER_NAME_hash}-->rendered block content<!--/{MY_PLACEHOLDER_NAME_hash}--> 替代 block 的内容缓存起来, block 也被缓存起来。在最终页面发送之前,再把上面的 placeholder 替换成缓存的 block 内容。缓存的 block 内容会有过期时间。

所以我们第二次刷新的时候,页面处于状态 2 ,那么这个时候会请求 container 的 applyWithoutApp($content)。按照道理应该返回 block 的内容(如果 block 没有过期的话)。但是我们返回的是 false ,这时候就是状态 3 了,请求转向 pagecache/request/process 最后来到了 container 的 applyWithApp() ,而 applyWithApp() 则会调用 $this->_renderBlock()。所以我们得到了随机的字符串。

下面讨论下过期时间

File: app\code\local\VendorName\TestModule\Model\Container\Fpctest.php

<?php
class VendorName_TestModule_Model_Container_Fpctest extends Enterprise_PageCache_Model_Container_Abstract{

    protected function _renderBlock()
    {
        $block = $this->_getPlaceHolderBlock();
        return $block->toHtml();
    }

    protected function _getCacheId()
    {
        return 'TESTMODULE_CACHE' . md5($this->_placeholder->getAttribute('cache_id')).'_'.$this->_getIdentifier();
    }

    protected function _getIdentifier()
    {
        return $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '');
    }

    protected function _saveCache($data, $id, $tags = array(), $lifetime = 5)
    {
        parent::_saveCache($data, $id, $tags, $lifetime);
    }

}

刷新缓存,我们会发现随机字符串在 5 s 内保持一样,5 s 后由于缓存过期,拿到新的值。如果 $lifetime = 0 那么就会一直过期。

充分利用缓存, placeholder 带参

在 fpc 上打孔,不能直接使用 Mage::registry() 和 Mage::app()->getFrontController()->getAction()->getFullActionName() 这种,因为在状态 3 的时候,页面是转向 pagecache/request/process ,它并不是一个通常的请求。如果你这么用了,那么就会发现,结果很怪异,访问 A 页面正常,访问下 B 页面也正常,再回到 A 页面,哎~ ,怎么把 B 的结果带过来了。。

下面我们让 block 缓存起来,通过 placeholder 带参来解决上面提到的问题。

File: app\code\local\VendorName\TestModule\Block\Fpctest.php

<?php
class VendorName_TestModule_Block_Fpctest extends Mage_Core_Block_Template
{
    // !important
    public function getCacheKeyInfo() {
        $info = parent::getCacheKeyInfo();
        $info['page_type'] = $this->getPagetype();
        return $info;
    }

    protected function _construct()
    {
        $this->setTemplate('testmodule/fpctest.phtml');
    }

    public function getRandString($length = 4)
    {
        $chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
        $str   = '';
        for ($i = 0; $i < $length; ++$i) {
            $str .= substr($chars, mt_rand(0, strlen($chars) - 1), 1);
        }
        return $str;
    }

    public function getPagetype()
    {
        if ($this->hasData('page_type')) {
            return $this->getData('page_type');
        } else {
            return Mage::app()->getFrontController()->getAction()->getFullActionName();
        }
    }
}

File: app\code\local\VendorName\TestModule\Model\Container\Fpctest.php

<?php
class VendorName_TestModule_Model_Container_Fpctest extends Enterprise_PageCache_Model_Container_Abstract{

    protected function _renderBlock()
    {
        $block = $this->_getPlaceHolderBlock();
        $pageType = $this->_placeholder->getAttribute('page_type');
        $block->setPageType($pageType);
        return $block->toHtml();
    }

    protected function _getCacheId()
    {
        return 'TESTMODULE_CACHE' . md5($this->_placeholder->getAttribute('cache_id')).'_'.$this->_getIdentifier();
    }

    protected function _getIdentifier()
    {
        return $this->_getCookieValue(Enterprise_PageCache_Model_Cookie::COOKIE_CUSTOMER, '');
    }

    protected function _saveCache($data, $id, $tags = array(), $lifetime = 5)
    {
        parent::_saveCache($data, $id, $tags, $lifetime);
    }
}

File: app\design\frontend\rwd\default\template\testmodule\fpctest.phtml

This is from block
<br/>
<?php echo $this->getRandString() ?>
<br/>
<?php echo $this->getPagetype();?>

参考链接

How do I include a dynamic block in the product page with full page caching turned on?
Overcoming Magento’s full-page cache through hole punching
what do cache.xml in etc folder of any module?

发表评论

电子邮件地址不会被公开。 必填项已用*标注