我们商店的首页上有一个小部件。
该店使用FPC和块缓存。
如果管理员在后端更改了小部件设置,我们如何更新小部件内容?
是否可以在 CacheKey 中使用 Widget 的配置数据?
还是我们必须将缓存生命周期设置得非常小?
编辑:我自己的答案使用缓存生命周期。仅在编辑小部件实例时,从 FPC 中明确清除缓存块的方法是什么?
第一步是创建前端小部件本身,但提供一些完整页面缓存可以使用的附加信息。第二步
是
在小部件的全页缓存中处理打孔。
第三步是仅在管理区域中更改此小部件时自动清理小部件缓存。
首先,为您的模块创建etc/widget.xml文件:
<widgets>
<netzarbeiter_test type="netzarbeiter_test/widget_test">
<name>FPC Holepunch Cache Test</name>
<description>Dummy test widget</description>
<parameters>
<!-- This is the important parameter here: -->
<unique_id>
<required>1</required>
</unique_id>
<example_text>
<visible>1</visible>
<label>Example Text Parameter</label>
<type>text</type>
</example_text>
</parameters>
</netzarbeiter_test>
</widgets>
注意参数<unique_id>
。我们不提供输入类型或值,它将通过以下Mage_Widget_Block_Adminhtml_Widget_Options::_addField()
方法自动填充生成的值:
// Excerpt from Mage_Widget_Block_Adminhtml_Widget_Options::_addField()
if ($values = $this->getWidgetValues()) {
$data['value'] = (isset($values[$fieldName]) ? $values[$fieldName] : '');
}
else {
$data['value'] = $parameter->getValue();
//prepare unique id value
if ($fieldName == 'unique_id' && $data['value'] == '') {
$data['value'] = md5(microtime(1));
}
}
由于这个小宝石,我们不必重写Mage_Widget_Model_Widget_Instance
类来将小部件 id 注入生成的布局 xml。
接下来,创建小部件类本身。这只是一个实现Mage_Widget_Block_Interface
接口的常规块实例,除了unique_id
在cacheKeyInfo
方法中使用。
class Netzarbeiter_Test_Block_Widget_Test
extends Mage_Core_Block_Abstract
implements Mage_Widget_Block_Interface
{
public function getCacheKeyInfo()
{
$info = parent::getCacheKeyInfo();
if ($id = $this->getData('unique_id')) {
// Because the array key is a string, it will be added to the FPC placeholder
// parameters. That is how the FPC container can access it (see below).
$info['unique_id'] = (string) $id;
}
return $info;
}
protected function _toHtml()
{
// The FPC was completely cleared (or not created yet),
// recreate the widget parameter cache
if (! $this->getFullPageCacheEnvironment() && $this->getUniqueId()) {
$id = Netzarbeiter_Test_Model_Fpc_Container_Widget_Test::CACHE_PREFIX . $this->getUniqueId() . '_params';
Enterprise_PageCache_Model_Cache::getCacheInstance()->save(serialize($this->getData()), $id);
}
// Just some dummy output to display the text parameter and the render time
$time = now();
$textParam = $this->escapeHtml($this->getExampleText());
return <<<EOF
<p><b>Render Time:</b> {$time}<br/>
<b>Example Text:</b> {$textParam}<br/></p>
EOF;
}
}
当我们现在通过管理界面添加一个小部件实例时, widget.xmlunique_id
中指定的参数将被添加到布局 xml(为便于阅读而添加的格式:
SELECT layout_update_id, handle, xml FROM core_layout_update;
+------------------+--------------------------+----------------------------------------------------------------------------------------------------+
| layout_update_id | handle | xml |
+------------------+--------------------------+----------------------------------------------------------------------------------------------------+
| 17 | catalog_category_layered | <reference name="left">
| | | <block type="netzarbeiter_test/widget_test" name="2a3b55e13549e176709fc6c67a4a7bd8">
| | | <action method="setData">
| | | <name>unique_id</name>
| | | <value>7fa2574145ef204fb6d179cfc604ac76</value>
| | | </action>
| | | <action method="setData">
| | | <name>example_text</name>
| | | <value>This is a String!</value>
| | | </action>
| | | </block>
| | | </reference>
+------------------+--------------------------+----------------------------------------------------------------------------------------------------+
这意味着,每当通过布局 xml 渲染创建块实例时,unique_id
块对象上的参数都是已知的。
添加etc/cache.xml文件:
<config>
<placeholders>
<cms_block_widget>
<block>netzarbeiter_test/widget_test</block>
<placeholder>WIDGET_FPC_TEST</placeholder>
<container>Netzarbeiter_Test_Model_Fpc_Container_Widget_Test</container>
</cms_block_widget>
</placeholders>
</config>
接下来,创建容器类:
class Netzarbeiter_Test_Model_Fpc_Container_Widget_Test
extends Enterprise_PageCache_Model_Container_Abstract
{
const CACHE_PREFIX = 'TEST_WIDGET_';
protected function _getCacheId()
{
return self::CACHE_PREFIX . $this->_placeholder->getAttribute('unique_id');
}
protected function _renderBlock()
{
$block = $this->_getPlaceHolderBlock();
// Set any parameters from the placeholder on the block as needed.
// See observer below where the current parameters are cached.
$id = $this->_getCacheId() . '_params';
if ($parameters = Enterprise_PageCache_Model_Cache::getCacheInstance()->load($id)) {
$block->addData(unserialize($parameters));
// Set environment information on block (used in _toHtml,
// the params cache is recreated when not set)
$block->setFullPageCacheEnvironment(true);
}
Mage::dispatchEvent('render_block', array('block' => $block, 'placeholder' => $this->_placeholder));
return $block->toHtml();
}
}
这就是使我们的小部件成为完全缓存页面中的动态块所需的全部内容。但到目前为止,小部件块被无限期地缓存。我们仍然需要处理缓存刷新。
*_save_commit_after
对所有 Magento 模型提供的特定于小部件实例的自动事件使用事件观察器。每次在管理界面中保存小部件实例时都会触发它。
<adminhtml>
<events>
<widget_widget_instance_save_commit_after>
<observers>
<netzarbeiter_test>
<model>netzarbeiter_test/observer</model>
<method>widgetWidgetInstanceSaveCommitAfter</method>
</netzarbeiter_test>
</observers>
</widget_widget_instance_save_commit_after>
</events>
</adminhtml>
最后一块拼图是观察者方法:
public function widgetWidgetInstanceSaveCommitAfter(Varien_Event_Observer $observer)
{
/** @var $widget Mage_Widget_Model_Widget_Instance */
$widget = $observer->getEvent()->getObject();
$parameters = $widget->getWidgetParameters();
$uniqueId = isset($parameters['unique_id']) ? $parameters['unique_id'] : '';
if (strlen($uniqueId)) {
$id = Netzarbeiter_Test_Model_Fpc_Container_Widget_Test::CACHE_PREFIX . $uniqueId;
Enterprise_PageCache_Model_Cache::getCacheInstance()->remove($id);
$id = Netzarbeiter_Test_Model_Fpc_Container_Widget_Test::CACHE_PREFIX . $uniqueId . '_params';
Enterprise_PageCache_Model_Cache::getCacheInstance()->save(serialize($parameters), $id);
}
}
现在,前端的实例每次保存时都会自动更新,即使是激活了全页缓存。在所有其他时间,将从缓存中获取动态块。
编辑:将缓存的小部件参数示例添加到代码中,因为正如 Alex 在下面的评论中指出的那样,只要 FPC 条目存在,占位符参数就不会被刷新。
使用缓存将小部件参数传递给块避免了重写它的小部件实例模型并在动态呈现块时加载小部件实例。
您可以实现该方法getCacheKeyInfo
并使用一些特定于块的值来生成唯一的哈希。
在 Magento EE 中你可以看到这个 ie in blockEnterprise_Banner_Block_Widget_Banner
我认为唯一的可能性是通过 cache_lifetime 参数来做到这一点。因此,我们将其设置为大约 30 秒,并且每 30 秒根据当前配置刷新小部件。
我没有找到解决方案来在参数更改时立即从缓存中删除该项目。
这就是我所做的:
首先,小部件块需要知道小部件实例的 ID。为此,我不得不在我的一个模块中重写 Mage_Widget_Model_Widget_Instance::generateLayoutUpdateXml:
public function generateLayoutUpdateXml($blockReference, $templatePath = '')
{
$xml = parent::generateLayoutUpdateXml($blockReference, $templatePath);
$injectXml = '<action method="setData"><name>widget_instance_id</name><value>' .
$this->getId() . '</value></action>';
$xml = str_replace('</block>', $injectXml . '</block>', $xml);
return $xml;
}
在我的小部件块中,我将 cachelifetime 设置得非常小以避免双重缓存:编辑:不需要,因为 FPC 禁用了块缓存
public function getCacheLifetime()
{
return 1;
}
在缓存信息中包含当前模板和小部件实例 ID 。我发现那些带有字符串键的数组条目被映射到 FPC 占位符中的属性:
public function getCacheKeyInfo()
{
$instanceId = $this->getWidgetInstanceId();
return array(
'MYBOX',
Mage::app()->getStore()->getId(),
(int)Mage::app()->getStore()->isCurrentlySecure(),
Mage::getDesign()->getPackageName(),
Mage::getDesign()->getTheme('template'),
'widget_instance_id' => $instanceId,
'template' => $this->getTemplate(),
);
}
现在我们必须为这个小部件创建一个自己的 FPC 占位符。此占位符加载小部件实例的当前配置并呈现小部件
class MyCompany_PageCache_Model_Container_WidgetInstance
extends Enterprise_PageCache_Model_Container_Abstract
{
protected function _renderBlock()
{
$instanceId = $this->_placeholder->getAttribute('widget_instance_id');
$widgetInstance = Mage::getModel('widget/widget_instance')->load($instanceId);
if($widgetInstance->getId()) {
Mage::logException( new Exception('Widget Instance '.$instanceId.' not found') );
}
$data = $widgetInstance->getWidgetParameters();
$block = $this->_placeholder->getAttribute('block');
$name = $this->_placeholder->getAttribute('name');
$template = $this->_placeholder->getAttribute('template');
$block = new $block;
$block->setTemplate($template);
if ($name !== null) {
$block->setNameInLayout($name);
}
$block->setLayout(Mage::app()->getLayout());
$block->setData($data);
return $block->toHtml();
}
}
我们必须为这个占位符添加 cache.xml 配置:
<mycompany_mybox>
<block>mycompany_mybox/widget</block>
<placeholder>MYCOMPANY_MYBOX_WIDGET</placeholder>
<container>MyCompany_PageCache_Model_Container_WidgetInstance</container>
<cache_lifetime>30</cache_lifetime>
</mycompany_mybox>
最后,小部件必须在后端保存一次,以便生成新的布局 xml。
尝试下一种方法:
Enterprise_PageCache_Model_Validator
在您的模块中覆盖。添加Mage_Widget_Block_Interface
到protected $_dataChangeDependency
此类中的数组中。
protected function _getObjectClasses($object)
用下一种方式重写:
/**
* Get list of all classes related with object instance
*
* @param $object
* @return array
*/
protected function _getObjectClasses($object)
{
$classes = array();
if (is_object($object)) {
$classes[] = get_class($object);
$classes = array_merge($classes, class_implements($object), class_parents ($object));
}
return $classes;
}