rulururu

post Bootstrapping the Doctrine 2.0 Autoloader in Zend Framework

September 7th, 2009

Filed under: Code,PHP,Zend Framework — Brenton Alker @ 15:03

Please Note: This post was based on the Alpha release of Doctrine2. They have since added Doctrine\Common\IsolatedClassLoader which (among other things) doesn’t automatically register itself so we no longer have to unregister it, making things much easier.

Doctrine 2.0 looks like it might finally be the ORM framework I have been seeking for PHP. While the older versions of Doctrine provided great functionality, they were too intrusive for my taste. I think an ORM should provide a true data mapper; in which the domain entities need know nothing about their persistence.

Matthew Weier O’Phinney has already posted about autoloading Doctrine in Zend Framework, but Doctrine2 presents some new challenges. Mainly that Doctrine2 is fully PHP5.3, including "real" namespaces, so its classes don’t follow the (current) Zend naming standard and the ZF autoloader won’t load them for us.

Good News, Doctrine provides its own autoloader that we can leverage to load its own classes.

Bad News, the Doctrine autoloader automatically registers itself with spl_autoload_register, causing the normal Zend loader to be forgotten (well, pushed down the stack, where it isn’t very useful).

Good News, it’s easy to remove the doctrine autoloader using spl_autoload_unregister, then push it onto the ZF autoloader stack, targeting the Doctrine namespace. Letting the ZF autoloader call it as necessary.

Enough jibber-jabber, how do we do all this? In the bootstrap! Adding this method to your Bootstrap.php will achieve what we want; adding the Doctrine autoloader to the Zend Framework autoloader queue for the "Doctrine\" namespace.

    protected function _initDoctrine()
    {
        // Create the doctrine autoloader and remove it from the spl autoload stack (it adds itself)
        require_once 'Doctrine/Common/ClassLoader.php';
        $doctrineAutoloader = array(new \Doctrine\Common\ClassLoader(), 'loadClass');
        spl_autoload_unregister($doctrineAutoloader);

        // Fetch the global Zend Autoloader
        $autoloader = Zend_Loader_Autoloader::getInstance();

        // Push the doctrine autoloader to load for the Doctrine\ namespace
        $autoloader->pushAutoloader($doctrineAutoloader, 'Doctrine\\');
    }

We can use all of Doctrine’s classes anywhere else in our code.

use Doctrine\ORM;
$manager = EntityManager::create(array('driver' => 'pdo_sqlite', 'path' => ':memory:/'));
// ...

I still have a lot to learn and the documentation on 2.0 is a little sparse as yet, but this is a start.

post Dependency Injection Container Resource in Zend Framework

July 6th, 2009

Filed under: Code,PHP,Zend Framework — Brenton Alker @ 03:00

A good dependency injection container is a godsend when it comes to managing the dependency tree of even a moderately complex domain model. As such, it comes as no surprise there has been much discussion about them of late in the PHP and Zend Framework communities.

Based on the Yadif and Benjamin Eberlei’s recent look at Using a Dependency Injection Container with Zend_Application, where he replaces Zend_Applications default container instance (A Zend_Registry instance) with a Yadif_Container, I have created a Zend_Application_Resource to allow configuration based injection of dependencies into the container via the normal ZF configuration file (application.ini)

The container resource copies any already instantiated objects from the old container into the new one, then replaces the default container.

class Tek_Application_Resource_Container extends Zend_Application_Resource_ResourceAbstract
{
    protected $_container = null;

    public function getContainer()
    {
        if (null === $this->_container) {
            $options = $this->getOptions();
            $container = $this->_getBootstrap()->getContainer();
            if (!$container instanceof Yadif_Container) {
                $config = isset($options['options']) ? new Zend_Config($options['options']) : null;
                $container = new Yadif_Container(array(), $config);

                // import instances from the existing (Zend_Registry) container
                foreach ($this->_getBootstrap()->getContainer() as $key => $instance) {
                    $container->__set($key, $instance);
                }
            }
            $container->addComponents($options['objects']);
            $this->_container = $container;
        }

        return $this->_container;
    }

    public function init()
    {
        $this->getContainer();
        $this->_getBootstrap()->setContainer($this->_container);
        return $this->_container;
    }

    protected function _getBootstrap()
    {
        $app = $this->getBootstrap()->getApplication();
        if ($app instanceof Zend_Application) {
            return $app->getBootstrap();
        } else {
            return $app;
        }
    }
}

I’ve also created a simple action helper to allow easy grabbing of resources from the action controllers. Both reside in my extensions repository.

To use the container resource you will need to add the prefix and path to the bootstrappers plugin loader:

class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
// ...
    protected function _initPlugins()
    {
       $this->getPluginLoader()->addPrefixPath(
           'Tek_Application_Resource',
           'Tek/Application/Resource'
       );
    }
// ...
}

Then you can add the resources and their dependencies via the normal configuration system. This means adding lines like:

resources.container.objects.Log.class = "Zend_Log"
resources.container.objects.Log.arguments.0 = "Log_Writer"

resources.container.objects.Log_Writer.class = "Zend_Log_Writer_Stream"
resources.container.objects.Log_Writer.arguments.0 = "%Log_Writer.stream%"

resources.container.options.Log_Writer.stream = APPLICATION_PATH "/../log/application.log"

There are 2 resources defined here, the "Log" and the "Log_Writer".

  1. Log is an instance of Zend_Log and takes a Log_Writer resource as the first (and only) argument to its constructor.
  2. The Log_Writer resource is an instance of Zend_Log_Writer_Stream and takes a scalar as its only argument. The scalar value is defined in the container option specified.

Now, the controller can write a log like this.

$logger = $this->getInvokeArg('bootstrap')->getResource('Log');
// or, with the helper
$logger = $this->_helper->resource('Log');

$logger->log('A log message', Zend_Log::NOTICE);

While this is a simple example, it can be really beneficial when working with, for example, a service layer. The service you need might depend on another service, both of which may depend on an Authorization service. All the services depend on their data mappers (which themselves depends on a database connection) and their entity factories, etc.
Instantiating a dependency tree like this for every object you need can lead to duplicate and hard to modify code. Dependency injection coupled with a good container can provide highly versatile code whose behaviour can be drastically changed by only modifying a configuration file.

post Building a Modular Application in Zend Framework – Part 2

June 27th, 2009

Filed under: Code,PHP,Zend Framework — Brenton Alker @ 14:43

Welcome to part two of the series exploring the modular application structure in Zend Framework. With the basic application set up in part one, we can get down to actually creating our first module. We’re going to refactor the official quickstarts guestbook. This will let us focus on the modular structure without getting bogged down in business logic.

Note, there are still some bugs in Zend_Tool that prevent this working as it should, I will note the bugs and their fixes as we encounter them.

Create the module

From the base path of our application (/WORKING/PATH/aza from the last article), we can issue the command to the Zend_Tool CLI to create our guestbook module.

zf create module guestbook

Then, create the index controller within the guestbook module.

zf create controller index 1 guestbook

The "1" argument tells Zend_Tool that we want to automatically create an index action within the new controller. We can get a help listing like this:

zf create controller ?

Once the new module and controller is created we need to tell the application that we are using modules. We do this by adding two lines to the configs/application.ini. The first activates the modules resource. The second configures the front controller, telling it where the modules are located. These lines should be added to the end of the production section of the .ini file.

resources.modules = ""
resources.frontController.moduleDirectory = APPLICATION_PATH "/modules"

To check our module is working, we can navigate to our guestbook at http://aza/guestbook and we should see the default view for the index action.

Bug: Zend_Tool doesn’t prefix the controller names within the module name. The guestbook index controller class IndexController needs to be changed to Guestbook_IndexController.
Bug: The default view for the controller is the same as the main page, it shouldn’t be but we don’t really care, we’re going to replace it anyway.

Create module bootstrap

In the same way that the Bootstrap.php set up the environment for our main application (also known as the default module) each module has its own Bootstrap.php that adds anything additional that each module needs. Zend_Tool doesn’t create this bootstrap by default, so we need to create application/modules/guestbook/Bootstrap.php and it should contain.


<?php
class Guestbook_Bootstrap extends Zend_Application_Module_Bootstrap
{
}

By creating this file, the application will automatically perform module bootstrap tasks such as adding autoloaders for the default resources; including models, forms and services. Any other module specific bootstrapping tasks can be added as _init*() functions. In our case, we don’t need any further bootstrapping.

Important note: All bootstrap functions for every module are run for every request.

The bootstrap process occurs before routing and dispatch, so during bootstrap there is no way to know which module/controller/action is being requested. Therefore, any setup that should be done only if a particular module is requested should be done in plugins, not bootstrap.

Getting Quickstart

Now we have the module skeleton in place, lets start porting the guestbook code to our module. This turns out to be fairly easy; the majority of the changes involve prefixing class names with the module name.

To make life easy, start by acquiring a copy of the completed quickstart application (it’s on the right hand side in zip or tar.gz form).

Importing Quickstart

Once you have downloaded and extracted the files into a temporary folder, we can start copying in the files we need.

We need to copy the GuestbookController from the Quickstart (making it the IndexController) and all of the Quickstart models, views and forms to the appropriate places within our module.

From (Quickstart) To (Aza)
application/controllers/GuestbookController.php application/modules/guestbook/controllers/IndexController.php
application/models/* application/modules/guestbook/models/
application/views/scripts/guestbook/* application/modules/guestbook/views/scripts/index/
application/forms/* application/modules/guestbook/forms/

The controller and the views will require overwriting the existing files.

Porting Quickstart

Now we have the files in the right place, we need to update the files to be modular.

We’ll start with the easiest one, the form. It is simple because it is already prefixed for the Default module, all we need to do it change the prefix to Guestbook_. So the class in application/modules/guestbook/forms/Guestbook.php changes from Default_Form_Guestbook to Guestbook_Form_Guestbook.

Now the models. There are many more changes here but they are just as simple because the models (like the form) are already prefixed with "Default_", but the classes also contain references to each other, so we need to change more than just the class names. A simple search and replace of "Default_" with "Guestbook_" in the application/modules/guestbook/models/ directory is all we need.

The controller is a little trickier because it isn’t already prefixed (controllers in the default module aren’t), but it’s still not too hard. The name of the class in application/modules/guestbook/controllers/IndexController.php just needs to be changed from GuestbookController to Guestbook_IndexController, as it has changed from the guestbook controller within default module (no prefix) to the index controller within the guestbook module. We also need to update the references to the models and forms, the same search and replace as we used in the models will suffice.

Finally, we get to the view. In our index view (application/modules/guestbook/views/scripts/index/index.phtml) we need to update the parameters passed to the url helper to reference our controller. Adding the module, and changing the controller leaves the first link looking like this:

<p><a href="<?php echo $this->url(
array(
'module' => 'guestbook',
'controller' => 'index',
'action' => 'sign'
),
'default',
true) ?>">Sign Our Guestbook</a></p>

Done!

Connect the database

I’ll leave the actual creation of the database to you. It is he same as the Quickstart and this post is already particularly long. You will need to create the database, and add the configuration to the application.ini.

Conclusion

We have just ported the Quickstart guestbook application to a Zend Framework Module. Modularizing applications allows for easier code reuse across applications. Hopefully modules will become standardized to the point that there will be a repository of modules that can be added to your application and providing drop in functionality.

Download

For those who had trouble following along, I’ve made the entire application (including database) available via my github repository.

ruldrurd
Next Page »
Powered by WordPress, Web Design by Laurentiu Piron Monitored by SiteUpTime
Entries (RSS) and Comments (RSS)