RSS delivery with the APF
This article deals with RSS streams and the APF tools, that help you to publish your own streams.
For this reason, this page contains two main sections: the first section describes, how the page
controller can be used to generate XML using a document controller and the second part demonstrates
the front controller's capability of delivering RSS streams dynamically.
By default, RSS streams are just simple xml documents, that follow the RSS specification. A common
RSS may contain the following content
(taken from http://www.weather.com/weather/rss/subscription):
APF-Template
<?xml version="1.0" encoding="ISO-8859-1" ?>
<rss version="2.0">
<channel>
<title>The Weather Channel: National Weather Outlook</title>
<link><![CDATA[http://www.weather.com/newscenter/fcstsummary.html?cm_ven=NWF&cm_pla=news&cm_ite=fcstsummary&site=news&cm_cat=rss&par=NWF_rss]]></link>
<description>
Since 1982, The Weather Channel has brought timely weather information to the world. Now via our National Weather Outlook RSS feed we can keep you up-to-date on the latest weather affecting the nation, including severe weather alerts and video forecasts with expert commentary, delivered right to your desktop. The Weather Channel...Bringing Weather To Life
</description>
<language>en-us</language>
<copyright>Copyright © 2006, The Weather Channel Interactive, Inc.</copyright>
<pubDate>Tue, 11 Nov 2008 16:44:01 EST</pubDate>
<docs>http://blogs.law.harvard.edi/tech/rss</docs>
<ttl>60</ttl>
<image>
<title>The Weather Channel: National Weather Outlook</title>
<url><![CDATA[http://image.weather.com/web/common/logos/twci_logo_110x104.jpg?cm_ven=NWF&site=logo&cm_pla=logo&cm_ite=homepage&cm_cat=rss&par=NWF_rss]]></url>
<link><![CDATA[http://www.weather.com?cm_ven=NWF&site=logo&cm_pla=logo&cm_ite=homepage&cm_cat=rss&par=NWF_rss]]></link>
<description>National Weather Outlook from The Weather Channel</description>
</image>
<item>
<title><![CDATA[ Current Weather Conditions Across The 48 Contiguous United States ]]></title>
<link><![CDATA[ http://www.weather.com/maps/maptype/currentweatherusnational/index_large.html?cm_ven=NWF&cm_cat=rss&cm_pla=map&cm_ite=cc_us&par=NWF_rss&site=map ]]></link>
<description><![CDATA[ Northwest Flood; Central Storm. For more details... ]]></description>
<pubDate>Tue, 11 Nov 2008 16:44:01 EST</pubDate>
</item>
...
</channel>
</rss>
Analyzing the document's structure, the weather forecast channel presented here, contains some static
and some dynamic sections. The latter ones have to be filled dynamically to guarantee best actuality.
To be compatible with most of the readers, please have a look at
to get a detailed idea about the content presented via rss.
Due to the fact, that XML documents are similar to HTML files, the page controller and it's
mechanisms can also be used to generate XML output. As we have seen above, the rss content can be
devided into two main sections: the header including the channel definition and the channel's items.
This seems to be like a web page with a design template and a content area created by an included
template.
So let us first of all define the base document. To do so, we create a rss base template and fill it
with the necessary structural elements mentioned in the of rss specification:
APF-Template
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title></title>
<link></link>
<description></description>
<language></language>
<pubDate></pubDate>
<lastBuildDate></lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>The adventure php framework's page controller</generator>
<managingEditor></managingEditor>
<webMaster></webMaster>
...
</channel>
</rss>
To be flexible with the values presented in the stream we add some place holders and a document
controller, that is instructed to fill them as desired. The template now looks like this:
APF-Template
<@controller namespace="..." file="rss_controller" class="rss_controller" @>
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title><html:placeholder name="title" /></title>
<link><html:placeholder name="link" /></link>
<description><html:placeholder name="description" /></description>
<language><html:placeholder name="language" /></language>
<pubDate><html:placeholder name="pubdate" /></pubDate>
<lastBuildDate><html:placeholder name="lastbuilddate" /></lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>The adventure php framework's page controller</generator>
<managingEditor><html:placeholder name="editor" /></managingEditor>
<webMaster>The name of the great webmaster</webMaster>
...
</channel>
</rss>
Before we are going to create the document controller, let me talk about application design in common.
Typically, your application design features a three layer architecture, where the data layer is
intended to care about persistent data, the business layer, that controls the application and the
presentation tier to generate the user interface. In case of good designs, the presentation layer
is convertible with other implementations or output formats. Despite the powerful HTML and XML
implementation of the adventure php framework you are free to choose and deploy tools for generating
other output formats such as PDF. In this case, we can use the built-in mechanisms to generate
dynamical rss streams.
Let us now define the document controller's functionality. As mentioned before, the items of the
channel are going to be presented by a sub template, that we are discussing later on. So the
controller's logic filling the place holder looks quite simple:
PHP-Code
import('namespace::of:my::business::component','BusinessComp');
class rss_controller extends baseController
{
function rss_controller(){
}
function transformContent(){
// create / get the business layer as a service object
$bC = &$this->__getServiceObject('namespace::of:my::business::component','BusinessComp');
// read the channel information
$channel = $bC->getChannelInformation('desired_channel');
// fill the place holders
$this->setPlaceHolder('title',$channel->getAttribute('...'));
$this->setPlaceHolder('link',$channel->getAttribute('...'));
$this->setPlaceHolder('description',$channel->getAttribute('...'));
$this->setPlaceHolder('language',$channel->getAttribute('...'));
$this->setPlaceHolder('pubdate',$channel->getAttribute('...'));
$this->setPlaceHolder('lastbuilddate',$channel->getAttribute('...'));
$this->setPlaceHolder('editor',$channel->getAttribute('...'));
}
}
As you can see in the code box, our current application features a business layer, that we use to
gather the desired data. Please note, that we will use this component for the items as well.
Next, we care about the channel's items. As already mentioned before, we can use the business layer
to load the items for the desired channel and we can use subtemplates to include the item list
into the main template.
For this reason, we update the template definition above like this:
APF-Template
<@controller namespace="..." file="rss_controller" class="rss_controller" @>
<?xml version="1.0"?>
<rss version="2.0">
<channel>
<title><html:placeholder name="title" /></title>
<link><html:placeholder name="link" /></link>
<description><html:placeholder name="description" /></description>
<language><html:placeholder name="language" /></language>
<pubDate><html:placeholder name="pubdate" /></pubDate>
<lastBuildDate><html:placeholder name="lastbuilddate" /></lastBuildDate>
<docs>http://blogs.law.harvard.edu/tech/rss</docs>
<generator>The adventure php framework's page controller</generator>
<managingEditor><html:placeholder name="editor" /></managingEditor>
<webMaster>The name of the great webmaster</webMaster>
<strong><core:importdesign namespace="..." template="items" /></strong>
</channel>
</rss>
The
items template then contains an iterator tag, that is intended to display the
list of available items. Do fill the iterator's data container a second document controller is defined
at the top of the template:
APF-Template
<@controller namespace="..." file="rss_controller" class="rss_items_controller" @>
<core:addtaglib namespace="tools::html::taglib" prefix="html" class="iterator" />
<html:iterator name="items">
<iterator:item getter="getAttribute">
<item>
<title><item:placeholder name="title" /></title>
<link><item:placeholder name="link" /></link>
<description><item:placeholder name="content" /></description>
<author><item:placeholder name="author" /></author>
<pubDate><item:placeholder name="date" /></pubDate>
</item>
</iterator:item>
</html:iterator>
The content of the document controller class looks like this:
PHP-Code
import('namespace::of:my::business::component','BusinessComp');
import('tools::html::taglib::documentcontroller','iteratorBaseController');
class rss_items_controller extends iteratorBaseController
{
function rss_items_controller(){
}
function transformContent(){
// create / get the business layer as a service object
$bC = &$this->__getServiceObject('namespace::of:my::business::component','BusinessComp');
// read the channel information
$channel = $bC->getChannelInformation('desired_channel');
// get the channel's items
$items = $channel->getItems();
// reference the iterator template
$iterator = &$this->__getIterator('items');
// fill the data container
$iterator->fillDataContainer($items);
// display the item list
$iterator->transformOnPlace();
}
}
To sum things up, the page controller's functionality can also be used to generate XML output instead
of HTML content. Second, the framework contains various tools, that can help to generate an rss
stream out of existing content and with help of an application's business component. But please be
aware, that good software design is a central condition to be able to exchange the presentation! To
be a bit more explicitly: good software design should be independent of tools or frameworks. :)
Chapter 3 now describes, how the rss stream generated through the APF page controller can be
presented to the user in a convenient way. For this reason, each content page should get a rss tag.
When the user clicks on that tag, the content should be presented as an rss stream in a new window.
To achieve this, a front controller action can be used to deliver the rss document generated in
chapter 2. To keep things simple, the action definition contains no input parameters, but reads the
page id from the "normal" GET params. As an example, the rss stream URL may then look like this:
Code
http://adventure-php-framework.org/Page/082-RSS-delivery-with-the-APF/~/my_namespace-action/deliverRSS
In order to execute the action described before, we have to ensure, that the bootstrap file uses the
front controller to create the page output. If this is not the case, please take a look at the
front controller documentation.
After having done that, we can define the action configuration for the current context of our
application and the action implementation. If you are not yet familiar with the implementation of
front controller actions, take a short break and read about this topic on the
appropriate page.
As we have already implemented the major part of the functionality of the action and the action has
not input params by definition, the implementation of the action is quite easy. The only thing it
has to do is to extract the page id from the url, create the page and flush the output to the user.
You might expect, that this job is not as easy as it sounds. :) This is because the implementation
above does not contain a page configuration parameter, that is applied to the business component.
But don't be afraid, there's a very elegant way of injecting this parameter into the DOM tree. To
get a better understanding, let me say a few words about the DOM tree, the page controller creates:
Every template file is searched for known tags. Every tag is then extracted, the taglib implementation
is executet and the object created in this way is stored as a child node of the current DOM node.
Due to the fact, that the
importdesign tags include functionality to include another template
file as a new DOM node and parse the tags within that template in the same way, a tree containing
objects derived from the
Document class. After the
loadDesign()
method is called in the bootstrap file, the complete DOM tree exists in memory and can be manipulated
using the API functions of the
Page and
Document classes. This fact
enables the developer to subsequently "inject" attributes into the DOM document objects as desired.
In this simple case, we exactly know the structure of the DOM tree. I consists of the
Page
object, the root
Document and it's child - the items template.
Now, that we know about the characteristics of the DOM tree created by the templates described in
chapter 2, the parameter injection can be done by the following code snippet:
PHP-Code
// declare page id
$id = '001';
// create the DOM tree
$page = new Page();
$page->loadDesign('namespace::to::the::desired::template','template_name');
// get the root node of the tree and add attribute
$doc = &$page->getByReference('Document');
$doc->setAttribute('page_id',$id);
Now the
rss_controller must be adapted to use the
Document's attribute instead of
the static channel name. This can be achieved by changing
PHP-Code
$channel = $bC->getChannelInformation('desired_channel');
to
PHP-Code
$channel = $bC->getChannelInformation($this->__Document->getAttribute('page_id'));
In case of the item controller, the parent document must be asked for the page id. Here,
PHP-Code
$channel = $bC->getChannelInformation('desired_channel');
must be changed to
PHP-Code
$parent = &$this->__Document->getByReference('ParentObject');
$channel = $bC->getChannelInformation($parent->getAttribute('page_id'));
because the document referenced within the items controller is the child of the document node we
added the secondary attribute to.
The action implementation now contains to following code:
PHP-Code
class DeliverRSSAction extends AbstractFrontcontrollerAction
{
function run(){
// get page id (to keep it simple)
$id = $_REQUEST['id'];
// create the DOM tree
$page = new Page();
$page->loadDesign('namespace::to::the::desired::template','template_name');
// get the root node of the tree and add attribute
$doc = &$page->getByReference('Document');
$doc->setAttribute('page_id',$id);
// transform and display rss stream
header('Content-Type: text/xml');
echo $page->transform();
// exit execution, because we're done
exit(0);
}
}
Thanks to the clean DOM structure and the capabilities of the front controller it is easy to create
an rss stream within an exosting application. Moreover, the developer is - also thanks to the front
controller - not forced to create and maintain several bootstrap or
rss.php files to deliver
the rss content.
Comments
Do you want to add a comment to the article above, or do you want to post additional hints? So please click
here. Comments already posted can be found below.
There are no comments belonging to this article.