关键字
文章内容
理解Zend 框架(4): 用Zend_HTTP_Client...
 
 
修改时间:[2009/11/04 05:33]    阅读次数:[1133]    发表者:[起缘]
 
    并非所有的站点都包含提要,但是跟进某个地方的所有改动仍然是很有用的。本文将介绍如何使用 Zend_HTTP_Client 模块创建代理,从而将数据提取到提要阅读器界面中。在本文中,您将学到:

如何使用 Zend_HTTP_Client 模块加载网站数据。
如何保存提要条目的全文以及那些不支持提要的网页的全文。
如何在提要阅读器界面中阅读已保存提要条目的全文。
在本文的末尾,将完成提要阅读器应用程序的框架。首先,修改数据库纲要,其次,更新代码以支持新的纲要,然后添加将提要条目和网页保存到数据库中的功能。最后,使用 Zend_HTTP_Client 模块支持用户有选择地将条目保存到数据库中,并在已更新的在线提要阅读器中查看它们。

更新数据库纲要

为了将提要条目保存到提要阅读器界面中,首先我们需要更新数据库纲要。在 MySQL 控制台中键入清单 1 中的SQL 语句。


清单 1. 更改数据库纲要

drop table feeds;

create table feeds
(feedname varchar(256), link varchar(512), rss varchar(5));

insert into feeds values
('Fox Sports',
 'http://feeds.feedburner.com/foxsports/rss/headlines',
 'true'),
('Google News',
 'http://news.google.com/?output=rss',
 'true'),
('Yahoo News',
 'http://rss.news.yahoo.com/rss/topstories',
 'true'),
('phpbb',
 'http://www.phpbb.com/phpBB/viewforum.php?f=14',
 'false'),
('MySQL Forums :: PHP',
 'http://forums.mysql.com/list.php?52',
 'false'),
('SitePoint Forums :: PHP',
 'http://www.sitepoint.com/forums/forumdisplay.php?forumid=34',
 'false');

drop table savedentries;

create table savedentries
(username varchar(20), feedname varchar(256), channelname varchar(256),
 link varchar(512), entrysaved varchar(5), entrydata varchar(307200));
 


您能看到 feeds 表添加了一个新的字段:rss。这个字段用于辨别该提要是 RSS 提要还是不支持提要的网页。订阅列表中添加了另外三个不同 PHP 论坛的提要。请注意,对于第 3 部分中的提要,这个新字段值为 true,而三条新提要的值则为 false,这说明它们是网页,而非 RSS 提要。savedentries 表有两个新的字段:entrysaved 和 entrydata。entrysaved 字段说明 entrydata 字段中的数据是有效数据。entrydata 字段保存了该文章的全文。新的可订阅网页如图 1 所示。

现在需要回到第 3 部分的代码中做一些更改。

更新 IndexController 类

稍后我们将对 viewFeeds 视图进行更新,这需要当前用户所订阅的非 RSS 提要的列表,该列表列出了已订阅的提要和网页。在 IndexController 类中更改 indexAction 方法,如下所示。


清单 2. IndexController 类中的 indexAction 方法

    public function indexAction()
    {
...
            $select->where('feeds.feedname=subscribedfeeds.feedname');
            $select->where('feeds.rss=?', 'true');
            $rssResults = $db->fetchAll($select);

            $select = $db->select();
            $select->from('subscribedfeeds, feeds', '*');
            $select->where('subscribedfeeds.Username = ?', $username);
            $select->where('feeds.feedname=subscribedfeeds.feedname');
            $select->where('feeds.rss=?', 'false');
            $webResults = $db->fetchAll($select);

            $view = Zend::registry('view');
            $view->username = $username;
            $view->rssFeeds = $rssResults;
            $view->webFeeds = $webResults;
            echo $view->render('viewFeeds.php');
        }
    }
 


这里包含了两个结果列表:一个包含用户订阅的 RSS 提要,而另一个包含当前用户订阅的网页。这些提要随后被传送到 viewFeeds 视图,并被显示出来。

更新 saveEntryAction 方法

我们需要更新那些可以用来将条目保存到数据库中的链接,这样我们就可以更新这两个新字段了。更改 FeedController 类中的 saveEntryAction 方法,如下所示。


清单 3. FeedController 类中的 saveEntryAction 方法

    public function saveEntryAction()
    {
        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');

        $filterPost = Zend::registry('fPost');
        $feedTitle = $filterPost->getRaw('feedTitle');
        $channelTitle = $filterPost->getRaw('title');
        $channelLink = $filterPost->getRaw('link');
        $type = $filterPost->getRaw('type');
        $saveFullText = $filterPost->getRaw('saveFullText');
...
        $db = Zend::registry('db');
        $row = array(
                     'Username' => $username,
                     'feedname' => $feedTitle,
                     'channelname' => $channelTitle,
                     'link' => $channelLink,
                     'entrysaved' => $saveFullText ? 'true' : 'false',
                     'entrydata' => $fullText
                     );
       
        $table = 'savedentries';
        $rowsAffected = $db->insert($table, $row);

        if($type == 'webPage')
            $this->_redirect("/");
        Else
            $this->_redirect("/feed/viewChannel?title=$feedTitle");
    }
 


清单 3 中第一段黑体代码将数据从 POST 数组(而不是 GET 数组)中提取出来,因为这就是请求保存提要的方式(不想在 URL 中发送文章的全文)。请注意 savedentries 表中的两个新字段 type 和 saveFullText 是如何被检索的。已经检索的数据被保存为 savedentries 表中的一个新行,并且,如果保存了一个网页,那么用户将被转回到主页;否则用户将被转回到他正在查看的频道。

更新 deleteEntryAction 方法

我们更新了从数据库中删除条目的代码。更改 FeedController 类中的 deleteEntryAction 方法,如下所示。


清单 4. FeedController 类中的 deleteEntryAction 方法

    public function deleteEntryAction()
    {
        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');

        $filterPost = Zend::registry('fPost');
        $feedTitle = $filterPost->getRaw('feedTitle');
        $channelTitle = $filterPost->getRaw('channelTitle');
        $type = $filterPost->getRaw('type');

        $db = Zend::registry('db');
        $table = 'savedentries';
        $where = "username='$username' and feedname='$feedTitle'";
        if($type == 'rssFeed')
            $where = "$where and channelname='$channelTitle'";
        $rowsAffected = $db->delete($table, $where);
       
        $this->_redirect('/feed/viewSavedEntries/');
    }
 


第一段黑体代码从 POST 数组中获取数据。同样,也获得了条目中的 type 字段。我们可以看到新的 where 子句根据 RSS 提要搜寻匹配的 channelname,因为网页不包含 channelname。

将数据库变更和新功能添加到视图中

既然控制器与将传送给视图的新数据是一起更新的,那么我们需要更新该视图来捕获此数据,并适当地将其展示给用户。

viewFeeds 视图

这个视图向已登录用户展示了已订阅的提要和 Web 站点。我们需要更改此视图,以展示用户当前订阅的非 RSS Web 站点。所以,让我们来更改 viewFeeds.php 文件,如下所示。


清单 5. viewFeeds 视图

...
         echo "<a href='feed/viewChannel?title=$feedTitle'>".
              "$feedTitle</a><br>";
     }
     ?>
  <br><br>
  <table>
    <tr>
      <td>Subscribed Web Pages:     
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
      <td>Save Entry to Database
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
      <td>Save Full Text</td>
    </tr>
  <?php
     foreach($this->webFeeds as $row){
         $feedTitle = $row['feedname'];
         $link = $row['link'];
         echo "<form method='POST' action='feed/saveEntry'>";
         echo "<input type='hidden' name='feedTitle' ".
              "value='$feedTitle'/>";
         echo "<input type='hidden' name='link' value='$link'/>";
         echo "<input type='hidden' name='type' value='webPage'/>";
         echo "<tr><td><a
 href='$link'>$feedTitle</a><br></td>";
         echo "<td><input type='submit'
 value='save'/></td>";
         echo "<td><input name='saveFullText' ".
              "type='checkbox'/></td></tr></form>";
     }
     ?>
  </table>
</body>
...
 


清单 5 中的表显示了已订阅的网页。当我们在每个循环遍历每个条目时,会嵌入了一些包含页面标题、链接和类型(webPage,与 rssFeed 相对)的隐藏输入项,并且显示了一个到实际网页的链接。在这个网页中,包含了一个带复选框的用来保存条目的表单,可通过该复选框支持用户保存页面的全文。参见图 2。

试着保存 “MySQL Forums :: PHP” 页面条目及其全文,我们稍后就会看到它的样子。

viewChannel 视图

更新 viewChannel 视图,以包含如清单 5 中所示的用于保存条目的表单。修改 viewChannel 视图,如下所示。


清单 6. 修改后的 viewChannel 视图

...
     <td>Save entry to database
        &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</td>
      <td>Save Full Text</td>
    </tr>
  <?php
     $feedTitle = $this->title;
     foreach ($this->rssFeed as $item) {
         $entryTitle = $item->title();
         $link = $item->link();
         echo "<form method='POST' action='/feed/saveEntry'>";
         echo "<input type='hidden' name='feedTitle' ".
              "value='$feedTitle'/>";
         echo "<input type='hidden' name='title' ".
              "value='$entryTitle'/>";
         echo "<input type='hidden' name='link' value='$link'/>";
         echo "<input type='hidden' name='type' value='rssFeed'/>";
         echo "<tr><td><a href='$link'>$entryTitle</a><br></td>";
         echo "<td><input type='submit' value='save'/></td>";
         echo "<td><input name='saveFullText' ".
              "type='checkbox'/></td></tr></form>";
     }
?>
  </table>
...
 


可以看到上面清单与清单 5 有相似之处,但要注意的是,该隐藏值类型被设置为 rssFeed,而不是 webPage。查看已更新示例的浏览器输出,如图 3 所示。

viewSavedEntries 视图

有了与(或不同)全文一起保存的条目,还需要相应地修改 viewSavedEntries 视图。为此,请参照下列清单。


清单 7. viewSavedEntries 视图

...
      <td>Delete Channel Entry
      
 &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;
</td>
      <td>View Saved Full Text</td>
    </tr>
  <?php
     foreach ($this->entries as $row) {
         $link = $row['link'];
         $channelTitle = $row['channelname'];
         $feedTitle = $row['feedname'];
         $entrysaved = '';
         if($row['entrysaved'] == 'true')
             $entrysaved = 'Full Text';
         $title = "$feedTitle";
         if($row['channelname'] != ''){
             $title = "$title > $channelTitle";
             $type = 'rssFeed';
         } else {
             $type = 'webPage';
         }
         echo "<form method='POST' action='/feed/deleteEntry'>";
         echo "<input type='hidden' name='feedTitle' ".
              "value='$feedTitle'/>";
         echo "<input type='hidden' name='channelTitle' ".
              "value='$channelTitle'/>";
         echo "<input type='hidden' name='link' value='$link'/>";
         echo "<input type='hidden' name='type' value='$type'/>";
         echo "<tr><td><a
 href='$link'>$title</a></td>";
         echo "<td><input type='submit'
 value='delete'/></td>";
         echo "<td><a
 href='/feed/fullText?feedTitle=$feedTitle&".
              "channelTitle=$channelTitle'>".
              "$entrysaved</td></tr></form>";
     }
?>
  </table>
...
将 delete 链接修改成一个带有 Delete 按钮的表单。在 foreach 循环中,查看条目是否保存了全文,如果是,则将 entrysaved 变量设置为 "Full Text "。否则,该变量仍为空。然后设置条目标题及其类型。在表单中,我们嵌入了四个隐藏的输入项:条目的 feedname、channelname(此值在 webPage 类型时为空)、完整的 link 和 type。随后我们显示了到实际网页的链接、删除条目按钮以及到已保存网页的全文的链接(如果保存了全文)。请注意,该行为指向 /feed/fullText,所以在下一部分中,将在 FeedController 中定义一个新的 fullTextAction 方法。可以在图 4 中看到修改后的 viewSavedEntries 视图。

保存提要条目

有了这个代码,用户就能够选择是否要保存提要条目和网页的全文,现在惟一余下的事情是用于抓取网页的代码(使用 Zend_HTTP_Client )。在这一小节中,我们定义了该代码和 fullText 行为,该行为用于向用户展示已保存条目的全文。

使用 Zend_HTTP_Client:saveEntryAction

是时候在 FeedController 类中完成 saveEntryAction 方法了,此方法所对应的行为是通过选中复选框保存条目的全文来完成的。修改 saveEntryAction 方法,如下所示。

清单 8. FeedController 类中的 saveEntryAction 方法

    public function saveEntryAction()
    {
        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');
...
        if($saveFullText){
            $http = new Zend_Http_Client($channelLink);
            $response = $http->get();
            if ($response->isSuccessful())
                $fullText = $response->getBody();
            else{
                echo 'Error occurred, full text not saved, '.
                     'please reload.';
                return;
            }
        }

        $db = Zend::registry('db');
...
    }
 

如果复选框指出要保存条目,则使用 Zend_Http_Client 类抓取它,如上所示。将条目的全文保存到 fullText 变量中,随后,该变量在此方法中把全文保存到数据库条目中(参见 清单 3)。如果检索失败,则将一个错误消息显示给用户,用户可通过重新加载页面来再次尝试。

查看已保存条目的全文

定义 fullTextAction 方法,用户就能查看已保存条目的全文了。定义 FeedController 类中的 fullTextAction 方法,如下所示。

清单 9. FeedController 类中的 fullTextAction 方法

    public function fullTextAction()
    {
        $filterSession = Zend::registry('fSession');
        $username = $filterSession->getRaw('username');
       
        $filterGet = Zend::registry('fGet');
        $feedTitle = $filterGet->getRaw('feedTitle');
        $channelTitle = $filterGet->getRaw('channelTitle');

        $db = Zend::registry('db');
        $select = $db->select();
        $select->from('savedentries', '*');
        $select->where("username=?", $username);
        $select->where("feedname=?", $feedTitle);
        if($channelTitle)
            $select->where("channelname=?", $channelTitle);
        $sql = $select->__toString();
        $fullText = $db->fetchAll($sql);

        echo $fullText[0]['entrydata'];
    }
 

从 Session 及 Get 数组中获取 username、feedname 和 channelname。随后搜索 savedentries 表来获得匹配条目,然后获取匹配条目的全文并显示给用户。已保存的 “MySQL Forums :: PHP” 页面条目的全文如图 5 所示。

结束语

这样就完成了提要阅读器的创建!使用 Zend_HTTP_Client 从互联网抓取网页,并将它们保存到您的提要阅读器中。您的在线提要阅读器同样支持 RSS 提要和网页。

本系列余下的部分包含对如何为 Chomp 应用程序增值的介绍。第 5 部分将介绍用户如何使用 Zend_PDF 模块为已保存的文章、图形和搜索结果创建一个定制的 PDF。第 6 部分中,将介绍如何使用 Zend_Mail 模块提醒用户有新的文章发布。第 7 部分将探讨如何搜索已保存内容并返回排列好的结果。第 8 部分中,我们将创建自己的混合模块,以添加 Amazon、Flickr 和 Yahoo! 中的信息。而在第 9 部分中,我们将使用 JavaScript 对象符号为网站添加 Ajax 交互。