Monday, August 21, 2006

Zend Framework Tutorial by Rob Allen

This is a new tutorial by Rob, which is pretty good. So if you're new to Zend Framework, this is a quick start.

Thanks Rob!

Tuesday, August 01, 2006

How to Configure and use Zend Controller

Zend_Controller component is the key component to realize MVC pattern in Zend framework.. It consists of a collection of classes to parse requests for an application. I would lilke to share my experience of configuring and using Zend Controller

Working Flow of Zend_Controller

1. Script(usually index.php) runs Zend_Controller_Front.

2. Router object will be called to build Zend_Controller_Dispatcher_Token object which contains information about controller, action and params. Before starting routing process routeStartup() method of plugins will be executed to notify plugins that the router is starting up. routeShutdown() method of plugins will be called to notify plugins that the router is shutting down. It’s possible to replace Zend_Controller_Dispatcher_Token returned by router here.

3. dispatchLoopStartup() method of plugins will be called to notify plugins that the dispatch loop is starting up.

4. Dispatch loop will be started. Zend_Controller_Dispatcher is repeatedly called until all actions have been dispatched sequentially. This means that you can build chain of actions for execution several operations (for example, authenticate user and forward flow to user’s private area on the server without round-trip.) Before each iteration preDispatch() and after each one postDispatch() methods of plugins will be called to notify plugins that a dispatch iteration occurs.

5. On each dispatch iteration Zend_Controller_Dispatcher will instantiate Zend_Controller_Action object and will call it’s method run().

6. Zend_Controller_Action object will call method corresponding to action in Zend_Controller_Dispatcher_Token object. In this class you can use protected method _forward() to set next action to execute without round-trip (in this case dispatch iteration will be repeated again for new action).

7. dispatchLoopShutdown() method of plugins will be called to notify plugins that the dispatch loop is shutting down.

Zend_Controller Workflow:




Configuration
Here I would like to give a simple example on how the Zend_Controller works. We use Apache as the app server. As in the example I used the Rewrite module via .htaccess file, so we should do some configuration first.
1.Go to the httpd.conf of your Apache and find “#LoadModule rewrite_module modules/mod_rewrite.so”, just uncomment it by removing “#”. If you can’t find it, please and this, of course, without “#”.

2.Also with httpd.conf, set AllowOverride? to be all for directory document_root.
For example my document root is “D:\www” so the setting is:


Options Indexes FollowSymLinks
AllowOverride All
Order allow,deny
Allow from all



3 Before use it, we need to direct all incoming requests to a single PHP script.This tutorial uses mod_rewrite for this purpose. The task is very simple, just create a .htaccess file in the document root with the following directives:


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


Start the application

Now we can start our application. Here is the directory structure of my example.


myApp/appZendController/controllers
myApp/appZendController/viewers
www/.htaccess www/index.php
www/library/zend


The code for index.php is
<?php
require_once 'Zend.php';
Zend::loadClass('Zend_Controller_Front');
$controller = Zend_Controller_Front::getInstance();
$controller->setControllerDirectory('D:\myApp\appZendController\controllers');
$controller->dispatch();
?>



Now, if you request http://localhost/aaa/bbb , in which aaa is the controller and bbb is the action, the default value for each is index. When receiving such request the framework looks for a file named “AaaController.php” in the controller directory. Because this does not exist, the framework falls back to IndexController?.php. Not finding either, it reports the error.
Then we create the file “IndexController.php” in controllers directory.
<?php
require_once 'Zend/Controller/Action.php';
class IndexController extends Zend_Controller_Action {
public function indexAction() {
echo 'Hello from IndexController';
$data = array ( array ( 'year' => '1998', 'address' => 'France', 'champion' => 'France' ), array ( 'year' => '2002', 'address' => 'Japan & korea', 'champion' => 'Brazil' ), array ( 'year' => '2006', 'address' => 'Germany', 'champion' => 'Italy' ) ); // now assign the book data to a Zend_View instance Zend :: loadClass('Zend_View'); $view = new Zend_View(); $view->setScriptPath('D:\myApp\appZendController\views');
$view->worldcups = $data; // and render a view script called worldcuplist.php echo $view->render('worldcuplist.php');
}
public function noRouteAction() {
echo "no route";
}
public function testAction(){
echo "test";
$this->_forward("foo","bar");
}
public function __call($action, $arguments) {
echo 'IndexController:__call()';
}
}
?>

The

IndexController class handles requests for which the controller is index or for which the indicated controller does not exist, as just explained. The indexAction() method handles requests for which the action is index. Remember that index is the default value for both the controller and the action. If you try a request for /, /index, or /index/index, the indexAction() method is executed. There is another useful method to add to IndexController. The noRouteAction() method is called whenever a request is made for a controller that doesn't exist. For example, a request for /foo/bar executes noRouteAction() if FooController.php does not exist. __call($action,$argument) is also useful. It is called when a request is made for the controller but the not existing action. For example, a request for /index/bar executes __call($action,$argument). Since no barAction exists in IndexController?. There is another function I want to explain._forward($controller,$action), it will forward to /$controller/$action. So when you request index/test, besides output “test”, it will forward to foo/bar. Now create another controller FooController.php:
<?php
require_once "Zend/Controller/Action.php";
class FooController extends Zend_Controller_Action{
public function indexAction(){
echo 'Hello from IndexController';
$data = array (
array (
'wanju',
'title' => 'The Mystery of Capitalism'
),
array (
'author' => 'xiuxiu',
'title' => 'Economics in One Lesson'
),
array (
'author' => 'Milton Friedman',
'title' => 'Free to Choose'
)
); // now assign the book data to a Zend_View instance
Zend :: loadClass('Zend_View');
$view = new Zend_View();
$view->setScriptPath('D:\myApp\appZendController\views');
$view->books = $data; // and render a view script called "booklist.php"
echo $view->render('booklist.php');
}

public function barAction(){
echo 'MyconController::barAction()';
}
}
?>


It’s quite similar to IndexController?. What I want to talk about here is the Zend_View, which is a class that helps you organize your view logic. Go back to the code of indexAction in IndexController.

Zend :: loadClass('Zend_View');
$view = new Zend_View();
$view->setScriptPath('D:\myApp\appZendController\views');
$view->worldcups = $data; // and render a view script called worldcuplist.php
echo $view->render('worldcuplist.php');
This section of code first set the view script path and then render worldcuplist.php as the viewer. Meanwhile, pass $data to the viewer. I want to mention here is that if you don’t like to set the script path of viewer in each case you can register it by the code: Zend::register('view', $view); To retrieve it, use the registry() method: $view = Zend::registry('view');

Let’s look at worldcuplist.php:

<?php
if ($this->worldcups)
:
?>



Year
Country
Champion

<?php
foreach ($this->worldcups as $key => $val):
?>   
<?php
echo $this->escape($val['year'])
?>


<?php
echo $this->escape($val['address'])
?>


<?php
echo $this->escape($val['champion'])
?>

<?php
endforeach;
?>

<?php else: ?>

There are no wordcups to display.


<?php  endif;
?>


It simply displays the data passed in.