wellrested/docs/source/dependency-injection.rst

103 lines
3.8 KiB
ReStructuredText

Dependency Injection
====================
WellRESTed strives to play nicely with other code and not force developers into using any specific libraries or frameworks. As such, WellRESTed does not provide a dependency injection container, nor does it require you to use a specific container (or any).
This section describes a handful of ways of making dependencies available to middleware.
Request Attribute
^^^^^^^^^^^^^^^^^
``Psr\Http\Message\ServerRequestInterface`` provides "attributes" that allow you attach arbitrary data to a request. You can use this to make your dependency container available to any dispatched middleware.
When you instantiate a ``WellRESTed\Server``, you can provide an array of attributes that the server will add to the request.
.. code-block:: php
$container = new MySuperCoolDependencyContainer();
$server = new WellRESTed\Server(["container" => $container]);
// ... Add middleware, routes, etc. ...
When the server dispatches middleware, the middleware will be able to read the container as the "container" attribute.
.. code-block:: php
function ($request, $response, $next) {
$container = $request->getAttribute("container");
// It's a super cool dependency container!
}
.. note::
This approach is technically more of a `service locator`_ pattern. It's easy to implement, and it allows you the most flexibility in how you assign middleware.
It has some drawbacks as well, though. For example, your middleware is now dependent on your container, and describing which items needs to be **in** the container provides its own challenge.
If your interested in a truer dependency injection approach, read on to the next section where we look at registering middleware factories.
Middleware Factories
^^^^^^^^^^^^^^^^^^^^
Another approach is to use a factory function that returns middleware, usually in the form of a ``MiddlewareInterface`` instance. This approach provides the opportunity to pass dependencies to your middleware's constructor, while still delaying instantiation until the middleware is used.
Imagine a middleware ``FooHandler`` that depends on a ``BarInterface``, and ``BazInterface``.
.. code-block:: php
Class FooHandler implements WellRESTed\MiddlewareInterface
{
private $bar;
private $baz;
public function __construct(BarInterface $bar, BazInterface $bar)
{
$this->bar = $bar;
$this->baz = $baz;
}
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, $next);
{
// Do something with the bar and baz and update the response.
// ...
return $response;
}
}
When you add the middleware to the server or register it with a router, you can use a callable that passes appropriate instances into the constructor.
.. code-block:: php
// Assume $bar and $baz exist in this scope.
$fooHandlerFactory = function () use ($bar, $bar) {
return new FooHandler($bar, $baz);
}
$server = new Server();
$server->add(
$server->createRoute()
->register("GET", "/foo/{id}", $fooHandlerFactory)
);
$server->respond();
You can combine this approach with a dependency container. Here's an example using Pimple_).
.. code-block:: php
$c = new Pimple\Container();
$c["bar"] = /* Return a BarInterface */
$c["baz"] = /* Return a BazInterface */
$c["fooHandler"] = $c->protect(function () use ($c) {
return new FooHandler($c["bar"], $c["baz"]);
});
$server = new Server();
$server->add(
$server->createRoute()
->register("GET", "/foo/{id}", $c["fooHandler"])
);
$server->respond();
.. _Pimple: http://pimple.sensiolabs.org
.. _service locator: https://en.wikipedia.org/wiki/Service_locator_pattern