Skip to main content

skip to main content

developerWorks  >  Open source  >

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

Building the perfect reader

developerWorks
Document options

Document options requiring JavaScript are not displayed

Sample code


Rate this page

Help us improve this content


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.



Back to top


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.



Back to top


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.



Back to top


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.



Back to top


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.



Back to top


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.



Back to top


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


Back to top


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


Back to top


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.




Back to top


Download

DescriptionNameSizeDownload method
Source codeos-php-zend4.source.zip7.5KBHTTP
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.




Rate this page


Please take a moment to complete this form to help us better serve you.



YesNoDon't know
 


 


12345
Not
useful
Extremely
useful
 


Back to top