Click on the home page link, and you'll notice it is taking us to the PHOCOA examples page. We definitely don't want this as the home page of our blog. Instead, we'll create a new home page to show the 10 latest blog posts.
To accomplish this, we're going to create one module that will handle displaying blog posts to the public. Then, we'll create a home page implementation that leverages our public-facing blog page to create a nice home portal.
First, let's create a new module for our public-facing blog access. CD to the modules directory and use the phocoa utility to do this:
$ phocoa createModule
phing -f /Users/alanpinstein/dev/sandbox/phocoa/phocoa/phing/build.xml -Dusing.phocoa.make=true -Dphocoa.pwd=/Users/alanpinstein/dev/sandbox/blog/blog/modules -Dphocoa.dir=/Users/alanpinstein/dev/sandbox/phocoa/phocoa -Dphocoa.project.name=blog -Dphocoa.project.dir=/Users/alanpinstein/dev/sandbox/blog/blog createModule
Buildfile: /Users/alanpinstein/dev/sandbox/phocoa/phocoa/phing/build.xml
phocoa > prepareGeneral:
[echo] PHOCOA framework base dir at: /Users/alanpinstein/dev/sandbox/phocoa/phocoa
phocoa > prepareProject:
[echo] 1
[php] Evaluating PHP expression: $_ENV['_']
[echo] PHOCOA project dir at: /Users/alanpinstein/dev/sandbox/blog/blog
[realpathexpandhome] Resolved /Users/alanpinstein/dev/sandbox/blog/blog/.. to /Users/alanpinstein/dev/sandbox/blog
[echo] PHOCOA project container dir at: /Users/alanpinstein/dev/sandbox/blog
[property] Loading /Users/alanpinstein/dev/sandbox/blog/blog/conf/build.properties
[property] Unable to find property file: /Users/alanpinstein/dev/sandbox/blog/blog/conf/build.properties... skipped
phocoa > createModule:
Module name: blogview
Default page [blank for none]: list
[exec] Executing command: /opt/local/bin/php /Users/alanpinstein/dev/sandbox/phocoa/phocoa/framework/createModule.php blogview list 2>&1
[exec] Writing blogview/blogview.php
[exec] Done building module blogview!
[exec] Writing list.tpl
[exec] Writing list.yaml
[exec] Done!
BUILD FINISHED
Total time: 37.1642 seconds
This new module/page is accessible at http://servername/blogview. Notice how PHOCOA automatically redirects the user to http://servername/blogview/list since we've set up our list view as the default page.
Now we need to add code to this module to display our list. First off, we need to create a shared instance to hold our array of blog posts, by editing shared.yaml:
Blog :
class : WFArrayController
properties:
automaticallyPreparesContent: false
class : Blog
classIdentifiers : blogId
postDateFormatter:
class : WFUNIXDateFormatter
properties:
formatString: M j \a\t H:m
Next, we need to have our list page delegate load the 10 most recent blog posts into our Blog array controller. Add the following code to blogview.php:
class module_blogview_list
{
function parametersDidLoad($page, $params)
{
// load the 10 most recent blog posts in desc order.
$c = new Criteria;
$c->addDescendingOrderByColumn(BlogPeer::POST_DTS);
$c->setLimit(10);
$page->sharedOutlet('Blog')->setContent( BlogPeer::doSelect($c) );
}
}
Now we need to add our page UI objects to display the blog posts. For each post, we'll show the title (hyperlinked to the full post) and the post date.
postDts:
children :
postDtsPrototype:
bindings :
value:
controllerKey: '#current#'
instanceID : Blog
modelKeyPath : postDts
class : WFLabel
properties:
formatter: '#module#postDateFormatter'
class : WFDynamic
properties:
arrayController: '#module#Blog'
title :
children :
titlePrototype:
bindings:
label:
controllerKey: '#current#'
instanceID : Blog
modelKeyPath : title
value:
controllerKey: '#current#'
instanceID : Blog
modelKeyPath : blogId
options :
ValuePattern: /blogview/read/%1%
class : WFLink
class : WFDynamic
properties:
arrayController: '#module#Blog'
There's a lot going on in this setup! Let's break it down.
PHOCOA has a special mechanism for automatically creating UI
widgets for each iteration in a loop. A special widget called
WFDynamic is used to handle the creation of
widgets dynamically, one for each item in the associated array
controller. A WFDynamic should have one child,
named <id of WFDynamic>Prototype, which is used as
a prototype for each widget created. This prototype can have formatters,
bindings, etc on it, and PHOCOA will create each widget and attach data
bindings as appropriate based on your prototype.
Notice the binding on the titlePrototype; it has a
ValuePattern option. This is a mechanism similar to
printf to make it easy to contstruct strings
dynamically. The value property of a UI widget supports
multi-value bindings, which allows you to string together one or more
distinct values along with a format string into a single value to be
used by the widget. The value property is mapped to
%1%, value2 to %2%, etc.
This makes it very easy to create URL's on the fly within the bindings
mechanism.
Now, let's create the view code needed to display our list. Edit list.tpl:
<table>
{section name=posts loop=$__module->valueForKeyPath('Blog.arrangedObjectCount')}
<tr>
<td>{WFView id="title}</td>
<td>{WFView id="postDts"}</td>
</tr>
{sectionelse}
<tr><td>No blog posts.</td></tr>
{/section}
</table>This simple bit of smarty creates our entire blog post list.
Notice the $__module variable used to generate the loop
count for section. PHOCOA automatically assigns a few variables to the
template for you. See the WFPage API docs for a
complete list.
Also notice that we're using Key-Value Coding to access the loop count from our array controller.
Reload the page, and you'll now see the working version of the last 10 blog posts. If you click on one of our links, you'll notice you get a 404 error since we haven't yet created the page to read the blog post. Let's do that now.
$ phocoa createPage
phing -f /Users/alanpinstein/dev/sandbox/phocoa/phocoa/phing/build.xml -Dusing.phocoa.make=true -Dphocoa.pwd=/Users/alanpinstein/dev/sandbox/blog/blog/modules/blogview -Dphocoa.dir=/Users/alanpinstein/dev/sandbox/phocoa/phocoa -Dphocoa.project.name=blog -Dphocoa.project.dir=/Users/alanpinstein/dev/sandbox/blog/blog createPage
Buildfile: /Users/alanpinstein/dev/sandbox/phocoa/phocoa/phing/build.xml
phocoa > prepareGeneral:
[echo] PHOCOA framework base dir at: /Users/alanpinstein/dev/sandbox/phocoa/phocoa
phocoa > prepareProject:
[echo] 1
[php] Evaluating PHP expression: $_ENV['_']
[echo] PHOCOA project dir at: /Users/alanpinstein/dev/sandbox/blog/blog
[realpathexpandhome] Resolved /Users/alanpinstein/dev/sandbox/blog/blog/.. to /Users/alanpinstein/dev/sandbox/blog
[echo] PHOCOA project container dir at: /Users/alanpinstein/dev/sandbox/blog
[property] Loading /Users/alanpinstein/dev/sandbox/blog/blog/conf/build.properties
[property] Unable to find property file: /Users/alanpinstein/dev/sandbox/blog/blog/conf/build.properties... skipped
phocoa > createPage:
Page Name: read
[exec] Executing command: /opt/local/bin/php /Users/alanpinstein/dev/sandbox/phocoa/phocoa/framework/createPage.php read 2>&1
[exec] Writing read.tpl
[exec] Writing read.yaml
[exec] Done!
BUILD FINISHED
Total time: 2.9682 seconds
Our shared instances are already set up from the list page. So all we need to do is load the correct data in the page delegate for the read page.
For kicks, we'll implement this template in good old-fashioned smarty, just to show that you can seamlessly fall back on lower-level coding techniques if needed.
class module_blogview_read
{
function parameterList()
{
return array('blogId');
}
function parametersDidLoad($page, $params)
{
// load the 10 most recent blog posts in desc order.
$post = BlogPeer::retrieveByPK($params['blogId']);
if (!$post) throw( new WFRequestController_NotFoundException("That post is not available.") );
$page->assign('blog', $post);
}
}
Now we need to implement the template.
<h2>{$blog->getTitle()}</h2>
<p>{$blog->getPostDts()|date_format}</p>
<div>{$blog->getPost()}</div>
Our public-facing blog posts pages are now done. Let's move on to the home page!