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:
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns=
"http://www.w3.org/1999/xhtml" xml:lang=
"en" lang=
"en">
<head>
<meta http-equiv=
"Content-Type" content=
"text/html; charset=utf-8" />
<meta name=
"language" content=
"en" />
<title><?php
echo $this->
escape($this->
title) ?></title>
<link rel=
"stylesheet" href=
"/css/blueprint/screen.css" type=
"text/css" media=
"screen, projection">
<link rel=
"stylesheet" href=
"/css/style.css" type=
"text/css" media=
"screen, projection">
<link rel=
"stylesheet" href=
"/css/blueprint/print.css" type=
"text/css" media=
"print">
<!--
[if IE
]><link rel=
"stylesheet" href=
"/css/blueprint/ie.css" type=
"text/css" media=
"screen, projection"><!
[endif]-->
</head>
<body>
<div
class=
"container">
<div
class=
"block">
<div id=
"zfHeader" class=
"column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div
class=
"block">
<div id=
"zfContent" class=
"column span-16">
<p>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.</p>
</div>
<div id=
"zfExtraRight" class=
"column span-7">
<p>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.</p>
</div>
</div>
<div
class=
"block">
<div id=
"zfFooter" class=
"span-24">
<p>Copyright ©
2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
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.
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns=
"http://www.w3.org/1999/xhtml" xml:lang=
"en" lang=
"en">
<head>
<meta http-equiv=
"Content-Type" content=
"text/html; charset=utf-8" />
<meta name=
"language" content=
"en" />
<title><?php
echo $this->
escape($this->
title) ?></title>
<link rel=
"stylesheet" href=
"/css/blueprint/screen.css" type=
"text/css" media=
"screen, projection">
<link rel=
"stylesheet" href=
"/css/style.css" type=
"text/css" media=
"screen, projection">
<link rel=
"stylesheet" href=
"/css/blueprint/print.css" type=
"text/css" media=
"print">
<!--
[if IE
]><link rel=
"stylesheet" href=
"/css/blueprint/ie.css" type=
"text/css" media=
"screen, projection"><!
[endif]-->
</head>
<body>
<div
class=
"container">
<div
class=
"block">
<div id=
"zfHeader" class=
"column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div
class=
"block">
<div id=
"zfContent" class=
"column span-16">
<h2>Excepteur Sint</h2>
<p><?php
echo gmdate('D, j M Y H:i:s T',
time()) ?><br />
Posted by Pádraic Brady</p>
<p>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.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
<h2>Excepteur Sint</h2>
<p><?php
echo gmdate('D, j M Y H:i:s T',
time()) ?><br />
Posted by Pádraic Brady</p>
<p>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.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
</div>
<div id=
"zfExtraRight" class=
"column span-7">
<p>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.</p>
</div>
</div>
<div
class=
"block">
<div id=
"zfFooter" class=
"span-24">
<p>Copyright ©
2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
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.
<!DOCTYPE html PUBLIC
"-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns=
"http://www.w3.org/1999/xhtml" xml:lang=
"en" lang=
"en">
<head>
<meta http-equiv=
"Content-Type" content=
"text/html; charset=utf-8" />
<meta name=
"language" content=
"en" />
<title><?php
echo $this->
escape($this->
title) ?></title>
<link rel=
"stylesheet" href=
"/css/blueprint/screen.css" type=
"text/css" media=
"screen, projection">
<link rel=
"stylesheet" href=
"/css/style.css" type=
"text/css" media=
"screen, projection">
<link rel=
"stylesheet" href=
"/css/blueprint/print.css" type=
"text/css" media=
"print">
<!--
[if IE
]><link rel=
"stylesheet" href=
"/css/blueprint/ie.css" type=
"text/css" media=
"screen, projection"><!
[endif]-->
</head>
<body>
<div
class=
"container">
<div
class=
"block">
<div id=
"zfHeader" class=
"column span-24">
<h1>Lorem Ipsum</h1>
</div>
</div>
<div
class=
"block">
<div id=
"zfContent" class=
"column span-16">
<?php echo $this->
layout()->
content ?> </div>
<div id=
"zfExtraRight" class=
"column span-7">
<p>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.</p>
</div>
</div>
<div
class=
"block">
<div id=
"zfFooter" class=
"span-24">
<p>Copyright ©
2008 Pádraic Brady</p>
</div>
</div>
</div>
</body>
</html>
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:
<h2>Excepteur Sint</h2>
<p><?php
echo gmdate('D, j M Y H:i:s T',
time()) ?><br />
Posted by Pádraic Brady</p>
<p>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.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
<h2>Excepteur Sint</h2>
<p><?php
echo gmdate('D, j M Y H:i:s T',
time()) ?><br />
Posted by Pádraic Brady</p>
<p>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.</p>
<hr />
<p>Tags: Lorem, Ipsum, Excepteur</p>
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.
<?phprequire_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(<u>_FILE_</u>
));
// 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();
} }
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.
class IndexController extends Zend_Controller_Action
{
public function indexAction()
{
$this->view->title = 'Maugrim\'s Marvelous Blog';
}
}
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