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

developerWorks 中国  >  Open source  >

使用 CakePHP 快速打造 Web 站点,第 4 部分: 使用 CakePHP 的 Session 和 Request Handler 组件

使 PHP 应用程序更加流线化

developerWorks
前一页第 4 页,共 9 页后一页

文档选项

样例代码


对本教程的评价

帮助我们改进这些内容


使用 Request Handler

CakePHP 附带一个 Request Handler 组件,它允许以不同的方式来表示应用程序 —— 以请求类型为依据。在先前的部分中,您看到了布局如何控制应用程序的外观和感觉,在默认情况下,在 CakePHP 中布局代码是 XHTML Transitional。在这里,我们将讨论如何使用 Request Handler 以一种不同于 XHTML 的形式来返回应用程序的数据,这取决于是什么在请求页面。

剖析 HTTP 请求

Web 页面中的每个元素都是通过 HTTP 请求获得的,请求包含一个文本头部(包含被请求资源)和关于发出请求的代理的信息。下面给出一个示例 HTTP 请求。


清单 11. 示例 HTTP 请求
                    
GET / HTTP/1.1
Host: www.google.com
User-Agent: Mozilla/5.0 (Macintosh; U; PPC Mac OS X Mach-O; en-US; rv:1.8.1) 
Gecko/20061010 Firefox/2.0
Accept: text/xml,application/xhtml+xml,text/html
Accept-Language: en-us,en
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8
Keep-Alive: 300
Connection: keep-alive
Cache-Control: max-age=0

在以往的 Web 站点中,常常通过尝试解析 User-Agent 字符串,向不同的 Web 浏览器提供完全不同的内容。例如,针对 Internet Explorer® 设计站点的公司可能会发现,在其他浏览器中查看站点时站点看起来不正常,他们会建议访问者使用 IE。

幸运的是,这种日子已经一去不复返了,友好的 Web 站点意味着:对于完全相同的资源,每个 Web 浏览器都应当获得完全相同的内容。请注意,相同的内容并不意味着必须以相同的方式来表示。

不建议使用 User-Agent 来确定如何设定内容的格式,更好的方法是使用 Accept 头部。在以上示例中,Firefox 接受三种类型的内容,这通过其 MIME 类型来识别:xml、xhtml+xml 和 html。





回页首


创建 RSS feed

现在将为 Tor 创建一个 RSS feed,它是所有新产品的更新列表。从 CakePHP V1.1.8 开始,这个特性发生了显著变化 — 现在更容易实现它了。需要在 Products 控制器中包含 Request Handler 组件,创建一个使用 RSS Helper 的视图,并让 Router 解析 RSS 扩展。

当使用 Request Handler 组件时,提供产品列表是很容易的。由于 feed 位于 /products,因此打开包含 ProductsController 类的文件。首先包含 Request Handler 组件。


清单 12. 包含 Request Handler 组件
                    
<?php
class ProductsController extends AppController
{

  var $name = 'Products';
  var $helpers = array('Html', 'Form' );
  var $components = array('Acl', 'Security', 'RequestHandler'); 
  ...
  
}

除了这里提到的特性之外,Request Handler 还提供了许多功能。可以使用它判断接收到的请求的类型等等。但是目前,仅仅包含这个组件就够了。

接下来,需要为 RSS feed 创建一个视图。这个视图将在需要时自动显示,并使用 RSS Helper 完成工作。创建 app/views/products/rss/index.ctp 文件(可能需要创建 RSS 目录)。这个视图应该像下面这样。


清单 13. products index RSS 视图
                    
<?php
     echo $rss->items($products, 'transformRSS');
     function transformRSS($products) {
           return array(
                'title'  => $products['Product']['title'],
                 'link'    => array('url'=>'/products/view/'
				       .$products['Product']['id']),
                        'description'   => $products['Product']['description'],
                );
        }
?>

最后,在 app/config/routes.php 文件中添加下面这一行:Router::parseExtensions('rss');

好了!现在可以在 http://localhost/products/index.rss 查看新的 RSS feed。您可能需要查看源代码。如果调试级别设置为 2 或 3,那么调试信息肯定会导致 XML 格式失效。但是,您应该能够体会到在 CakePHP 中操作 RSS 是很容易的。





回页首


添加 Ajax

Asynchronous JavaScript and XML(Ajax)是创建交互式 Web 应用程序而无需牺牲浏览器兼容性的流行方法。在本节中,将说明如何使用 CakePHP 的 Ajax Helper 组件和 Request Handler 组件,使产品的更新流线化。本节假定您基本熟悉 Ajax 概念。如果需要查阅介绍性文章,可以阅读 “Mastering Ajax” 和 “Considering Ajax”(请参阅 参考资料)。

Ajax 是一系列从客户机发给服务器的异步请求,通常由用户交互操作发起。由于 CakePHP 把后端插件和前端插件分别分为组件和 helper,这意味着所有 JavaScript 函数都将由 Ajax helper 实现,而所有后端功能都将由 Request Handler 组件来支持。

实际上,Request Handler 组件允许重用已创建的非 Ajax 代码。它有一个 isajax 方法,如果请求是由 XMLHttpRequest 调用发出的,这个方法就返回 true。在控制器中,可以使用此方法来检查请求的性质,并根据是否涉及 Ajax 调整输出。

script.aculo.us

CakePHP 的 Ajax helper 要求使用 Prototype for Ajax 和 script.aculo.us。它们都是在 MIT 许可下发行的。(MIT 许可与 CakePHP 相同,基本上意味着可以将软件用于各种用途,基本不受限制)。script.aculo.us 附带 Prototype 的拷贝,所以不需要同时下载它们。首先应当下载最新版本的 Script.aculo.us(请参阅 参考资料)。实际上,并不需要了解关于 Script.aculo.us 的很多信息,只需在站点中包含它的 JavaScript 文件即可。

下载并解压 Script.aculo.us 之后,应当会看到一个名为 src 的文件夹,其中有许多 JavaScript 文件。您可能不会用到所有这些文件,但也应该将它们都复制到站点的 app/webroot/js 文件夹中。另外,将文件 lib/prototype.js 复制到 app/webroot/js 中。

既然已经有了 Script.aculo.us,现在需要在每个页面上包含 JavaScript。打开文件 app/views/layouts/default.ctp 并添加与 JavaScript 相关的代码,如下所示:


清单 14. 更改头部
                    
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<?php
if ( isset($javascript) ) {
  echo $javascript->link('prototype.js');
  echo $javascript->link('scriptaculous.js?load=effects');
  echo $javascript->link('controls.js');
}
?>
...

以上就是与 Script.aculo.us 库相关的所有工作。以后的所有操作仅涉及 CakePHP 中的对象。





回页首


添加收藏列表

在第 2 部分中提到,可以用 hasManybelongsTo 关系将两个模型链接起来。具体地说,我们把经销商和产品链接了起来。还有另一种关系 hasAndBelongsToMany,当要链接多个模型时这种关系特别有用。

为了演示,假设您通过使用 hasMany 关系创建了用户收藏的产品。那么,每个产品必须通过 belongsTo 关系链接到一个特定的用户,这意味着任何其他用户都不能将其标为收藏。这不是一种好的做法。产生的数据库结构如图 2 所示。


图 2. hasManybelongsTo 关系的数据库模式
hasMany 和 belongsTo 关系的数据库模式

请注意,一个产品只能属于一个用户。相反,hasAndBelongsToMany 关系使用一个 “联结表”,这是一种确保一个用户拥有多个产品,同时不让产品专属于一个用户的方法。联结表只记录用户-产品对列表,以表示两者是链接在一起的。产生的数据库结构如下所示。


图 3. hasAndBelongsToMany 关系的数据库模式
hasAndBelongsToMany 关系的数据库模式

在两个表之间可以建立任意数目的链接。如果正确地设置了联结表,CakePHP 就会自动地为您创建正确的链接。为了确保 CakePHP 可以找到您的联结表,必须遵守一些命名约定:

  • 表的名称应当是两个模型的名称,使用复数形式并按字母顺序排序。例如,如果要链接 user 和 group 对象,表的名称应当是 groups_users。
  • 表应当有两个外键,每个外键的命名都与 “belongsTo” 关系相同。例如,users/groups 联结表应该有 user_id 和 group_id 字段。
  • 为了优化,两个键应当构成联结表中的主键。

在我们的例子中,要把产品与用户链接起来,因此 SQL 查询将创建一个满足这些标准的表。


清单 15. 创建表把产品与用户链接起来
                    
CREATE TABLE products_users (
  product_id int(5) NOT NULL,
  user_id int(5) NOT NULL,
  PRIMARY KEY (product_id,user_id)
) ENGINE=MyISAM;
 

为了链接两个模型,只需把 hasAndBelongsToMany 关系添加到 product 和 user 模型上。


清单 16. 把 hasAndBelongsToMany 关系添加到 product 和 user 模型上
                    
//In app/models/user.php:
<?php
  class User extends AppModel
  {
    ...
    
    var $hasAndBelongsToMany = array( 'Product' =>
        array(
            'className' => 'Product'
        )
    );
}
?>

//In app/models/product.php:
<?php
  class Product extends AppModel
  {
    ...
    
    var $hasAndBelongsToMany = array( 'User' =>
        array(
            'className' => 'User'
        )
    );
}
?>

现在,每当从数据库中读取产品或用户,都将检查 products_users 表,查看有没有应当建立的链接。如果它找到适用的行,则将装载对应的对象。在 users_controller.php 中创建一个称为 favorites 的新操作。


清单 17. 获取收藏列表
                    
   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);
       }
   }





回页首


addToFavorites 操作

从界面的角度来看,我们需要让用户能够在他们的收藏中添加产品。因而,需要在 ProductsController 中添加 addToFavorites 操作。

打开 app/controllers/products_controller.php 文件并添加 addToFavorites 操作,如下所示。


清单 18. 添加 addToFavorites 操作
                    
 function addToFavorites($id) {
    $product = $this->Product->read(null, $id);
    $username = $this->Session->read('user');
    $success = false;
         if ($this->Acl->check($username, $id.'-'.$product['Product']
		                            ['title'], 'read')) {
              $result = $this->Product->User->findByUsername($username);
               product['User'] = array( 'User' =>
                   array($result['User']['id'])
               );
               $this->Product->save($product);
               $success = true;
         }
         if ( $this->RequestHandler->isAjax() ) {
               $this->layout = 'ajax';
               if ( $success ) {
                     echo 'Added product to favorites';
               } else {
                     echo 'Access denied';
                      }
         } else {
                if ( $success ) {
                     $this->Session->setFlash('Added product to favorites');
                     $this->redirect(array('controller' => 'users', 'action' 
					               => 'favorites'), null, true);
                 } else {
                     $this->Session->setFlash('Access denied');
                     $this->redirect(array('action' => 'index'), null, true);
                 }
         }
   }

让我们大致了解一下这个操作执行的内容。首先,它执行快速的 ACL 检查,查看用户是否能够读取产品。接下来,它将根据 user 会话变量中存储的值装载用户对象。然后,它设置数据的格式并将产品保存到收藏列表中。

最后,调用 Request Handler 来查看操作是不是 Ajax 请求。如果是,则返回一条信息性消息。否则,用户将被重定向到适当的页面。

现在可以访问 /products 来尝试这个操作,这会显示如图 4 所示的结果。查看产品列表并根据 ID 选择一个产品(比如产品 8)。然后访问 http://localhost/products/addToFavorites/8 并查看结果。虽然除了输出消息以外没有其他证据,但是链接已经建立。连接数据库并查看 products_users 表,应当会看到把您的 user_id 与选定的 product_id 链接起来的一行。


图 4. 成功地添加了产品
完成的收藏页





回页首


链接至 addToFavorites 操作

最后,在查看产品页面中应当提供一个指向 addToFavorites 操作的链接。将利用 Ajax helper 的链接方法来创建链接。$ajax->link 方法的用法与 $html->link 方法很相似。它接受以下参数:

  • $title — 将用作链接标题的字符串,在本例中为 “Delete”
  • $href — 链接将执行的操作
  • $options — 选项数组,其中最重要的是更新,也就是响应文本应当填入的元素的 ID;在本例中,我们要更新整个表
  • $confirm — 弹出一个确认操作的 JavaScript 警告框

这个方法输出建立完整的 Ajax 请求所需的全部 JavaScript。在后端,只需更新 products 控制器来处理新的请求类型。如果操作是 Ajax 调用的一部分,就使用 Request Handler 组件更改行为,如清单 18 所示。还需要在 products 视图中添加一个测试,检查是否启用了 Ajax;如果启用了 Ajax,就显示 Ajax 链接。这个链接将更新一个称为 favMessage 的 DOM 元素。需要在视图中的适当位置创建这个链接,比如在 product div 的末尾。


清单 19. 将收藏链接添加到 products 视图中
                    
<?php
if ( isset($ajax) ) {
      echo $ajax->link('Add to Favorites', array('action' => 'addToFavorites/' . 
         $product['Product']['id']), array('update' => 'flashMessage'));
} else {
      echo $html->link('Add to Favorites', array('action' => 'addToFavorites/' . 
         $product['Product']['id']));
}
?>

...

<span id='favMessage'> </span>
...

为了让链接生效,需要在 products 控制器中添加 JavaScript 和 Ajax helper:var $helpers = array('Html', 'Form', 'Javascript', 'Ajax' );

通过使用 CakePHP 的 Ajax helper,我们添加了 Ajax 功能,而完全不必编写 JavaScript 代码。





回页首


补充功能

应当尝试自己编写显示收藏的代码。需要将 favorites 方法添加到 users 控制器和 users/favorites 视图中。

完成后,尝试将 “Remove from favorites” 链接添加到产品列表中。设置它,以便根据产品是否已在收藏中把 “Add/Remove” 链接显示给用户。





回页首



前一页第 4 页,共 9 页后一页
    关于 IBM 隐私条约 联系 IBM 使用条款