使用 CakePHP 快速打造 web 站点,第 5 部分: 添加缓存

提高应用程序性能的好方法

CakePHP 是一种用 PHP 构建 Web 站点的辅助工具,它稳定、可直接用于生产及快速开发。“使用 CakePHP 快速打造 Web 站点” 系列教程向您展示如何使用 CakePHP 构建在线产品目录。第 5 部分处理缓存,特别是视图和布局缓存,可以帮助减少服务器资源消耗,加速您的应用程序。

Duane O'Brien, PHP 开发人员, 自由撰稿人

当 Oregon Trail 还仅仅是文字的时候,Duane O'Brien 就已经是一名全能的技术人员了。他最喜欢吃的食物是寿司。他还未曾到过月球。



2011 年 3 月 17 日 (最初于 2007 年 1 月 16 日)

编者按:本系列最初发表于 2006 年,并在 2007 年和 2008 年进行了更新。由于最后一版的发布,CakePHP 开发人员,

自从上次发布之后,CakePHP 开发人员对它进行了多次修改,并产生了本系列的多个修改版本。本修改版用的是 V1.3.4。

使用 CakePHP 快速打造 Web 站点” 系列教程是专为 PHP 应用程序开发人员而设计的,使用 CakePHP 可以使他们的生活变得轻松。在本系列的最后这篇文章中,您将学习如何安装和配置 CakePHP、Model-View-Controller(MVC)设计基本概念、如何在 CakePHP 中检验用户数据、如何使用 CakePHP 帮助函数、如何使用 CakePHP 快速建立并运行应用程序。听起来好像有很多东西要学习,但不必担心 — CakePHP 会替您完成其中的大部分工作。

本文假设您已经学习了 使用 CakePHP 快速打造 Web 站点,第 1 部分:入门使用 CakePHP 快速打造 Web 站点,第 2 部分:用 CakePHP 打造更大更好的站点使用 CakePHP 快速打造 Web 站点,第 3 部分: 使用 Sanitize 进行保护使用 CakePHP 快速打造 Web 站点,第 4 部分:使用 CakePHP 的 Session 和 Request Handler 组件,并仍保留为这些教程设置的工作环境。如果尚未安装 CakePHP,则应当回顾第 1 部分和第 2 部分,然后再继续学习。

本教程假设您熟悉 PHP 编程语言,基本掌握数据库设计且喜欢动手实践。

系统需求

开始之前,需要具备一个工作环境。CakePHP 的最低服务器需求为:

  1. 支持会话(并且最好支持 mod_rewrite)的 HTTP 服务器。本教程采用的是支持 mod_rewrite 的 Apache V2.2.4。
  2. PHP V4.3.2 或更高版本(包括 PHP V5)。本教程采用的是 PHP V2.3。
  3. 一个受支持的数据库引擎。本教程采用的是 MySQL V5.0.4。

还需要准备好一个数据库以供应用程序使用。本教程将提供在 MySQL 中创建任何必需的表的语法。

下载 CakePHP 的最简单方法是访问 CakeForge.org,并从“下载” 部分下载最新的稳定版本。本教程采用的是 V1.2.812.0。还可以直接使用来自 Subversion 的每日构建和拷贝。CakePHP Manual 中有更详细的信息(见 参考资料)。

目前为止的 Tor

使用 CakePHP 快速打造 Web 站点,第 4 部分:使用 CakePHP 的 Session 和 Request Handler 组件 中,您有机会使 Tor 更加流线化。您是如何做的呢?

添加 favorites 操作和视图

要查看收藏列表,需要将 favorites 操作添加到 users 控制器中。它可能类似于清单 1。

清单 1. 将 favorites 操作添加到 users 控制器中
function favorites () {
  $username = $this->Session->read('user');
  $favorites = array();
  if ($username)
  {
    $this->User->recursive = 2;
    $results = $this->User->findByUsername($username);
    foreach($results['Product'] as $product) 
    {
      $favorites[] = array('Product' => $product, 'Dealer' => $product['Dealer']);
    }
    $this->set('products', $favorites);
  } else {
    $this->redirect(array('controller' => 'users', 'action' => 'login'));
  }
}

注意,如果用户未登录则会重定向。这将使用户在未登录的情况下查看页面时不会看到错误。

还需要在 app/views/users/ 目录中编写 favorites.ctp 文件。它可能类似于以下清单。

清单 2. Favorites.ctp
<div class='favorites'>
<h2>Favorites</h2>
<table cellpadding="0" cellspacing="0">
<tr>
        <th>Id</th>
        <th>Title</th>
        <th>Dealer</th>
        <th>Description</th>
        <th class="actions">Actions</th>
</tr>
<?php
$i = 0;
foreach ($products as $product):
        $class = null;
        if ($i++ % 2 == 0) {
                $class = ' class="altrow"';
        }
?>
        <tr<?php echo $class;?>>
                <td>
                        <?php echo $product['Product']['id'] ?>
                </td>
                <td>
                        <?php echo $product['Product']['title'] ?>
                </td>
                <td>
                        <?php echo $html->link($product['Dealer']
                  ['title'], array('controller'=> 'dealers',
                  'action'=>'view', $product
                  ['Dealer']['id'])); ?>
                </td>
                <td>
                        <?php echo $product['Product']['description'] ?>
                </td>
                <td class="actions">
                        <?php echo $html->link('View', array
                    ('controller' => 'products', 
                    'action'=>'view', $product['Product']
                    ['id'])); ?>
                </td>
        </tr>
<?php endforeach; ?>
</table>
</div>

视图实际需要做的全部操作是显示产品表。但是,目前无法从收藏列表中删除产品。


插入 Remove From Favorites 链接

另一项任务是将 Remove From Favorites 链接插入产品表并设置它,以便当产品位于用户的收藏列表中时用户会看到 Remove 链接,当产品不在收藏列表中时用户会看到 Add 链接。再来看一看产品的 View 视图,以下部分是最重要的。

清单 3. 产品表
<?php
if ( isset($ajax) ) {
        echo $ajax->link('Add to Favorites', array('action' =>
 'addToFavorites/' . $product['Product']['id']),  array('update' =>
 'favMessage'));
} else {
        echo $html->link('Add to Favorites', array('action' =>
 'addToFavorites/' . $product['Product']['id']));
}
?>

Remove From Favorites 链接很可能与如下所示的清单相同。

清单 4. Remove From Favorites 链接
echo $ajax->link('Remove From Favorites', array('action' =>
 'removeFromFavorites/' . $product['Product']['id']),  array('update' =>
 'favMessage'));

您还需要在 users 控制器中添加 JavaScript 和 Ajax 帮助程序,并在产品控制器中添加 removeFromFavorites 操作。

清单 5. removeFromFavorites 操作
function removeFromFavorites($id) {
                $username = $this->Session->read('user');
                $success = false;
                $user = $this->Product->User->findByUsername
			($username, 'User.id');
                $this->Product->User->id = $user['User']['id'];
                $success = $this->Product->User->removeFavorite($id);
                if ( $this->RequestHandler->isAjax() ) {
                        echo 'Removed product from favorites';
                        exit;
                } else {
                        if ( $success ) {
                                $this->Session->setFlash('Removed 
						product from favorites');
                                $this->redirect('/users/favorites');
                        } else {
                                $this->Session->setFlash('Access denied');
                                $this->redirect('/products/index');
                        }
                }
        }

同样,您需要在用户模型中添加 removeFavorite 方法。

清单 6. 创建 removeFavorite 方法
function removeFavorite($product_id) {
                if($this->exists()) {
                        $user = $this->read();
                        $fav = array();
                        foreach($user['Product'] as $product) {
                                if ($product_id != $product['id']) {
                                        $fav[] = $product['id'];
                                }
                        }
                        $user['Product'] = array('Product'=> $fav);
                        if($this->save($user)) {
                                return true;
                        } else {
                                return false;
                        }
                }
                return false;
        }

正如您所注意到的,在测试时,如果您查看产品,系统总是显示 Add to Favorites。相反,用户可以看到已经添加到收藏列表中的产品的 Remove From Favorites 链接。您是如何实现的呢?

您的解决方案可能很不一样。这很好。现在我们来讨论本系列中的下一个主题:缓存。


缓存

在概念方面,缓存有时可能会造成混淆。缓存有多种类型,并且每种缓存都有一些挑战和优点。因此很有必要了解在这种环境下缓存的具体含义。

缓存意味着什么?

通常,如果发出请求,且应用程序响应 “我没必要再去获取数据。我已经获得了数据”,那么就是发生了缓存。在大多数情况下,当计算机用户听到 “缓存” 一词时,都会想到浏览器的缓存。通常,为了加快响应速度,浏览器会保留它认为是静态文件的拷贝 — 通常包括图片、样式表、静态 HTML 和脚本文件。虽然此类缓存有时可能会给 Web 应用程序开发人员造成麻烦,但此类缓存不是本文所关注的缓存。

当浏览器向 Web 应用程序发出请求以获得一些内容时,可能会发生另一种缓存。如果 Web 应用程序使用缓存,它可能用以前生成内容的拷贝来响应请求,这会免除第二次生成内容所涉及的资源开销。这才是本文所关注的缓存类型。

为什么要进行缓存?

通常,在应用程序中使用缓存有两个原因。第一,缓存帮助减少服务器上的资源开销。虽然在大多数情况下节省的资源开销可能很少,但是对于处理大量请求的高流量站点来说,这些小节省可以迅速地累积为明显的性能优势。第二个原因通常是速度。由于应用程序无需执行为请求重新生成内容的过程,因此可以更快速地提供内容。同样,虽然在大多数情况下节省的时间可能很少,但是通过使用缓存,高流量站点可以快速地获得速度优势。

怎样进行缓存?

OK — 理解了缓存之后,就可以准备缓存任何内容了。那么,如何才能进行缓存呢?CakePHP 提供了哪些功能使您可以轻松地执行此操作?

首先,需要启用缓存。默认情况下,该特性处于禁用状态。可以在 app/config/core.php 中启用缓存。查找以下内容://Configure::write('Cache.check', true);,然后取消注释。

通过将 Cache.check 设置为 true,可以通知 CakePHP 缓存现在已启用。来吧现在就开始,稍后对缓存进行设置。仅仅启用缓存还不够,还必须准确地告诉 CakePHP 需要缓存的内容以及缓存多久。


缓存哪些内容?

启用了缓存后,必须指定您想缓存的内容。首先,在需要缓存视图的控制器中,将 Cache 添加到 helpers 数组中。例如,如果需要缓存产品视图,则必须在产品控制器的 helpers 数组中包含 Cache。在为 Tor 构建产品控制器时,已指定了要使用 HTML 和表单 helpers。在这个列表中添加 Cache,helpers 数组将类似于:var $helpers = array('Html', 'Form', 'Javascript', 'Ajax', 'Cache' );

既然已经使用了缓存帮助程序,就需要准确地指定需要缓存的内容。有多种方法可以执行此操作,但是所有这些方法都依赖于 $cacheAction 数组。

缓存特定请求

假定您需要缓存一个特定请求。假定有 3、4 种产品在 view 操作上遇到高流量,您希望为这些产品缓存 View 视图。在这种情况下,您需要把希望缓存的请求指定为 $cacheAction 的数组键并把时间长度指定为键值。与 $helpers 数组一样,$cacheAction 数组是一个类变量。要缓存这些特定视图,$cacheAction 可能类似于清单 7。

清单 7. $cacheAction
<?php
class ProductsController extends AppController {
...

var $cacheAction = array (
  'view/1/' => 3600,
  'view/2/' => 3600,
  'view/3/' => 3600
);

这个 $cacheAction 告知 CakePHP 把产品 1 至产品 3 的 View 视图缓存 3,600 秒(一小时)。指定的时间长度可以是 strtotime() 能够解释的任意格式。可以简单地指定 1 hour

缓存整个操作

只缓存某些产品可能还不够。可能还需要缓存特定操作的所有视图。假定需要对 edit 操作的视图使用缓存。为此,把这个操作指定为数组键并指定在缓存中保留视图的时间长度,像上面一样。

var $cacheAction = array (
  'edit/' => '+1 hour'
);

您可以混合搭配这两种方法。

清单 8. 混合搭配
var $cacheAction = array (
  'view/1/' => 3600,
  'view/2/' => 3600,
  'view/3/' => 3600,
  'edit/' => '+1 hour'
);

这会帮助节省一些时间。现在已经缓存了最常查看的产品的 View 视图和所有 edit 视图。但您可能还想做更多事情。

缓存控制器执行的所有操作

您可能希望缓存控制器执行的所有操作。如果是这样,则没必要在 $cacheAction 数组中指定每个操作。只需将 $cacheAction 的值设为一个时间长度: var $cacheAction = "+1 hour";,保证全部被缓存。

这个值必须是 strtotime() 可以解释的字符串。通过将 $cacheAction 设置为一个单一值,就让 CakePHP 知道要缓存控制器的所有视图。

从操作内缓存

由于 $cacheAction 是一个类变量,因此也可以从操作内访问这个变量。假定需要从操作内修改 $cacheAction,可以使用与修改任何类变量一样的语法。

function foo() {
  $this->cacheAction = array()...
}

通常,不必这么做,但是可能会遇到确实需要这么做的情况。如果是这样,您现在知道该怎么做了。CakePHP 提供了几种缓存视图的方法。这是很简单的部分。了解何时进行缓存 — 或者更具体地说,何时缓存 — 才可能有点儿麻烦。


何时缓存?

缓存的好处可能很吸引人。而且,在大多数情况下,缓存都会完全按照您的要求执行。那么,究竟在何时不需要缓存视图呢?

在大多数情况下,不希望完全缓存那些经常更新的数据。例如,假定 Tor 的用户要在一分钟内多次添加产品。在这种情况下,缓存索引视图可能会弊大于利。如果内容更新得过于频繁,以至于缓存的页面从未被实际使用过,则所做的操作无谓地增加了以下开销:保存缓存的页面和对于每个请求查看缓存的页面是否已更新。

但是,这不意味着无法使用缓存。只需具体告诉 CakePHP 需要缓存哪些内容。

查看 <cake:nocache></cake:nocache> 标记

在视图或布局内,CakePHP 允许您明确地从缓存的范围中排除一些内容,方法为将内容放在 <cake:nocache></cake:nocache> 标记中。正确地使用此标记将让 CakePHP 缓存视图或布局的静态部分,同时确保对于每个请求都检索页面的动态部分。

并非所有内容都可以放在 <cake:nocache></cake:nocache> 标记中。具体地说,不能只将变量放在 <cake:nocache></cake:nocache> 标记中以使其保持动态。一般而言,可以放在 <cake:nocache></cake:nocache> 标记中的内容是 CakePHP 结构,如帮助程序和元素调用。

清单 9. 默认布局
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

...
   <div id="header">
     <h1><?php echo $html->link('Tor', '/') ?> : Welcome <?php echo 
$session->read('user') ?></h1>

...

如果启用了缓存,在首次装载使用默认布局的任何页面时,Welcome <?php echo $session->read('user') ?> 就会被替换为诸如 Welcome 或 Welcome wrestler 之类的内容 — 这意味着无论哪个用户登录,都会看到一个缓存用户名。

为了指定不应当缓存的用户名,需要把 <?php echo $session->read('user') ?> 行放在 <cake:nocache></cake:nocache> 标记中。最终的结果类似于以下清单:

清单 10. 指定不应当缓存用户名
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>

...

   <div id="header">
     <h1><?php echo $html->link('Tor', '/') ?> : Welcome 
<cake:nocache><?php echo $session->read('user') 
?></cake:nocache></h1>

...

您可以通过指定产品控制器对 view 操作使用缓存来测试这段代码。将 Cache 添加到帮助程序列表中并在 $cacheAction 中指定一个视图。

清单 11. 指定 products 控制器对 view 操作使用缓存
<?php
class ProductsController extends AppController
{
  var $name = 'Products';
  var $helpers = array('Html', 'Form', 'Javascript', 'Ajax', 'Cache' );
  var $components = array('Acl','Security','RequestHandler');
  var $cacheAction = array(
    'view/' => '+1 hour'
  );
...

现在,请查看一个产品。应当会看到在 app/tmp/cache/views 中创建了一个与所查看产品对应的文件。以另一个用户的身份登录并查看同一个产品。您将发现自己的用户名并未被缓存。编辑该产品。您将发现 CakePHP 知道应该删除缓存的视图。这真是太棒了。

使用 <cake:nocache></cake:nocache> 标记可以帮助您在对视图进行缓存和让内容保持最新之间取得平衡。有时这还不够。有时需要自己清除缓存。


何时清除缓存

即使您在缓存与不缓存哪些内容方面的决策充满智慧,有时仍然有必要手工清除缓存。您可能会想到,在数据已经被更新后显然需要这么做。例如,如果编辑了一个产品,则该产品的 view 和 edit 视图以及产品 index 视图都必须从缓存中清除。您是正确的。确实需要清除这些视图。

不必亲自这样做。CakePHP 会替您完成此操作。CakePHP 知道在插入、更新或删除数据时,如果影响到缓存的视图,就需要清除该视图的缓存。这将避免大量工作。

但是有时也可能有必要显式地清除缓存。例如,如果一个外部进程(例如安排好的批处理脚本)要直接更新数据库中的数据,而不是通过应用程序,则需要手工清除缓存。这可以使用全局函数 clearCache 来完成。

如何清除缓存

您通常不需要清理缓存,因为当对这些文件的数据进行 INSERT、UPDATE 或 DELETE 操作时,CakePHP 会清理相关缓存文件。

如果需要彻底清理整个缓存,您可以在一个指定缓存清理操作中调用函数 Cache::clear(),或者您也可以创建一个控制器来实现这一目的。如果您需要自动化进程也是非常方便。


结束语

祝贺您!您已经完成了 “使用 CakePHP 快速打造 Web 站点” 教程的学习。现在,您可能很兴奋,迫不及待地想构建自己的应用程序。

但在这样做之前,请在 app/config/core.php 中将 DEBUG 设置为 2。这将让 CakePHP 在视图底部显示一些 SQL 调试信息。可以在 app/tmp/cache/views 中找到缓存的页面。打开其中一个来看一下缓存页面的样子。删除缓存的文件并查看一个产品。记下运行了多少个查询及其花费的时间。现在重新装载。对视图进行缓存。这次运行了多少个查询?花了多少时间?通过练习逐渐熟悉缓存,寻找在 Tor 中使用缓存的方法。完成后,抛开这些,全身心地投入到您自己的应用程序中。

您已经在这个系列教程中学到了很多知识,但这还远远不够,若要加深对 CakePHP 的理解,最好的方法是在 CakePHP 中从头开始编写自己的应用程序。


下载

描述名字大小
第 5 部分源代码os-php-cake5.source.zip14KB

参考资料

学习

获得产品和技术

讨论

条评论

developerWorks: 登录

标有星(*)号的字段是必填字段。


需要一个 IBM ID?
忘记 IBM ID?


忘记密码?
更改您的密码

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件

 


这是您第一次登陆到 developerWorks,已经自动为您创建了您的概要文件。 选择您概要文件中可以公开的信息的信息(如姓名、国家/地区,以及公司),这些信息同时也会与您所发布的内容相关联。 您可以随时更新您的 IBM 账号。

所有提交的信息确保安全。

选择您的昵称



当您初次登录到 developerWorks 时,将会为您创建一份概要信息,您需要指定一个昵称。您的昵称将和您在 developerWorks 发布的内容显示在一起。

昵称长度在 3 至 31 个字符之间。 您的昵称在 developerWorks 社区中必须是唯一的,并且出于隐私保护的原因,不能是您的电子邮件地址。

标有星(*)号的字段是必填字段。

(昵称长度在 3 至 31 个字符之间)

单击提交则表示您同意developerWorks 的条款和条件。 查看条款和条件.

 


所有提交的信息确保安全。


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source
ArticleID=220965
ArticleTitle=使用 CakePHP 快速打造 web 站点,第 5 部分: 添加缓存
publish-date=03172011