I've been using the Zend Framework a lot lately and have come to really appreciate it. Today I want to write about ZF and how to use the MVC components. This post will be all about ZF itself, how the MVC components work, and getting a simple example up and going. I will write another post later on some more advanced usage.

About Zend Framework

The Zend Framework is a free (BSD-like license) PHP framework. It contains components to tackle many common problems including authentication, cacheing, mail, sessions, and of course MVC.

ZF is simple. It is a "use at will" framework, meaning you can take specific components and use them wherever you want. You can, for example, take just the Mail component and stick it into your already existing PHP application. This decoupled architecture is contrary to some other frameworks (including symfony or CakePHP to certain extents) where you are forced into using a single paradigm.

This framework is written for PHP5 only, so it is not encumbered by the limitations of PHP4. It is written using the best OOP principals, is thoroughly tested, and has a marvelous community of maintainers that keep it up to date.

Zend Framework versus other frameworks (symfony, CakePHP etc)

Typically when you think of a framework, you envision a completely developed codebase that includes its own of configuration, its own conventions, and specific programming paradigms. Many frameworks including symfony, CakePHP, Code Igniter and Ruby's Rails, follow this kind of thinking.

Zend Framework is a collection of loosely coupled components. As mentioned earlier, you can use any one of the components separately without running into any problems. Even so, the components still work very well together (for example, Zend_Auth can seamlessly use Zend_Session for persistence).

You need to do a bit more work to use ZF initially. ZF is not all tied together, so if you want to use MVC (for example) then you have to create your own bootstrap, and create your own directory structure etc. When you download ZF it's simply a directory that contains the library files, you're the one who has to initialize and use everything. Compare this to symfony (or many others) where everything is ready to go after you edit a couple configuration lines.

I prefer ZF because I feel like I have more control. I am not tied into any one paradigm, I don't need to play around with configuration files and I can mix and match my code to my hearts content.

Please note that I am not belittling the other frameworks, I am just trying to explain what sets ZF apart. If you read some of my previous posts, you'll see I'm also an advocate of symfony.

The Parts

MVC with Zend Framework is quite easy. Here's a brief overview of each part and how they relate to each other.

1. Front Controller

Most people will use a Front Controller. Using a Front Controller means that all requests come through a central starting point. Usually a single index.php will instantiate the front controller object and handle all incoming requests to your site. The file is referred to as a "bootstrap" file, and will contain code to initialize other things in your system too (for example, loading configuration settings).

If you've been developing on the web for a while, you've probably seen lots of scripts that have many entry points. For example, you might have index.php, login.php, article.php etc. Most of the time you also have a global.php file that is included at the top of each script. When you have all these entry points, it can quickly become hard to manage and you also start to get a bunch of code duplication.

A Front Controller solves this. Instead of physical files on the disk defining how a request should be handled, the request itself is entered through the Front Controller and then analyzed to see what should be done with it.

2. The Request Object

Each request made has a corresponding Request object. This object wraps up the entire request environment. For example, the default HTTP environment contains things like request parameters from GET and POST. The Request object also contains which controller and action should be called upon.

3. The Router

The Router is what actually inspects the incoming request and decides which Controller and Action should be called up. For example, it will look at a request of /login and decide that the Login Controller should handle the request, and the Index Action should be called.

ZF comes with a default router, so you don't need to change anything unless you want special behavior. By default, the incoming URL is analyzed and the parts are mapped to specific controllers and actions: /:controller/:action. If they are not provided at all, then they are assumed to be 'index'.

3. The Dispatcher

Dispatching is the process of looking at the requested controller and action, and actually including the correct Controller file, instantiating it, and calling on the action. The Dispatcher is in a loop. That means you can run as many controllers and actions as you like.

Again, ZF comes with a default dispatcher that sets down some naming conventions that we'll explore in a bit.

4. The Controller

Finally there is your actual controller. A controller handles a specific part of your app (for example, working with articles) and can have multiple actions (for example, listing articles, viewing a single article, viewing a printable version etc).

The Process

A request is handled like so:

  1. A web request is made to a single entry point (usually an index.php file)
  2. The entry point creates a Front Controller and runs it
  3. The Front Controller creates a Request object that wraps up the environment. At this point there is no controller or action set.
  4. The Front Controller uses a Router to inspect the request to figure out which controller and action should be called upon. It sets these values in the Request object.
  5. The Front Controller starts the Dispatcher loop which includes the correct Controller source file, instantiates it, and runs the correct Action.
  6. A Controller does what it does. It can handle forms, get database info, render HTML etc.
  7. Any Controller can also modify the Request object and set a different Controller or Action, so the Dispatch loop is run again. If no change is made, then the Dispatch loop ends and the result is served to the user

You can get a visual representation of the process by viewing this image.

Let's Get Started

The File Structure

I like to separate my files into two parts. One part for the actual application files (controllers, libs, views, functions etc) and then public files that the user needs access to (javascript, css, images etc).

For the sake of simplicity, I also like to package ZF with my app. If you have a shared directory where you place libraries (PEAR, for example), then you of course don't need to include it with your own files.

Here's the structure I use:

  • root
    • /client
      • /css
      • /images
      • /js
    • /appfiles
      • /application
        • /controllers
        • /views
          • /scripts
      • /lib
        • /Zend
    • /.htaccess
    • /index.php

This also makes it easy to split up the appfiles from the public files. For example, may people like to place the application source files outside of the document root on the webserver.

The /appfiles/application directory contains all of your controller and view files. We'll talk about the naming convention the default Dispatcher expects in a sec.

The /appfiles/lib directory is where I personally like to keep the Zend Framwork files.

You can of course have any other directories you'd like. On one of my recent projects I had /appfiles/functions and /appfiles/classes for some various custom code files.

Rewrite URLs

To create friendly URL's like /login or /article/view, you need a way to rewrite all URL's to go through the main index.php file. If you are using an Apache webserver and htaccess is a viable option, then the following simple code snippet should work fine for you:

  1. RewriteEngine on
  2. RewriteRule !\.(js|ico|gif|jpg|png|css)$ index.php

If you are on a different server, there are other ways to rewrite URLs.

Note that you can still use ZF's MVC components without rewriting URL's. Refer to the documentation for info on how to do this.

Create Bootstrap

The bootstrap file is the "single entry point" I've been talking about. It contains the code that launches the Front Controller. In this case, our bootstrap file is index.php.

Here's the code for a simple bootstrap file:

  1. <?php
  2.  
  3. #------------------------------
  4. # Get the paths set
  5. #------------------------------
  6.  
  7. define('ROOT', realpath(dirname(__FILE__) . '/appfiles'));
  8. set_include_path('.' . PATH_SEPARATOR . ROOT . PATH_SEPARATOR . ROOT.'/lib');
  9.  
  10.  
  11. #------------------------------
  12. # Register the Zend autolaoder
  13. #------------------------------
  14.  
  15. require_once(ROOT . '/lib/Zend/Loader.php');
  16. Zend_Loader::registerAutoload();
  17.  
  18.  
  19. #------------------------------
  20. # Dispatch
  21. #------------------------------
  22.  
  23. Zend_Controller_Front::run(ROOT . '/application/controllers');

Line 7 defines the ROOT constant to be the full path to the appfiles/ directory. If you were to move the appfiles/ directory somewhere else, you'd want to edit this line so the path stays correct.

Line 8 sets the PHP include path so that the Zend files are all included without any problems. We need this line because we are distributing ZF with the project, we need to tell PHP where to look for the files when we include them.

Line 15 and 16 include Zend_Loader and register it's autoload functionality. PHP5 introduced the ability to autoload classes. Whenever a class is used but has not been defined, PHP will try and find the file and include it for you. This saves you from having to manually write all of the require_once()'s you would normally write under PHP4.

Line 23 fires off the Front Controller which starts up your whole application. As a parameter you need to pass the path to your controller files which in our case is /appfiles/application/controllers.

If you run this now (http://yoursite.com/path/to/project/), you'll see an exception because ZF can't find your controller file to handle the request. So let's write it.

Controllers and Actions

What are Controllers? Actions?

Lets get this question out of the way for those who are new to this kind of website architecture. A Controller is a class that handles the business logic of a given part of your website. It fetches data from a datasource, interacts with output, and then passes any calculations to the View to be displayed to the user (usually as HTML). Some examples of Controllers might be: LoginController, ArticleController and SearchController. Before you might have had files like login.php, article.php and search.php -- now you have classes that control all of this behavior.

Each Controller has a set of Actions, which is just a method you define within the Controller class. An Action is something specific the Controller needs to do. Using our examples listed before, this might be processing a login, displaying an article or performing a search. Before you might have used a variable in the URL to determine what needed to be done. For example, "article.php?do=display" now becomes the ArticleController with the 'display' Action.

Naming Conventions

So you know that controllers are classes, and actions are methods in the controllers. Now the only thing left to cover is how ZF knows which file to load, and which method to call.

You defined the location of the controller files when you called Zend_Controller_Front::run() in the bootstrap. In our example, it is /appfiles/application/controllers.

The Dispatcher (explained above) is responsible for loading the appropriate files, instantiating the correct class, and then calling the correct method. The default Dispatcher that comes with ZF uses a naming convention so you can add controllers and actions easily (instead of say, creating a configuration file that defines it all).

There are two very simple rules:

  • All controllers need to be called SomethingController where "Something" can be whatever you want. The class source code needs to be in a file called "SomethingController.php".
  • All actions need to be called someAction() where "some" is anything you want.

The default Router and Dispatcher will route a URL /something/some to the SomethingController and the someAction() action. (Meaning: Remember that the default URLs are /:controller/:action).

If a controller is not specified, then the 'index' controller (IndexController class) will be assumed. If an action is not specified, then the 'index' action is assumed (indexAction() method).

Just to hammer it into your head, let's have a few examples of how some possible URLs might be loaded using the default Router and Dispatcher:

  • /: IndexController class, indexAction() method
  • /login: LoginController class, indexAction() method
  • /login/lostpass: LoginController class, lostpassAction() method
  • /article/new: ArticleController class, newAction() method

You can also use hyphens in your URL's if you use camelCase in the naming of controllers or actions:

  • /download/message-attachment: DownloadController class, messageAttachmentAction() method
  • /sign-up/confirm-email: SignUpController class, confirmEmailAction() method

Views

What are Views?

A View is the thing that you give back to the user after they request something. Usually this is an HTML page, but it can be other things like XML or RSS feeds, file downloads etc.

File Locations

Controllers will try to render a view script automatically. It will search for view scripts in /views/scripts/ in the directory above the controller directory (for us that means /appfiles/applications/views/scripts/). Each controller is expected to have a directory of its own, and each action is expected to have a .phtml file that is the actual PHP script to render. For example, LoginController's indexAction() will have a view in views/scripts/login/index.phtml.

View Files

The default view scripts in ZF are plain old PHP. You can add a different templating system if you'd like (like Smarty), but for simplicity, this article will use the default.

In the controller you can set variables to be available to the view, so you can do all of the usual stuff you'd expect like looping, formatting etc.

An Example Page

Time for a really simple example page that demonstrates everything you've just read.

First step is creating a new controller. This controller will be located at: /appfiles/application/controllers/IndexController.php with the following code:

  1. <?php
  2.  
  3. class IndexController extends Zend_Controller_Action {
  4.     public function indexAction() {
  5.        
  6.         $this->view->name = "Christopher";
  7.        
  8.     }
  9. }

Next we need to create a view script to render the HTML page. The view script will be located at: /appfiles/application/views/scripts/index/index.phtml with the following code:

  1. <h1>Hi, <?php echo $this->name; ?></h1>

Now if you run this in your browser, you'll get a simple page that displays a name. So what is going on here?

The Controller

The IndexController is a class the extends Zend_Controller_Action. All of your controllers must extend this base class (or a child of this base class -- you can create your own if you needed to). And on line 4 we have defined the indexAction() method to handle the default index action.

By defining both the index controller with an index action, we now have a default page do display whenever there was no URL supplied (remember that the dispatcher assumes 'index' if no controller or index was supplied).

On line 6 you'll notice we are assigning a name to a variable $this->view->name. This is how you pass values to your view script. You can assign any values. The view script is just PHP remember, so it doesn't matter if you assign arrays, objects, integers or strings.

The View

The view in our case is very simple. I just wanted to highlight how we use the variables assigned in the controller. All values assigned in the controller are available as $this->varname.

What about Models?

I haven't discussed anything about the models in this article. That's because the model in ZF's MVC can be just about anything.

A model is an object, or a system of objects, that represent some sort of data. Most often models are objects that represent specific database records, or database tables. For example, if you have a "user" table then you'd also have a "User" object to represent records from the table. You might also have a "UserTable" object to make finding records easier (sometimes called "peer objects"). So you might have classes with methods like User::setPassword() and UserTable::findUserByEmail(). This technique of mapping a table to objects is the ActiveRecord pattern.

You can code these objects yourself, but usually some sort of ORM (Object-relational mapping) tool/library is used. Two of the most popular are Doctrine and Popel (I suggest Doctrine). With these libraries, you define the structure of your database and the rest of the work is done for you. That is, the means of satisfying relationships and filling data objects with actual data form the database is done by the library rather then by hand. In the case of Doctrine, actual SQL is abstracted into so-called "DQL" (Doctrine Query Language) to make your programs 100% portable.

The point of a model is to create a separation between the data-tier and the logic-tier in your application. For example, your application's "change password" controller shouldn't need to know the low-level details of how a password is changed (the hashing, salting etc). That kind of thing should be coded into the model.

Finished

That's all there is to it. Click here to download the sample files used in this article (note that you need to add your own Zend library to the /appfiles/lib directory).

Next article I will go over some more advanced techniques including subclassing the front controller, action controllers, creating plugins and creating helpers.

4 Responses to “MVC With The Zend Framework”

  1. Raymond Gao Says:

    I read you article about the Zend Framework. It does a good job giving a 10,000 feet overview of the framework. It will be interesting to do a comparison between Zend Framework vs. other MVC patterns, e.g. Struts, Java Server Faces, Dot Net Framework, etc. That way, you get a comparison across languages and frameworks.

  2. Amit Shah Says:

    How could we call an action of one controller into another controller?

  3. Yujin Says:

    Good article, i am trying to choose between ZF and CodeIgniter. CI is told be much faster than ZF. How you compare ZF with codeigniter ? If ZF is slow, why use it ?

  4. Dan Joo Says:

    Great Tutorial. I'm on ZF 1.8 and some things have changed but for the most part you did an excellent job explaining how the zf components work into the mvc paradigm.

Leave a Reply