TAAT Digital Technologies

Zend Framework Tutorial

Tutorial table of contents

Step 1: Basic Zend Framework application

You probably already know, how to create Hello World Application with Zend Framework, but we need something to start with. I'll try to show also few good practices, so I hope this will be no waste of time.

If you don't want to learn or if you are impatient, you may download Zipped Hello World with Zend Framework.

The Zend Framework structure

Before we begin, we have to prepare basic application structure. I'll create folders needed for the purpose of this tutorial only. The main are three:

/application     # this is where we put our application's files
/library         # directory for includes, such as ZF or PHPTAL
/www             # this is your web root for static HTML files, images, stylesheets and so,
                   # accessible from the browser (unlike the other directories)

You can download Zend Framework library from framework.zend.com or checkout from svn. All we need is the access to the Zend directory. Extract it and put in the /library directory. Alternatively, you can put it somewhere else and add to your include_path in your httpd.conf or php.ini.

As usual, we put index.php in the web root directory (we will add content of index.php later). Then, we redirect all browser requests to the index.php using simple mod_rewrite rules in .htaccess:

RewriteEngine On                        # start mod_rewrite
RewriteCond %{REQUEST_FILENAME} !-f     # execute next line only if requested file does not exist
RewriteRule .* index.php                # redirect all requests to index.php

Now, our structure should look like this (filenames in italics):

/application
/library
    /Zend      # Zend Framework library with all subdirectories
        /Acl
        /Amf
        /...
/www
    .htaccess  # our files
    index.php   

The Modules

Modularity is very important because it clarifies structure of the application and makes it easier to extend. Each project based on Zend Framework consist of at least one, default module. Then application may be extended with next modules, such as user or admin module. Each of the modules has his own controller actions and own view scripts bound to those actions. They are stored on disk on the respective directories.

I usually put those files in /application/modules directory, so the structure goes like this:

/application
    /modules                   # modules directory
        /default               # directory of the module named 'default'
            /contollers        # controllers of the default module
            /views             # views of the default module (may contain also helpers and filters)
                /templates     # templates of the default module (default ZF name is scripts)
                    /index     # this one holds scripts (templates) for the IndexController
        /nextmodule
            /controllers
            /...
/library
    /Zend
        /Acl
        /Amf
        /...
/www
    .htaccess
    index.php

For next modules structure is similar to /application/modules/default. But for our Hello world, the default one is enough. Since we will not be using database in this example, we don't need to create directory for models.

You would probably insert <?php echo 'Hello world!' ?> in index.php and call it finished ZF Hello world, but there is no real ZF Application without the Bootstrap. The Bootstrap.php is the place where we set up MVC and finally hire ZF. We place it in /application directory. Note that the filename starts with uppercase letter, since the bootstrap will be a class and we are following PEAR naming conventions.

The Bootstrap

What we have to do in bootstrap?

<?php
/**
 * Manage ZF start
 * @name Bootstrap
 */
class Bootstrap
{
        // 1. Prepare environment
        // 2. Prepare MVC
        //      2.1. Prepare Controller (PHP Actions)
        //      2.2. Prepare View (HTML code)
        //      2.3. Prepare Model (Database)
        // 3. Run application and recieve response
        // 4. Send response to the browser
}

The bootstrap is also a good place to set up Zend_Config and Zend_Registry, but we will not need it in this tutorial. Although, treat learning this good practice as a homework.

Why class? Why not a simple few procedural lines? First of all, it is easier to test with PHPUnit, easier to access and easier to read. So - procedural is evil.

Let's take a look how it may be done:

<?php
require 'Zend/Loader.php';

/**
 * The Bootstrap
 *
 * @author TAAT
 * @link http://taat.pl
 * @version 1.0
 */
class Bootstrap
{
        // PROPERTIES
        /**
         * Front controller instance
         * @access public static
         * @var object Zend_Controller_Front
         */
        public static $frontController = null;

        /**
         * root directory
         * @access public static
         * @var string
         */
        public static $root = '';

        /**
         * Run app
         * @access public static
         * @return null
         */

        // PUBLIC METHODS

        public static function run ()
        {
                self::setupEnvironment();
                self::setup();
                $response = self::$frontController->dispatch();
                self::sendResponse($response);
        }

        /**
         * Prepare Front Controller and View
         * @access public static
         * @return null
         */
        public static function setup ()
        {
                self::setupFrontController();
                self::setupView();
        }

        /**
         * setup environment
         * @access public static
         * @param
         * @return null
         */
        public static function setupEnvironment ()
        {
                error_reporting(E_ALL | E_STRICT);
                ini_set('display_errors', true);
                date_default_timezone_set('Europe/Warsaw');
                self::$root = dirname(dirname(__FILE__));
                set_include_path(
                        get_include_path() .
                        PATH_SEPARATOR . self::$root . '/library/'
                );
        }

        /**
         * Prepare front controller
         * @access public static
         * @param
         * @return null
         */
        public static function setupFrontController ()
        {
                Zend_Loader::registerAutoload();
                self::$frontController = Zend_Controller_Front::getInstance();
                self::$frontController->throwExceptions(true);
                self::$frontController->setParam('noErrorHandler', false);
                self::$frontController->returnResponse(true);
                self::$frontController->addControllerDirectory(self::$root .  '/application/modules/default/controllers');
        }

        /**
         * Setup View
         * @access public static
         * @return null
         */
        public static function setupView ()
        {
                self::$frontController->registerPlugin(new My_View_Setup());
        }

        /**
         * Setup Model
         * @access public static
         * @return null
         */
         public static function setupDatabase ()
        {
                // TODO Setup database
        }

        /**
         * Send response to the browser
         * @access public static
         * @param  Zend_Controller_Response_Http $response
         * @return null
         */
        public static function sendResponse (Zend_Controller_Response_Http $response)
        {
                $response->setHeader('Content-Type', 'text/html; charset=UTF-8');
                // $response->setHeader( 'Accept-encoding', 'gzip,deflate');
                $response->sendResponse();
        }
}

And now, let's analyse this step by step…

The Environment

Setting up environment is pretty obvious: error reporting, locales and paths. We will store path to root of our application in the $root property for later usage. We also register Zend_Loader, so we have to include at the beginning of the file Zend/Loader.php. But since now, all required classes will be loaded automatically.

When you will be expanding your application, you probably choose to load those settings from file, set up debug flag and so on. For now we don't need feaures such this.

The Controller

Here we instantiate the Front Controller and save the instance to private property to access later. Then set up options to throw exceptions and return response. Finally we tell ZF where files of our modules will be stored.

The controller is set up, but it doesn't exist yet. We have to create appropriate class in /application/modules/default/controllers:

<?php
/**
 * Default Index Controller
 * /application/modules/default/controllers/IndexController.php
 * @name IndexController
 */
class IndexController extends Zend_Controller_Action
{
        /**
         * Default index action
         * Displays /application/modules/default/views/templates/index.phtml
         * @name indexAction
         */
    public function indexAction()
    {
                // set variable to display in the template
                $this->view->test = 'Hello world!';
    }
}
 
The View

Method prepareView() is marked as TODO, because I want to discuss it a bit more. A very good practice described by Rob Allenis to move view setup to the Front Controller Action Plugin. We want to run our view setup when the dispatch loop starts up. So we create appriopriate plugin extending Zend_Controller_Plugin_Abstract.

We will place our plugin in /library/My/Controller/Plugin/, name it ViewSetup.php according to plugin class name:

<?php
require_once 'Zend/Controller/Plugin/Abstract.php';
/**
 * Zend_Controller_Plugin preparing View
 */
class My_Controller_Plugin_ViewSetup extends Zend_Controller_Plugin_Abstract
{
    public function dispatchLoopStartup (Zend_Controller_Request_Abstract $request)
    {
        $root = Bootstrap::$root;

        $viewRenderer = Zend_Controller_Action_HelperBroker::getStaticHelper('viewRenderer');

        $view = new Zend_View();

        $view->addScriptPath($root . '/application/modules/'
                             . $request->getModuleName() . '/views/templates'
                             )
             ->doctype('XHTML1_STRICT');

        $viewRenderer->setView($view)
                     ->setViewSuffix('tpl.html')
                     ->init();

        // setup Zend_Layout (optional)
        Zend_Layout::startMvc(array(
                                    'layoutPath' => $root
                                     . '/application/modules/default/views/layouts',
                                     'layout' => 'main' , 'viewSuffix' => 'tpl.html'
                                    )
                              );
    }
}

What we do here, is create new view using default Zend_View, set up directory for view scripts (templates), default template extension and default doctype. You may also setup Zend_Layout here.

Next thing we need, is to register our plugin in the Bootstrap.php:

/**
 * The View
 * Prepare View
 * @access public static
 * @return null
 */
public static function prepareView()
{
        self::$frontController->registerPlugin(new My_Controller_Plugin_ViewSetup());
}

Another thing we need, is template file for default action, which will be placed in /application/modules/default/views/templates/index/index.tpl.html:

<!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">
        <head>
                <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
                <title>ZF Hello World</title>
        </head>
        <body>
        <h1><?php echo $this->test ?></h1>
        </body>
</html>
The Model

As it has been said before, we will not use database in this tutorial, so method setupDatabase() may be empty for now. Maybe I'll someday write a brief tutorial on plugging Doctrine here, but it not the case now.

Run!

Here is the place, where all the methods meet. Now we have to simply execute it. Where? In /www/index.php:

<?php
require '../application/Bootstrap.php';
Bootstrap::run();

Our application starts, but we don't see anything, until response is sent…

The Response

In the sendResponse method in Bootstrap.php we set HTTP headers we need and send response (headers and data) to the browser.

Complete Basic Application using Zend Framework

Now everything should be ready. Point your browser to your index.php and see if it works.

If not, you should compare your files with:

Zipped Hello World with Zend Framework
Next step
Step 2: Integrating Zend Framework and PHPTAL