应用Yii1.1和PHP5进行敏捷Web开发11

第十章:迭代7:添加RSS Web Feed

在上次的迭代中,我们添加了用户对问题(issue)进行评论的功能及利用portlet结构轻松整合列表显示在应用程序中的任何位置。在本次迭代中,我们将利用这一点,实现一个RSS数据feed的评论列表。此外,我们将使用另一个开源框架Zend Framework中现有的可用的feed功能。以证明在Yii应用中整合第三方工具是多么的容易。

迭代计划

本次迭代的目标是使用用户生成的评论内容创建一个RSS feed。我们应该允许用户订阅所有项目(project)及单独项目的评论feed。幸运的是,我们之前使用挂件功能返回了所有项目最近评论列表或限制为指定的一个项目的数据。所以,我们已经编写了适当的方法来访问所需的数据。这次迭代大部分精力将放在对这些数据以正确RSS feed格式进行发布,并添加链接允许订阅的用户访问我们的应用程序。

为了实现这些目标,我们将需要完成以下更高级的任务列表:

    • 下载并安装Zend FrameWork 到Yii应用程序中。
    • 在一个控制器类中创建一个新的操作,用来响应feed请求并返回RSS格式的数据。
    • 改变易于使用的URL结构。
    • 添加我们新创建的feed到两个项目(project)的列表页和每个项目的详细页面

与往常一样,在作出任何更改之前,一定要先运行单元测试套件来保证预期的工作相符。

关于RSS内容聚合及Zend Framework的背景

Web的内容聚合已经出现很多久了,但最近才比较流行。Web内容聚合指的是一种发布信息的标准格式,通过这种格式其他网站的读取可以很容易地访问应用程序。许多新闻网站一直以这种格式发布它们的内容,从互联网上网络日志(也称博客)提供内容聚合到几乎每一个网站都有这个功能。当然我们的 TrackStar应用程序也不会例外。

RSS是一个缩写,全称Readlly Simple Syndication。它是一种XML格式文件用来提供一个Web内容聚合的标准。还有其他的格式可以使用,但由于绝大多数的网站都使用RSS,我们将着重介绍这种格式。

Zend是一家”PHP公司”。其创始人是PHP语言的核心贡献者,Zend致力于创造新产品用来帮助开发人员改善PHP应用程序的整个开发生命周期。他们提供的产品和服务会帮助你配置和安装PHP,以及应用程序的开发,部置,生产管理和维护。其中一款有助于应用程序开的产品是Zend Framework。该框架可以作为一个整体来提供一个完整的应用程序,比如可以使用同样的方式开发Trackstar应用程序,或者使用单独的功能组件库。Yii的灵活之处是让我们可以使用其他的框架组件。我们将使用Zend Framework中的一个组件,它是Zend_Feed。使用它可以让我们不用写任何底层的代码就能生成一个RSS格式的Web feed。关于更多的Zend_Feed信息,请访问 http://www.zendframework.com/manual/en/zend.feed.html

安装Zend框架

由于我们使用Zend框架的帮助完成我们的RSS需求,我们首先需要下载和安装它。要获取最新版本,请访问http://framework.zend.com/download/latest。我们将只采单这个框架的一部份Zend_Feed,所以Zend框架的最小版本就中足够了。

当你展开下载文件后,你应该看到以下文件及目录结构:

INSTALL.txt 
LICENSE.txt 
README.txt 
bin/ 
library/

为了在Yii应用程序中使用这个框架,我们需要将一些文件移到我们的应用程序的目录结构中。让我们在 /protected目录下创建一个新的目录叫vendors 。然后移动Zend框架目录下的/library/Zend目录到新创建的目录下。移动完成了,确保在TrackStar应用程序中存在 protected/vendors/Zend/Feed.php文件。

使用Zend_Feed

Zend_Feed是Zend框架中的一个小组件,它封这装了创建Web Feed背后复杂的过程,提供了简单易于使用的接口,这将有助于我们在很短的时间就可以完成这项功能和测试RSS兼容数据。我们所需要做的是通过Zend_Feed来格式化我们的评论数据。

我们需要一个地方放置我们需要处理的feed的请求的位置。我们可以创建一个新的控制器,但是为了简便起见,我们只需要在CommentController.php文件中添加一个新的操作来处理请求。现在我们只为此方法添加一些功能,然后我们再讨论整个方法。

打开CommentController.php文件,并添加如下公有方法:

public function actionFeed()
{
    if(isset($_GET['pid'])) $projectId = intval($_GET['pid']);
    else $projectId = null;
 
    $comments = Comment::model()->findRecentComments(20, $projectId);
 
    //convert from an array of comment AR class instances to an  name=>value array for Zend
    $entries=array();
 
    foreach($comments as $comment)
    {
        $entries[]=array(
            'title'=>$comment->issue->name,
            'link'=>CHtml::encode($this->createAbsoluteUrl('issue/view',array('id'=>$comment->issue->id))),
            'description'=> $comment->author->username . 'says:' . 
                    $comment->content,'lastUpdate'=>strtotime($comment->create_time),
            'author'=>$comment->author->username,
        );
    }
 
    //now use the Zend Feed class to generate the Feed
    // generate and render RSS feed
    $feed=Zend_Feed::importArray(array(
            'title'=> 'Trackstar Project Comments Feed',
            'link'=> $this->createUrl(''),
            'charset' => 'UTF-8',    
            'entries' => $entries,
    ), 'rss');
 
    $feed->send();
 
}

这一切都非常简单。首先,我们检查输入请求的pid参数是否存在,这里我们所采取的是一个具体的项目ID。记住这里我们希望有选择地让数据的内容限制在一个指定的项目的相关评论。接下来,我们用findRecentComments的方法获得20条最近评论列表,这里如果指定项目ID,则是具体的项目的评论,否则是所有的项目的评论。

你可能还记得,这个方法返回一个Comment AR类实例的数组。我们遍历这个数,通过Zend_Feed组件来返回转换成所需格式的数据。Zend_Feed期望的是简单的数组,其本身的每个元素是一个包含评论实体的数组。每个实体是以一个简单的name=>value对的关联数组。为了符合具体的RSS格式,我们的每个条目必须至少包含 一个title(标题) ,link(链接)和description(描述)。我们还增加了两个可选的字段 ,一个名为lastUpdate(最后更新),这是Zend_Feed转换为RSS格式的发布日期,另一个指定为author(作者)。

有一些额外的辅助方法,让我们可以获取正确的数据格式。首先,我们使用控制器的createAbsoluteUrl()方法,而不仅仅是createUrl()方法,以创建一个完整的URL,使用createAbsoluteUrl()将产生类似下面的链接:http://localhost/trackstar/index.php?r=issue/view&id=5 相对于 /index.php?r=issue/view&id=5。

同时,为了避免出现”unterminated entity reference”错误,使用PHP的DOMDocument::createElement(),它是由Zend_Feed用来生成RSS格式的 XML,我们需要使用辅助函数如:CHTML:encode 来转换所有使用了HTML实体的字符。所以我们进行编码这样的链接的URL看起来像这样:

http://localhost/trackstar/index.php?r=issue/view&id=5

将转换为:

http://localhost/trackstar/index.php?r=issue/view&id=5

一旦我们的所有实体都已经被正确填充和格式化,我们可以使用Zend_Feed的importArray()方法,它期望一个数据来构造 RSS内容。最后,一旦Zend_Feed类从输入的数组返回,我们将调用Zend_Feed类的send()方法。这将返回正确的RSS格式的XML和发送给客户端的XML文件头。

我们需要修改些配置,在文件CommentController.php中的类前添加代码。首先我们需要导入/vendors/Zend /Feed.php文件以及Feed/目录下的Rss.php文件。在CommentController.php文件的顶部添加以下代码:

Yii::import('application.vendors.*'); 
require_once('Zend/Feed.php'); 
require_once('Zend/Feed/Rss.php');

然后,修改CommentController::accessRules()方法,允许任何用户访问我们新增方法actionFeed():

public function accessRules()
{
    return array(
        array('allow',  // allow all users to perform 'index' and 'view' actions
            'actions'=>array('index','view', 'feed'),
             'users'=>array('*'),
        ),
    …

这就是全部内容。如果我们访问http://localhost/trackstar/index.php?r=comment/feed,我们能看到我们努力的结果。不同的浏览器将显示不同的RSS结果,你所看到的可能与截图不同。下面的截图是使用Firefox浏览器查看RSS的结果。

应用Yii1.1和PHP5进行敏捷Web开发11_第1张图片

创建友好的URL

到目前为止,整个开发过程中,我们使用了yii应用程序默认格式的URL结构,在第2章,我们讨论了这种格式,它采用了查询参数的方式。主要参数‘r’代表路由,是由controllerID/actionID对组成,其次是action方法所需要的一些可选的查询参数。我们新创建的feed操作也不例外,但这是一个很长,繁琐且不友好的URL结构。有更好的方法吗?嗯,事实上是有的。

使用路径格式,我们可以让上面的URL看起来更清晰,更明了。消除URL中的查询参数字符串,把查询参数放到路径信息中:

以我们的评论feed URL为例,替换:

http://localhost/trackstar/index.php?r=comment/feed

为我们需要的:

http://localhost/trackstar/index.php/comment/feed/

更多情况,我们甚至不需要指定入口文件。我们也可以利用Yii的路由配置选项删除指定的controllerID/actionID对。我们的要求将像这样:

http://localhost/trackstar/commentfeed

还有,普遍的feed URL最后都有一个.xml扩展名。所以,如果我们可以改成下面的这样那就更好了:

http://localhost/trackstar/commentfeed.xml

这样将大大简化了URL地址,并且也符合各大搜索引擎的收录格式(通常被称为“友好的搜索引擎URL”)。让我们看看如何使用Yii的URL管理功能,改变我们的URL以符合我们需要的格式。

使用URL管理器

在Yii中内置的URL管理器是一个应用程序组件,它可以在protected/config/main.php文件中配置。让我们打开这个文件,并在组件数组中添加一个新的URL管理器组件的声明:

'urlManager'=>array( 
    'urlFormat'=>'path',
),

只要我们使用默认的名字urlManager,我们不需要指定组件的类名,因为它已经在CWebApplication.php框架类中预定义为CUrlManager.php。

通过简单的添加配置,整个站点的URL结构变成了'path'格式。例如,以前如果我们想查看一个具体的ID为1的issue,我们会使用下面的URL:

http://localhost/trackstar/index.php?r=issue/view&id=1

但有了这些变化,我们的网址现在看起来像这样:

http://localhost/trackstar/index.php/issue/view/id/1

你将会注意到,我们所作出的修改已经影响了整个应用程序。注意到这点,再次访问feed将使用http://localhost/trackstar/index.php/comment/feed/。我们注意到,所有issue的联系都被重新格化成了新的URL结构。这一切都要感谢控制器方法和助手方法来帮助我们生成URL。我们仅修改了配置文件中的URL格式,整个应用程序都会更改。

我们的URL看起来很好,但我们仍然有一个指定的入口文件index.php。并且我们还没有追加一个.xml的后缀。因此,我们将隐藏URL中index.php的部份,并且设置请求路由,让它明白请求commentfeed.xml就是请求CommentController.php类中的actionFeed()方法。让我们先解决后者。

配置路由规则

Yii的URL管理器允许我们指定规则来定义URL的解析和创建。定义一个路由规则和模式,该模式用于匹配URL,以确定哪些规则用于解析或创建URL。该模式可以包含命名参数,它使用的语法为ParamName:RegExp(参数名:正则表达式)。当解析一个URL,一个匹配的规则将提取路径信息中的命名的参数放到$_GET变量中。当通过应用程序创建一个URL时,一个匹配的规则将从$_GET变量中提取命名的参数并放到路径信息中。如果模式以‘/*’结尾,它的意思有更多的GET参数附加到URL路径信息的中。

指定URL规则,设置CUrlManager的rules属性,rules是一个数组,其中的格式为pattern=>route(模式=>路由)。

举个例子,让我们看看下面两条规则:

'urlManager'=>array( 
    'urlFormat'=>'path',
    'rules'=>array(
        'issues'=>'issue/index', 
        'issue//*'=>'issue/view', 
    )

上面代码中指定了两条规则。第一条规则说明,如果用户请求的URL是http://localhost/trackstar/index.php/issues,它应被视为http://localhost/trackstar/index.php/issue/index,并且这条规则也同样适用于创建URL。

第二条规则使用了语法包含了一个命名参数ID,它的意思,例如,如果用户请求的URL为http://localhost/trackstar/index.php/issue/1,它应被视为http://localhost/trackstar/index.php/issue/view?id=1。并且这条规则也同样适用于创建URL。

路由部分本身也可以指定为一个数组,如指定URL后缀或是否区分大小写等其它属性。我们将利用这些优势,指定我们的评论feed规则。

让我们将下列规则添加到我们的urlManager组件配置中:

'urlManager'=>array( 
    'urlFormat'=>'path',
    'rules'=>array(
        'commentfeed'=>array('comment/feed', 'urlSuffix'=>'.xml', 'caseSensitive'=>false),
    ),
),

在这里,我们使用urlSuffix属性指定了我们想要的.xml作为后缀。

现在,我们可以通过下面的URL访问feed:http://localhost/trackstar/index.php/commentFeed.xml

从URL中移除入口文件

现在我们需要从URL中移除index.php。需要两步完成:

    • 修改Web服务器配置,重定向所有请求的路由到index.php,但不包括已经存在的文件或目录。
    • 设置UrlManager的showScriptName属性为false。

第一步操作是告诉应用程序怎样处理路由请求,第二步操作是告诉应用程序如何创建URL。

由于我们使用的是Apache HTTP Server,第一步我们可以在应用程序根目录创建一个.htaccess文件,并加入以下指令:

Options +FollowSymLinks 
IndexIgnore */* 
RewriteEngine on

# if a directory or a file exists, use it directly 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteCond %{REQUEST_FILENAME} !-d

# otherwise forward it to index.php 
RewriteRule . index.php

这种方式只适用于Apache HTTP Server。如果你使用的是不同的Web服务器,你将需要编写新的重写(rewrite)规则文件。另外请注意,这些信息可以放在Apache的配置文件中使用以替代.htaccess文件。

.htaccess文件创建后,我们现在可以通过以下URL访问feed,http://localhost/trackstar/commentfeed.xml(或http://localhost/ trackstar/commentFeed.xml因为我们设置了caseSensitive为false,不区分大小写)

然而,即使这样,如果我们在应用程序中使用一个控制器方法或CHTML助手方法创建URL,在一个控制器类中执行下面的代码:

$this->createAbsoluteUrl('comment/feed');

它将生成的URL仍然包括index.php: http://localhost/trackstar/index.php/commentfeed.xml

当生成URL时为了不使用入口文件,我们需要设置urlManager组件的属性。我们再才修改main.php配置文件,例如:

'urlManager'=>array( 
    'urlFormat'=>'path', 
    'rules'=>array(
        'commentfeed'=>array('site/commentFeed', 'urlSuffix'=>'.xml', 'caseSensitive'=>false),
    ),
    'showScriptName'=>false,
),

在URL中为了处理指定project ID的评论feed,我们需要添加另外一个规则:

'urlManager'=>array( 
    'urlFormat'=>'path',
    'rules'=>array(
        '/commentfeed'=>array('site/ commentFeed', 'urlSuffix'=>'.xml', 'caseSensitive'=>false),
        'commentfeed'=>array('site/commentFeed', 'urlSuffix'=>'.xml', 'caseSensitive'=>false),
    ), 
    'showScriptName'=>false,
),

这条规则同样也使用了语法指定模式,URL的commentfeed.xml部分之前指定了一个project ID。通过这条规则,我们可以限制评论的feed指定到一个具体的project。例如,如果我们只想要project #2的评论的feed,URL格式是:http://localhost/trackstar/2/commentfeed.xml

添加feed链接

现在我们已经创建了更加友好的feed的URL结构。我们需要添加用户可订阅feed的功能。一种方法是增加以下代码,在页面渲染前添加RSS feed链接。让我们对项目列表页和具体的项目详细页进行修改。我们首先开始制作项目列表页。这个页面的操作方法是ProjectController::actionIndex(),修改此方法为如下代码:

public function actionIndex() 
{
    $dataProvider=new CActiveDataProvider('Project');
    Yii::app()->clientScript->registerLinkTag(    //这行
        'alternate',     //这行
        'application/rss+xml',     //这行
        $this->createUrl('comment/feed'));    //这行
    
    $this->render('index',
        array( 'dataProvider'=>$dataProvider,)
    );
}

上述高亮代码将在再部份渲染以下HTML:

在许多的浏览器中,地址栏上将自动生成一个RSS feed小图标。下面的截图描绘了这个图标像在Firefox3.6地址栏中的外观:

images/book1/chapter10/2.jpg

我们将类似的修改添加到具体的项目详细页,这个页面的操作方法是ProjectController::actionView(),修改此方法为如下代码:

public function actionView() 
{
    $issueDataProvider=new CActiveDataProvider('Issue', array(
        'criteria'=>array(
            'condition'=>'project_id=:projectId', 
            'params'=>array(':projectId'=>$this->loadModel()->id), 
        ),
        'pagination'=>array( 
            'pageSize'=>1,
        ),
    ));
 
    Yii::app()->clientScript->registerLinkTag( 
        'alternate',
        'application/rss+xml',
        $this->createUrl('comment/feed',array('pid'=>$this->loadModel()->id)));
 
    $this->render('view',array( 
        'model'=>$this->loadModel(), 
        'issueDataProvider'=>$issueDataProvider,
    ));
}

除了指定project ID,几乎与添加到index方法中的代码一样。这将使评论的feed仅限制在指定项目。现在我们在项目详细页面的地址栏中也显示了feed订阅图标,点击图标将允许用户订阅这些评论。

小结

本次迭代证明在Yii框架中融入其他外部框架是多么的容易。我们特地使用了流行的Zend Fraemwork快速添加网站的RSS feed符合我们的应用程序的要求来证明这一点。虽然我们仅指定使用了Zend_Feed,但我们真正展示了是如何将Zend Framework的任何组件融入到我们的应用程序中。这进一步扩大了Yii的功能,使Yii应用程序实现更丰富的功能。

我们也了解了使用Yii中的URL管理功能来改变整个应用程序的URL为更加友好的格式。这是提升应用程序外观和感觉的第一步。我们有太多的东西被忽视。在下一次迭代中,我们将重点转向风格,主题。

你可能感兴趣的:(Yii学习)