IBM®
跳转到主要内容
    中国 [选择]    使用条款
 
 
Select a scope: Search for:    
    首页    产品    服务与解决方案     支持与下载    个性化服务    
跳转到主要内容

developerWorks 中国  >  Open source  >

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

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

developerWorks
文档选项

未显示需要 JavaScript 的文档选项

样例代码


级别: 中级

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

2007 年 1 月 16 日
更新 2008 年 2 月 21 日

CakePHP 是一种用 PHP 构建 Web 站点的辅助工具,它稳定、可直接用于生产及快速开发。“使用 CakePHP 快速打造 Web 站点” 系列教程向您展示如何使用 CakePHP 构建在线产品目录。

编辑说明:本系列最初发表于 2006 年和 2007 年。自从本系列发表以来,CakePHP 开发人员对 CakePHP 做了重大修改,因此原来的内容过时了。为了反映这些修改并充实本系列的内容,作者修订了本系列的五个部分,使它与 2008 年 1 月发布的 CakePHP 版本保持一致。

简介

使用 CakePHP 快速打造 Web 站点” 系列教程适合希望开始使用 CakePHP 轻松构建应用程序的 PHP 应用程序开发人员学习。通过本系列教程,您将了解如何安装和配置 CakePHP 以及有关 Model-View-Controller(MVC)设计、如何在 CakePHP 中检验用户数据、如何使用 CakePHP helper、如何使用 CakePHP 快速建立并运行应用程序的基本知识。听起来好像有很多东西要学习,但不必担心 — CakePHP 会替您完成其中的大部分工作。

本教程假定您已经完成了 第 1 部分第 2 部分第 3 部分第 4 部分 的学习,并仍具有为这些教程设置的工作环境。如果尚未安装 CakePHP,则应当回顾第 1 部分和第 2 部分,然后再继续学习。

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

系统需求

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

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

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

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





回页首


目前为止的 Tor

第 4 部分 中,您有机会使 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>

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





回页首


插入 Remove From Favorites 链接

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


清单 3. products 视图
                
<?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('Add to Favorites', array('action' =>
 'removeFromFavorites' , $product['Product']['id']),  array('update' =>
 'favMessage'));

还需要在 users 控制器中添加 JavaScript 和 Ajax helper,并在 products 控制器中添加 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');
                        }
                }
        }

同样,需要在 products 控制器中添加 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 链接。您是如何实现这个功能的呢?

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





回页首


缓存

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

缓存意味着什么?

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

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

为什么要进行缓存?

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

怎样进行缓存?

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

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

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





回页首


缓存哪些内容?

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

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

缓存特定请求

假定需要缓存一个特定请求。假定有三、四种产品在 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 的用户要在一分钟内多次添加产品。在这种情况下,缓存 index 视图可能会弊大于利。如果内容更新得过于频繁,以至于缓存的页面从未被实际使用过,则所做的操作无谓地增加了以下开销:保存缓存的页面和对于每个请求查看缓存的页面是否已更新。

但是,这种情况不意味着无法使用缓存。只需更具体地告诉 CakePHP 需要缓存的内容。

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

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

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

例如,在 Tor 中,第 4 部分中创建的默认布局包含一个根据用户名定制的欢迎词。


清单 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>

...

可以通过指定 products 控制器对 view 操作使用缓存来测试这段代码。将 Cache 添加到 helper 列表中并在 $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 视图以及 products index 视图都必须从缓存中清除。您是正确的。确实需要清除这些视图。

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

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

如何清除缓存

正如 CakePHP 提供了多种将内容放入缓存中的方法,也有多种方法可以将内容清除出缓存:

  • 要想只清除为产品 1 缓存的 view 视图,语法是 clearCache('products_view_1');
  • 要想清除 products 控制器的所有 view 视图的缓存,语法是 clearCache('products_view');
  • 要想清除 products 控制器的所有视图的缓存,语法是 clearCache('products');
  • 要想清除多种类型的视图的缓存,需要将一个数组传递给 clearCache
    clearCache('products', 'users_view');
    

    如果需要彻底清除缓存,只需调用函数 clearCache(); 而不提供任何参数。可以通过创建 emptycache 控制器并将这个函数调用放在 index 操作中来调用这个函数。然后可以直接地、手工地或者通过自动化过程调用这个操作。




回页首


结束语

分享这篇教程……

digg 提交到 Digg
del.icio.us 发布到 del.icio.us
Slashdot Slashdot 一下!

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

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

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






回页首


下载

描述名字大小下载方法
第 5 部分源代码os-php-cake5.source.zip14KBHTTP
关于下载方法的信息


参考资料

学习

获得产品和技术
  • 使用 IBM 试用软件 改进您的下一个开放源码开发项目,这些软件可以下载或者通过 DVD 获得。

  • 下载 IBM 产品评估版,试用这些来自 DB2®、Lotus®、Rational®、Tivoli® 和 WebSphere® 的应用程序开发工具和中间件产品。


讨论


关于作者

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




对本文的评价










回页首


IBM 公司保留在 developerWorks 网站上发表的内容的著作权。未经IBM公司或原始作者的书面明确许可,请勿转载。如果您希望转载,请通过 提交转载请求表单 联系我们的编辑团队。
    关于 IBM 隐私条约 联系 IBM 使用条款