Route Result Observers
DEPRECATED!
The route result observers feature existed prior to the stable 1.0 release, but was deprecated with 1.0.0RC6. Please do not use this feature; instead, you can inject middleware between the routing and dispatch middleware that can act on the matched route result.
Occasionally, you may have need of the RouteResult
within other application
code. As a primary example, a URI generator may want this information to allow
creating "self" URIs, or to allow presenting a subset of parameters to generate
a URI.
Consider this URI:
'/api/v{version:\d+}/post/{post_id:\d+}/comment/{comment_id:\d+}'
If you wanted to generate URIs to a list of related comments, you may not want
to pass the $version
and $post_id
parameters each and every time, but
instead just the $comment_id
. As such, route result observers exist to allow
you to notify such utilities of the results of matching.
RouteResultObserverInterface
Route result observers must implement the RouteResultObserverInterface
:
namespace Zend\Expressive\Router;
use Zend\Expressive\Router\RouteResult;
interface RouteResultObserverInterface
{
/**
* Observe a route result.
*
* @param RouteResult $result
*/
public function update(RouteResult $result);
}
These can then be attached to the Application
instance:
$app->attachRouteResultObserver($observer);
As noted, the observer receives the RouteResult
from attempting to match a
route.
You can detach an existing observer as well, by passing its instance to the
detachRouteResultObserver()
method:
$app->detachRouteResultObserver($observer);
RouteResultSubjectInterface
Zend\Expressive\Application
implementsZend\Expressive\Router\RouteResultSubjectInterface
, which defines methods for attaching and detaching route result observers, as well as a method for notifying observers. Typically you'll only see theApplication
class as an implementation of the interface, but you can always create your own implementations as well if desired — for instance, if you are implementing your own middleware runtime using the various interfaces Expressive provides.
Example
For this example, we'll build a simple URI generator. It will compose a
RouterInterface
implementation, implement RouteResultObserverInterface
, and,
when invoked, generate a URI.
use Zend\Expressive\Router\RouterInterface;
use Zend\Expressive\Router\RouteResult;
use Zend\Expressive\Router\RouteResultObserverInterface;
class UriGenerator implements RouteResultObserverInterface
{
private $defaults = [];
private $routeName;
private $router;
public function __construct(RouterInterface $router)
{
$this->router = $router;
}
public function update(RouteResult $result)
{
if ($result->isFailure()) {
return;
}
$this->routeName = $result->getMatchedRouteName();
$this->defaults = $result->getMatchedParams();
}
public function __invoke($route = null, array $params = [])
{
if (! $route && ! $this->routeName) {
throw new InvalidArgumentException('Missing route, and no route was matched to use as a default!');
}
$route = $route ?: $this->routeName;
if ($route === $this->routeName) {
$params = array_merge($this->defaults, $params);
}
return $this->router->generateUri($route, $params);
}
}
Now that we've defined the UriGenerator
, we need:
- a factory for creating it
- a way to attach it to the application
First, the factory, which is essentially a one-liner wrapped in a class:
use Container\Interop\ContainerInterface;
use Zend\Expressive\Router\RouterInterface;
class UriGeneratorFactory
{
public function __invoke(ContainerInterface $container)
{
return new UriGenerator($container->get(RouterInterface::class));
}
}
Attaching the observer to the application can happen in one of two ways:
- Via modification of the bootstrap script.
- By updating the factory to register the observer with the application.
Modifying the bootstrap script
If you choose this method, you will modify your public/index.php
script (or
whatever script you've defined as the application gateway.) The following
assumes you're using the public/index.php
generated for you when using the
Expressive skeleton.
In this case, you would attach any observers between the line where you fetch the application from the container, and the line when you run it.
$app = $container->get('Zend\Expressive\Application');
// Attach observers
$app->attachRouteResultObserver($container->get(UriGenerator::class));
$app->run();
Via the observer factory
This approach requires a slight change to the factory to:
- Check for a
Zend\Expressive\Application
service; and, if found, - Attach the observer to it.
use Container\Interop\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\Router\RouterInterface;
class UriGeneratorFactory
{
public function __invoke(ContainerInterface $container)
{
$generator = new UriGenerator($container->get(RouterInterface::class));
if ($container->has(Application::class)) {
$container
->get(Application::class)
->attachRouteResultObserver($generator);
}
return $generator;
}
}
Note: Helpers included!
You do not need to create the above URI generator for your code; this functionality is already present in the zendframework/zend-expressive-helpers package, and, if you started with the Expressive skeleton, may already be installed by default!
See the helpers documentation for more information.