CakePHP 2.1 RSS Component

Currently, I'm getting familiar with the "latest" (at the time of writing) version of CakePHP, 2.1 and am building a project with it. For this project I also needed a helper that can read RSS files (rather than write them, what Cake's core RssHelper does). I have done this before for CakePHP 1.3, but that code was not really compatible with 2.1.

During the development I came to the conclusion that the cleanest way to achieve my goal was to actually create a component, rather than a helper, because there is still some logic that needs to be done with the resulting object. And obviously, logic goes in the Controller and not in the View. So, a component it is!

The component is pretty simple and straight forward. Here is the entire source code (should go under app\\Controller\\Component\\RssComponent.php), which we will discuss in a bit.

<?php
App::uses('Xml', 'Utility');

class RssComponent extends Component {

    /**
     * Reads an (external) RSS feed and returns it's items.
     *
     * @param $feed - The URL to the feed.
     * @param int $items - The amount of items to read.
     * @return array
     * @throws InternalErrorException
     */
    public function read($feed, $items = 5) {
        try {
            // Try to read the given RSS feed
            $xmlObject = Xml::build($feed);
        } catch (XmlException $e) {
            // Reading XML failed, throw InternalErrorException
            throw new InternalErrorException();
        }

        $output = array();

        for($i = 0;$i < $items;$i++):
            if(is_object($xmlObject->channel->item->$i)) {
                $output[] = $xmlObject->channel->item->$i;
            }
        endfor;

        return $output;
    }

}

First off, we'll be using the Xml Utility, that is shipped with CakePHP, which we include using:

App::uses('Xml', 'Utility');

The component itself contains just 1 method, the read method. It takes 1 required argument ($feed), which holds the URL to the feed and 1 optional argument ($items) to determine the amount of items to retrieve from the RSS feed. The default is set to 5 here, but you can obviously change that if you want to.

The method itself is pretty simple, it just calls the Xml::build method to create a SimpleXMLElement of the feed. It catches any error that the Xml::build method might throw (e.g. if the feed URL is dead). We can catch that error from our controller.

In my case, the feed is part of the layout, so I called the component from the AppController in my beforeRender method, like this:

public function beforeRender() {
    try {
        $newsItems = $this->Rss->read('http://example.com/feed');
    } catch(InternalErrorException $e) {
        $newsItems = null;
    }

    $this->set('news', $newsItems);
}

So, basically we tell the AppController to try and fetch the news items and pass it to our views as the $news variable. If the reading of the items fails, $news will be set to null, which you can then check in your View. Also, make sure you don't forget to load up the Component in your controller by putting it in the $components array.

public $components = array('Rss');

Finally, this is what my layout bit looks like. It will be different in your case, but just to give you an idea.

<?php
if(isset($news) && !is_null($news)) {
    $newsItems = array();
    foreach($news as $newsItem):
        $newsItems[] = $this->Html->link(
            $newsItem->title,
            $newsItem->link,
            array('class' => 'new_window')
        ) . '<p>' . $newsItem->description . '</p>';
    endforeach;

    echo $this->Html->nestedList(
        $newsItems,
        array('class' => 'news')
    );
} else {
    echo $this->Html->nestedList(
        array('<p>News unavailable.</p>'),
        array('class' => 'news')
    );
}

This bit checks whether we have the $news variable set and if it's not null (so, it contains items). Then, the foreach loop prepares the items that will be passed to the nestedList method of the HtmlHelper. This way, we'll get a nice unordered list containing the news items, which is exactly what I needed in my case to fit it into my layout. Again, your View/Layout will be different, no doubt. But, this should help you get going on your way with the RssComponent.

If you have any question or comments feel free to post yout comments below!