| [ Index ] |
PHP Cross Reference of Web Application Component Toolkit |
[Summary view] [Print] [Text view]
1 <!DOCTYPE html PUBLIC '-//W3C//DTD XHTML 1.0 Strict//EN' 2 'http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd'> 3 <html xmlns='http://www.w3.org/1999/xhtml' xml:lang='en' lang='en'> 4 <head> 5 <meta http-equiv='Content-Type' 6 content='text/html; charset=iso-8859-1' /> 7 8 <title>Generating an RSS feed with WACT</title> 9 10 <link href="../tutorial.css" type="text/css" rel="stylesheet"/> 11 12 </head> 13 <body> 14 15 <h1>Generating syndication feeds with WACT</h1> 16 17 <p><a href="../" shape="rect">Tutorials Home</a></p> 18 19 <p class="abstract">This tutorial demonstrates that 20 21 <acronym title="The Web Application Component Toolkit">WACT</acronym> 22 23 can be used to generate content other than HTML. It assumes no prior 24 knowledge of the WACT framework.</p> 25 26 <p class="docinfo"> 27 Author: <a href="mailto:jon@bangoid.com" shape="rect">Jon Ramsey</a><br /> 28 Created: 2003-12-07<br /> 29 $Date: 2004/11/12 21:25:03 $<br /> 30 $Author: jeffmoore $<br /> 31 $Revision: 1.6 $<br /> 32 </p> 33 <h2>Syndicate Me</h2> 34 35 <p>So, you have a lot to say, and you think that the world should hear 36 it. Are you sure? Oh, ok, well then you <strong>need a feed</strong>. This 37 tutorial walks you through creating a simple syndication feed using WACT 38 templating. The rest is up to you.</p> 39 40 <h2>Choices, choices, an awful lot of choices</h2> 41 42 <p>Syndication formats come in a <a 43 href="http://xml.com/pub/a/2002/12/18/dive-into-xml.html" 44 shape="rect">multitude of flavors</a>. With all the <a 45 href="http://www.sifry.com/alerts/archives/000299.html" 46 shape="rect">controversy</a> flying around it can be hard to choose which 47 format you should use for your application. I'm going to plump for <a 48 href="http://web.resource.org/rss/1.0/" shape="rect">RSS 1.0</a>.</p> 49 50 <h3>What makes a good feed?</h3> 51 52 <p>Burgers.</p> 53 54 <p>Oh, a syndication feed… well, a feed is a simple beast providing 55 at least two things to its users:</p> 56 57 <ul> 58 <li>Some general information about the source (e.g. publication date, 59 copyright info.)</li> 60 61 <li>A collection of (<em>more or less</em>) interesting items</li> 62 </ul> 63 64 <p>Through the use of some of basic <a 65 href="http://wact.sourceforge.net/index.php/SupportedTags" shape="rect">WACT 66 tags</a> we can create and populate feed templates in a very simple 67 fashion.</p> 68 69 <h2>Infrastructure</h2> 70 71 <p>I'm going to be using a <a 72 href="http://wact.sourceforge.net/index.php/PageController" 73 shape="rect">Page Controller</a>, a <a 74 href="http://wact.sourceforge.net/index.php/Template" 75 shape="rect">Template</a> and a config file to generate the output feed.</p> 76 77 <h3>Control freak</h3> 78 79 <p>First off, lets get the controller moving. We'll also use this 80 opportunity to get <strong>“Hello World!”</strong> out of the 81 way. <code>feed.1.php</code>:</p> 82 83 <pre xml:space="preserve"> 84 <?php 85 require_once '../wact/framework/common.inc.php'; /* Your path to WACT may differ... */ 86 require_once WACT_ROOT . 'template/template.inc.php'; /* Include the base WACT template system */ 87 88 $feed =& new Template('/hello-world.1.html'); /* generate a template object from a template file */ 89 $feed->display(); /* display the rendered template */ 90 ?></pre> 91 92 <p>Things to note:</p> 93 94 <ul> 95 <li>The first require (for WACT common functions) must be given an 96 absolute path to your WACT installation. The second require can use the 97 <code>WACT_ROOT</code> constant — it points to the framework 98 directory under your WACT installation.</li> 99 100 <li>The second require (<code>template.inc.php</code>) brings in the WACT 101 templating system.</li> 102 103 <li>We set up a <code>Template</code> object using the file 104 <code>/hello-world.1.html</code>.</li> 105 106 <li>We then call the Template object's <code>display()</code> method. This 107 will output the compiled template to a visiting user agent.</li> 108 </ul> 109 110 <p>Hitting <code>feed.1.php</code> from a browser 111 at this point will generate an error indicating that 112 <code>hello-world.1.html</code> can't be found. I guess we'd better create 113 it.</p> 114 115 <h3>Absolute beginners</h3> 116 117 <p>You probably noticed that the path to the template file is absolute. But 118 absolute in relation to what?</p> 119 120 <p>The default WACT templating <a 121 href="http://wact.sourceforge.net/index.php/FileScheme" shape="rect">file 122 scheme</a> expects to find template files in a directory 123 <code>./template/source</code>. When WACT compiles templates it expects to 124 be able to write them to a directory <code>./templates/compiled</code> 125 — this directory needs to be writable by your webserver. Let's set 126 these directories up:</p> 127 128 <pre xml:space="preserve"> 129 $ mkdir templates 130 $ mkdir templates/source 131 $ mkdir templates/compiled 132 $ chmod 777 templates/compiled</pre> 133 134 <h3>Canonical, dull</h3> 135 136 <p>Ok, with that out of the way we can finally create our very exciting 137 template, saving it as 138 <code>templates/source/hello-world.1.html</code>:</p> 139 140 <pre xml:space="preserve"> 141 <html> 142 <body> 143 <h1>Hello, World!</h1> 144 </body> 145 </html></pre> 146 147 <p>You'll notice that it's just a vanilla HTML file (missing a DOCTYPE 148 etc. but hey).</p> 149 150 <p>Now hit <a href="feed.1.php" shape="rect">feed.1.php</a> from your 151 browser, pat yourself on the back, go home and sleep the contented sleep of 152 someone who has implemented <strong>“Hello, World!”</strong> in 153 a new framework. Really, what more do you people want?</p> 154 155 <h2>Fame</h2> 156 157 <p>Ok, just to make you happy. Your 15 minutes are coming right 158 up. <code>feed.2.php</code>:</p> 159 160 <pre xml:space="preserve"> 161 <?php 162 require_once '../wact/framework/common.inc.php'; /* Your path to WACT may differ... */ 163 require_once WACT_ROOT . 'template/template.inc.php'; /* Include the base WACT template system */ 164 165 $feed =& new Template('/hello-world.2.html'); /* generate a template object from a template file */ 166 $feed->set('name', 'Your name here'); 167 $feed->display(); /* display the rendered template */ 168 ?></pre> 169 170 <p>Things to note:</p> 171 172 <ul> 173 <li>The template source has changed to <code>/hello-world.2.html</code></li> 174 175 <li>We are calling the <code>set()</code> method of the 176 <code>Template</code> object, with the arguments <code>'name'</code> and 177 <code>'Your name here'</code>. The <code>set()</code> method is part of 178 WACT's <a href="http://wact.sourceforge.net/index.php/DataSpace" 179 shape="rect">DataSpace interface</a>.</li> 180 </ul> 181 182 <p>We also create the new template file 183 <code>templates/source/hello-world.2.html</code>:</p> 184 185 <pre xml:space="preserve"> 186 <html> 187 <body> 188 <h1>Hello, World!</h1> 189 <h2>From &ldquo;{$name}&rdquo;.</h2> 190 </body> 191 </html></pre> 192 193 <p>Hitting <a href="feed.2.php" shape="rect">feed.2.php</a> you will note 194 that the <code>{$name}</code> variable reference in the template file is 195 replaced by the value provided to the <code>set()</code> method in the 196 controller. Straightforward, no?</p> 197 198 <h3>Think local, not global</h3> 199 200 <p>Unfortunately the world didn't come knocking. Perhaps a little more 201 personalisation is required? <code>feed.3.php</code>:</p> 202 203 <pre xml:space="preserve"> 204 <?php 205 require_once '../wact/framework/common.inc.php'; /* Your path to WACT may differ... */ 206 require_once WACT_ROOT . 'template/template.inc.php'; /* Include the base WACT template system */ 207 208 $feed =& new Template('/hello-world.3.html'); /* generate a template object from a template file */ 209 $feed->display(); /* display the rendered template */ 210 ?></pre> 211 212 <p>We lost the call to the <code>set()</code> method, and the template file 213 has changed, <code>templates/source/hello-world.3.html</code>:</p> 214 215 <pre xml:space="preserve"> 216 <html> 217 <body> 218 <core:import file="hello.ini"/> 219 <h1>Hello, {$location}!</h1> 220 <h2>From &ldquo;{$name}&rdquo;.</h2> 221 </body> 222 </html></pre> 223 224 <p>We've introduced another variable reference, <code>{$location}</code>, 225 but the main thing here is the use of the <code><a 226 href="http://wact.sourceforge.net/index.php/CoreImportTag" 227 shape="rect"><core:import/></a></code> element.</p> 228 229 <p>This WACT element can be used to pull in a dictionary of key-value pairs 230 from a file (<code>templates/source/hello.ini</code> here). The entries 231 are stored in the familiar <code>php.ini</code> syntax, 232 <code>templates/source/hello.ini</code>:</p> 233 234 <pre xml:space="preserve"> 235 location = wherever here is 236 name = Your name here</pre> 237 238 <p>The import implicitly calls the <code>Template::set()</code> method for 239 each value, so you can go ahead and use the keys as variable references in 240 your template.</p> 241 242 <p>If you check out <a href="feed.3.php" shape="rect">feed.3.php</a> after 243 customising <code>hello.ini</code> appropiately you will get amazing, 244 personalised, localised output!</p> 245 246 <h2>Feed me up!</h2> 247 248 <p>Ok, ok. Heeeere we go.</p> 249 250 <p>Now remember, syndication feeds generally have a set of headers, followed 251 by collection of newsworthy(!) items. The header information isn't likely 252 to change much, so we're going to use the <code><core:import/></code> 253 element and <code>Template::set()</code> to deal with it.</p> 254 255 <h3>From template to shining, validating feed…</h3> 256 257 <p>Firstly, the template, <code>templates/source/rss1-0.1.rdf</code>:</p> 258 259 <pre xml:space="preserve"> 260 <?xml version="1.0" encoding="UTF-8"?> 261 <core:import file="rss1-0.ini"/> 262 <rdf:RDF 263 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 264 xmlns="http://purl.org/rss/1.0/" 265 xmlns:dc="http://purl.org/dc/elements/1.1/" > 266 267 <channel rdf:about='{$feedlink}'> 268 <title>{$feedtitle|utf8encode}</title> 269 <link>{$feedlink}</link> 270 <description>{$feeddescription|utf8encode}</description> 271 <dc:language>{$feedlanguage}</dc:language> 272 <dc:date>{$feedpublicationdate}</dc:date> 273 <dc:creator>{$feedcreator|utf8encode}</dc:creator> 274 <dc:subject>{$feedsubject|utf8encode}</dc:subject> 275 </channel> 276 277 </rdf:RDF></pre> 278 279 <p>Check out the <a href="http://web.resource.org/rss/1.0/" shape="rect">RSS 280 1.0 specification</a> for full details of the format.</p> 281 282 <p>You'll notice quite a few variable references in there 283 (<code>{$feedlink}, {$feedlanguage}</code> etc.).</p> 284 <p>What's new here is the use of "output expressions" like 285 <code>{$feedtitle|utf8encode}</code>. This particular 286 example encodes the output with PHP's 287 <a href="http://www.php.net/utf8_encode">utf8_encode()</a> 288 function. See the <a href="http://wact.sourceforge.net/index.php/TemplateAuthorsGuide"> 289 Template Authors Guide</a> for more information on output expressions.</p> 290 <p>There's also a 291 <code><core:import/></code> tag to import a dictionary from 292 <code>templates/source/rss1-0.ini</code>:</p> 293 294 <pre xml:space="preserve"> 295 feedlink = http://www.example.com/myfeed 296 feedtitle = My cool feed 297 feeddescription = All kinds of ultra-cool stuff 298 feedlanguage = en-GB 299 feedcreator = me@example.com 300 feedsubject = Cool stuff from me</pre> 301 302 <p>The import takes care of most of the variable references, but there are a 303 couple that we have to deal with in our controller, 304 <code>feed.4.php</code>:</p> 305 306 <pre xml:space="preserve"> 307 <?php 308 require_once '../wact/framework/common.inc.php'; /* Your path to WACT may differ... */ 309 require_once WACT_ROOT . 'template/template.inc.php'; /* Include the base WACT template system */ 310 311 $feed =& new Template('/rss1-0.1.rdf'); /* generate a template object from a template file */ 312 313 /* set the feed publication date (i.e. now) */ 314 putenv("TZ=UTC"); 315 $tstamp = strftime("%Y-%m-%dT%H:%M:%SZ"); 316 $feed->set('feedpublicationdate', $tstamp); 317 318 /* set the content type to get nicer output in browsers 319 plus set the character set to utf-8 for i18n chars */ 320 header("Content-Type: application/xml; charset=utf-8"); 321 $feed->display(); /* display the rendered template */ 322 ?> 323 </pre> 324 325 <p>We have to set the <code><?xml version="1.0"?></code> header 326 — PHP has problems with the <? if we include it directly in the 327 template. The only other variable that we set from the controller is 328 <code>{$feedpublicationdate}</code>.</p> 329 330 <p>As it currently stands, <a href="feed.4.php" shape="rect">feed.4.php</a>, 331 this sample feed <a href="http://feedvalidator.org/" 332 shape="rect">validates</a>, but it's a bit dull — where's the 333 content?</p> 334 335 <h2>Items, schmitems</h2> 336 337 <p>RSS 1.0 provides it's content as a simple list of items. Well, actually 338 there are two lists.</p> 339 340 <ul> 341 <li>One list is stored in the channel element. This list is just a menu of 342 the <em>items du jour</em>; each item is identified with an 343 <code>rdf:resource</code> — a unique identifier (a <acronym 344 title="Universal Resource Indicator">URI</acronym>, normally the 345 permalink to the item).</li> 346 347 <li>The second list provides the meat of the dish as a series of 348 <code>item</code> elements, each containing a <code>title</code>, a 349 <code>link</code> (the same URI as the <code>rdf:resource</code> from 350 the menu) and a <code>description</code>. Items may also contain other 351 optional elements (check the spec for more details).</li> 352 </ul> 353 354 <h3>So, lists you say…</h3> 355 356 <p>Time to introduce another WACT element or two — 357 <code><list:list></code> and its child, 358 <code><list:item></code>. We'll also have to check out <a 359 href="http://wact.sourceforge.net/index.php/ArrayDataSet" 360 shape="rect">ArrayDataSets</a>.</p> 361 362 <p>A WACT ArrayDataSet is a <a 363 href="http://wact.sourceforge.net/index.php/RecordSet" 364 shape="rect">RecordSet</a> — it implements the <a 365 href="http://wact.sourceforge.net/index.php/DataSource" 366 shape="rect">DataSource</a> interface and a standard <a 367 href="http://wact.sourceforge.net/index.php/Iterator" 368 shape="rect">Iterator</a> interface. WACT treats an ArrayDataSet in the 369 same way that it treats recordsets coming from a database, i.e. as a 370 collection of DataSpace records that it can iterate through using the 371 common iterator interface.</p> 372 373 <p>This behaviour allows us to cheat for our feed example, by using an 374 ArrayDataSet to represent our collection of items. In reality (<em>and 375 possibly in future versions of this tutorial</em>) this item list would 376 probably come from a database, or some other persistent storage 377 mechanism. But for now we'll take the easy route.</p> 378 379 <h3>Cheater</h3> 380 381 <p>We'll set up the ArrayDataSet in our controller, 382 <code>feed.5.php</code>:</p> 383 384 <pre xml:space="preserve"> 385 <?php 386 require_once '../wact/framework/common.inc.php'; /* Your path to WACT may differ... */ 387 require_once WACT_ROOT . 'template/template.inc.php'; /* Include the base WACT template system */ 388 require_once WACT_ROOT . 'util/arraydataset.inc.php'; /* Include WACT ArrayDataSet */ 389 390 $items_array = array(0 => array('itemlink' => 'http://www.example.com/item.1', 391 'itemtitle' => 'The first item in my feed.', 392 'itemdescription' => "This is the first item in my rilly cool feed. Don't you agree, it is very cool?"), 393 1 => array('itemlink' => 'http://www.example.com/item.2', 394 'itemtitle' => 'The second item in my feed.', 395 'itemdescription' => 'Like I say, this feed is just so cool.'), 396 2 => array('itemlink' => 'http://www.example.com/item.3', 397 'itemtitle' => 'The third item in my feed.', 398 'itemdescription' => 'I feel like I have a lot to offer. Oh by the way, Hello, World!')); 399 $items_recordset =& new ArrayDataSet($items_array); 400 401 402 $feed =& new Template('/rss1-0.2.rdf'); /* generate a template object from a template file */ 403 404 /* set the feed publication date (i.e. now) */ 405 putenv("TZ=UTC"); 406 $tstamp = strftime("%Y-%m-%dT%H:%M:%SZ"); 407 $feed->set('feedpublicationdate', $tstamp); 408 409 /* set up the items lists */ 410 $items_menu =& $feed->getChild('itemsmenu'); 411 $items_menu->registerDataSet($items_recordset); 412 $items =& $feed->getChild('items'); 413 $items->registerDataSet($items_recordset); 414 415 /* set the content type to get nicer output in browsers 416 plus set the character set to utf-8 for i18n chars */ 417 header("Content-Type: application/xml; charset=utf-8"); 418 $feed->display(); /* display the rendered template */ 419 ?></pre> 420 421 <p>Things to note:</p> 422 423 <ul> 424 <li><code>$items_array</code> contains our multi-dimensional array of 425 records. Individual records are stored in a dictionary as key-value 426 pairs.</li> 427 428 <li>We create a new ArrayDataSet, <code>$items_recordset</code> and 429 populate it with <code>$items_array</code>.</li> 430 431 <li><code>getChild($id)</code> finds the element in our template that has 432 an id attribute with the value <code>$id</code>, in this case a couple of 433 <code><list:list></code> elements. We can then call 434 <code>registerDataSet()</code> on each of the elements that we found.</li> 435 436 <li><code>registerDataSet($dataset)</code> does what it says it's going to 437 do — it associates our ArrayDataSet as a data provider with our 438 lists. WACT lists iterate over recordsets for their output as a standard 439 part of their behaviour.</li> 440 </ul> 441 442 <h2>Show me the magic</h2> 443 444 <p>Let's take a look at our revised template file, 445 <code>templates/source/rss1-0.2.rdf</code>:</p> 446 447 <pre xml:space="preserve"> 448 <?xml version="1.0" encoding="UTF-8"?> 449 <core:import file="rss1-0.ini"/> 450 <rdf:RDF 451 xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 452 xmlns="http://purl.org/rss/1.0/" 453 xmlns:dc="http://purl.org/dc/elements/1.1/" > 454 455 <channel rdf:about='{$feedlink}'> 456 <title>{$feedtitle|utf8encode}</title> 457 <link>{$feedlink}</link> 458 <description>{$feeddescription|utf8encode}</description> 459 <dc:language>{$feedlanguage}</dc:language> 460 <dc:date>{$feedpublicationdate}</dc:date> 461 <dc:creator>{$feedcreator|utf8encode}</dc:creator> 462 <dc:subject>{$feedsubject|utf8encode}</dc:subject> 463 464 <items> 465 <rdf:Seq> 466 <list:list id='itemsmenu'> 467 <list:item> 468 <rdf:li rdf:resource="{$itemlink}" /> 469 </list:item> 470 </list:list> 471 </rdf:Seq> 472 </items> 473 474 </channel> 475 476 <list:list id='items'> 477 <list:item> 478 <item rdf:about="{$itemlink}"> 479 <title>{$itemtitle|utf8encode}</title> 480 <link>{$itemlink}</link> 481 <description>{$itemdescription|utf8encode}</description> 482 </item> 483 </list:item> 484 </list:list> 485 486 </rdf:RDF></pre> 487 488 <p>Well, we've added a couple of WACT lists. Each list is contained in a 489 <code><list:list></code> element, and each item of the list is 490 contained by a <code><list:item></code> element. Within the list item, 491 variables are referenced just as they are in the other parts of the 492 template. The WACT magic comes in in that the variable references resolve to 493 the records within the ArrayDataSet — <code><list:list></code> 494 is a simple-to-use loop construct for your templates.</p> 495 496 <h3>Bringing home the bacon</h3> 497 498 <p>We'll hit <a href="feed.5.php" shape="rect">feed.5.php</a> and we'll see 499 what we shall see. Hmmm, interesting.</p> 500 501 <p>The feed <a href="http://feedvalidator.org/" shape="rect">still 502 validates</a>, so we're obviously doing something right.</p> 503 504 <p>Viewing the feed from an aggregator also produces the output we expected 505 — that's our RSS 1.0 feed finished! Easy-peasy.</p> 506 507 <p><img src="straw-small.png" alt="The proof is in the pudding"/> 508 </p> 509 510 <p>Now all we have to do is create some compelling, original content!</p> 511 512 <h2><span class="strike">Inconclusion</span> In conclusion</h2> 513 514 <p>Hopefully this gentle introduction to WACT has given you the confidence 515 to produce Enterprise Grade Applications using the framework. Well, maybe 516 not quite yet…</p> 517 518 <p>There are a lot of possible improvements to make to this setup, two 519 obvious ones:</p> 520 521 <ul> 522 <li>You could provide feeds in alternate formats, perhaps using the HTTP 523 <code>Accept:</code> header to determine which format to serve up to a 524 particular user-agent.</li> 525 526 <li>You could look at cacheing the output — all those aggregators 527 hitting your <em>ne plus ultra</em> feed regularly could bring your server 528 to its knees if you rely on dynamic generation.</li> 529 </ul> 530 531 <p><a href="../" shape="rect">Tutorials Home</a></p> 532 533 <hr/> 534 535 <p>If you use WACT, please contribute to the <a 536 href="http://wact.sourceforge.net" shape="rect">Wiki</a> as you learn about 537 it. See the <code>README</code> file in the root of the distribution 538 for other pointers on further information.</p> 539 540 <hr/> 541 542 <p>Copyright © 2003 Jon Ramsey</p> 543 <p> 544 Permission is granted to copy, distribute and/or modify this document 545 under the terms of the GNU Free Documentation License, Version 1.2 or any 546 later version published by the Free Software Foundation; with no Invariant 547 Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the 548 license is available in the file <a href="../fdl.txt" 549 shape="rect">fdl.txt</a>. 550 </p> 551 552 </body> 553 </html>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Sun Nov 28 19:36:09 2004 | Cross-referenced by PHPXref 0.5 |