如何在产品页面中包含动态块并启用全页面缓存? [英] How do I include a dynamic block in the product page with full page caching turned on?

查看:68
本文介绍了如何在产品页面中包含动态块并启用全页面缓存?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们想在产品页面上添加一个动态块.问题在于产品页面具有全页面缓存(由于速度问题,我们无法将其关闭).我们希望根据登录的用户帐户在每个产品页面上显示不同的信息,并且每个产品的信息都不同.

我创建了一个单独的块,该块具有自己的缓存,但是显示的是与上一个产品页面相同的块.我正在尝试更改其缓存方法,以便它不会保存上一个产品页面的缓存.

它在我进入产品页面的前几次有效,但是随后突然开始显示Magento错误页面,上面显示:该网站在检索

这是我到目前为止所做的.

我创建了 app/code/local/MyCompany/MyModule/PageCache/etc/config.xml 来添加 MyCompany_PageCache_Model.

然后,我创建了用于控制 app/code/local/MyCompany/MyModule/PageCache/Model/Container/MyFile.php
中的缓存的文件 具有以下功能:

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

protected function _saveCache($data, $id, $tags = array(), $lifetime = null)
{
    return false;
}

protected function _renderBlock()
{
    $blockClass = $this->_placeholder->getAttribute('block');
    $template = $this->_placeholder->getAttribute('template');

    $block = new $blockClass;
    $block->setTemplate($template);
    $block->setLayout(Mage::app()->getLayout());
    return $block->toHtml();
}

我还使用占位符为 CONSTANT_CACHE Catalog/etc 下创建了 cache.xml .

以上语法是否正确,或者有更简单的方法吗?

概述

为了回答,我需要先解释一下. Magento FPC流程知道四个状态.

  1. 页面在缓存中,没有动态块
  2. 页面在缓存中,动态块已缓存
  3. 页面在缓存中,动态块未缓存
  4. 页面不在缓存中

状态1和2在未初始化完整的Magento应用程序的情况下被处理.状态3和4要求初始化应用程序并处理路由.因此,如果可能的话,请尽可能满足状态1和状态2的请求,否则您将失去FPC可能改进的很大一部分.

状态1

从开发人员的角度来看,状态1很无聊,无事可做,所以让我们继续...

状态2

在状态2中,页面包含动态块. 目前,Magento尚未完全初始化.
FPC处理器加载一个缓存的页面并在其中找到一个动态块的占位符.
通过分析占位符,处理器能够识别动态块的容器类,实例化它,并在其上调用applyWithoutApp($content). (方法的名称指的是Magento应用程序到目前为止尚未初始化的事实). 然后,容器尝试使用方法$this->_getCacheId()返回的缓存键从块缓存中加载动态块内容. 如果返回了缓存键,并且可以加载缓存项,则容器类将$content中的占位符替换为缓存的块输出,并完成FPC.
到目前为止,并没有产生太多开销.

状态3

因此处于状态2的applyWithoutApp($content)无法获取和交付动态块内容,因此即使已在FPC中找到了页面的其余部分,也需要生成块内容.
为此,FPC模块将请求设置为pagecache/request/process,并遵循常规的Magento应用程序初始化和路由.
这意味着,与状态2相比,产生了更多的开销,尽管与没有FPC的常规页面加载相比,它还是要好一些,因为例如URL重写被跳过.
最后,前端控制器和标准路由器将请求委托给RequestController::processAction()方法.
该方法为动态块获取先前实例化的容器类,并在其上调用applyInApp($content).
此方法运行$this->_renderBlock()实例化实际的块类并返回其输出.您已经根据自己的问题实施了此方法. FPC现在可以用块内容替换占位符,然后发送页面.
需要注意的一件事是,这不是常规的产品详细信息页面请求,例如Mage::registry('current_product')不可用! 根据您的块实现,这可能会影响块级别的缓存或动态块的内容生成.我怀疑这可能是您的问题源于此,但我将进一步探讨可能的解决方法.

状态4

在这种状态下,FPC找不到所请求页面的缓存记录,因此Magento像往常一样生成该页面.产品详细信息页面输出由Mage_Catalog_ProductController::viewAction()创建.
根据cache.xml,所有配置为动态的块都包装在占位符标记中.
占位符标记包含参数,这些参数随后将传递给步骤2和3的容器对象.始终设置的唯一参数是容器和块类名称.但几乎总是同时设置cache_idtemplate.
在容器类中,可以使用$this->_placeholder->getAttribute('cache_id')访问这些值(就像您在容器的_getCacheId()方法中所做的一样).

即使您在这个冗长的答案的大部分内容上都没有什么用,这对您来说可能还是很有趣的.如果需要其他值来生成块缓存ID或块输出(例如产品ID或客户ID),请将其设置为占位符的参数.

为此,您需要将其设置在由块getCacheKeyInfo()方法返回的数组上,并以字符串作为数组键.如果使用数字数组索引,则不会将其设置为占位符上的参数.

public function getCacheKeyInfo() {
    $info = parent::getCacheKeyInfo();
    $info['current_product_id'] = Mage::registry('current_product')->getId();
    $info['customer_id'] = Mage::getSingleton('customer/session')->getCustomerId();
    return $info;
}

现在可以使用$this->_placeholder->getAttribute('current_product_id')在容器类中访问这些值.

结论

您可能不想覆盖容器类中的_saveCache()以返回false.而是将客户ID和产品ID包含在_getCacheId()返回的字符串中.这样,每个客户都会获得自己的缓存条目.因为applyWithoutApp()可以从高速缓存中保存和加载动态块(如果同一客户两次查看某个页面),将减少一些开销.

_renderBlock()中设置所需的其他值,以使块能够在其上生成其内容,例如

$block->setProductId($this->_placeholder->getAttribute('current_product_id'));

在块方面,包括缓存信息数组中的产品ID和客户ID,即使在缓存块的情况下,也可以确保每个客户都能获得所请求页面的正确输出.

我不确定(您尚未提供块代码),但是我怀疑您使用的缓存ID并不包含将块的缓存记录唯一映射到的所有参数.合适的产品.

使用这些步骤并知道如何将参数传递给动态块容器,即使创建自定义动态块,也可以保留大部分FPC性能提升.我希望这些信息足以使您能够找到正在描述的问题并加以解决.

We would like to add a dynamic block to the product page. The problem is that the product page has full page caching (and we cannot turn that off due to speed issues). We want to display different information on each product page based on the logged in user's account, and it varies from product to product.

I created a separate block that has its own caching, but this displays the same block from the previous product page. I'm trying to alter it's caching method so it doesn't save the cache from the previous product page.

It works the first few times I go to the product pages, but then suddenly starts displaying a Magento error page that says, "The website encountered an error while retrieving http://www.mycompany.com/productpage.html.
It may be down for maintenance or configured incorrectly."

Here is what I've done so far.

I created app/code/local/MyCompany/MyModule/PageCache/etc/config.xml to add MyCompany_PageCache_Model.

Then I created the file that controls caching in app/code/local/MyCompany/MyModule/PageCache/Model/Container/MyFile.php
with these functions:

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

protected function _saveCache($data, $id, $tags = array(), $lifetime = null)
{
    return false;
}

protected function _renderBlock()
{
    $blockClass = $this->_placeholder->getAttribute('block');
    $template = $this->_placeholder->getAttribute('template');

    $block = new $blockClass;
    $block->setTemplate($template);
    $block->setLayout(Mage::app()->getLayout());
    return $block->toHtml();
}

I also created cache.xml under Catalog/etc with my placeholder for CONSTANT_CACHE.

Is the syntax above incorrect, or is there an easier way to do this?

解决方案

Overview

In order to answer I need to explain a little first. The Magento FPC process knows four states.

  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

State 1 and 2 are processed without the full Magento application being initialized. State 3 and 4 require the application to be initialized and routing to be processed. For that reason, aim to serve requests from state 1 and 2 if possible, otherwise you are losing a big part of the possible improvements of the FPC.

State 1

State 1 is boring from a developer point of view, nothing to do, so lets move on to...

State 2

In state 2 a page contains dynamic blocks. Right now, Magento has not been fully initialized.
The FPC processor loads a cached page and finds a placeholder for a dynamic block in it.
By analyzing the placeholder, the processor is able to identify the container class for the dynamic block, instantiates it, and calls applyWithoutApp($content) on it. (The name of the method refers to the fact that the Magento application hasn't been initialized so far). The container then tries to load the dynamic block contents from the block cache, using the cache key returned by the method $this->_getCacheId().
If a cache key is returned and a cache entry could be loaded, the container class replaces the placeholder in the $content with the cached block output and the FPC is done.
So far not much overhead has been produced.

State 3

So applyWithoutApp($content) in state 2 was unable to fetch and deliver the dynamic block content, so the block content needs to be generated, even though the rest of the page has been found in the FPC.
For this purpose the FPC module sets the request to pagecache/request/process, and the regular Magento application initialization and routing is followed.
This means a lot more overhead is produced then with state 2, even though it still is a bit better then a regular page load without the FPC, because e.g. the URL rewriting is skipped.
Finally the front controller and standard router delegate the request to the RequestController::processAction()method.
The method fetches the previously instantiated container class for the dynamic block, and calls applyInApp($content) on it.
This method runs $this->_renderBlock() to instantiate the real block class and return it's output. You already implemented this method according to your question. The FPC can now replace the placeholder with the block content and deliver the page.
One thing to be aware of is that this is not a regular product detail page request, so e.g. Mage::registry('current_product') is not available! Depending on your block implementation, this might influence the block level caching or content generation of the dynamic block. I suspect this might be where your problem stems from, but I'll get to a possible workaround a bit further down.

State 4

In this state the FPC didn't find a cache record for the requested page, so Magento generates the page as usual, e.g. the product detail page output is created by the Mage_Catalog_ProductController::viewAction().
All blocks that are configured to be dynamic, according to the cache.xml, are wrapped in placeholder tags.
The placeholder tags contain arguments, that are later passed to the container object for step 2 and 3. The only arguments that always are set are the container and the block class names. But almost always a cache_id and a template are set as well.
In the container class, these values can be accessed using $this->_placeholder->getAttribute('cache_id') (like you did in the _getCacheId() method of your container).

Even if you where glossing over most of this lengthy answer, this is where it might get interesting for you. If you need additional values to generate the blocks cache id or the block output, (e.g. the product id or the customer id), you can set these as arguments to the placeholder.

To do so you need to set them on the array returned by the block getCacheKeyInfo() method with a string as an array key. If you use a numeric array index they will not be set as arguments on the placeholder.

public function getCacheKeyInfo() {
    $info = parent::getCacheKeyInfo();
    $info['current_product_id'] = Mage::registry('current_product')->getId();
    $info['customer_id'] = Mage::getSingleton('customer/session')->getCustomerId();
    return $info;
}

These values are now accessible in the container class using $this->_placeholder->getAttribute('current_product_id').

Conclusion

You probably don't want to override _saveCache() in your container class to return false. Instead, include the customer id and product id in the string returned by _getCacheId(). That way each customer get's his own cache entry. Some overhead will be reduced because applyWithoutApp() can save and load the dynamic block from the cache (if a page is viewed twice by the same customer).

In _renderBlock() set the additional values you need in order for the block to be able to generate it's contents on it, e.g.

$block->setProductId($this->_placeholder->getAttribute('current_product_id'));

On the block side of things, including the product id and the customer id in the cache info array will ensure that each customer get's the correct output for the requested page, even when the block is cached.

I can't know for sure, (you haven't provided the block code), but I suspect the cache id you are using doesn't contain all the arguments it needs to uniquely map the cache record for the block to the right product.

Using the steps and knowing how to pass arguments to a dynamic block container it is possible to retain most of the FPC performance gain, even when creating custom dynamic blocks. I hope this information is enough for you to be able to track down the problem you are describing and fix it.

这篇关于如何在产品页面中包含动态块并启用全页面缓存?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆