Zend Framework 2 ServiceManager – Web Application Development Simplified

Zend Framework 2 Event Manager

Summary

The Zend Framework 2 Service Manager simplifies web application development in a number of ways, primarily by making configuration a breeze. In this, the 4th post introducing Zend Framework 2, you will learn what the ServiceManager is, how to use it and how it simplifies app development time.

Getting Started

Welcome to the Zend Framework 2 Introduction 4-part series finale. Recapping the series so far:

In this, the fourth and final installment, we look at the ServiceManager component. This is a highly critical, but potentially misunderstood, aspect of the framework. One that, when understood, makes the rest of the framework a breeze (well, close).

From reading various blog posts and comments, it’s often believed that the ServiceManager over-complicates the entire framework. Some people say it’s an attempt to: out Java Java.

I – completely – disagree!

I believe that it’s this fundamental concept that makes the framework simpler, that makes developing web applications with it easier, less stressful and more maintainable. I hear scoffing, I hear peals of laughter, I hear the embers of a flamewar being stoked to burn me alive at the stake.

To support my point, in this post, I’m going to do 3 things:

  1. Give you a good introduction to what the ServiceManager is
  2. Show you how to configure it
  3. Step you through a hands-on example of using it

Stay on to the end as we consider the critical question.

What is the ZF2 ServiceManager

Put in simplest terms, the ZF ServiceManager component is an implementation of the Service Locator pattern; in short a simple application registry that provides objects (in a lazy loaded fashion) within application as needed – but with some nice additions.

It allows us to perform Inversion of Control (IoC), which allows us to remove dependencies and tight coupling from our applications. The result of this, when combined with dependency injection and simple event management is applications that are simpler to build, test and maintain.

You can try to think of it in more complex ways if you’d like to, but really there’s no need. Where it does get a little complicated however, at least initially, is how it’s all configured. I grant you, that does take a bit of getting your head around – but not too much.

How Do You Configure It?

The Service Manager is able to be configured in 7 key ways. These are:

  • abstract factories: This is one of a fully qualified class name, or an object implementing Zend\ServiceManager\AbstractFactoryInterface. This approach provides flexibility and extensibility to the ServiceManager. If, on request, the ServiceManager isn’t able to locate a class, it iterates through the registered Abstract Factories to see if any of them are able to satisfy the object request.
  • factories: A fully qualified class name, PHP callable object, or a class implementing Zend\ServiceManager\FactoryInterface. These handle the dependency injection aspect of the ServiceManager.
  • invokables: A string, which is a fully qualified class name, able to be instantiated later.
  • initializers: A fully qualified class name, PHP callable object, or a class implementing Zend\ServiceManager\InitializerInterface. If listed, will be applied to objects retrieved from the Service Manager to perform additional initialization.
  • configuration classes: This is a class implementing Zend\ServiceManager\ConfigInterface. Classes implementing this interface are able to configure the Service Manager, performing the initialization that is covered in these seven points.
  • aliases: An associative array of aliases to services (or aliases to aliases). It may not seem like a good idea, but from a readability perspective, I believe this is an excellent technique to have available.
  • shared: Depending on your application or circumstance, you may want to provide single access to an object, or return a new copy on each request. By default, when an object is retrieved from the Service Manager if it’s the first request, the object is instantiated and returned. If it’s any after that, the original object is returned.

What’s really great about this approach, is that each module loaded or created can also be a service provider. To do so, they need to do one of the following:

  1. Implement the Zend\ModuleManager\Feature\ServiceProviderInterface
  2. Implement getServiceConfig()

As the manual indicates, this function needs to return:

  1. An array or Traversable object
  2. The name of a class implementing Zend\ServiceManager\ConfigInterface
  3. An instance of either Zend\ServiceManager\Config, or an object implementing Zend\ServiceManager\ConfigInterface.

A Simple Code Example

I’ve been creating a Generic module throughout this series and in it, I’ve added a simple model that lets me retrieve information from a SQLite database. So what better choice to use for an example that that.

In module/Generic/Module.php, I have an implementation of getServiceConfig which is as follows (formatted for readability):

// Add this method:
public function getServiceConfig()
{
    return array(
        'factories' => array(
            'Generic\Model\AlbumTable' =>  function($sm) {
                $tableGateway = $sm->get('AlbumTableGateway');
                $table = new AlbumTable($tableGateway);
                return $table;
            },
            'AlbumTableGateway' => function ($sm) {
                $dbAdapter = $sm->get(
                    'Zend\Db\Adapter\Adapter'
                );
                $resultSetPrototype = new ResultSet();
                $resultSetPrototype->setArrayObjectPrototype(
                    new Album()
                );
                return new TableGateway(
                    'album', 
                    $dbAdapter, 
                    null, 
                    $resultSetPrototype
                );
            },
        )
    );
}

You can see here I’m configuring it with the ‘factories‘ option. In there I have two options:

  • Generic\Model\AlbumTable
  • AlbumTableGateway

Both of these use anonymous functions for the configuration. Now, let’s pop over to where they’re used in the IndexController. The default action, Index, goes as follows:

public function indexAction()
{
    return new ViewModel(array(
        'albums' => $this->getAlbumTable()->fetchAll(),
    ));
}

It calls the getAlbumTable method, which is as follows:

public function getAlbumTable()
{
    if (!$this->albumTable) {
        $sm = $this->getServiceLocator();
        $this->albumTable = $sm->get('Generic\Model\AlbumTable');
    }
    return $this->albumTable;
}

Now that we have the configuration and usage together, let’s see how it works. In indexAction, we set a variable ‘albums‘, which is the result of calling the fetchAll method on the object returned from the getAlbumTable method.

In the getAlbumTable method, we retrieve a copy of the application Service Locator object via the getServiceLocator method. We then retrieve the AlbumTableGateway object, by passing the name, as a string, to the get method of the Service Locator object.

Assuming the object is in the Service Locator registry and suitably configured, it’s then passed as the return value from the function, allowing us to call the method on it.

Looking back in to the configuration of the object we see it as follows:

'AlbumTableGateway' => function ($sm) {
    $dbAdapter = $sm->get(
        'Zend\Db\Adapter\Adapter'
    );
    $resultSetPrototype = new ResultSet();
    $resultSetPrototype->setArrayObjectPrototype(
        new Album()
    );
    return new TableGateway(
        'album', 
        $dbAdapter, 
        null, 
        $resultSetPrototype
    );
},

We see we have a factory configuration, labelled AlbumTableGateway configured by an anonymous function.

You can see we first we use the ServiceManager to get access to the application-wide database adapter (I’ll cover it in a sec), by retrieving the object configured with the string Zend\Db\Adapter\Adapter.

We then create a new ResultSet object and set the prototype to be an Album object. When that’s done, we then create and return a new TableGateway object, specifying the type, database adapter and result set prototype.

Now, I’m deliberately skipping over delving in to the intricacies of the Zend_Db class here – as it’s really, a bit outside the purposes of this post. But I encourage you to give it a good read in addition to what you read here.

Now, for the final part, in config/autoload/global.php I have the following configuration:

$dbDSN = 'sqlite:' . dirname(__DIR__) . 
    '/../data/databases/zf2skeleton.sq3.sqlite';

return array(
    'db' => array(
        'driver' => 'Pdo',
        'dsn'    => $dbDSN,
        'driver_options' => array(
            PDO::MYSQL_ATTR_INIT_COMMAND => 'SET NAMES \'UTF8\''
        ),
    ),
    'service_manager' => array(
        'factories' => array(
            'Zend\Db\Adapter\Adapter'
                => 'Zend\Db\Adapter\AdapterServiceFactory',
        ),
    ),
);

You can see how the Database adapter is made available, configured using the same convention – allowing me to query a SQLite version 3 database throughout the application.

Don’t forget, all code is available for download from the Malt Blue Github repository. Grab a copy of it and tell me what you think.

What We Have Learned?

This process of stepping through the configuration and usage of the ServiceManager has shown us X things:

  1. Configuration Is Simple: Whilst a bit involved (because of all the locations where configurations can be located) it actually is simple because it follows a clearly defined set of conventions and stored in a standard set of locations.
  2. It’s Easy to Debug: By having standard conventions and locations, we can track configurations in the application logically and systematically.
  3. It Works with Across Modules: Whereas in Zend Framework 1, we had to jump through hoops to work with multiple modules, in ZF2 it’s all baked in. Just follow the conventions and it works.
  4. Applications Are More Dependable: This is a bit of a reiteration of the previous points. But by being able to configure applications reliably, simpler, in a readily debuggable manner, you spend less time and effort, effort which can be spent more meaningfully in other aspects of development (and your business).
  5. Easier to Maintain: When applications as more consistent, predictable, debuggable, logically (in my mind if nowhere else), they’re easier to optimise. Why? Because you’re working with a known quantity. Possibly a little “boring” but following convention makes adding new components quicker and more predictable. Being a maintainer, you know how applications hang together, you know where to look, the options which can be used, how to override them and so on.

Does it Really Make Development Simpler?

This is the burning question in this post. Does it really make web application development simpler? In short, yes. Now, at first, there’s a lot to cover, there’s a lot to learn. I grant you that.

There’s the 7 points mentioned above and you have to work with the different ways in which each work. Fine – I agree it does require work.

But think about any skill you’ve learned. Whether it was riding a bike, learning calligraphy, building running fitness, or what I’m doing – learning German.

At first it requires effort, it requires discipline and it requires concentration. It’s not easy, sure. But with time, this new skill makes doing what you used to do much simpler, such that you do more in less time, with less effort, with less concentration.

Consider the amount of work that went in to Zend Framework 1 applications and configuring them – especially integrating modules and the associated configurations.

As Evan Coury well saidthey sucked!

There was the application.ini file, the module configuration that you could choose from a number of good implementations to get it to work, you could roll your own option and more.

Now, we have the option to spend some time learning, but with the result of ultimately writing cleaner applications, which are simpler to maintain both in terms of time and effort.

We’ll have applications which are much more:

  • Flexible
  • Testable
  • Configurable
  • Optimizable

I believe, therefore, it’s worth the investment of your time to learn. I believe it really is a blessing (potentially in disguise). I’m a learner, like you. So I want to know what you think, if you disagree with me. If you do, comment and tell me.

Get the Code

The code for this post – and all the posts in the Zend Framework 2 introductory series – is available on the Malt Blue Github account. I encourage you to clone a copy, play with it, comment on it, improve on it.

Over to You

Do you agree with me or want to flame me right now? If you agree with me that the ServiceManager will simplify use of Zend Framework 2, share with us how in the comments.

Alternatively, if you agree more with others, that it’s a pointless, over-complication, of what should be kept simple, say that too.

We can all grow through shared, constructive, opinions.

featured Intermediate Tutorial
  • mweierophinney

    So, one thing I’d do quite differently: I would not pull objects from the service locator from within your controller. There’s a much better way: define a factory for your controller, and inject the dependencies from there. This makes it much easier to document your dependencies, and makes your code more easily testable — you don’t need to configure a service locator with the correct services to get started, you simply mock your dependencies and inject them.
     
    Over all, though, nicely written article!

    • http://www.matthewsetter.com/ maltblue

      @mweierophinney thanks for leaving such a helpful comment, both technical and overall on the article. I’m still learning the framework and your feedback really helps. Any other suggestions, please let me know.

  • ChrisdeKok

    It makes code more testable sure. but also adds complexity, btw if have not tried this but what happens if there are collisions in names? like one module uses db, and another module want’s to use their own db connection?

    • http://www.matthewsetter.com/ maltblue

      @ChrisdeKok Sorry that it’s taken me so long to get back to you on this one. I’m not that conversant yet, I’ll be quite honest about it. But would you not configure an alias in the ServiceManager? What’s your thoughts @mweierophinney?

    • codeTHISS

      @ChrisdeKok That is what alias is for

  • landofziploc

    “Simple” is just advertising and marketing speak.  It’s relatively simpler to use a SM than it is to make your objects ad-hoc, if you’re going to test and reuse and revisit the software in 6 months and have to add more components or alter what code is there.  But, that is just relatively simpler — in absolute terms, it’s more complex and won’t yield benefits until after the codes written.  There’s all this overhead to learn the SM and to use it both to offer services and consume them.I’m inclined to say it’s more complex, but it’s better, and helps you avoid much more difficult complexity in the future.Of course, you could say the exact same thing about classical object oriented programming.  It’s more complex than something like QBASIC or C, but you can write much larger projects with OOP, because OOP techniques help manage complexity at the cost of initial complexity.  And, again, you can say that about various design patterns.
    Nobody has to use these systems.  It’s really nice to have them there, though, for when your application grows to require them.

    • http://www.matthewsetter.com/ maltblue

      @landofziploc I wasn’t sure for a bit which way you were heading. I disagree that simpler is marketing speak. I meant it in a broader sense.
       
      For example, you have two products in your hands. One’s twice the price of the other. But from your understanding, the build quality, after sales service, and reputation of the dearer one is far in excess of the cheaper one. I would say that it’s cheaper to buy the dearer product.
       
      But I agree with you wholeheartedly that, whilst you don’t have to use these options, in the long run it will be a better choice to do so (assuming it’s composed correctly etc). Thanks kindly for contributing such meaningful feedback.

  • bhaskarudu

    Very helpful post. This post clarified many doubts about SM

    • http://www.matthewsetter.com/ maltblue

      @bhaskarudu great to hear. After a recent project I have loads more to write about service manager. A great tool.

  • Dona

    hi! I’have a question!
    if i have this function in my class Module.php

    public function getServiceConfig()
    {
    return array(
    ‘factories’ => array(
    ‘ApplicationModelUserTable’ => function($sm) {
    $tableGateway = $sm->get(‘UserTableGateway’);
    $table = new UserTable($tableGateway);
    return $table;
    },
    ‘UserTableGateway’ => function ($sm) {
    $dbAdapter = $sm->get(‘ZendDbAdapterAdapter’);
    $resultSetPrototype = new ResultSet();
    $resultSetPrototype->setArrayObjectPrototype(new User());
    return new TableGateway(‘user’, $dbAdapter, null, $resultSetPrototype);
    },
    ),
    );
    }

    How i can use it in a action of my controller to write on the db?I need of the TableGateway object:
    $tableGatewat->insert($data);

    How i can get it from getServiceConfig?

    THank you!!

    • http://www.matthewsetter.com/ Matthew Setter

      Hi Dona, sorry for not replying sooner. I’m really not sure how this one slipped my attention. The simplest way is, in your action, to call the following code: $userTable = $this->getServiceLocator()->get(‘ApplicationModelUserTable’);
      This will retrieve the service from the service locator. However, if there are any exceptions thrown in the instantiation of the service, then these will need to be caught.

  • Maxwell

    I am having issues here. I have started off with ZF 2 from last week. The challenge that I have is the I have a module with 5 controllers. I have managed to configure the routes to these controllers successfully.

    Problem comes in if I want to add table mappers for each of my respective controllers. Any idea on hos I can achieve this? I am using 2.2.6 to be exact.

    • http://www.matthewsetter.com/ Matthew Setter

      Hi Maxwell. On the surface of it, it sounds like you’re getting things a bit mixed up. Do you have a public github repo or Gist I can take a look at to help you out?

      • Maxwell

        Hi! Thanx Matt for the reply.

        This is the scenario. I have an application built on top of the Zend Skeleton Application with 8 modules.

        One particular use case is one mpdule with 5 controllers that each has a corresponding table. Setting up one table is not an issue on my module’s class. However, am struggling when I want to add a second table in the ServiceConfig function so that I do not return one instance of table mapper.
        The other issue is when I want to call table mappers from other modules. How do I achieve that?

        • http://www.matthewsetter.com/ Matthew Setter

          That’s a little hard to answer here in the comments. Can you send me a secret Gist link to an example?

          • Maxwell

            I will try to create on tomorrow and send you the link.

            Thank you

          • Maxwell

            Hello again Mattew

            if its fine with you I will just add a screen on this comment hoping that you can see it. I couldnt setup the git repository due to network issues. I think, however, the picture will give you an idea of what am talking about or want.

            tha is basically my module with those controllers. Now if I want to have access to the tables that corresponds with each of the controllers extending the getControllerConfig() function in the module class doesnt seem to work (see code below).

            public function getServiceConfig()
            {
            return array(
            ‘factories’ => array(
            ‘ContentsModelMapperWard’ => function($sm){

            $tableGateway = $sm->get(‘WardGateway’);
            $wardTable = new WardMapper($tableGateway);

            return $wardTable;
            },
            ‘WardGateway’ => function($sm){

            $dbAdapter = $sm->get(‘ZendDbAdapterAdapter’);

            $rsPrototype = new ResultSet(); $rsPrototype->setArrayObjectPrototype(new WardEntity());

            return new TableGateway(‘wards’, $dbAdapter, null,
            $rsPrototype );
            },
            ),
            );
            }
            I can only have that and thats it…. Now I have tried to use the ConfgAwareInterface aswell where I created the interface file inside my Application module of my zend skeleto but the namespace couln’t be resolved to the interface. I have just updated from 2.2.6 to 2.3.1.

            I am not sure what am doing wrong if I want to ingect more database table objects and the Service Manager seems to be the best solution except I do not have any clue how it works even after having read the documentation for both versions and tutorials out there. i must be missing something small though please help…

          • http://www.matthewsetter.com/ Matthew Setter

            Hey @disqus_OWjn7oSNkv:disqus the part that’s been confusing me is where you’ve been writing “Now if I want to have access to the tables that corresponds with each of the controllers”.

            I’m not sure why you’re taking the approach of making a correlation between controllers and tables. Perhaps I’ve just misunderstood.

            If you’ve setup services for all of your tables that you need to access, how are you not able to retrieve them in the respective controller actions?

          • Maxwell

            Hi Matt

            I did an update on my Zend Framework Library to 2.3.1 and was able to use the Service Locator to inject my model into the controller. I think the main issue I was having is that inside my model directory I created Entity and Mapper folders where I could save the respective classes for my models.
            When I indexed my factory setting inside the Module class’s getControllerConfig method the Service Locator simple couldnt find or create the mapper class for my table. I am happy to say removing the extra entity and Mapper folders seem to help me in that regard.

            I have gained a little bit of confidence and belief in myself after those results. Thank you for your time and if I bump into any issues I will let you know.
            :-)

          • http://www.matthewsetter.com/ Matthew Setter

            Hey Maxwell, glad to hear that you were able to resolve the issue and that you’re now up and running with increased confidence in your abilities. Confidence can often go a long way! Anytime you need a hand, feel free to get in touch.

  • anonym

    Hello Matthew,
    Could I have a question?
    Why are you implementing getAlbumTable(); method in Controller? The functionality that method provide is already provided by ServiceManager as it sets by default Services as shared, so $sm->get(‘AlbumTableGateWay’) always return one same instance of object…
    Thanks for answer.

    • http://www.matthewsetter.com/ Matthew Setter

      To be perfectly honest, I really don’t know. I think when I wrote the article there was so much that I was still learning. So I’ll put it down to what I knew at the time. If I were writing the post today, I’d initialise the controller using the factories or abstract_factories element in the ServiceManager, and specify the table as a dependency. During instantiation, the service would be retrieved and set as a member variable in the constructor.

  • Morgan Gonzales

    Correct me if I’m wrong but I think it should be the ‘GenericModelAlbumTable’ that was retrieved from the Service Managers instead of the ‘AlbumTableGateway’. The ‘GenericModelAlbumTable’ returns the Album class wherein you basically implement the fetchAll().

    • http://www.matthewsetter.com/ Matthew Setter

      Morgan, you’re right. Thanks for picking that up. The post is now corrected.