Monday, January 29, 2007

Zend Framework Overview

Bootstrapping

All requests are sent to a single application entry-point. The bootstrap file loads classes and libraries common to all pages on the site. It then does things that need to be done for every request like setting up the database with Zend_Db or loading permission rules via Zend_Acl or starting the session with Zend_Session. It then creates an instance of the Zend_Controller_Front class which is used to dispatch the request to the correct controller and return the response to the client.

For objects that may need to be accessed by controllers or other classes after dispatching, a registry has been created. The bootstrap file can create objects with complicated initialization, possibly utilizing config files, and store these objects in the registry with the Zend::register function. Values can then simply be pulled out of this at will with the Zend::registry function. The advantage of using this over globals is that it is clear whenever you are accessing a shared variable, which can be especially important with PHP's lack of variable declaration. (Although, if you stick to an all-object-oriented design, this might not be a problem.) Additionally, the Zend_Registry class can be subclassed, allowing for exotic data structures, logging of registry use, persistence, etc.
// Create the view and store it for later use.
$view = new Zend_View();
$view->setScriptPath('./application/views');
Zend::register('view', $view);

Controllers and Actions

Every valid URI must have a corresponding action that is executed when it is requested. If the router does not find one, an exception is thrown — either an instance of Zend_Exception or one of its subclasses like Zend_Controller_Exception. The current default router, implemented in the Zend_Controller_Router class, matches URIs against the pattern "/controller/action/param1/value1/param2/value2". There can be zero or arbitrarily many parameters. If the action is omitted, it defaults to the index action, and if the controller is omitted, it defaults to the index controller. The parameters are stored in an array that can be accessed via the _getParam or _getAllParams methods of the Zend_Controller_Action class, the class which all controllers must subclass. Trailing slashes are ignored, mapping to identical actions with identical parameters.

To map a URI to an action method, first create the controller class by postfixing its name with "Controller" and extending Zend_Controller_Action. In this class, any method named with the postfix "Action" will be mapped to a URI. For example, to create a handler for the URI "/" — which gets routed to the index action of the index controller — we need a class named IndexController that extends Zend_Controller_Action, with a method named indexAction. In order for the framework to load this class automatically, put this class definition in a file named IndexController.php your controller directory — /webroot/application/controllers/ if you're using the standard bootstrap file I described.
<?php

class IndexController extends Zend_Controller_Action
{
// Handles "/", "/index", "/index/", "/index/index", "/index/index/"
public function indexAction()
{
}

// Handles "/index/foo", "/index/foo/"
public function fooAction()
{
}
}
Regardless of how many parameters are expected, action methods are always called with zero parameters. But parameters specified in the URI can be accessed with _getParam and the like. For example, if /index/index/param1/value1/ were requested, $this->_getParam('param1') in the indexAction method would return 'value1'.

To handle the URI /downloads/list, create a file named DownloadsController.php in the controllers directory. Make a class named DownloadsController extending Zend_Controller_Action, and in it, create a method named listAction.
<?php

class DownloadsController extends Zend_Controller_Action
{
// Handles "/downloads/list", "/downloads/list/"
public function listAction()
{
}
}

When you echo or print in an action method, the output is sent in the body of the response to the client. The Zend_Controller_Response_Abstract class was created to abstract over this and allow for easy switching between output formats, say, from XML to JSON. The interface though is a little annoying since
echo coolStuffToOutput($howCool);
becomes
$this->getResponse()->appendBody(coolStuffToOutput($howCool));
which is much clumsier. One might argue that this method of outputting shouldn't be used often anyway since most output will be part of a template and output using a view.

1 comment:

Anonymous said...

> The Zend_Controller_Response_Abstract class was created to abstract over this and allow for easy switching between output formats <

Why couldn't/shouldn't the output presentation format be a class Property, rather than requiring an abstract class? ... which, I agree, looks butt-ugly to read ... and is hard to make sense of without looking up the abstract class methods and properties?