Add an RSS Feed to your CakePHP Models
I recently developed a prototype for a client who insisted that the site include an RSS feed for the articles of the site. Not being one to turn down a challenge, and being aware of the Power of Cake, I assured them it could be completed with such a feature.
The newsfeed would provide the latest articles to be consumed by an aggregation software or other feed reader.
So in the text that follows I’ll outline what is involved to add news feed or rss feeds to a particular model of your CakePHP site.
The Problem
News feeds are everywhere and essential in todays web development. Your sites are just as good as anyone elses, and deserve this critical feature.
The Solution
Implement a url that can be consumed by web browsers or feed readers to publicize your latest content to the world.
Implementing a Feed Generator in CakePHP
If we think about this rationally there are 3 peices;
- A controller action to retrieve the latest articles
- A view to generate the xml formatted excerpts
- A routing piece to provide an acceptable url
The RSS Feed Action
No need to over complicate things here. Imagine you have a standard Model, oh let’s say ‘Posts’. All we need to do here is add a newsfeed action.
By default the action will pull 5 posts within the last month, but the number can be overridden by passing a different number in the url (as with any action parameter).
app/controllers/posts_controller.php -snippet
/** * Returns an array of all public posts less than one month old, orderd by date * * @return unknown */ function newsfeed($count=null) { if(!$count) $count=5; $this->Post->recursive = 0; $posts=$this->Post->findAll('is_public = 1 AND is_published = 1 AND Post.date_modified >= now() - INTERVAL 1 MONTH' ,null,'Post.date_modified DESC',$count); if(isset($this->params['requested'])) { return $posts; } $this->set('posts',$posts ); }
It would be really helpful if CakePHP provided an RSS helper. Zoot alors ! They do!
app/controllers/posts_controller.php -snippet
var $helpers = array('Rss'); var $components = array ('RequestHandler');
The XML Generating View
Just like any other view, all we need to do is transform some provided data into a formatted page. This time it will be strict XML without all the html clutter.
app/views/posts/rss/newsfeed.ctp
<?php function rss_transform($item) { return array('title' => $item['Post']['title'], 'link' => array('controller' => 'posts', 'action' => 'view', $item['Post']['id']), 'guid' => array('controller' => 'posts', 'action' => 'view', $item['Post']['id']), 'description' => strip_tags($item['Post']['body']), 'pubDate' => $item['Post']['date_added'], ); } $this->set('items', $rss->items($posts, 'rss_transform')); $this->set('channelData', $channelData); ?>
And of course sticking with format we should want to add an RSS layout.
app/views/layouts/rss/default.ctp
<?php echo $rss->header(); $channelData = array('title' => 'Recent News | Digital Business', 'link' => array('controller' => 'posts', 'action' => 'index', 'ext' => 'rss'), 'url' => array('controller' => 'posts', 'action' => 'index', 'ext' => 'rss'), 'description' => 'The best Digital Business RSS Feed on the web', 'language' => 'en-us' ); $channel = $rss->channel(array(), $channelData, $items); echo $rss->document(array(), $channel); ?>
The RSS compliant URL
You only need to add 2 lines to your routes configuration file for CakePHP to catch and handle the url to pint to your RSS feed. You might use feed.rss, I choice live.rss.
app/config/routes.php -snippet
/** * ...allow rssextensions * and send live.rss to the rss feed */ Router::connect('/live', array('controller' => 'posts', 'action' => 'newsfeed')); // see my posts on sitemaps to use this next line ;) Router::connect('/sitemap', array('controller' => 'sitemaps', 'action' => 'index')); Router::parseExtensions('rss','xml');
Yes, you’ll notice an additional route there for dynamic sitemaps, very useful as well.
Sweet! Now just visit http://example.com/live.rss, or throw that url in your favorite Feed Reader to see the results.
http://example.com/live.rss
<?xml version="1.0" encoding="UTF-8"?> <rss version="2.0"> <channel> <title>Recent News | Digital Business</title> <link>http://digbiz.localhost/posts.rss</link> <url> <posts/> <index/> <rss/> </url> <description>The best Digital Business RSS Feed on the web</description> <language>en-us</language> <item> <title>This is a Public, Published General News Article</title> <link>http://digbiz.localhost/posts/view/1</link> <guid>http://digbiz.localhost/posts/view/1</guid> <description>It should be visible by ALL users and guests.</description> <pubDate>Tue, 22 Jul 2008 11:22:51 -0400</pubDate> </item> <item> <title>New Public News</title> <link>http://digbiz.localhost/posts/view/6</link> <guid>http://digbiz.localhost/posts/view/6</guid> <description>Lets spice this up a bit...</description> <pubDate>Fri, 01 Aug 2008 13:00:25 -0400</pubDate> </item> <item> <title>Neil Hair Guest Essay in Democrat and Chronicle</title> <link>http://digbiz.localhost/posts/view/9</link> <guid>http://digbiz.localhost/posts/view/9</guid> <description> Dr. Neil Hair comments on the Virtual Workforce of the future. http://www.democratandchronicle.com/apps/pbcs.dll/article?AID=2008810050343</description> <pubDate>Mon, 06 Oct 2008 09:03:36 -0400</pubDate> </item> </channel> </rss><!-- 0.3199s -->
Thank you for this post! Is there a way to use a single feed across models?
@T.Javsier
Interesting Question..
I am assuming you are referring to related models like perhaps Posts and Comments.
If that is the case you could probably just use the primary controller to retrieve the additional values and add the comments array to the posts array before passing off to the view. But I have never tried.
So i followed these steps almost exactly (making changes for the names of models and controllers, etc) and it’s not working. I can get to the feed.rss but there is no data for the feed. it outputs like this:
Articles/rsshttp://localhost/newsaga/
I’m not sure what’s going on … there is definitely data in the database, and that data appears in the array from the find method.
Any help?
@my previous post:
it should be:
@Gregg
Well I wrote this article a while back and had to do some digging. I left out to critical pieces, the RequestHandler component (in the controller) and the RSS layout (in the views). I have updated those sections above.
I’ve been trying to implement an xml sitemap using this approach and I can access the dynamic xml document at example.com/sitemap but when I go to example.com/sitemap.xml I get an error. I guess this is a routes problem but I can’t seem to fix it. Anyone else hads this problem or know how to fix it?
I’ve got a very similar codebase to this for the RSS feeds in one of my applications, however i always find firefox tries to force a file download with the rss feed, the line that causes this seems to be echo $rss->header(); . Just wondered if anyone knew what was going wrong here I want the rss feed to opened up the browser.
@Jim
I am not sure how similar your code is to this post, but the method here works fine for FF. Just off the top of my head I would check that you have the RequestHandler component and the RSS helper in the controller.
Also, I don’t believe I used the line you included in my controller or view. If that is the problem just remove it.
@Paul
I think you should try this article instead, http://edwardawebb.com/programming/php-programming/cakephp/generating-dynamic-sitemaps-cakephp.
Check you have the parse extensions configuration properly set in your routes to include .xml, and that your view is in the xml folder.
Wow. That’s great!
I spent much time to research this problem.
But, this site below gave me the incorrect guideline.
- http://anupom.wordpress.com/2008/03/12/rss-in-cakephp-12/
This url is enough good, but lead us not using RSS helper of cakePHP
- http://bakery.cakephp.org/articles/view/rss-feed-them-cake
And now, I found that, this location is very good, very exactly for dummy. Also, all of you could visit here:
- http://mark-story.com/nodes/view/creating-rss-feeds-with-cakephp-and-extensionless-routing
The key problem is, it must be $items and $channel called at default layout of rss/xml, not $content_for_layout as shown at normal layout.
Is there any way we can exclude .rss extension from the url?
simply we need
http://example.com/live
@bdwankhede
The extension is converted into a subdirectory for the templates. So app/views/layouts/rss/default.ctp moved to app/views/layouts/default.ctp should meet your needs.
If you want both to be available you can add another route.