How to bootstrap, autoload modules and classes with Zend framework

This post is for you that have read this quick start from Zend and our goal is to create a bootstrap like this one. We assume that you know how to create a project, layout, module, controller and action by using zf.bat, zf.sh or manually and further that you are able to create a helloworld application with structure as bellow (I used zf.bat)

application
   configs
      application.ini
   layouts
      scripts
         layout.phtml
   modules
      default
         controllers
            ErrorController.php
            HelloworldController.php
            IndexController.php
         forms
            HelloWorld.php
         models
         views
            filters
            helpers
            scripts
              error
                 error.phtml
              helloworld
                 hello.phtml
              index
                 index.phtml
      second
         controllers
            HelloworldController.php
            IndexController.php
         forms
            HelloWorld.php
         models
         views
            filters
            helpers
            scripts
              helloworld
                 hello.phtml
              index
                 index.phtml
   Bootstrap.php
library
   Zend
   Foo
     Bar.php
public
   index.php
tests

We have now created two modules “default” and “second”. So far I can not create a project with default module inside folder modules so you need to move controllers, forms, models, views into default (create it first). However, the main purpose here is to show how to alter the Bootstrap that generated by zf.bat so that modules will be loaded and classes within modules, such as forms, models, can be autoloaded. In this example we will test for the form HelloWorld which is loaded in controller Helloworld. Here is the content of those files

default/controllers/HelloworldController.php

<?php
 
class HelloworldController extends Zend_Controller_Action
{
 
    public function init()
    {
        /* Initialize action controller here */
    }
 
    public function indexAction()
    {
        // action body
    }
 
    public function helloAction()
    {
        $request = $this->getRequest(); 
        $form = new Default_Form_HelloWorld();
        if ($request->isPost()) {
        	print $request->getParam('hello');
        }
        $this->view->form = $form;
    }
}
?>

default/forms/HellowWorld.php

<?php
class Default_Form_HelloWorld extends Zend_Form
{
	public function init()
	{
		$this->setMethod('post');
		$this->addElement('text', 'hello', array(
            'label'      => 'Hello default:',
            'required'   => true,
            'filters'    => array('StringTrim')
		));
		$this->addElement('submit', 'submit', array(
            'ignore'   => true,
            'label'    => 'Say',
		));
 
	}
}
?>

second/controllers/HelloworldController.php

<?php
class Second_HelloworldController extends Zend_Controller_Action
{
 
    public function init()
    {
        /* Initialize action controller here */
    }
 
    public function indexAction()
    {
        // action body
    }
 
    public function helloAction()
    {
        $request = $this->getRequest();
        $form    = new Second_Form_HelloWorld();
        if ($request->isPost()) {
        	print $request->getParam('hello');
        }
        $this->view->form = $form;
    }
}
?>

second/forms/HelloWorld.php

<?php
class Second_Form_HelloWorld extends Zend_Form
{
    public function init()
    {
        $this->setMethod('post');
        $this->addElement('text', 'hello', array(
            'label'      => 'Hello second:',
            'required'   => true,
            'filters'    => array('StringTrim')
        ));
        $this->addElement('submit', 'submit', array(
            'ignore'   => true,
            'label'    => 'Say',
		));
    }
}
?>

default/views/scripts/helloworld/hello.phtml
second/views/scripts/helloworld/hello.phtml

<?php 
$this->form->setAction($this->url());
echo $this->form;
?>

The first step we will try to do is to make sure that we can load the modules. We start to alter Bootstrap.php What we need to do is telling Front Controller where our modules are located

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
	protected function _initDoctype()
	{
	}
 
	protected function _initApplication()
	{
		$this->bootstrap('FrontController');
		$front = $this->getResource('FrontController');
		$front->throwExceptions(false);
		$front->addModuleDirectory(APPLICATION_PATH . '/modules');
	}
}
 
?>

Be aware that this can also be done in bootstrap file (public/index.php). One can do that by adding follow

$frontController = Zend_Controller_Front::getInstance();
$frontController->addModuleDirectory(APPLICATION_PATH . '/modules');

Or add this line in application.ini that is generated by zf.bat

resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"

Now you should be able to navigate to http://yourserver/helloworld/hello or http://yourserver/second/helloworld/hello without any complain about “missing default module in front controller”. We got instead a fatal error which says that our form classes Default_Form_HelloWorld/Second_Form_HelloWorld is not found. The next is to enable autoloading of those form classes. To do that we start to edit Bootstrap.php again by adding init method _initAutoload() which will be called automatically

<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
 
	protected function _initDoctype()
	{
	}
 
	protected function _initApplication()
	{
		$this->bootstrap('FrontController');
		$front = $this->getResource('FrontController');
		$front->throwExceptions(false);
		$front->addModuleDirectory(APPLICATION_PATH . '/modules');
	}
 
	protected function _initAutoload()
	{
		// BLOCK 1
		// this is a fallback to autoload our own classes in library
		$autoLoader = Zend_Loader_Autoloader::getInstance();
		$autoLoader->setFallbackAutoloader(true);
 
		// BLOCK 2
		// this is for loading forms classes in default module
		$autoloader = new Zend_Application_Module_Autoloader(array(
        		'namespace' => 'Default_',
            		'basePath'  => APPLICATION_PATH . '/modules/default',
        		'resourceTypes' => array(
                		'forms'=>array('path'=>'/forms',
					'namespace'=>'Form')
				)
			)
		);
 
		// BLOCK 3
		// this is for loading form classes in second module
		$autoloader = new Zend_Application_Module_Autoloader(array(
        		'namespace' => 'Second_',
          		'basePath'  => APPLICATION_PATH . '/modules/second',
        		'resourceTypes' => array(
                 		'forms'=>array('path'=>'/forms',
					'namespace'=>'Form')
				)
			)
		);
	}
}
?>

BLOCK 1:
By default Zend_Loader_Autoloader will only load Zend classes. This code block tells Zend_Loader_Autoloader to include class files in arbitrary libraries. For instance, when you create a new “Bar”

$bar = new Foo_Bar();

It will try to include the file by looking in includepath/Foo/Bar.php and in our case it will include the file Foo/Bar.php

BLOCK 2 & 3:
Since Zend_Loader_Autoloader replaces _ in classname with / when loading the class file. But this will not work for classes in module folder. In these two code blocks we register the namespace and the corresponding path, ie if a class start with Second_ then it should look for the class file in application/modules/second/. When loading the form, in our case, it will add basePath and path, ie it will look for the class file in applicaton/modules/second/forms/.

Be aware that the namespace for your classes in forms and classes in library should distinct, for instance if I create a Foo class for module second in my library I should start with application name (helloworld) Helloworld_Second_Foo instead of Second_Foo which has the start namespace for the form, ie start-namespace conflict. I hope that this is the answer you are looking for …

3 Comments

  1. Virgie Prow says:

    Marvellous article you have created here! The world wide web is full of horrid authorship and I was taken hold of by your lucidity. Your closings are dead-on and I will forthwith subscribe to your rss feed to stay up to date with your up approaching postings. Yes! I admit it, your authorship style is unbelievable and i will now work harder on improving mine.

  2. Picturito says:

    I want to quote your post in my blog. It can?
    And you et an account on Twitter?

  3. Van Nhu says:

    Thx guys, I hope that I have time to write more often …

    @Picturito: yes, you can quote my post. And sorry I am not active on Twitter. As you can see I don’t have much time to write …