ZF2 for the ZF1 Developer – Part 2

This is part 2 of my series aimed at giving Zend Framework 1 developers an introduction to Zend Framework 2, if you haven’t read part 1, there’s a link at the top of the page.

I’ve decided to actually make this series 3 parts, in this part I’ll be talking about Routing, the Event Manager, and finally the Service Manager.

Routing

ZF1

Routing is ZF1 is quite straight-forward. You typically add configured Zend_Controller_Router_Route objects to the standardZend_Controller_Router_Rewrite object during bootstrapping or using the shortcuts in application.ini. The router is pretty good (although its restricted to HTTP requests), if a little cumbersome, and its performance isn’t all it can be. Typically, routes are added to ZF1 in the application.ini like this:

application.ini

;Match route /product/1289 to ProductController DetailsAction with the paramters ProductId set to 1289
resources.router.routes.product.route = "/product/:ProductId"
resources.router.routes.product.defaults.controller = "product"
resources.router.routes.product.defaults.action = "details"

ZF2

Routing in ZF2 is fundamentally changed. There is no application level routing, instead, routing is added on a per-module basis. This has the benefit of allowing a module to add routes to an application on an ad-hoc basis. A great example of this is the ZfcUser module; it adds a new /user entry point that by default includes a login and registration form. In my personal opinion, while the routing is much more flexible and considerably quicker in ZF2, I find the trade-off is usability and debugging. It can be difficult to debug problems as finding out which particular module is contributing the problem route can be painful. Adding the equivalent route to the example above in ZF2 can be done like this:

module.config.php

return array(
    'router' => array(
        'routes' => array(
            'category' => array(
                'type' => 'Segment',
                'options' => array(
                    'route' => '/product/:ProductId',
                    'constraints' => array(
                        'ProductId' => '[0-9]*'
                    ),
                    'defaults' => array(
                        '__NAMESPACE__' => 'Application\Controller',
                        'controller' => 'Product',
                        'action' => 'details',
                    ),
                ),
            ),
            ...

Note!

A nice point of note is that you can perform some data validation in the route; the `constraints` key allows you to specify a regular expression that a given parameter should match.

Service Manager (Service Locator)

ZF1

ZF1 doesn’t have a Service Manager as such, I guess the closest thing is Zend_Registry which is really only an object store.Zend_Registry basically allows you to store objects somewhere that other classes will look to pull from, in order to setup things like view helpers. It’s really just a simple storage engine. A good example of how Zend_Registry can work is it’s usage within Zend_Translate. If you put a Zend_Translate object into the registry in your bootstrap (under the Zend_Translate key), then ZF1 will look for it during the dispatch process, and automatically setup the translate view helper for you. This means that in your view you can use $this->translate('ZF is great!'), and the framework will check your translations for the right code.

ZF2

All change! Zend_Registry doesn’t exist in ZF2, it’s been indirectly been replaced by Zend\ServiceManager, and, to a lesser extent,Zend\Di. There are a few different SM (or ServiceLocator) in use around the framework. I won’t go in to detail here, because Jurian Sluiman has done a great blog post about them here:

http://juriansluiman.nl/en/article/120/using-zend-framework-service-managers-in-your-application

Note! – Zend\Di

As a point of note, `Zend\Di` (Dependency Injector) is loosely a Service Manager itself. Di actually magically handles your dependencies by using your auto-loader to know where it should load classes from. It then introspectively determines the dependencies of any given class and tries to fulfil those dependencies in an interative process. It’s very cool, but actually not that feasible in a production environment because it’s a little sloooooow. You can speed it up by generating configuration classes before moving to production, but that’s pretty much what the Service Manager does anyway, so I prefer to use that from the start.

Zend\ServiceManager (or SM as I’m going to call it from now on) basically allows you to manager all your services in one place. It will lazily load classes as and when you need them, fulfilling their dependencies by using the factories you define (usually within closures). The great thing about this is, that you can do a $sm->get('My\Cool\Service') and let the SM worry about whether to create a new instance, or use one that’s been created earlier on in your application flow. Because these configurations can be stored in functions of your Module.php, it means the process is very quick; it can also happily be cached by an opcode cache (like APC).

Note! – SM Configuration

It’s worth noting, to avoid confusion, that the SM can also be configured through a `service_manager` key in the `module.config.php` array. This is to be avoided where possible as these arrays with closures can’t be cached by an opcode cache.

Using the SM couldn’t really be easier. The SM is injected into any class that is called via itself and implements theServiceLocatorAware interface. Zend\Mvc\Controller\ActionController implements this interface, so it’s really easy to access any required services from you controllers, but even better is to use the SM to inject the dependencies from a factory:Modules\Application\src\Application\Controller\MyController.php

namespace Application;

use Zend\Mvc\Controller\ActionController;
use Application\Service\MyService;

class MyController extends ActionController
{

    protected $myService;

    public function __construct(MyService $myService)
    {
        $this->myService = $myService;
    }

    public function indexAction()
    {
        return array('myViewVar' => $myService->getMyViewVar());
    }

}

Modules\Application\Module.php

public function getServiceConfig()
{
    return array(
        'factories => array(
            'myService' => function(ServiceManager $sm) {
$myService = new \Application\Service\MyService();
$myService->setSomeProperty(true);
return $myService;
            },
        ),
    );
}

public function getControllerConfig()
{
    return array(
        'factories' => array(
             'Application\Controller\MyController' => function(ServiceLocator $sl) {
                 $myService = $sl->getServiceManager()->get('myService');
                 $myController = new \Application\Controller\MyController($myService);
                 return $myService;
             },
        ),
    );
}

An important point to note is that controller factories do not get passed the same service locator as service factories. For that reason we need to get the generic service manager from the service locator with getServiceManager() method before grabbing the myServiceinstance.

But really, that’s it, that’s the underlying principles of the SM in a nutshell. As with Zend_Registry in ZF1, certain components will look in the service manager in pre-configured keys for factories. For example, Zend\Translate will look in the translate key. This means that the bootstrapping of your translate class can be done in the service manager and not a bootstrap file.

Event Manager

ZF1

Again, the Event Manager is a new concept for ZF2. Technically it exists in ZF 1.12 as it was back-ported from the ZF2 implementation, but I don’t count that! In a nutshell, the Event Manager (henceforth knows as the EM) allows you to attach code to discrete events that may (or may not) happen in your application. In broad terms the EM replaces the Front Controller Plugin from ZF1, but realistically its so much more.

ZF2

The EM is used in a number of places in the ZF2 core MVC framework. For example, let’s add some code to the loadModules.pre event to do something clever before modules are loaded:

Module.php

public function init(ModuleManager $moduleManager)
{
    $moduleManager->getEventManager()->attach('loadModules.pre', function(\Zend\ModuleManager\ModuleEvent $e) {
        // do some pre module loading code here
    });
}

More useful is using the Event Manager to attach code to user-defined events in your own modules. A great example is if you want to ensure you perform some cleanup code every time a user logs out of your site. You can easily create a User.Logout event, and fire that event any time a user logs out (or is logged out) defining the event in your module is easy…

Module.php

public function init(ModuleManager $moduleManager)
{
    $moduleManager->getEventManager()->attach('myUser.Logout', function(\Zend\ModuleManager\ModuleEvent $e) {
        // cleanup some cache/temp files here
    });
}

MyUserService.php

/**
 *  Presume you have passed the EM into $eventManager via SM or Di
 **/
public function logout()
{
    \\ do Logout code
    $this->eventManager->trigger('myUser.Logout');
}

This is a pretty flaky example, but the application event manager is available through the service manager, and you can also create your own instances of an EM for handling custom event if you so desire.

Part 3

I’m hoping the delay between part 1 and part 2 isn’t going to be replicated between part 2 and part 3. Apologies for the delay, I’m so very busy in work!

One thought on “ZF2 for the ZF1 Developer – Part 2

  1. Pingback: An Introduction to Zend Framework 2 for the Zend Framework 1 Developer – Part 1 | Gary Hockin @GeeH

Your Comments are Awesome, Please Leave Them!

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s