Zend Framework - How To Implement RSS Feeds the Easy Way

Ever wanted to know how to take an existing Zend Framework application where you have posts, records, news or anything else stored in a data source and updated regularly, and make it available as a feed for your users that they can subscribe to?


Ever wanted to know how to take an existing Zend Framework application where you have posts, records, news or anything else stored in a data source and updated regularly, and make it available as a feed for your users that they can subscribe to?

Well today, that’s what I want to show you. Recently, during the development of the burgeoning PHP cloud development casts site, I had just that situation.

A report came in indicating that the subscribe route was broken - sadly, in reality, it had not yet been implemented. Now there were a number of paths that could have been taken using some very capable libraries. Doing a quick Google search reveals the following options:

I’ve used Magpie RSS previously and can happily speak well of it, but I’ve not used SimplePie. But why would I use an external library, when I can use the library that comes with the zend framework, which the rest of the app’s built in along with some other components to complete the work?

So with a little help from two outstanding components of the Zend Framework, I’m going to show you how to implement subscriber feeds in your app, in both RSS 2.0 and Atom 1.0 formats, peppered with a little bit of magic from context switches and some simple, custom, routes.

Setup the Routes

  <td class="code">
    <pre class="php" style="font-family:monospace;"><span style="color: #339933;">;</span> Atom

routes.subscribe-atom.type = “Zend_Controller_Router_Route” routes.subscribe-atom.route = “subscribe/atom” routes.subscribe-atom.defaults.module = “screencasts” routes.subscribe-atom.defaults.controller = “subscribe” routes.subscribe-atom.defaults.action = “subscribe” routes.subscribe-atom.defaults.format = “atom”   ; RSS routes.subscribe-rss.type = “Zend_Controller_Router_Route” routes.subscribe-rss.route = “subscribe/rss” routes.subscribe-rss.defaults.module = “screencasts” routes.subscribe-rss.defaults.controller = “subscribe” routes.subscribe-rss.defaults.action = “subscribe” routes.subscribe-rss.defaults.format = “rss”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

The routing is critical, but arguably one of the simplest steps in the whole process. As the Zend_Feed_Writer class, at the time of writing, supports only Atom 1.0 & RSS 2.0, then we’re going to setup routes just for these two formats.

You can see in the two routes above, that they’re largely similar. In the first, we’ve linked subscribe/atom to the subscribe action of the subscribe controller of the screencasts module. To properly interact with the ContextSwitch later, we’ve specified the format as “atom”.

For the RSS format, we copy & past the previous route and change atom to rss, where appropriate. Now, we could have probably condensed these two routes in to one route, but that’s a story for another day. With the routes done, let’s move on to retrieving the data and building the feeds.

Load the Feed with your Data Source Data

The code below looks at the core functions in the subscribe controller in the php cloud development casts codebase that we referenced a moment ago.

I like to keep code nice, simple and professional, as anti-monolithic as possible. So I’ve separated out code in to blocks that keep to the traditional UNIX programming principle of do one thing well and no more.

  <td class="code">
    <pre class="php" style="font-family:monospace;"><span style="color: #009933; font-style: italic;">/**
  • An action to allow subscribing to feeds */ public function subscribeAction() { $format = $this->_getParam(‘format’, self::DEFAULT_FORMAT); $feed = $this->_getFeed($format); $this->view->feedOutput = $feed; }
1
2
3
4
5
6
7
8
9

You can see that the subscribe action is very small. It sets a format from the format supplied, or sets one from the class default. After that, it runs the class function, _getFeed, to generate the feed and assigns the result to the view variable feedOutput.

  <td class="code">
    <pre class="php" style="font-family:monospace;"><span style="color: #009933; font-style: italic;">/**
  • Build the core feed information */ protected function _getFeed($format) { $front = Zend_Controller_Front::getInstance();   // get a handle on the screencasts datasource $this->_config = new Zend_Config($front->getParam(‘bootstrap’)->getOption(‘app’));   $feed = new Zend_Feed_Writer_Feed; $feed->setTitle($this->_config->feed->title); $feed->setLink($this->view->fullUrl($this->view->baseUrl())); $feed->setDescription($this->_config->feed->description);   if ($format == ‘atom’) { $feed->setFeedLink($this->view->fullUrl($this->view->baseUrl()) . ’/subscribe/atom’, ‘atom’); } else { $feed->setFeedLink($this->view->fullUrl($this->view->baseUrl()) . ’/subscribe/rss’, ‘rss’); }   $feed->addAuthor(array( ‘name’ => trim($this->_config->feed->author->name), ‘email’ => trim($this->_config->feed->author->email), ‘uri’ => $this->view->fullUrl($this->view->baseUrl()), )); $feed->setDateModified(time()); $feed->addHub($this->view->fullUrl($this->view->baseUrl())); $feed = $this->_addFeeds($feed);   return $feed->export($format); }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32

The, protected, _getFeed method retrieves the key configuration settings that we’ve placed in the application.ini configuration file.

This, along with a custom view helper, allows us to initialise the Zend_Feed_Writer_Feed object; setting the title, link, description, feedLink, hub and author details. I likely should not set the modified date everytime the page is generated, but for now, it’s ok and so it’s set with the timestamp retrieved from the time() function.

The feed configuration in the application.ini is as follows:

  <td class="code">
    <pre class="php" style="font-family:monospace;"><span style="color: #339933;">;</span> Core Feed <span style="color: #009900;">&#40;</span>RSS<span style="color: #339933;">/</span>ATOM<span style="color: #009900;">&#41;</span> Settings

app.feed.vendor = Malt Blue Ltd app.feed.title = PHP Cloud Development Casts Feed app.feed.description = Learn Cloud Development Through The Lens of PHP app.feed.author.name = Matthew Setter app.feed.author.email = subscribe@maltblue.com

1
2
3
4
5
6

After that, we’re down to the, protected, _addFeeds function. It’s here where we retrieve the screencasts data and use it to create feed items for the feed object.

So firstly, we retrieve feeds that are published, and retrieve an iterator to use when iterating over the results in the foreach loop. We then iterate through whilst we have screencast items and built a feed entry, adding the title, link, author, date modified and created, description and content from the screencast object.

After it’s all done, we return the feed object and we’re just about ready.

  <td class="code">
    <pre class="php" style="font-family:monospace;"><span style="color: #009933; font-style: italic;">/**
  • Add on currently published feed items to the feed */ protected function _addFeeds($feed) { // find all the published feeds $this->_castsDatasource->isPublished(true) ->find(); $results = $this->_castsDatasource->getIterator();   if (count($results) > ) { foreach ($results as $result) { $entry = $feed->createEntry(); $entry->setTitle($result->title); $entry->setLink($this->view->fullUrl(’/screencast/’ . urlencode($result->title))); $entry->addAuthor(array( ‘name’ => trim($result->author->fullName()), )); $publishDate = new DateTime($result->publishDate); $createdDate = new DateTime($result->createdDate); $entry->setDateModified($publishDate->getTimestamp()); $entry->setDateCreated($createdDate->getTimestamp()); $entry->setDescription($result->description); $entry->setContent($result->synopsis); $feed->addEntry($entry); } }   return $feed; }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

Pepper Your Controller with a Context Switch

Here’s where we put the previous work together. If you’re not familiar with ContextSwitch in Zend Framework, then you’re in for a treat. I won’t go over it specifically in too much detail, as the manual and Michelangelo van Dam cover it in great detail already.

But what we’re doing is that we’re setting up two new context’s, as the only one’s supported, out of the box, are JSON and XML. For our purposes, we need to setup one for RSS and ATOM.

So what we do is call setContext twice, one for each context and indicate the extension of the output and the header that needs to be sent with that kind of output. We then indicate, via addActionContext that the subscribe action can switch between the two formats and call initContext and we’re right to go.

  <td class="code">
    <pre class="php" style="font-family:monospace;"><span style="color: #000088;">$contextSwitch</span> <span style="color: #339933;">=</span> <span style="color: #000088;">$this</span><span style="color: #339933;">-&gt;</span>_helper<span style="color: #339933;">-&gt;</span><span style="color: #004000;">getHelper</span><span style="color: #009900;">&#40;</span><span style="color: #0000ff;">'contextSwitch'</span><span style="color: #009900;">&#41;</span><span style="color: #339933;">;</span>

$contextSwitch->setContext(‘rss’, array( ‘suffix’ => ‘xml’, ‘headers’ => array( ‘Content-Type: application/rss+xml; charset=ISO-8859-1’ ) )) ->setContext(‘atom’, array( ‘suffix’ => ‘xml’, ‘headers’ => array( ‘Content-type: application/atom+xml’ ) )) ->addActionContext(‘subscribe’, array(‘rss’,‘atom’)) ->initContext();

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

Render Your Feed

Now since we’ve said that the file extension is xml, we need to setup a template slightly differently. Normally, we’d create a view script called subscribe.phtml for the subscribe action.

As it’s a context switch, we have to name the file slightly differently. We have to name it subscribe.xml.phtml. If we were switching to, say, txt, we’d name it subscribe.txt.phtml instead. Can you see the connection between extension and file name?

<?php print $this->feedOutput; ?>

In the file, we then output the value of the feedOutput view variable we created earlier, which will output the content of the feed we’ve generated, which you can see below.

  <td class="code">
    <pre class="xml" style="font-family:monospace;"><span style="color: #009900;"><span style="color: #000000; font-weight: bold;">&lt;?xml</span> <span style="color: #000066;">version</span>=<span style="color: #ff0000;">"1.0"</span> <span style="color: #000066;">encoding</span>=<span style="color: #ff0000;">"UTF-8"</span><span style="color: #000000; font-weight: bold;">?&gt;</span></span>

<rss version=“2.0” xmlns:dc=http://purl.org/dc/elements/1.1/" xmlns:atom=http://www.w3.org/2005/Atom" xmlns:content=http://purl.org/rss/1.0/modules/content/"> <channel> <title>PHP Cloud Development Casts Feed</title> <description>Learn cloud development through the lens of PHP</description> <pubDate>Wed, 11 Jul 2012 11:00:26 -0700</pubDate> <generator>Zend_Feed_Writer 1.11.2 (http://framework.zend.com)</generator> <link>http://testing.phpclouddevelopmentcasts.com</link> <author>subscribe@maltblue.com (Matthew Setter)</author> <dc:creator>Matthew Setter</dc:creator> <atom:link rel=“self” type=“application/rss+xml” href=http://testing.phpclouddevelopmentcasts.com/subscribe/rss"/> <atom:link rel=“hub” href=http://testing.phpclouddevelopmentcasts.com/> <item> <title>Introduction to cloudControl</title> <description><![CDATA[In this, the first episode of PHP Cloud Development Casts, we go through how to get started deploying to the cloudControl (cloudControl.com) platform.   We go through: taking an existing project and putting it under git control, initialising the cloudControl environment, pushing the code out, initialising the MySQL database and creating a testing branch based off of the master branch]]></description> <pubDate>Wed, 04 Jul 2012 00:00:00 -0700</pubDate> <link>http://testing.phpclouddevelopmentcasts.com/screencast/Introduction+to+cloudControl</link> <guid>http://testing.phpclouddevelopmentcasts.com/screencast/Introduction+to+cloudControl</guid> <author>Matthew Setter</author> <dc:creator>Matthew Setter</dc:creator> <content:encoded><![CDATA[In this, the first episode of PHP Cloud Development Casts, we go through how to get started deploying to the cloudControl (cloudControl.com) platform.   We go through: taking an existing project and putting it under git control, initialising the cloudControl environment, pushing the code out, initialising the MySQL database and creating a testing branch based off of the master branch]]></content:encoded> </item> </channel> </rss>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

You’re All Done!

So there you have it. Through loading the data as we would in the rest of our application and then using that information with the Zend_Feed components, we’re able to transform our data in to a standards compliant RSS or Atom feed.

Then, by setting up a ContextSwitch for our subscribe action, we are able to dramatically minimise the code that that we need to write and instead, setup a few routes that simply supply a different format parameter to our query string.

We’ve been able to utilise further components of the framework, keeping our work clean, professional, tidy and maintainable. Want to add support for other feed formats? That’s a lesson left to you dear reader. But should you do so, feel free to email me and I’ll plug your work.

What Else Could We Do?

Now this isn’t all that we could have done. We could have combined the routes in to just one, we could have created a feed class as a minimalist wrapper over the Zend Feed code instead of directly including it in the controller.

What do you think? How would you handle this scenario?


You might also be interested in these tutorials too...

Thu, Jul 5, 2012

Why Zend Framework Plugins Save You Time

During the recent development of the new PHP cloud development casts site, which has been developed with the Zend Framework, so much development time has been saved by using one of the simplest and arguably best features of the framework: Controller Plugins. So today I want to introduce you to them and walk you through a working plugin so you can see just how effective and efficient they can make your development workflow.

Tue, Jun 5, 2012

Zend Form Mastery with Zend Config – Part 4 Configuring Zend Validators

Welcome to the fourth and final part in the Zend Form Mastery with Zend Config series. Previously we looked at the basic form options, element and form wide prefixes, filters and element options. In this installment, we’re going to look at configuring Zend Validators via our XML config.

Fri, Apr 27, 2012

Zend Form Mastery with Zend Config - Part 1 Custom Filter Paths

When you’re working with Zend Form you keep your configuration as much out of code as you can - right? Well, if you’ve been working withZend Form for more than a little while, you know that Zend Config really makes that pretty simple - well, some of the time. In this series we look, comprehensively at how to do it all with Zend_Config.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus