PHP, Zend Framework and Other Crazy Stuff
Archive for April, 2008
An Example Zend Framework Blog Application - Part 4: Setting the Design Stage with Blueprint CSS Framework and Zend_Layout
Apr 29th
One of the things I know with a certainty that sometimes depresses me is that my design skills are on the level of a 6 year old. In fact the 6 year old with crayons is likely superior in all respects . To disguise this failing, I have fallen back on the big guns in the CSS world who have decided it might be a good idea to make my life easier by inventing CSS Frameworks I can use. My current favourite is the Blueprint CSS Framework which I’ve found let’s me create simple designs that can get close enough to being good, that most professionals might even be fooled - until they read the source code and realise just how bad I am!
Previously: Part 3: A Simple Hello World Tutorial
But, to be honest, it saves me a lot of time. I really don’t like editing CSS when I can entice a monkey with vast bags of peanuts to do a better job later on. For the moment I just need a nice default design which looks okay. In this article we’re going to take a stab at setting up a default blog style, using some filler content, and finally capturing the design with a Zend_View template to be consumed by Zend_Layout as a common HTML Layout for the entire future blog.
Step 1: Adding Blueprint to the Project
You can download the Blueprint CSS framework from http://code.google.com/p/blueprintcss/. It is released under a dual licensing system using a Modified MIT License, or the GNU General Public License. Decompress the archive of the latest 0.7 release (as at time of writing) and return to our current project’s public
directory.
Inside public
create a new directory called css
. Into this new directory, copy the decompressed blueprint
directory and LICENSE
file from the Blueprint distribution. We retain the license so future redistributions of the project (if any) include a copy for reference.
Blueprint contains a default set of reasonable styling. This include the most fantastic of them all - when included correctly into any HTML document it sets all browsers to the same set of defaults. This completely negates the usual pain of Firefox, IE6, IE7 and Safari having sufficiently different styles internally that designs need a ton of small browser specific adjustments by hand. By hand is evil - Blueprint does it without interference.
To complete this step, create an empty CSS file called style.css
inside the /public/css
directory. We’ll include this in our HTML as a point for customising the Blueprint CSS and adding additional styling as needed.
Step 2: Creating a Test HTML Document applying Blueprint CSS
The first step to any good design is using Photoshop or gimp to draw one up. I’m really bad at that however - so I stick with lots of filler text and write plain HTML from the start. Besides, it’s a blog page - how complex can it be? Famous last words…
We already have a “Hello, World” page from Part 3 of this article series - so let’s stick with what we have and throw in a bunch of HTML for a potential main blog index page. We’ll use lot’s of words like “lorem ipsum” in keeping with a long history (“lorem ipsum” is the start of a long block of text used as filler text in printers since the 1500′s - the full traditionally used text itself is over 2000 years old!)
A quick word on using Blueprint CSS. Blueprint assumes you can’t align divs (in this case not far from the truth ) so they use a handy grid based system. Each page is divided into a number of fixed width grids. If you know the proportions, you can give columns a grid-width and then never look at anything in a CSS file using the term “float” again. Here I assume a page has 24 fixed length columns. Main text from blog entries will be displayed within columns 1-16. I’ll have a column for non-entry content spanning 7 columns to the right. Headers and footers will take up the full page width of 24 columns.
Starting simple, open up the /application/views/scripts/index/index/phtml
template used by our default Index controller and edit to contain the following:
[geshi lang=php]
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
Lorem Ipsum
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
[/geshi]
Go ahead and reopen http://zfblog/
in a browser. You’ll quickly notice that with little effort, it looks oddly pretty and the main content and right column are aligned exactly even.
In the HTML we did some very simple things. We have the standard 3 block solution - Header, Content, Footer. Both are contained in div’s with assigned “span” width. Content is further divided into two columns with “span” widths - the total of which add to 1 less than the total of 24 spans. The reason for the missing span is simply to allow room for further styling which in varying browsers can have knock on effects - typically using the full span wodth across multiple column blocks ends up with a browser breaching the fixed 24 width and leaving a column dropped below the others.
Let’s add a few modifications to allow for a more realistic appearance of blog entries with a title and other assembled summary data.
[geshi lang=php]
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
Lorem Ipsum
Excepteur Sint
Posted by Pádraic Brady
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Tags: Lorem, Ipsum, Excepteur
Excepteur Sint
Posted by Pádraic Brady
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Tags: Lorem, Ipsum, Excepteur
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
[/geshi]
What we now have is pretty basic - but even in black and white it looks clean and quite blog-like. If you disable CSS for your browser, well, it still looks quite fine which is always a positive thing.
Step 3: Splitting persistent markup into a Zend_Layout template
Traditionally, PHP templating has rarely addressed a common problem we have using templates. Somewhere in the design above (you can spot this easily) are elements common to almost every future page of our blog application. A stock solution is often to push these elements into separate templates, and include them as needed. This poses the problem of widespread references to these templates. What happens if we decide they only apply to half our site now, and need to replace them for 50% of all existing page specific templates? I call it PAIN. Others call it MIND-NUMBING.
A simple tactic to avoid these symptoms is to segregate common elements in one single location, and make sure individual templates remain blissfully unaware of it’s existence (then those 100 templates have nothing to edit if the central common template is replace for 50 of them!).
In the Zend Framework this tactic was initially proposed over a year ago when attention was slowly turning towards the weak Zend_View side of the MVC equation. I proposed Zend_View Enhanced which include, among a lot of things, a Zend_View based solution to the common layout problem. Ralph Schindler proposed around the same time a Zend_Layout component for similar purposes. Eventually I lost on that score, and Zend_Layout was adopted. Such is life - at least the rest of Zend_View Enhanced with some modification was also adopted, making for a powerful templating solution taking full advantage of the PHP language (and not a limited subset obscured behind a secondary tag language).
Examining our design above we can spot the common bits: header, footer and the righthand column. Zend_Layout uses a separate template - the Zend Framework manual is extremely obscure about how to actually get the Layout part working (hopefully fixed soon) but it’s very simple.
Create a new file in /application/views/layouts
called common.phtml. What we’ll do is copy in the above index.phtml template but delete the sections that are likely to change with different page views - namely the entries.
[geshi lang=php]
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
Lorem Ipsum
layout()->content ?>
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
[/geshi]
You’ll notice I replaced the fake entries with a reference to a layout()
function. Function calls on the template object instance generally are either called from the template object (an instance of Zend_View) or are references to a View Helper (a small class encapsulating reusable presentation logic - we’ll hear a lot more about these later). In this case, it refers to the Zend_View_Helper_Layout class. This class instance has a public variable $content
which contains all content rendered from all preceeding templates (bear in mind Layouts are rendered last - after Action specific templates).
The process is simple. You visit a Controller and Action. That Action’s template is rendered, and saved to the Layout View Helper. The Layout is rendered last, and the pre-rendered content inserted as the Layout template defines using a call to $this->layout()->content
.
With our Layout template prepared, let’s remove all those common items from our Action specific template at /application/views/scripts/index/index.phtml
:
[geshi lang=php]
Excepteur Sint
Posted by Pádraic Brady
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Tags: Lorem, Ipsum, Excepteur
Excepteur Sint
Posted by Pádraic Brady
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
Tags: Lorem, Ipsum, Excepteur
[/geshi]
Now we’re getting somewhere interesting. The revised template, only contains the content unique to that Controller/Action combination and it’s output will be inserted into the common Layout we created - after we setup Zend_Layout!
Step 4: Setting up Zend_Layout for operation
Getting Zend_Layout working is again quite simple. We just need to make a few additions to our Bootstrap file. Specifically to the setupView()
static method.
[geshi lang=php]
require_once 'Zend/Loader.php';
class Bootstrap
{
public static $frontController = null;
public static $root = '';
public static function run()
{
self::setupEnvironment();
self::prepare();
$response = self::$frontController->dispatch();
self::sendResponse($response);
}
public static function setupEnvironment()
{
error_reporting(E_ALL|E_STRICT);
ini_set(‘display_errors’, true);
date_default_timezone_set(‘Europe/London’);
self::$root = dirname(dirname(__FILE__));
// FILE constant should have two underscores
// preceeding and following “FILE”
}
public static function prepare()
{
self::setupFrontController();
self::setupView();
}
public static function setupFrontController()
{
Zend_Loader::registerAutoload();
self::$frontController = Zend_Controller_Front::getInstance();
self::$frontController->throwExceptions(true);
self::$frontController->returnResponse(true);
self::$frontController->setControllerDirectory(
self::$root . ‘/application/controllers’
);
}
public static function setupView()
{
$view = new Zend_View;
$view->setEncoding(‘UTF-8′);
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
Zend_Layout::startMvc(
array(
‘layoutPath’ => self::$root . ‘/application/views/layouts’,
‘layout’ => ‘common’
)
);
}
public static function sendResponse(Zend_Controller_Response_Http $response)
{
$response->setHeader(‘Content-Type’, ‘text/html; charset=UTF-8′, true);
$response->sendResponse();
}
}[/geshi]
Since we’re using paths a lot, I also added a $root static property rather than repeating dirname()
everywhere!
The new addition sets up Zend_Layout for use - it’s automatically integrated into our application thereafter so there’s no specific on/off switch outside the Boostrap we need worry about. We pass in a few starting options to customise Layouts. We set the path where Layouts can be found, and provide a default file name (minus the .phtml file extension) to use.
One final step, because it’s bugging me as I write this. Let’s replace the Hello, World title in our IndexController (/application/controllers/IndexController.php
) with something more meaningful.
[geshi lang=php]class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = ‘Maugrim\’s Marvelous Blog’;
}
}[/geshi]
Go ahead and fire up a browser pointing to http://zfblog/
.
Conclusion
This series, as you can tell, is all about taking small concrete steps. In this installment we’ve added a basic design, added the Blusprint CSS Framework to our toolbox, and set the pace for future templating by eradicating repetitive template code; moving it to a discrete layout template. I see it this as the conclusion of our setup tasks. In Part 3, we had a simple Hello World example. Now we have a skeletal blog design and a Layout moving. We’re looking good!
Next up: It’s time to do away with the filler content and investigate a means of writing and saving real entries to the database. Once we have the most basic blog imaginable running, we can turn our attention to a new round of requirements like syntax highlighting (it will be a programming blog afterall) and perhaps a few design touchups to add a dash of colour.
Note: The source code for this entry is available to browse, or checkout with subversion, from http://svn.astrumfutura.org/zfblog/tags/Part4. The full source code for the entire application (as it exists thus far) from http://svn.astrumfutura.org/zfblog.
Continue to: Part 5: Creating Models with Zend_Db and adding an Administration Module
An Example Zend Framework Blog Application - Part 3: A Simple Hello World Tutorial
Apr 28th
It’s almost obligatory when introducing a new programming topic, that the author present the simplest possible example. Usually this means getting a programming language or framework to print “Hello World” to the screen. I’m going to be no different. So much for originality…
Previously: Part 2: The MVC Application Architecture
Step 1: Creating A New Application
Before we jump into programming with the Zend Framework there are a few setup tasks. We need to install the Zend Framework, get a virtual domain running, and have a basic collection of directories and emtpy files created to hold our source code.
You’ll need to download the Zend Framework 1.5 (latest minor release) from http://framework.zend.com and put it somewhere on your include path. I try to minimise the number of include paths I end up having so I sometimes pick very odd places (like the PEAR directory which is usually already in the include_path) or another central library location like PEAR where I might have a dozen libraries all congregated.
A common solution found on blogs and articles is to put the framework into a “library” directory within your application directory tree. Ordinarily I don’t recommend this unless your application needs a specific version of the Zend Framework - centralising a Zend Framework location for multiple applications to access can make maintenance a bit easier.
I’ve settled on using a virtual host mainly because it’s easy to setup, and it helps avoid some of those annoying base path issues you get in your HTML when using mod_rewrite pretty urls - especially in development where, otherwise, the locahost document root becomes a mass of sub-directories upon sub-directories that end up causing base href issues so easily. Usually I open up the virtual-hosts.conf file from Apache’s conf directory and add something like:
NameVirtualHost ∗:80
<VirtualHost ∗:80>
DocumentRoot "/path/to/htdocs"
ServerName localhost
</VirtualHost>
<VirtualHost ∗:80>
ServerName zfblog
DocumentRoot "/path/to/project/trunk/public"
</VirtualHost>
This should result in all HTTP queries for http://zfblog/ reaching the selected document root (which is the public directory for the project containing index.php, located in our subversion’s working copy “trunk” directory). We’ll create this directory structure a little later. We usually also need to reference localhost to ensure it’s recognised as a host correctly - otherwise any localhost addresses you already have will suddenly divert to zfblog’s document root myseriously .
For those of you on Windows XP or Vista, you also need to add the new virtual domain to your hosts file (usually at C:\WINNT\system32\drivers\etc\hosts). This is tricky in Vista - you will probably need to alter the file’s permissions using an Administrator account to grant the local user additional Modify privileges. Make sure to reverse the privileges after! Edit it to look this:
# Copyright (c) 1993-1999 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
127.0.0.1 localhost
127.0.0.1 zfblog
Restart your browser and with a little luck the browser will at least give us a directory listing (once you add the document root “public” directory as shown below).
The least exciting part is the creation of a tree of directories to contain our source code. Wil Sinclair over at Zend is working on a Zend_Build component and this will one day hopefully eliminate too much of the repetition in creating these directories and default files. Until then let the monotony ensue (or keep a skeleton setup handy for copy and pasting ).
Here’s a snapshot of what the Hello World example looks like in the directory view from Eclipse:
Copy this and create the same directory structure in a new directory called something like “zfblog”. Putting it all in “trunk” is optional - I’ve using subversion here which is why that directory exists. If you haven’t used Subversion before I highly recommend it - it’s simple to learn and use.
Let’s examine each of these directories briefly.
The “application” directory is where we place all the components of an application implementing the Model-View-Controller. Inside “application”, “controllers”, “models” and “views” represent respective locations of controller, model and view source code and templates. Inside “views” there is a subdivision between “filters”, “helpers”, “layouts” and “scripts”. For now, remember we put all templates rendered by the View inside “scripts”. In the screenshot, we see and index directory which will contain an “index.phtml” file, which is the view template for indexAction of an IndexController class. The others we’ll meet later.
The “modules” directory is also for controllers, models, and views, but in this case they are catagorised into multiple divisions of an application. If you think about it, an application can be broken into several parts, for example, the main application and an administration part. The administration section could be partitioned into the Admin Module so it’s not intermixed with the main application.
A Bootstrap.php file exists in the “application” directory which represents a class called “Bootstrap”. It’s purpose is to initialise the Zend Framework, adjust environment settings (for example, timezone and error_reporting level), and otherwise make application specific tweaks and additions before a HTTP request is processed. Most tutorials take an alternative view and put Bootstrap code into index.php. I strongly suggest you avoid this and use an actual class to organise the Bootstrap code - it makes it a lot easier to read if nothing else!
The “config” directory simply holds the configuration data for the application, for example, database connection details.
The “library” directory can hold a copy of the Zend Framework. Generally I avoid this since it’s fairly easy to just add the Zend Framework to the PHP include_path, but it’s still very useful if you want to change Zend Framework versions in use more flexibly. It’s also simple to manage if you use it to add an svn::external reference to the Zend Framework subversion repository.
The “public” directory holds all public files accessible by a request to the web server. This includes an index.php file, which handles all application requests by calling on the Bootstrap class, as well as any CSS, HTML, Javascript files, etc.
A note on deployment practice: generally when deploying you’d want to move the “public” directory to your web server to be internet accessible, but keep the non-public directories somewhere below the web root. Since the public index.php ends up only making a simple reference to the Bootstrap file served by including it, this means most of the application files will not be accessible by internet users. It also means that the index.php file will end up deciding where the Bootstrap file is located - so it’s the one public file that always needs to be edited by hand for deployment to change that location as needed.
The “tests” directory usually holds any application specific unit, integration and acceptance tests. As I noted previously I’m deliberately taking a test-light approach to stay on topic (I have more than one testing/development article on Zend Devzone if interested in applying TDD or BDD). Main tests I expect to have are for application specific classes: any Model logic for example.
At the end of this step you should have the directory structure in place containing some empty files as follows (create them now, and we’ll fill them in later):
/application/Bootstrap.php
/application/controller/IndexController.php
/application/views/scripts/index/index.phtml
/public/index.php
/public/.htaccess
Step 2: Implementing Our Bootstrap File
Bootstrapping is when we setup the initial environment, configuration and Zend Framework initialisation for our application. It’s not a difficult file to write, but it can grow ever larger and complex as your application gains features. To manage this I strongly suggest implementing it as a class with bitesize methods. Breaking it up does wonders for your sanity. For our Hello World skeleton application, /application/Boostrap.php
contains:
[geshi lang=php]
require_once 'Zend/Loader.php';
class Bootstrap
{
public static $frontController = null;
public static function run()
{
self::setupEnvironment();
self::prepare();
$response = self::$frontController->dispatch();
self::sendResponse($response);
}
public static function setupEnvironment()
{
error_reporting(E_ALL|E_STRICT);
ini_set(‘display_errors’, true);
date_default_timezone_set(‘Europe/London’);
}
public static function prepare()
{
self::setupFrontController();
self::setupView();
}
public static function setupFrontController()
{
Zend_Loader::registerAutoload();
self::$frontController = Zend_Controller_Front::getInstance();
self::$frontController->throwExceptions(true);
self::$frontController->returnResponse(true);
self::$frontController->setControllerDirectory(
dirname(__FILE__) . ‘/controllers’
);
}
public static function setupView()
{
$view = new Zend_View;
$view->setEncoding(‘UTF-8′);
$viewRenderer = new Zend_Controller_Action_Helper_ViewRenderer($view);
Zend_Controller_Action_HelperBroker::addHelper($viewRenderer);
}
public static function sendResponse(Zend_Controller_Response_Http $response)
{
$response->setHeader(‘Content-Type’, ‘text/html; charset=UTF-8′, true);
$response->sendResponse();
}
}[/geshi]
Note: Due to some Serendipity bug in processing Geshi output, underline tags replace some underline characters such as for the PHP FILE constant which should have two underscrores proceeding and following “FILE”. Sorry about that - S9Y has it’s moments.
The Bootstrap class has two main methods here: run() and prepare(). run() executes a full application request, whereas prepare() only sets up (but doesn’t execute) the Front Controller. For now let’s focus on run() and all the steps our Bootstrap takes when it’s called. The prepare() method is something I use when applying TDD or BDD to developing controllers, since I then delegate controller execution to the testing framework.
The first thing is to setup the local environment. So here we’ve set a timezone (as required by PHP5), enabled PHP’s display_errors option, and set a fairly stiff error reporting level. If putting these basics into a class seems counter intuitive, you can simply extract them to the file head. Because I usually test extensively, I usually don’t want these running during my tests and interfering with test results.
Secondly we prepare the Front Controller. Zend_Controller_Front is an implementation of the Front Controller Design Pattern, which acts as a single point of entry into a Model-View-Controller architected application. I kid you not - every request which is not for an existing public file will go through it. In our case, since the Bootstrap is included into, and called by public/index.php, “index.php” is our single point of entry.
Here, the prepare() stage involves setting up the Front Controller:
- get an instance of Zend_Controller_Front
- set it to throw all Exceptions (might want to disable this in production)
- set it to return a Response object when dispatched
- tell it where to find the default controllers
The last step of prepare() makes some changes to the View. The reason I’ve done this is to show how to make an extremely common change - adding support for UTF-8 encoded output (such as my name!). Don’t worry too much about the details now - the ViewRenderer Action Helper and the Helper Broker lets one add or modify Action Helpers and is well covered by Matthew Weier O’Phinney over on Zend Devzone - http://devzone.zend.com/article/3350-Action-Helpers-in-Zend-Framework.
Now, what about /public/index.php
?
[geshi lang=php]
// Update after deployment for location of non-public files
$root = dirname(dirname(__FILE__));
// We're assuming the Zend Framework is already on the include_path
set_include_path(
$root . '/application' . PATH_SEPARATOR
. $root . '/library' . PATH_SEPARATOR
. get_include_path()
);
require_once 'Bootstrap.php';
Bootstrap::run();[/geshi]
Simplicity itself! The index is our Front Controller's single point of entry into a Zend Framework application, and all it needs to do is determine a valid include_path for the application and then delegate all other tasks to the Bootstrap class's run() method.
The final step is how to make all requests to "http://zfblog/" pass through index.php. This is solved for Apache web servers using it's Rewrite Module, which simply takes an incoming request URL, and rewrites it so it points to index.php. If you've never used Apache Rewrite before, you probably need to enable this module in http.conf before this works. Rewrite rules can be defined in Apache's http.conf file, or simply via a .htaccess file in "public" containing the rules:
RewriteEngine on
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule .∗ index.php
The .htaccess file here first turns on Rewriting, and then specifies rules. The rules here map all requests to index.php, unless they relate to a file which exists on the server (i.e. it won't rewrite URLs to CSS, Javascript or image files).
Step 4: Implementing The Index Controller
Before we continue, a quick word on controller/view wiring. Some time back it was decided to make automated rendering the default mode of operation for the Zend Framework. This means Controller will rarely need manual intervention to render a View - instead an Action Helper called the ViewRenderer (which should sound familiar since it used it to add UTF-8 encoding in our Bootstrap!) is called upon. This helper locates a View matching the name of the current Controller and Action and automatically renders it. There are ways to disable this automated rendering as documented in the manual - which is often useful when you want to bypass Zend_View (e.g. maybe you're outputting finalised JSON or XML and no further templating or processing is needed).
If a URL to a Zend Framework application does not contain a controller or action reference, then the Zend Framework uses the default "index" value. Since we only intend requesting to the root URL of "http://zfblog/", we'll need an Index Controller containing an Index Action. Let's create one!
Add a file called "IndexController.php" in application/controllers
containing:
[geshi lang=php]
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = 'Hello, World!';
}
}[/geshi]
All controllers must extend Zend_Controller_Action, since it contains all later referenced internal methods and properties common to all Controllers. If you need to tweak the basic Controller to add new methods or properties you may do so using a subclass of Zend_Controller_Action and then making all application Controllers extend this subclass instead. Be very careful however in subclassing this way - it's useful for adding new properties to the class, but additional methods are often better added as discrete Action Helpers.
Our new Action method ignores rendering; it's done automatically as discussed earlier. All we need to do is set View data we need. You can add almost anything to a View, since it just becomes a property in a View template. So feel free to add Objects and Arrays. If using Smarty or PHPTAL, this might be different.
Above we've told the View that templates might refer to a variable "title" and given it a value of "Hello, World!". Once the Action method is done, the ViewRenderer will kick in and try to render a template located at "/path/to/application/views/scripts/index/index.phtml".
And what use is a Controller, without a View to display?
Step 5: Implementing The View For IndexController::indexAction()
By default, the Zend Framework or more specifically the ViewRenderer Action Helper, will attempt to render a View template for all Controller Actions using a simple convention. For example, the template for IndexController::indexAction() should be located at "/index/index.phtml" within the "scripts" subdirectory of "views".
The Zend_View component is responsible for rendering all View template, often with a little assistance (e.g. ViewRenderer or Zend_Layout) from the Controller. A Zend_View template is HTML (or some other output type) interspersed with PHP and it doesn't use a separate tag language such as what the Smarty template engine, for example, uses. Here's something to put into your index.phtml file for our first Zend Framework application:
[geshi lang=php]
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
Hello, World!
[/geshi]
As you can see, it's almost pure HTML with a single PHP inclusion for the title text (which we set from our indexAction() method earlier!). To ensure security against Cross Site Scripting (XSS) we should escape all output from within the application, and Zend_View uses the PHP htmlspecialchars() function from it's Zend_View::escape() method. Another noteworthy mention is that, since we setup our View to use UTF-8 encoding, this encoding is also passed into htmlspecialchars().
If using escape() everywhere sounds bad - well, it is, and it isn't. It's a pain since it's something that would obviously benefit from automation - so not applying escaping is the exception by default. On the other hand it must be done at all times so if you ever see a variable being echoed that's not surrounded by the escape() method you should proceed with care.
Step 6: Did it work?
Go ahead and open up your browser. The base url of http://zfblog/ should now do it's thing and return a HTML document containing "Hello, World!".
Conclusion
Getting a simple example up and running has already provided a few insights. But don't let your guard down just yet. Like any battle plan, it will not survive first contact with the enemy (Maugrim's Marvellous Blog is so evil ). As we proceed, there will more than likely be a few flaws to the simple approach, our Bootstrap will need to expand yet remain maintainable, and we haven't even looked at Zend_Db yet!
It's a start. If you're lucky, it may be many starts since you can reuse a similar basic skeleton to skip all those setup tasks for other Zend Framework projects.
Note: The source code for this entry is available to browse, or checkout with subversion, from http://svn.astrumfutura.org/zfblog/tags/Part3. The full source code for the entire application (as it exists thus far) from http://svn.astrumfutura.org/zfblog.
Continue to: Part 4: Setting the Design Stage with Blueprint CSS Framework and Zend_Layout