For my first entry, I am going to talk about how to create an RSS Feed on your website. RSS (Really Simple Syndication) is a format used to publish frequently updated works such as blogs or featured products. RSS defines a set of XML elements that are used to describe a channel or feed of information. An RSS feed is comprised of two parts, first is the metadata describing the channel and second is the records that make up the elements of the feed. RSS feeds allow your sites visitors to access the information on your site using software that reads these feeds. This will allow your site's visitors to stay up-to-date on the information on your site.
CakePHP allows for easy integration of RSS feeds into existing controller actions through the automatic router extension parsing. This allows us to specify what type of response we want from a URL through adding the proper extension to the URL such as http://www.yoursite.com/entries.rss. This alerts the router that your are asking for RSS formatted data in return. In addition, CakePHP has an RssHelper class that can be used to output parts of the metadata and elements in the feed through an easy to use helper.
Preparation
Before we begin making the feed we must alert the router that we want to allow for extensions to be parsed in the URL and that we want it to accept .rss
as a valid extension. In your sites router file we add the following:
Router::parseExtensions('rss');
Also for CakePHP to work it magic we must also have the RequestHandler
in our controller's $components
array. Now the router knows that we would like to parse urls that end in .rss
as requesting RSS formatted responses. The next step of preparation is to add a default layout for rss feeds on your site. When you request a different format response the layout that is rendered will be selected from a sub-folder with the same name as the format. So in this case we would need a folder called /rss
in the layouts folder in our CakePHP install. The view class will search for a file that has the same name as the layout that would be rendered if you were just rendering the html. In most cases this is the default.ctp layout file in the main layouts directory, but because we are requesting the response in RSS format we must add a default.ctp
layout in the /layouts/rss/
sub-directory. This layout is our default RSS Feed layout.
echo $rss->header(); if (!isset($channel)) { $channel = array(); } if (!isset($channel['title'])) { $channel['title'] = $title_for_layout; } echo $rss->document($rss->channel(array(), $channel, $content_for_layout));
Here in the layout our RssHelper shines through. We use the method RssHelper::channel() which generates the element and associated metadata elements. The $content_for_layout
variable contains the output from the view. These then get passed to the RssHelper::document() method, which wraps the RSS document in the respective elements.
Controller
The controller needs no modification in the case of a simple RSS feed. This is because we are only adding a second view that is xml/rss
to the action. The same data is used in both views and because CakePHP automatically sets the correct response type we don't need to tell it to render the correct view and layout for RSS. Here is the action method in the EntriesController
for a basic view sorted by a published_date
field and showing only if it is published.
public function index() { $this->paginate['Entry'] = array( 'conditions' => array('Entry.published' => 1), 'order' => 'Entry.published_date DESC'); $this->set('entries', $this->paginate()); }
If you do have code that is specific for only the RSS view you can use the RequestHandler::isRss() to see if the action was called with the request for xml/rss
formatting on response. This method returns a boolean value based on if the .rss
extension was parsed in the URL.
if ($this->RequestHandler->isRss()) { // RSS feed specific code goes here }
Note About Channel Metadata
It may feel right to put your metadata information in the index method in the controller, using Controller::set() to send the information to the views. This is inappropriate and is one of the most common snags that we have seen in the CakePHP community with creating RSS feeds. That information which is passed in the layout file to the RssHelper::channel() method should be set in the view using View::set() which will set the $channel
variable for the layout in the view.
Views
As we had to put the layout in a subdirectory of the layouts folder we also need to create a view for the index action for the blogs controller. This is done by creating a directory /views/entries/rss/
which will hold our view file that will generate the RSS to render. You will need to add your RssHelper to the list of helpers in your controller so that it is automatically loaded in the view and the layout.
Our view begins by setting the $channel
variable for the layout, this contains all the metadata for our RSS feed.
$homeUrl = $html->url('/', true); $this->set('channel', array( 'title' => __("Daniel's Recent Articles", true), 'link' => $homeUrl, 'description' => __("Most recent articles from Daniel.", true), 'language' => 'en-us', 'image' => array( 'title' => 'Recent Articles from Daniel', 'url' => FULL_BASE_URL . $this->webroot('/img/rss_feed_image', true), 'link' => $homeUrl));
First we get the URL link for the website home that we will use for the links. Also we set the title, description and image to use for the RSS feed icon. By setting the channel variable using View::set()
we are providing the layout the information to render the RSS feed's metadata elements.
The second part of the view generates the elements for the actual records of the feed. This is accomplished by looping through the data that has been passed to the view and using the RssHelper::item() method. The other method you can use, RssHelper::items() which takes a callback and an array of items for the feed. (The method I have seen used for the callback has always been called transformRss()
. There is one downfall to this method, which is that you cannot use any of the other helper classes to prepare your data inside the callback method because the scope inside the method does not include anything that is not passed inside, thus not giving access to the TimeHelper
or any other helper that you may need. The RssHelper::item() transforms the associative array into an element for each key value pair.
foreach ($entries as $entry) { $postTime = strtotime($entry['Entry']['created']); $entryLink = array( 'controller' => 'entries', 'action' => 'view', 'year' => date('Y', $postTime), 'month' => date('m', $postTime), 'day' => date('d', $postTime), $entry['Entry']['slug']); // This is the part where we clean the body text for output as the description // of the rss item, this needs to have only text to make sure the feed validates $bodyText = preg_replace('=\(.*?)\=is', '', $entry['Entry']['body']); $bodyText = $text->stripLinks($bodyText); $bodyText = Sanitize::stripAll($bodyText); $bodyText = $text->truncate($bodyText, 400, '...', true, true); echo $rss->item(array(), array( 'title' => $entry['Entry']['title'], 'link' => $entryLink, 'guid' => array('url' => $entryLink, 'isPermaLink' => 'true'), 'description' => $bodyText, 'dc:creator' => $entry['Entry']['author'], 'pubDate' => $entry['Entry']['created'])); }
You can see above that we can use the loop to prepare the data to be transformed into XML elements. It is important to filter out any non-plain text charictars out of the description, especially if you are using a rich text editor for the body of your blog. In the code above we use the TextHelper::stripLinks() method and a few methods from the Sanitize class, but we recommend writing a comprehensive text cleaning helper to really scrub the text clean. Once we have set up the data for the feed, we can then use the RssHelper::item()
method to create the XML in RSS format. Once you have all this setup, you can test your RSS feed by going to your site /entries/index.rss
and you will see your new feed. It is always important that you validate your RSS feed before making it live. This can be done by visiting sites that validate the XML such as Feed Validator or the w3c site at http://validator.w3.org/feed/.