Skip to main content

Understanding the Zend Framework, Part 4: When there is no feed, the Zend_HTTP_Client

Building the perfect reader

Tyler Anderson (tyleranderson5@yahoo.com), Engineer, Stexar Corp.
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.

Summary:  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.

View more content in this series

Date:  25 Jul 2006
Level:  Intermediate
Activity:  1814 views

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
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:      
 &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>
...

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
The modified viewFeeds

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
        &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>
...

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 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
       
 &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>
...

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

DescriptionNameSizeDownload method
Source codeos-php-zend4.source.zip7.5KB HTTP

Information about download methods


Resources

Learn

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.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Open source
ArticleID=145591
ArticleTitle=Understanding the Zend Framework, Part 4: When there is no feed, the Zend_HTTP_Client
publish-date=07252006
author1-email=tyleranderson5@yahoo.com
author1-email-cc=troy@backstopmedia.com

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Rate a product. Write a review.

Special offers