Creating Custom Feeds with Zend_Feed - part 1
I had some time this weekend and I implemented the 'latest stuff'(news) feature. Now I can create feeds based on my posts or any other source I want. I can even combine external feeds,atom or rss ,to create a master feed and all this thanks to Zend_Feed
Here is an example on how you can combine three feeds , one rss , one atom and a local source to create a master feed:
$feed = new Wu_Feed();
$remote_source = new Wu_Feed_Source_Remote('http://remotefeed.com/atom');
$feed->add($remote_source);
$remote_source = new Wu_Feed_Source_Remote('http://remotefeed.com/rss');
$feed->add($remote_source);
//of course you can have a builder
//object or a factory to cotain
//the local source building part
$local_source = new Wu_Feed_Source();
$entry = new Wu_Feed_Source_Entry();
$entry->title = 'New Title';
$entry->content 'awesome content';
$local_source->add($entry);
$feed->add($local_source);
echo $feed->asRss();//outputs rss feed
//or
$feed->asAtom();//outputs atom.
//
$zend_feed = $feed->asZendFeed();//returns Zend_Feed object
I think asRss() and as asAtom() are clear, but what about the latest, asZendFeed()? Why would I need a Zend_Feed object, doesn't Wu_Feed inherit Zend_Feed ? No , it doesn't, but I am using it within Wu_Feed to do my work. I will explain more in the 'how it works' section.
If you noticed , my left navigation has a section 'Latest stuff'. Until now , it displayed my recent bookmarks , stored on a blogspot account. The code that did that was:
$feed = Zend_Feed::import('http://dordea.blogspot.com/feeds/posts/default');
$results = array();
foreach ($feed as $entry) {
$result = array();
$result['title'] = $entry->title();
$result['link'] = $entry->link('alternate');
$results[] = $result;
}
$this->view->results = $results;
Basically , to fill my left nav , I was using
an external feed , because I was to lazy to make my own. The reason why asZendFeed() method exists is:
- to have my original code working with no major modifications
- to be sure my addons will work with any other classes that use
Zend_Feed, from Zend Framework or from any other source
Zend_Cache does a wonderful job and allows me to ignore this part(at the moment) .
The code that fills my left nav is now:
$feed = Wu_Feed_Factory::getLocal()->asZendFeed();
$results = array();
foreach ($feed as $entry) {
$result = array();
$result['title'] = $entry->title();
$result['link'] = $entry->link('alternate');
$results[] = $result;
}
$this->view->results = $results;
At some point , I want to alternate the 'Latest stuff' source(so that i could combine and filter the news using external or internal feeds).It makes sense (at least for me) to treat my news as a feed. If this 'plan' will fail in the future , at least now I have rss and atom feeds. Another important reason for going trough this 'effort', is that I will also use this code as a base for my new project.
How it works
Zend_Feed, by default , allows you to create your own feed, not just to import one.
You can do that by using a big array (and by big i mean really big) or implementing the Zend_Builder_Interface(see the above link for details). Basically, you would do one of:
// importing a feed from a custom builder source
$atomFeedFromArray = Zend_Feed::importBuilder(new Zend_Feed_Builder($array));
// the following line is equivalent to the above;
//by default a Zend_Feed_Atom instance is returned
$atomFeedFromArray = Zend_Feed::importArray(new Zend_Feed_Builder($array), 'atom');
// importing a rss feed from a custom builder array
$rssFeedFromArray = Zend_Feed::importArray(new Zend_Feed_Builder($array), 'rss');
// Note: This sample of code is extracted from
// the Zend Official documentation
// http://framework.zend.com/manual/en/zend.feed.importing.html
The first line of code has the same effect as the second and it's even the same array, but do not be confused . Zend_Feed_Builder is just a sample implementation. The ideea is that you implement Zend_Feed_Builder_Interface and
trough that interface , the feed will query you for a Zend_Feed_Header(via getHeader()) and a set of Zend_Feed_Entry(via getEntries()) objects.
Sounds nice , but for my first version , I preferred a set of objects that build the array mentioned before Zend_Feed_Builder kicked in. The reason? I find it more comfortable and I will not be tied to a data source. I can implement(and I will) a builder later, for now here is how I am using the classes I created. Generic Example:
title = 'Example for my post';
$entry = new Wu_Feed_Source_Entry();
$entry->title = 'Hello world from a feed';
$entry->description = 'hello ..';
$entry->content = 'Elaborate words about hello
world';
$source->add($entry);
$feed = new Wu_Feed();
$feed->add($source);
echo $feed->asAtom();//outputs atom feed xml
To reduce the code I am writing, I am using some factory objects to create my feed source. One for the Wu_Feed objects, and one to create the source. Here is how the uml diagram looks:
Wherever I need my feed, I am using Wu_Feed_Factory . This will protect me from any changes that I will make regarding the feed building process. Also , important to note , is that I am not inheriting Zend_Feed. Why? To protect me from myself and from any future changes that (perhaps) will affect Zend_Feed. I am using my classes to build the array needed by Zend_Feed to create the actual feed. Zend_Feed is
used only when I actually need the output(or a Zend_Feed). There are other benefits to this approach , the most important for me: time. Again, I obtained a quick prototype of what I want to achieve in short time , but it's not a dirty solution. I can change anything regarding the building process , and my code that uses Wu_Feed will stay the same , as long as I obtain the feed object trough the same factory and my main methods add,asAtom,asRss
and asZendFeed have the same effect.
Now , again , I am sure there are way better implementations and uses of Zend_Feed. But as something
that I am doing in my free time , this solution works for me and took me around 1h to implement it. My point , again, is that you can quickly prototype something with a framework like ZF behind, and still , protect your present and future code from anything you don't master yet once you decide on how you want to use
your new feature , which is the most important(and difficult) part most times(aka design).
What's next
After I will provide a standard way for you to build feed sources wihthout having to modify and understand the existing code (non existent php5 late static binding support delayed me) , I'll provide a link to the code presented here. Before that , I will use ZF to implement 'nice urls'. So instead of /blog/index/view/entry/123 to have /thinking_about_auth.html.
P.S.: You are free to call me a retard if this post was a waste of your time and had problems reading my non-sense.
K.
Rss