 | Level: Intermediate Tyler Anderson (tyleranderson5@yahoo.com), Engineer, Stexar Corp.
25 Jul 2006 This "Understanding
the Zend Framework" series chronicles the building of an online feed reader, Chomp,
while explaining the major aspects of using the recently introduced open source
PHP Zend Framework. Part 3 explains how to use the Zend Framework to
construct our online feed reader, Chomp, by creating an interface to subscribe
to and read feeds, and to save feed entries into the database. Now you will
learn how to use the Zend Framework to incorporate Web sites that do not support RSS feeds into the online feed-reader interface.
About this series
In Part 1, we talk about the overall concepts of the
Zend Framework, including a list of relevant classes and a general
discussion of the MVC pattern. In Part 2, we
expand on that to show how MVC can be implemented in a Zend Framework
application. We also create the user registration and login process, adding
user information to the database and pulling it back out again.
Parts 3 and 4 deal with the actual RSS and Atom feeds. In Part 3, we enable users to subscribe to
individual feeds and to display the items listed in those feeds. We also
discuss some of the Zend Framework's form-handling capabilities, validating
data, and sanitizing feed items. Part 4 explains how to create a proxy to pull data from a site that has no feed.
The rest of the series involves adding value to the Chomp application. Part 5 explains how to use the Zend_PDF module to enable the user to create a
customized PDF of saved articles, images, and search results. In Part 6, we use the Zend_Mail module to alert users to new posts. In
Part 7, we look at searching saved content and
returning ranked results. In Part 8, we create our own mashup, adding
information from Amazon, Flickr, and Yahoo! And in Part 9, we add Ajax interactions to the site using JavaScript object notation.
Introduction
Not all sites have feeds, but it's still useful to track everything in one
place. This article shows how to use the Zend_HTTP_Client module to create a proxy to pull data into the feed-reader interface. In this article, you will learn:
- How to use the
Zend_HTTP_Client module to load Web-site data.
- How to save the full text of feed entries and that of Web pages not supporting feeds.
- How to read the full text of saved feed entries within the feed-reader interface.
At the end of this article, you will have completed the framework of the
feed-reader application. First, modify the database schema, then update the
code to support the new schema. Next, add the functionality to save feed
entry and Web pages to the database. Finally, use Zend_HTTP_Client to allow users to optionally save the entries to
the database and view them in your updated online feed reader.
Updating the database schema
To save feed entries into the feed-reader interface, we need to update the
database schema first. Enter the SQL statements in Listing 1 in the MySQL console.
Listing 1. Modifying the database schema
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));
|
You can see that one field is added to the feeds table: rss. This tells you if the feed is an RSS feed or
a Web page that doesn't support feeds. Three more feeds of various PHP
forums are added to the subscription list. Notice that the feeds from Part 3 have true in this new field, and the three new ones have false, indicating that they are Web pages and not RSS feeds. The savedentries table has two new fields: entrysaved and entrydata. The entrysaved field indicates that the data in the entrydata field is valid. The entrydata field holds the full text of the article. You can see the new subscribable Web pages in Figure 1.
Figure 1. Viewing the new subscribable Web pages
Now it's time to go back to the Part 3 code and make some changes.
Updating the IndexController class
Later, we update the viewFeeds view, which requires the list of
non-RSS feeds the current user is subscribed to, listing the subscribed
feeds and Web pages. Modify the indexAction method in the IndexController class, as shown below.
Listing 2. The indexAction method in the IndexController class
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');
}
}
|
Two lists of results are obtained here: One contains the RSS feeds, and the
other contains the Web pages to which the current user is subscribed. They
are then piped to the viewFeeds view and displayed.
Updating the saveEntryAction method
We need to update links that require you to save entries to the database, so we can update the two new fields. Modify the saveEntryAction method in the FeedController class, as shown below.
Listing 3. The saveEntryAction method in the FeedController class
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");
}
|
The first code in bold font in Listing 3 takes the data from the POST array, instead of GET, since this is how requests to save entries
are made (wouldn't want to send the full text of an article in the URL). Notice how the two new fields in the savedentries table are retrieved: type and saveFullText. The retrieved data is saved as a
new row in the savedentries table, and if a Web
page is being saved, the user is sent back to the main page; otherwise, the user is sent back to the channel he was viewing.
Updating the deleteEntryAction method
We update the code where we deleted entries from the database. Modify the deleteEntryAction method in the FeedController class, as shown below.
Listing 4. The deleteEntryAction method in the FeedController class
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/');
}
|
The first block of bold retrieves the data from POST. Also, the type of entry is also retrieved. We see the new where clause, which also searches for a match on the channelname for RSS feeds, since Web pages don't have a channelname.
Incorporating database changes and new functionality into the views
Now that the controllers are updated with new data being sent to the views,
we need to update the views to capture this data and display it appropriately to the user.
The viewFeeds view
This view displays the subscribed feeds and Web sites to logged-in users.
We need to modify this view to show the non-RSS Web sites currently
subscribed to by customers. So, modify the viewFeeds.php file, as shown below.
Listing 5. The viewFeeds view
...
echo "<a href='feed/viewChannel?title=$feedTitle'>".
"$feedTitle</a><br>";
}
?>
<br><br>
<table>
<tr>
<td>Subscribed Web Pages:
</td>
<td>Save Entry to Database
</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>
...
|
In Listing 5, a table is displayed that shows the subscribed Web pages. As
we loop through each entry, we embed hidden inputs that contain the page title, link, and type (webPage, as opposed to rssFeed), and we display a link to the actual Web page, including a
form to save the entry with a checkbox that allows users to save the full text of the page. See Figure 2.
Figure 2. The modified viewFeeds view
Try saving the "MySQL Forums :: PHP" page entry, along with its full text. We'll see what it looks like later.
The viewChannel view
We update the viewChannel view to include the form to save
entries as in Listing 5. Modify the viewChannel view, as shown below.
Listing 6. The modified viewChannel view
...
<td>Save entry to database
</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>
...
|
Notice the similarities above with that of Listing 5, but notice that the hidden value type is set to rssFeed, instead of webPage. View the updated example browser output in Figure 3.
Figure 3. The modified viewChannel view
The viewSavedEntries view
With entries being saved with and without full text into the database, we
have to modify the viewSavedEntries view accordingly. Do so, as shown below.
Listing 7. The viewSavedEntries view
...
<td>Delete Channel Entry
</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>
...
|
Modify the delete link into a form with a Delete button. In the foreach loop, check to see if the entry had its full text saved, and, if so, set the entrysaved variable to "Full Text." Otherwise,
it'll remain empty. Then set up the title of the entry and its type. In the form, we embed four hidden inputs: the entry's feedname, channelname (empty for webPage types), full link, and type. Then we display the link to the actual Web
page, a button to delete the entry, and a link to the full text of the
saved Web page, if the full text was saved. Note that the action pointed to is /feed/fullText, so you define a new fullTextAction method in the FeedController in the next section. The updated
viewSavedEntries view can be seen in Figure 4.
Figure 4. The modified viewSavedEntries view
Saving feed entries
With the code in place for users to choose to save the full text of feed
entries and Web pages, the only thing missing is the code that grabs the Web page using the Zend_HTTP_Client class. In this section, we
define that code and the fullText action that displays the full text of saved entries to users.
Using Zend_HTTP_Client: saveEntryAction
It's time to complete the saveEntryAction method in the FeedController class by saving the full text of
entries where the checkbox was checked. Modify the saveEntryAction method, as shown below.
Listing 8. The saveEntryAction method in the FeedController class
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');
...
}
|
If the checkbox indicates to save the entry, then grab it using the Zend_Http_Client class, as shown above. Save the full text of the entry into the fullText variable, which saves it into the
database entry later in this method (see Listing
3). If the retrieval was unsuccessful, an error message is displayed to the user who can try again by reloading the page.
Viewing full text of saved entries
Define the fullTextAction method so users can view the full text of saved entries. Define the fullTextAction method in the FeedController class, as shown below.
Listing 9. The fullTextAction method in the FeedController class
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'];
}
|
Grab the username, along with the feedname and channelname from the Session and Get arrays. Then search the savedentries table for a match, grab its full text and display it to
the user. See the full text of the "MySQL Forums :: PHP" page entry saved in Figure 5.
Figure 5. Viewing the full text of a saved entry
Summary
That completes the feed reader! Use Zend_HTTP_Client to grab Web pages from the Internet and save them
into your feed reader. Your online feed reader also supports RSS feeds and Web pages.
The rest of this series involves adding value to the Chomp application. Part 5 explains how to use the Zend_PDF module to enable the user to create a
customized PDF of saved articles, images, and search results. In Part 6, we use the Zend_Mail module to alert users to new posts. In
Part 7, we look at searching saved content and
returning ranked results. In Part 8, we create our own mashup, adding
information from Amazon, Flickr, and Yahoo. And in Part 9, we add Ajax interactions to the site using JavaScript object notation.
Download | Description | Name | Size | Download method |
|---|
| Source code | os-php-zend4.source.zip | 7.5KB | HTTP |
|---|
Resources Learn
-
The Zend Framework Web site maintains the latest documentation, as well as installation FAQs.
-
To better understand the goals of the feed-reader project, read "Introduction to Syndication, (RSS) Really Simple Syndication," by Vincent Luria.
-
The tutorial series "Learning PHP" takes you from the most basic PHP script to working with databases and streaming from the file system.
-
The PHP Function Reference is a valuable resource.
- Learn more about Zend Core for IBM, a seamless, easy-to-install, and supported PHP development and production environment that features integration with DB2 and Informix databases.
-
Thought Storms ModelViewController explains the MVC and the controversy and confusion surrounding it.
-
The phpPatterns Web site explains MVC from the PHP point of view.
-
View a screen cast that explains how to install Apache and PHP.
-
PHP.net is the central resource for PHP developers.
-
Check out the "Recommended PHP reading list."
-
Browse all the PHP content on developerWorks.
-
Expand your PHP skills by checking out IBM developerWorks' PHP project resources.
-
To listen to interesting interviews and discussions for software developers, check out developerWorks podcasts.
-
Using a database with PHP? Check out the Zend Core for
IBM, a seamless, out-of-the-box, easy-to-install PHP development and production environment that supports IBM DB2 V9.
-
Stay current with developerWorks' Technical events and webcasts.
-
Check out upcoming conferences, trade shows, webcasts, and other Events around the world that are of interest to IBM open source developers.
-
Visit the developerWorks Open source zone for extensive how-to information, tools, and project updates to help you develop with open source technologies and use them with IBM's products.
-
Watch and learn about IBM and open source technologies and product functions with the no-cost developerWorks On demand demos.
Get products and technologies
Discuss
About the author  | |  | Tyler Anderson graduated with a degree in computer science from Brigham Young University in 2004 and is currently in his last semester as a master's student in computer engineering. In the past, he worked as a database programmer for DPMG.com, and he is currently an engineer for Stexar Corp., based in Beaverton, Ore. |
Rate this page
|  |