How to Manually Create a Mezzio Application

How to Manually Create a Mezzio Application

If you want to build applications in PHP — from one-page apps to enterprise-grade applications — PHP’s Mezzio framework is a excellent framework to choose. In this tutorial, which is an excerpt from my new book, Mezzio Essentials, I’ll show you how to manually build an application in Mezzio.


Before we build the application, let’s discuss what we’re going to create and how we’re going to do it, so that the process which we follow makes sense. We’re going to build a small movie database application hand, one with no command-line tooling support or any other form of support.

As the book focuses on the essentials of building an application with Mezzio, we’re not going to build a fully-featured, world-killing, ready for IPO, application. Instead, it’s going to do as little as possible. That way, our focus stays with understanding the critical elements of functionality, instead of on all the add-on logic and functionality.

I’m not suggesting that this approach is the wrong way to build a {mezzio-docs-url}[Mezzio] application, so please don’t misunderstand me. Building Mezzio applications in this way can be a beneficial approach if it suits your needs. It’s just that, up until now, I’ve found that {mezzio-skeleton-url}[the Skeleton Installer], which we’ll cover in the next chapter, has suited my needs better.

We’ll build the application in a series of iterations. Here are the essentials:

  • xref:iteration-one[Iteration One] - A router, bootstrap file, and a Dependency Injection Container
  • xref:iteration-two[Iteration Two] - Make the default route’s handler reusable
  • xref:iteration-three[Iteration Three] - Register the handler with the DI Container
  • xref:iteration-four[Iteration Four] - Make the app configuration-driven
  • xref:iteration-five[Iteration Five] - Extract the routes and make it more crash resistant
  • xref:iteration-six[Iteration Six] - Let’s Make the Application More Resilient

Let’s get started!

[[iteration-one]] == Iteration One - A Router, a Bootstrap File, and a Dependency Injection (DI) Container

In the root directory of your project, from the terminal, run the code below to create the project directory structure and set the current user as the directory’s owner.

// TODO: Add instructions for Windows users and a note that the default reference is Ubuntu/Debian [source,console]

Create the base directory structure.

Feel free to change this directory to whatever works best for you.

If you do, remember to change the subsequent two references to it as well.

sudo mkdir -p /opt/laminas/mezzio/manual-build

Set your user as the owner of the directory structure.

sudo chown -Rv $(whoami) /opt/laminas

cd /opt/laminas/mezzio/manual-build/

[source,bash,linenos]

#————————————————————————–

Use Composer to bring in the five required project dependencies.

#————————————————————————– composer require
laminas/laminas-config-aggregator
laminas/laminas-diactoros
laminas/laminas-servicemanager
mezzio/mezzio
mezzio/mezzio-fastroute
mezzio/mezzio-helpers

#————————————————————————–

Create the project’s directory and file structure

#————————————————————————– mkdir -p public
src/Movies/Middleware resources/templates
config/autoload
data/cache

touch public/index.php
config/{config,container,development.config,pipeline}.php
config/autoload/{dependencies.global,mezzio.global}.php
data/movies.php
src/Movies/BasicRenderer.php
src/Movies/Middleware/BasicRenderer.php

This creates the core directory structure, consisting of the bootstrap file (public/index.php), the configuration files, and the five core dependencies; those being:

  • {laminas-config-aggregator-url}[laminas/laminas-config-aggregator]
  • {laminas-diactoros-url}[laminas/laminas-diactoros]
  • {laminas-servicemanager-url}[laminas/laminas-servicemanager]
  • {mezzio-url}[mezzio/mezzio]
  • {mezzio-fastroute-url}[mezzio/mezzio-fastroute]
  • {mezzio-helpers-url}[mezzio/mezzio-helpers]

Next, we need to add a PSR-4 namespace for our classes. To save time, add the following to the newly-generated composer.json file:

[source,json,indent=0]

“autoload”: { “psr-4”: { “Movies\”: “src/Movies/” } },

Now, let’s flesh out the configuration files that index.php uses.

=== config/config.php

This file takes care of gathering together the collective, configuration settings for the application. It’s simplified by the extensive use of {using-configproviders-article-url}[ConfigProvider classes].

[source,php,indent=0]

getMergedConfig(); ---- It starts by initializing a new `Laminas\ConfigAggregator\ConfigAggregator` object. If you're not familiar with them, they condense (or aggregate) multiple configuration objects, saving you the hassle of having to code it yourself. In our configuration, we'll make use of the configuration files in `config/autoload`, and the referenced _ConfigProvider_ classes, which simplify the process of setting up our `Application` object. [cols="1,3",options="header"] |=== |Package |Description |`Mezzio` |Provides the initial configuration for Mezzio. |`Mezzio Helper` |Sets up a range of helper services, including `ServerUrlHelper` and `UrlHelper`. |`Mezzio Router` |Provides the DI configuration for proper application routing, such as dispatching, routing, and handling requests to routes using disallowed methods. |`Mezzio Router FastRouteRouter`. |Provides the configuration for using {fastroute-url}[FasteRoute] as the application's router. |=== From this file, we return the merged configuration. === config/autoload/dependencies.global.php This file is where we wire up the dependency injection container's services (or dependencies). Strictly speaking, at this point, we don't, yet, need it. But, it makes sense to introduce it now, so that we are ready for it later. [source,php,indent=0] ---- [ // Use 'aliases' to alias a service name to another service. The // key is the alias name, the value is the service to which it points. 'aliases' => [ // Fully\Qualified\ClassOrInterfaceName::class => Fully\Qualified\ClassName::class, ], // Use 'invokables' for constructor-less services, or services that do // not require arguments to the constructor. Map a service name to the // class name. 'invokables' => [ // Fully\Qualified\InterfaceName::class => Fully\Qualified\ClassName::class, ], // Use 'factories' for services provided by callbacks/factory classes. 'factories' => [ // Fully\Qualified\ClassName::class => Fully\Qualified\FactoryName::class, ], ], ]; ---- You can see that it returns an array with the primary key being `dependencies`. I've used a generic copy from the Skeleton Installer project whose comments give an excellent explanation of what each key in the array and sub-arrays are for. === config/autoload/mezzio.global.php This configuration file stores the Mezzio `Application` object's configuration settings. I've set: - `ConfigAggregator::ENABLE_CACHE` to `true`. This enables the configuration cache. - `debug` to `true`. This enables debugging - `mezzio` > `raise_throwables` to `true`. This enables exception-based error handling. [source,php,indent=0] ---- true, // Enable debugging; typically used to provide // debugging information within templates. 'debug' => false, 'mezzio' => [ // Enable exception-based error handling via standard middleware. 'raise_throwables' => true, ], ]; ---- === config/container.php This file initializes the dependency injection container for the application, based on `laminas-servicemanager`. It does so by reading in the contents of `config/config.php`, and using that to initialize a laminas-servicemanager instance. [source,php,indent=0] ---- 'Hot Fuzz', 'director' => 'Edgar Wright', 'release_date' => '2007/02/14', 'synopsis' => 'Exceptional London cop Nicholas Angel is involuntarily transferred to a quaint English village and paired with a witless new partner. While on the beat, Nicholas suspects a sinister conspiracy is afoot with the residents.', 'stars' => [ 'Martin Freeman', 'Nick Frost', 'Simon Pegg', ], 'genre' => [ 'action', 'comedy', 'mystery', ] ] ]; ---- If you've not seen {hotfuzz-rottentomatoes-url}[Hot Fuzz], do yourself a favour and watch it - *TODAY!* *It's SO worth it!* === public/index.php Now, let's work through `public/index.php`, the application's heart. [source,php,indent=0] ---- setFactory('MovieData', function() { return include 'data/movies.php'; }); /** @var Application $app */ $app = $container->get( Application::class ); $factory = $container->get( MiddlewareFactory::class ); (require 'config/pipeline.php')($app, $factory, $container); $movieData = $container->get('MovieData'); $app->get( '/', function (ServerRequestInterface $request) use ($movieData) { return new HtmlResponse( (new BasicRenderer())( $movieData ) ); } ); $app->run(); })(); ---- We start by including the two classes we'll need, and require Composer's autoloader. After that, we retrieve the initialized container, storing it in an aptly named variable, called `$container`, by requiring the contents of `config/container.php`. We then declare an anonymous function, which will contain the core code for the file. There, we start by retrieving the dependency injection container from `config/container.php`, and storing it in a new variable, `$container`. We next call `$container::setFactory()` to define a new service, called `MovieData`, which will return the contents of `data/movie.php`, which we saw just before. After that, we initialize a new object, called `$app`, with the `Application` object retrieved from the container. Application objects are the core of a Mezzio application, whether they're built manually or using the Skeleton Installer project. They handle the core tasks that any Mezzio application needs, including building and managing the application's routing table, and middleware pipeline, handling responses to requests made to the application. There's quite a bit to it, which we'll see progressively, throughout the rest of the book. Next, we retrieve the `MiddlewareFactory` from the container. MiddlewareFactory marshals middleware for use in the application. It provides several methods for preparing and returning middleware for use within an application. With the application object instantiated, we then define a `GET` route, quite similar to how {sinatraframework-url}[Sinatra], {slimframework-url}[Slim], or {silexframework-url}[Silex] would. To it, we provide the route's path and the route's handler, a `RenderMoviesMiddleware` object, which you can see below. [source,php,indent=0] ---- movieData = $movieData; } public function __invoke(ServerRequestInterface $request) { $renderer = (new BasicRenderer())( $this->movieData ); return new HtmlResponse($renderer); } } ---- The route's handler is an implementation of Mezzio Middleware. It doesn't look that sophisticated, does it? However, it's good to know that it doesn't need to be. It's a PHP class, with the implementation of the `__invoke` magic method being where all the action happens. The method takes a `ServerRequestInterface` object, which lets us access the currently called request. However, in this particular case, we're not going to do much with it. Instead, we're initializing a new `BasicRender` object and passing the `movieData` object to it, which was passed to the class' constructor. That object is then passed to the constructor of a new xref:create-an-application-using-the-skeleton-installer.asciidoc#default-response-classes[HtmlResponse] object, which creates an HTML response to this request. [source,php,indent=0] ---- Title Director Release Date Stars Synopsis Genre '; const BODY_TEMPLATE = ' %s %s %s %s %s %s '; public function __invoke($movieData) { $tableBody = ''; foreach ($movieData as $movie) { $tableBody .= sprintf( self::BODY_TEMPLATE, $movie['title'], $movie['director'], (new \DateTime($movie['release_date']))->format('d/M/Y'), implode(', ', $movie['stars']), $movie['synopsis'], implode(', ', $movie['genre']) ); } return sprintf( '

%s

%s%s
', 'PHP Movie Database', self::HEADER, $tableBody ); } } ---- The body of the response comes from calling `BasicRenderer`'s `__invoke` method. You can see the code for `BasicRenderer` above. It's nothing too special. All it does is to render the movie array data in a hand-generated HTML table. Don't hate me for using tables, in this case: they're simple, they work, and they're uncomplicated. === config/pipeline.php In this file, we build up the application's middleware pipeline. This is the over-arching application pipeline where middleware that runs for every request is configured. [source,php,indent=0] ---- pipe(RouteMiddleware::class); // Register the dispatch middleware in the middleware pipeline $app->pipe(DispatchMiddleware::class); // At this point, if no Response is returned by any middleware, the // NotFoundHandler kicks in; alternately, you can provide other fallback // middleware to execute. $app->pipe(NotFoundHandler::class); }; ---- In our example, it's going to be rather modest, including (at this stage) just the bare basics. You can see that it only includes two middleware classes: `RouteMiddleware` and `DispatchMiddleware`. These two (respectively) ensure that the routes that we'll register are known to the application and that the application knows how to respond to them. Other than that, for this simplistic example, we don't need anything else. However, in a more sophisticated example, we'd make use of some of the other available middleware classes, including: [cols="35%,65%",options="header",stripes="even"] |=== |Class |Description |`ErrorHandler` |Use this middleware class to intercept PHP errors and exceptions. |`ImplicitHeadMiddleware` |Use this middleware class to handle implicit HEAD requests. |`ImplicitOptionsMiddleware` |Use this middleware class to handle implicit OPTIONS requests. |`MethodNotAllowedMiddleware` |Use this middleware class to handle requests to routes with disallowed methods. |`NotFoundHandler` |Use this middleware class to handle requests to undefined routes. |=== === The Route's Handler Let's look at the route's handler (the anonymous function) in a bit more depth. It takes one argument, an object which implements the `ServerRequestInterface`. This object represents an incoming, server-side HTTP request, which includes the following properties: - _Protocol version_ - _HTTP method_ - _URI_ - _Headers_; and - _Message body_ It also encapsulates all data from the CGI or PHP environment. This includes such things as _Server_, _Cookie_, and _variables_; _Query strings_, and _uploaded files_. In addition to the request, it also has access to the container (`$container`), as it's passed in via the anonymous function's `use` method. The middleware invokes `Movies\BasicRenderer.php`, which handles rendering the movie data using the `MovieData` service (retrieved from the container and passed to the anonymous function) in a tabular format. This is then passed to the constructor of a new `HtmlResponse` object. *Note:* I'm doing it this way, as we're not using a template engine, _yet_. An `HtmlResponse` object, to quote the source file: > Allows creating a response by passing an HTML string to the constructor; by default, sets a status code of 200 and sets the Content-Type header to `text/html`. The HtmlResponse object is returned from the anonymous function, which renders the generated HTML table output as the route's response. === Movies\BasicRenderer.php If you dive into `Movies\BasicRenderer.php`, you can see it uses two constants; one for the table header and one for the table body. The `__invoke` method iterates over the data, building the body rows of the table; it performs some minor formatting in the process. The body rows are combined with the header row, via `sprintf`, and the result is returned from the function. Getting back to the middleware, with the route created, it then calls the `Application` object's `run`, which launches the application. == How to Run the Application With all that hard work out of the way, let's run the application. We can use {php-builtin-webserver-url}[PHP's built-in web server], with the following command: [source,console] ---- # Update Composer's autoloader configuration composer dump-autoload # Start the internal web server on port 8080 php -S 0.0.0.0:8080 -t public/ &>/dev/null ---- Next, open your browser to http://localhost:8080, where you will see a page similar to the image below. image::posts/mezzio/zend-expressive-basic-app-running.png[caption="Figure 1: Mezzio's Most Basic Implementation", alt="Mezzio's Most Basic Implementation"] Wouldn't you agree it generates an amazingly exciting page? How about no?!.

Want to Learn More About Mezzio?

Mezzio Essentials teaches you the fundamentals of PHP's Mezzio framework — the fundamentals that you need — to begin building get paid for applications with the Mezzio framework right away. 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.
[[iteration-two]] == Iteration Two — Make the Default Route's Handler Reusable Looking at the route, while it is easy to create, it is also going to get quite cumbersome, and difficult to maintain, rather quickly. Not sure why? Consider the following scenarios: * What if we wanted to create a more sophisticated body, which would change based on several conditions? * What if we wanted to pass that body around to different routes? * What if we wanted to create a more sophisticated response? Let's say one or more of these is something that we want to do, and this rather simplistic way of working is getting in the way. So let's refactor it so that we can. Specifically, let's make the code more flexible and more accommodating by refactoring the route handler out into a standalone class, called `RenderMoviesMiddleware.php`. You can see its source below. [source,php,indent=0] ---- movieData = $movieData; } public function __invoke(ServerRequestInterface $request) { $renderer = (new BasicRenderer())( $this->movieData ); return new HtmlResponse($renderer); } } ---- To do so, we first create the class above, which primarily does nothing more than refactor the closure into a class. The `__invoke` method still takes the same parameters, contains the same body, and returns the same response. The one notable difference is that it has a class constructor, which takes in the array of movie data, and initializes a private member variable, `$movieData`, with it. By refactoring it out into a separate class, we're able to create a more reusable and maintainable handler. Now that we've completed this refactor, we next need to refactor `index.php` so that it uses the class instead of the anonymous function. There's not much to do. We only need to instantiate an instance of `\Movies\Middleware\RenderMoviesMiddleware`, passing it the result of calling `$container`'s `get` method (from before), which retrieves the `MovieData` service, containing the movie array data. You can see the changes below. [source,php,indent=0] ---- $app->get('/', new RenderMoviesMiddleware($movieData)); ---- [[iteration-three]] == Iteration Three - Register the Handler With the DI Container Now that the route's handler has become more flexible and maintainable let's see how to use a factory to register it as a dependency with the dependency injection container. You can see the source of the new factory class, `RenderMoviesMiddlewareFactory`, below. [source,php,indent=0] ---- has('MovieData') ? $container->get('MovieData') : null; return new RenderMoviesMiddleware($movieData); } } ---- Unlike factories in early Zend Expressive versions (which preceded Mezzio), it doesn't implement the `Laminas\ServiceManager\FactoryInterface`. However, it still follows part of that earlier convention by implementing the `__invoke` magic method. Following the standard naming convention in Mezzio, we'll call it, `RenderMoviesMiddlewareFactory`. In that method, we instantiate and return a `RenderMoviesMiddleware` object, passing to its constructor the `MovieData` service from the container. We also do a little bit of sanity checking by first checking if the container has such a service. Admittedly, it doesn't do much other than that and would crash if the service weren't defined. The next thing that we need to do in this refactor is to make a minor update to `RenderMoviesMiddleware`. In previous versions of Mezzio, the class could be used, as is, when returned from the dependency injection container. However, in version 3, it needs to implement the `RequestHandlerInterface`. Implementations of this interface process an HTTP request and produces an HTTP response. It requires one method, `handle`, which takes a `ServerRequestInterface` argument, and must return a `ResponseInterface` object. I don't know where you stand on the matter, but (for the most part) I'm all for the clarity and simplicity that {php-return-type-declarations-docs-url}[Return Type Declarations] provide. //Could do with a rework of this paragraph to make the steps obvious. Implementing this interface doesn't require much of a change. We need to rename `__invoke` to `handle`, and add `ResponseInterface` as the return type. Also, we’ll refactor the class to implement `RequestHandlerInterface`. You can see the revised class below. [source,php,indent=0] ---- movieData = $movieData; } /** * @param ServerRequestInterface $request * @return ResponseInterface */ public function handle(ServerRequestInterface $request): ResponseInterface { $renderer = (new BasicRenderer())( $this->movieData ); return new HtmlResponse($renderer); } } ---- With the factory created, we then make a few changes to `public/index.php`. First, we register `RenderMoviesMiddleware` as a service with the dependency injection container by adding a subsequent call to `setFactory`. The first argument is `RenderMoviesMiddleware::class` and the second is `RenderMoviesMiddlewareFactory::class`. Next, we update the default route's handler to be `RenderMoviesMiddleware::class`. The rest is as before. [source,php,indent=0] ---- setFactory('MovieData', function() { return include 'data/movies.php'; }); /** @var Application $app */ $app = $container->get( Application::class ); $factory = $container->get( MiddlewareFactory::class ); (require 'config/pipeline.php')($app, $factory, $container); $container->setFactory( RenderMoviesMiddleware::class, RenderMoviesMiddlewareFactory::class ); $app->get('/', RenderMoviesMiddleware::class); $app->run(); })(); ---- If this looks a bit confusing, what's happening is that when the default route is requested, `$app` will attempt to retrieve an instance of `RenderMoviesMiddleware` from the dependency injection container. In doing so, it will see that `RenderMoviesMiddlewareFactory` provides it, can return the result of invoking it, which returns the fully instantiated `RenderMoviesMiddleware` object, containing the movie data. [[iteration-four]] == Iteration Four — Make the App Configuration-driven How we've registered RenderMoviesMiddleware with the dependency injection container is now almost as simple as it could be. However, it still requires the developer to do more work than is necessary. Let's now do a tiny refactor so that almost no work is required to use this, or any, service that we may add in the future. To do so, we're going to create a `ConfigProvider` class. ConfigProvider classes are plain old PHP classes, with no inheritance hierarchy. As a result, there is minimal, _if any_, complexity to them. All they do is return an array that contains a set of configuration options. Specifically, an option that you can put in any one of the provided configuration files, whether that be dependencies, route configurations, middleware, etc., can be used. Under `src/Movies`, we'll create a new file called `ConfigProvider.php`, which looks as follows: [source,php,indent=0] ---- $this->getDependencyConfig(), ]; } public function getDependencyConfig() : array { return [ 'factories' => [ RenderMoviesMiddleware::class => RenderMoviesMiddlewareFactory::class ], ]; } } ---- You can see that it implements just two functions: `__invoke` and `getDependencyConfig`. When `__invoke` is called, it returns an array of configuration options for the module, containing one configuration option, `dependencies`, which contains a nested associative array. This array can contain the same keys that we've already seen in `config/autoload/dependencies.global.php`, such as `aliases`, `invokables`, and `factories`. We're only implementing `factories`, as in the previous refactor, we created `RenderMoviesMiddlewareFactory` to handle the instantiation of `RenderMoviesMiddleware`. In the `factories` element, similar to how we called `setFactory`, we provide the service name as the element's key and the service handler as the element's value. With that done, the class is ready to go. To use it, we now need to add it in `config/config.php`, as you can see in the example below, which will call its `__invoke` magic method, returning its configuration. When the configuration is returned, it will be merged on top of the application's existing configuration. [source,php,indent=0,linenums,highlight=15..16] ---- getMergedConfig(); ---- Be careful with the services that you register in your ConfigProvider classes! They can overwrite the global configuration, as well as configuration settings from other modules. If you're not careful, you may end up in a bit of debugging hell.

Want to Learn More About Mezzio?

Mezzio Essentials teaches you the fundamentals of PHP's Mezzio framework — the fundamentals that you need — to begin building get paid for applications with the Mezzio framework right away. 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.
[[iteration-five]] == Iteration Five — Extract the Routes The application is a lot more compact now, far more straightforward to use, and far less effort on a developer's part. What's more, we're almost ready to make our module separately available on GitHub for others to use in their applications - should they ever want to. However, there's still a bit to go. For example, the application's routes are defined in `public/index.php`. While okay, there are far more logical places to define them, than there, such as in a dedicated routes file like `config/routes.php`, or in the module's ConfigProvider class. In this section, we're going to refactor the application to see how to make use of either option. === config/routes.php We'll start with `config/routes.php` as it is likely the most obvious place to all developers on a project for storing the application's routes. First, create the new file, such as by using the command `touch config/routes.php`, or using your preferred text editor or IDE to do it, if it supports that functionality. Then, add the code in the example below. [source,php,linenos] ---- route( '/', \Movies\Middleware\RenderMoviesMiddleware::class, [ 'GET', 'PUT', ] ); ---- This example is the same as the `get` method we defined in the previous example, but by using the third parameter, it can now be accessed using both GET and PUT requests. If you don't supply an array, the route will be accessible with *all* HTTP methods. Alternatively, you can use the `Mezzio\Router\Route::HTTP_METHOD_ANY` constant, to be explicit. With the routes defined in `config/routes.php`, we need to enable the new routing table. To do that: . Add the following line, after the reference to `$container->setFactory` . Remove the call to `$app->get` [source,php] ---- // ... preceding code (require 'config/pipeline.php')($app, $factory, $container); (require 'config/routes.php')( $app, $factory, $container ); $app->run(); // ... following code ---- Now, let’s take it one step further, and refactor the routing table definition again. This time, we’ll move the enabling of the routing table for our module to to `src/Movies/ConfigProvider.php`. That way, when the module's enabled, its routes will be available as well. Be aware that if you define routes at the module-level, they may overwrite routes defined in config/routes.php or other modules that have been activated before your module. The first thing we need to do is to define a new method, `getRouteConfig`, which will store the routes. You can see it below. Here, unlike in the previous implementations, the route is defined in a more configuration-driven style. [source,php,linenos,indent=0] ---- public function getRouteConfig() : array { return [ [ 'path' => '/', 'middleware' => RenderMoviesMiddleware::class, 'allowed_methods' => ['GET'], ], ]; } ---- The method returns an array of arrays. Each nested array, like the route methods (other than `route`), can contain up to four keys. These are: `path`, `middleware`, `allowed_methods`, and `name`. These match the arguments that we previously supplied to the function calls. Once `getRouteConfig` has been defined, we next need to ensure that it's merged with the application's configuration. We do that in the `__invoke` method, by adding a new key, aptly named `routes`, whose value is the result of calling `$this->getRouteConfig`. You can see an example of this in the example below. [source,php,linenos,indent=0] ---- public function __invoke() : array { return [ 'dependencies' => $this->getDependencyConfig(), 'routes' => $this->getRouteConfig(), ]; } ---- With that done, to keep the application tidy, we remove the original route definition from `config/routes.php`. I don’t see any harm in leaving the rest of `config/routes.php`. It would make for a fun exercise to create another module and define its routes there, and see which of the approaches that you prefer. [#application-config-injection-delegator] We now have one last change to make. We need to add `Mezzio\Container\ApplicationConfigInjectionDelegator.php` to the Movie module’s dependencies. ApplicationConfigInjectionDelegator allows the Application object to read the routes from the Movie module’s ConfigProvider class. 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 will return an array. In that array, we've added `Mezzio\Application::class` as a key, which references an array with one element, `ApplicationConfigInjectionDelegator`. [source,php,linenos,indent=0] ---- public function getDependencyConfig() : array { return [ 'factories' => [ RenderMoviesMiddleware::class => RenderMoviesMiddlewareFactory::class ], 'delegators' => [ Mezzio\Application::class => [ Mezzio\Container\ApplicationConfigInjectionDelegator::class, ], ], ]; } ---- ### What Are Delegators? If you've not heard of the term "_delegator_" before, it comes from the Delegation software design pattern. To https://en.wikipedia.org/wiki/Delegation_pattern[quote Wikipedia]: > The delegation pattern is an object-oriented design pattern that allows object composition to achieve the same code reuse as inheritance. > In delegation, an object handles a request by delegating to a second object (the delegate). > The delegate is a helper object, but with the original context. > > With language-level support for delegation, this is done implicitly by having self in the delegate refer to the original (sending) object, not the delegate (receiving object). > In the delegate pattern, this is instead accomplished by explicitly passing the original object to the delegate, as an argument to a method. > > Note that "delegation" is often used loosely to refer to the distinct concept of forwarding, where the sending object simply uses the corresponding member on the receiving object, evaluated in the context of the receiving object, not the original object. The Delegation pattern is implemented in the laminas-servicemanager, which we’re making use of in our application, by [Delegators](https://docs.laminas.dev/laminas-servicemanager/delegators/), which implement the Delegator Factory Interface. [According to the official documentation][delegator-factories-docs-url], delegator factories: > Mezzio supports the concept of delegator factories, which allow decoration of services created by your dependency injection container, across all dependency injection containers supported by Mezzio To understand them better, [here is how Marco Pivetta (the original developer) describes them][delegator-factories-explained-url]: > A delegator factory is pretty much a wrapper around a real factory. In essence, 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. To put my spin on it, they allow you to add functionality dynamically to existing services. I hope one, or all, of these explanations, make sense. With the changes made, when the Application object is retrieved from the dependency injection container, it will have the functionality from `ApplicationConfigInjectionDelegator` dynamically added to it. Specifically, it will have the `injectRoutesFromConfig` method. This method is required, as the name implies, to use our ConfigProvider class' `getRouteConfig` method, to build up the application's routing table. The Delegate pattern and Delegator Factories might seem like advanced topics, and perhaps they are. However, they're well worth learning about and using for the advantages that they provide. ## Iteration Six — Let's Make the Application More Resilient At this stage, our application's looking pretty good. However, it's not that resilient. ### Handling Requests to Undefined Routes For example, try requesting any route other than the one route that we've defined. If you do, you'll see a Fatal error, as in the screenshot below. ![The Mezzio application fails because it cannot handle undefined routes](posts/mezzio/create-an-application-manually/without-notfoundhandler.png) In any application, there are going to be routes that don't exist. However, we should handle them a bit more professionally than this. So we're now going to see how to use some existing, pre-packaged, middleware, available with Mezzio, to do so. In the process, we're going to build up our middleware pipeline. In _config/pipeline.php_, where we store our application-level middleware pipeline, after the addition of `DispatchMiddleware`, we'll add `NotFoundHandler` into the pipe, as in the example below: ```php $app->pipe(NotFoundHandler::class); ``` Now, when the route's requested, we'll see a human-readable message, which tells the user, succinctly, what's happened, _without_ exposing any internals of our application, nor scaring away any non-technical users. Much more professional, no? ![Requesting an undefined route, handled with the NotFoundHandler.", alt="Requesting an undefined route, handled with the NotFoundHandler](posts/mezzio/zend-expressive-request-handled-undefined-route.png) ### Handling Requests to Routes With Undefined Methods Now let's make one final improvement and handle requests to routes with HTTP methods that our routes haven't been configured to handle. Currently, the sole route in our routing table only accepts GET requests. What about if we send a request with a PUT, POST, or DELETE method? If we do that, you'll see that it returns an `HTTP 404 NOT FOUND` response code, and in the body, it displays the message `Cannot PUT http://localhost:8000/`. You can see an example of that using Postman, in the image below. ![Requesting a route using a method that's not allowed, using Postman](posts/mezzio/zend-expressive-request-a-route-using-an-undefined-method.png) If, however, we added [MethodNotAllowedMiddleware](https://docs.mezzio.dev/mezzio/v3/features/middleware/method-not-allowed-middleware/) to `config/pipeline.php`, as in the example below, the application could respond in a more meaningful way, returning an [HTTP 405 Method Not Allowed](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/405) status code. ```php $app->pipe(MethodNotAllowedMiddleware::class); ``` ![Requesting a route using a method that's not allowed, handled by MethodNotAllowedMiddleware, using Postman.](posts/mezzio/zend-expressive-request-a-route-using-an-undefined-method-handled-by-methodnotallowedmiddleware.png) You may be okay with the previous functionality, but to me, an HTTP 405 response is the correct approach. I used [Postman][postman-url] in the last two screenshots. However, feel free to use [curl][curl-url] or any other tool that you're more familiar with (_or just plain prefer_). ### What Have We Achieved? It's been quite a journey from the start of this chapter until now. Let's reflect on what we've achieved. We've created a minimalist application by hand, which can render a simple movie listing. Initially, while it was simple, it was quite inflexible and limited in its ability to be reusable. It also would have been increasingly challenging to maintain. By the end of the refactoring efforts, it's much more flexible, more reusable, and much less labour-intense to maintain. What's more, using some of the available middleware classes, it's much more robust. By now, we've seen a glimpse of just how flexible and minimalist Mezzio-based applications are, and we've seen how uncomplicated middleware can be; Middleware's not something overly intense. It can be but a class which handles one concern and nothing more. ## Summary While all of this is great, and provides a pretty flexible configuration setup, where we could have a service for anything and everything, along with being able to add any number of services and routes as needed, it was a lot of work to create everything by hand. Moreover, let's not forget, now that everything's done, that we still don't have a view layer to render the movie output more flexibly than using templates. We're still using strings, along with other functionality, view output. Besides, given all the steps involved, it is more than likely you would spend more time debugging human errors than you would be creating the application code. This tutorial was an excerpt from my new book, [Mezzio Essentials](https://transactions.sendowl.com/products/624496/F3AF9449/view). If you enjoyed the process of building a Mezzio application, and you’d like to go further, then have a look at the table of contents, or buy a copy, via the link below.

Want to Learn More About Mezzio?

Mezzio Essentials teaches you the fundamentals of PHP's Mezzio framework — the fundamentals that you need — to begin building get paid for applications with the Mezzio framework right away. 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.
[curl-url]: https://curl.haxx.se/docs/ [laminas-config-aggregator-url]: https://github.com/laminas/laminas-config-aggregator-modulemanager [laminas-diactoros-url]: https://github.com/laminas/laminas-diactoros [laminas-servicemanager-url]: https://github.com/laminas/laminas-servicemanager [mezzio-fastroute-url]: https://github.com/mezzio/mezzio-fastroute [mezzio-helpers-url]: https://github.com/mezzio/mezzio-helpers [mezzio-url]: https://github.com/mezzio/mezzio [mezzio-docs-url]: https://docs.mezzio.dev/mezzio/ [mezzio-skeleton-url]: https://github.com/mezzio/mezzio-skeleton [postman-url]: https://www.getpostman.com/ [using-configproviders-article-url]: https://matthewsetter.com/using-configproviders/ [delegator-factories-explained-url]: https://ocramius.github.io/blog/zend-framework-2-delegator-factories-explained/ [delegator-factories-docs-url]: https://docs.mezzio.dev/mezzio/v3/features/container/delegator-factories/ [imagesdir]: ./resources/images/ [fastroute-url]: https://github.com/nikic/FastRoute [hotfuzz-rottentomatoes-url]: https://www.rottentomatoes.com/m/hot_fuzz/ [slimframework-url]: https://www.slimframework.com [silexframework-url]: http://silex.sensiolabs.org [sinatraframework-url]: http://www.sinatrarb.com [php-builtin-webserver-url]: https://secure.php.net/manual/en/features.commandline.webserver.php [php-return-type-declarations-docs-url]: https://secure.php.net/manual/en/functions.returning-values.php#functions.returning-values.type-declaration [imagesdir]: /images/ [highlightjs-theme]: ocean [source-highlighter]: highlightjs

Want to Learn More About Mezzio?

Mezzio Essentials teaches you the fundamentals of PHP's Mezzio framework — the fundamentals that you need — to begin building get paid for applications with the Mezzio framework right away. 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...

How to Use a CSRF Token in a Mezzio Application
Tue, Mar 2, 2021

How to Use a CSRF Token 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.

Goodbye Master Zend Framework. So Long. Farewell.
Wed, Jul 8, 2020

Goodbye Master Zend Framework. So Long. Farewell.

It’s with some sadness, that earlier this week, I wished a fond farewell to the biggest blog I ever created, Master Zend Framework. Given that, it only feels right — and responsible — to talk about what motivated the decision to close the blog, and what the future holds for the blog’s content.


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