How To Simplify Mezzio Application Configuration with ConfigProviders

In any application there are a lot of moving parts, which can lead to a lot of complexity — especially when it comes to configuration. Given Mezzio’s flexible nature, this can easily be the case — if we’re not careful — requiring a lot of supporting configuration. ConfigProvider classes, however, make managing application configuration very maintainable, even intuitive. This tutorial shows how to implement them.


In any application there are a lot of moving parts. This, if we’re not careful, can lead to a lot of complexity, making the code expensive to maintain.

Given Mezzio’s design (and accompanying flexible nature), this can easily be the case if we’re not careful. If we’re not, we can end up with a hell of a lot of classes (think factories, abstract factories, view helpers, and so on) requiring a lot of accompanying configuration.

Whilst it is great that we can do this — making every single component that we write beautifully testable — if we’re not careful, we’re may end up with a sizeable amount of accompanying configuration.

Specifically, we’ll likely end up with a config/autoload directory polluted with a plethora of configuration, including for dependencies, routing, and middleware.

I had such a situation on a recent project, and speaking from personal experience, it became a pain to manage. It was one of the key things that I identified as needing to change in future projects.

As it turns out, this was something which was already identified by other developers, including the Mezzio core contributors. Project team lead Matthew Weier O’Phinney put me on to said solution in his comments on the tutorial on enabling Zend\Form ViewHelpers in Expressive.

In there, he mentioned ConfigProvider classes as a simple way of enabling laminas-form ViewHelpers, which aren’t enabled by default in Mezzio.

As I looked at the composition of the file, I realized that this was the answer I needed to solve the configuration issue I created for myself.

What is a ConfigProvider?

ConfigProvider classes are PHP classes, with no inheritance hierarchy — plain and simple. As a result there is minimal, if any, complexity to them.

All they do is return an array which contains a set of configuration options. Specifically, any option which you can put in any one of the default provided configuration files, whether that be dependencies, route configurations, middleware, etc, can be contained there.

Then, to use the configuration, you need only create a file in config/autoload, which instantiates and invokes it, returning it’s configuration.

How do you create them?

With the sales pitch out of the way, let’s see create and use one. To keep the example concise, I’m going to refactor the configuration from a basic application, based on the Mezzio Skeleton.

I’ll assume for the purposes of today’s tutorial, that you have already done that.

Under src/App, create a new file called ConfigProvider.php, which looks as follows:

namespace App;

use App\Action;

class ConfigProvider
{

}

Nothing special, just a simple PHP class. Next, add an __invoke() method, which returns an array, containing two keys; one for dependencies, one for routes, as I have below.

public function __invoke()
{
  return [
    'dependencies' => $this->getDependencyConfig(),
    'routes' => $this->getRouteConfig(),
  ];
}

Following on in the style of the existing ConfigProvider classes, the values for both keys will be provided through get*Config() methods. You can find existing ConfigProviders for many of the laminas projects, including laminas-db, laminas-form, laminas-filter, laminas-hydrator, and laminas-inputfilter.

Refactor the DI configuration

For getDependencyConfig(), we’ll refactor the existing, App-related configuration settings from config/autoload/dependencies.global.php and config/autoload/routes.global.php, adjusting them for the new App namespace, defining it as follows:

public function getDependencyConfig()
{
  return [
    'invokables' => [
      PingAction::class => PingAction::class,
    ],
    'factories' => [
      HomePageAction::class      => HomePageFactory::class,
      ViewUrlsPageAction::class  => ViewUrlsPageFactory::class,
      ManageUrlPageAction::class => ManageUrlPageFactory::class,
    ],
  ];
}

Note: I’ve imported the App\Action namespace for better readability.

Refactor the route configuration

Next, we’ll refactor the routes from config/autoload/routes.global.php, in to getRouteConfig(), so that it will be defined as follows:

private function getRouteConfig()
{
  return [
    [
      'name'            => 'url.add',
      'path'            => '/add-url',
      'middleware'      => ManageUrlPageAction::class,
      'allowed_methods' => ['GET', 'POST'],
    ],
    [
      'name'            => 'home',
      'path'            => '/',
      'middleware'      => ViewUrlsPageAction::class,
      'allowed_methods' => ['GET'],
    ],
  ];
}

Again, we take advantage of the App\Action namespace to make the file that much more compact (and readable).

Enable the ConfigProvider

With the relevant configuration extracted from the global application configuration, we now need to make use of it. To do so, we create a new file, called config/autoload/app.global.php.

The is a simplistic naming convention I decided on, using the name of the module as the file prefix. In the file, we’ll instantiate and invoke the new ConfigProvider as follows:

use App\ConfigProvider;

return (new ConfigProvider())->__invoke();

With that done, when we reload any route in the skeleton application, we’ll see that it still works as before.

That’s how To simplify Mezzio application configuration with ConfigProviders

ConfigProvider’s are an excellent way of packaging up the configuration of a module so that it’s self-contained, easy to invoke, and avoids creating a bloated or polluted global application configuration setup.

What’s more, they’re ordinary PHP classes, so there’s no complicated setup nor inheritance hierarchy.

Mezzio provides many ways to do just about anything; resulting in us utmost choice and flexibility. This can be both a blessing and a curse.

By following the ConfigProvider convention we have an elegant way to focus that flexibility, so that our code remains readable and maintainable.

If you’ve not already tried it, definitely give it a go. I’d love to hear how you get on in the comments.

Want to Learn More About Mezzio?

Mezzio Essentials teaches you the fundamentals of PHP's Mezzio framework. It's a practical, hands-on approach, which shows you just enough of about the underlying principles and concepts before stepping you through the process of creating an application.

You might also be interested in these tutorials too...

Enable Mezzio Modules with laminas-component-installer
Mon, Dec 16, 2024

Enable Mezzio Modules with laminas-component-installer

When building reusable Mezzio packages, such as for user management, payments, and authentication, do users have to enable them manually, or are you automating it for them? In this short tutorial, I’ll show you how to enable them almost automatically, saving your users time and effort.

How Do You Use CSRF Tokens in a Mezzio Application?
Tue, Mar 2, 2021

How Do You Use CSRF Tokens in a Mezzio Application?

No matter how small your web app may be, security is essential! In this tutorial, you’ll learn how to add a CSRF token in forms used in Mezzio-based applications, to prevent attackers from being able to force your users to execute malicious actions.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus