What Are Delegator Factories and Why You Should Use Them

Ever wanted to dynamically expand the functionality of an object which you retrieve from your dependency injection container, based on different needs, yet without creating messy, hard to maintain configurations? Then you’re going to want to know about a powerful new technique - called Delegator Factories.

If you have not heard of Delegator Factories before, here’s a quote from Marco Pivetta (@ocramius), who initially implemented them.

They are pretty much a wrapper around a real factory: it allows us to either replace the actual service with a “delegate” or interact with an object produced by a factory before the Zend\ServiceManager returns it.

According to the official documentation, delegator factories:

Allow decoration of services created by your dependency injection container, across all dependency injection containers supported by Expressive.

To put my spin on it:

Delegator factories allow you to add dynamically functionality to existing services.

Let’s Consider a Practical Example

In the latest version of Zend Expressive (version 3.x), the ability to configure routes from a module’s ConfigProvider class was separated out from the Application object to an Application delegator factory called Zend\Expressive\Container\ApplicationConfigInjectionDelegator.php.

public function getRouteConfig() : array
{
    return [
        [
            'path'            => '/',
            'middleware'      => RenderMoviesMiddleware::class,
            'allowed_methods' => ['GET'],
        ],
    ];
}

What this means is that if you have been configuring your routes using a getRouteConfig function, such as in the above example, if you migrate from version 2 to 3, it’s no longer going to work. However, with a small code change, we can use the delegator factory to dynamically wrap the Application service, and continue using this configuration-driven approach, as before.

To enable it, as you can see in the code sample below, we’ve added a delegators element to config/autoload/dependencies.global.php, which returns an array. In that array, we’ve added Zend\Expressive\Application::class as a key, which references an array with one element, ApplicationConfigInjectionDelegator.

'delegators' => [
    Zend\Expressive\Application::class => [
        Zend\Expressive\Container\ApplicationConfigInjectionDelegator::class,
    ]
],

What will happen is when the DI container requests the Application service, it will have the functionality from ApplicationConfigInjectionDelegator dynamically added to it, specifically the injectRoutesFromConfig method, which is required to, as the name implies, use our ConfigProvider class’ getRouteConfig method to build up the application’s routing table.

As a result:

  • We don’t need to extend the Application object and define a new service, resulting in extra maintenance overhead for use, for the lifetime of the project.
  • We don’t need to create a one-off configuration that may get overlooked in the future, leading to hard-to-find bugs.

That’s a Wrap

The delegator factory might seem like an advanced topic, and perhaps at a development level, it is. However, at a usage level, it’s quite straight-forward and well worth using for the advantages that it provides.

If you would like help getting started, with Zend Expressive, check out the Zend Expressive Essentials book as well as the course I created from PluralSight. There are loads more resources, but I recommend these two as excellent places to start.

CC Image Courtesy of Grodenaue on Flickr


You might also be interested in...


comments powered by Disqus

Books

Buy Mezzio Essentials. Learn the fundamentals that you need, to begin building applications with the Mezzio framework today! Buy Now

Latest YouTube Video

Learn how to write SQL queries in PhpStorm