Skip to content

Commit

Permalink
Do not act as a route result observer
Browse files Browse the repository at this point in the history
This patch updates the `UrlHelper` such that it no longer acts as a
route result observer. The `UrlHelperMiddleware` was updated to look for
the `RouteResult` attribute, and, if present, inject the `UrlHelper` via
its `setRouteResult()` property.

These changes:

- are not backwards compatible, and will require a new major version.
- require merging of zendframework/zend-expressive#270 for the
  middleware updates to work.
  • Loading branch information
weierophinney committed Jan 18, 2016
1 parent a33e52b commit 52b0f79
Show file tree
Hide file tree
Showing 8 changed files with 89 additions and 163 deletions.
70 changes: 44 additions & 26 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,9 +28,11 @@ can recommend the following implementations:

`Zend\Expressive\Helper\UrlHelper` provides the ability to generate a URI path
based on a given route defined in the `Zend\Expressive\Router\RouterInterface`.
If registered as a route result observer, and the route being used was also
the one matched during routing, you can provide a subset of routing
parameters, and any not provided will be pulled from those matched.
The provided `Zend\Expressive\Helper\UrlHelperMiddleware` can look for a
`Zend\Expressive\Router\RouteResult` request attribute, and, if present, inject
the `UrlHelper` with it; when this occurs, if the route being used to generate
a URI was also the one matched during routing, you can provide a subset of
routing parameters, and any not provided will be pulled from those matched.

In order to use the helper, you will need to instantiate it with the current
`RouterInterface`. The factory `Zend\Expressive\Helper\UrlHelperFactory` has
Expand Down Expand Up @@ -76,14 +78,13 @@ return ['dependencies' => [
For the helper to be useful, it must be injected with a
`Zend\Expressive\Router\RouteResult`. To automate this, we provide a middleware
class, `UrlHelperMiddleware`, which accepts the `UrlHelper` instance and a
`Zend\Expressive\Router\RouteResultSubjectInterface` instance (typically a
`Zend\Expressive\Application` instance); when invoked, it attaches the
`UrlHelper` as a route result observer. To register this middleware, you
will need to:
class, `UrlHelperMiddleware`, which accepts the `UrlHelper` instance.
When invoked, it looks for a `RouteResult` request attribute, and, if found,
injects it into the `UrlHelper`. To register this middleware, you will need to:

- Register the `UrlHelperMiddleware` as a service in your container.
- Register the `UrlHelperMiddleware` as pre_routing pipeline middleware.
- Register the `UrlHelperMiddleware` as middleware between the Expressive
routing and dispatch middleware.

The following examples demonstrate registering the services.

Expand All @@ -108,19 +109,25 @@ $container->set(
);
```

To register the `UrlHelperMiddleware` as pre-routing pipeline middleware:
To register the `UrlHelperMiddleware`:

```php
use Zend\Expressive\Helper\UrlHelperMiddleware;

// Do this early, before piping other middleware or routes:
$app->pipeRoutingMiddleware();
$app->pipe(UrlHelperMiddleware::class);
$app->pipeDispatchMiddleware();

// Or use configuration:
// [
// 'middleware_pipeline' => [
// 'pre_routing' => [
// ['middleware' => UrlHelperMiddleware::class],
// 'routing' => [
// 'middleware' => [
// Zend\Expressive\Container\ApplicationFactory::ROUTING_MIDDLEWARE,
// UrlHelperMiddleware::class,
// Zend\Expressive\Container\ApplicationFactory::DISPATCH_MIDDLEWARE,
// ],
// 'priority' => 1,
// ],
// ],
// ]
Expand All @@ -140,8 +147,13 @@ return [
],
],
'middleware_pipeline' => [
'pre_routing' => [
['middleware' => UrlHelperMiddleware::class],
'routing' => [
'middleware' => [
Zend\Expressive\Container\ApplicationFactory::ROUTING_MIDDLEWARE,
UrlHelperMiddleware::class,
Zend\Expressive\Container\ApplicationFactory::DISPATCH_MIDDLEWARE,
],
'priority' => 1,
],
],
]
Expand Down Expand Up @@ -278,7 +290,7 @@ As such, you will need to:

- Register the `ServerUrlHelper` as a service in your container.
- Register the `ServerUrlMiddleware` as a service in your container.
- Register the `ServerUrlMiddleware` as pre_routing pipeline middleware.
- Register the `ServerUrlMiddleware` early in your middleware pipeline.

The following examples demonstrate registering the services.

Expand Down Expand Up @@ -309,19 +321,24 @@ $container->set(
);
```

To register the `ServerUrlMiddleware` as pre-routing pipeline middleware:
To register the `ServerUrlMiddleware` in your middleware pipeline:

```php
use Zend\Expressive\Helper\ServerUrlMiddleware;

// Do this early, before piping other middleware or routes:
$app->pipe(ServerUrlMiddleware::class);

/* ... */
$app->pipeRoutingMiddleware();
$app->pipeDispatchMiddleware();

// Or use configuration:
// [
// 'middleware_pipeline' => [
// 'pre_routing' => [
// ['middleware' => ServerUrlMiddleware::class],
// [
// 'middleware' => ServerUrlMiddleware::class,
// 'priority' => PHP_INT_MAX,
// ],
// ],
// ]
Expand All @@ -341,8 +358,9 @@ return [
],
],
'middleware_pipeline' => [
'pre_routing' => [
['middleware' => ServerUrlMiddleware::class],
[
'middleware' => ServerUrlMiddleware::class,
'priority' => PHP_INT_MAX,
],
],
]
Expand Down Expand Up @@ -407,7 +425,7 @@ use Zend\Expressive\Helper\BodyParams\BodyParamsMiddleware;
$app->pipe(BodyParamsMiddleware::class);
```

or, if using Expressive, as pre_routing pipeline middleware:
or, if using Expressive, as pipeline middleware:

```php
// config/autoload/middleware-pipeline.global.php
Expand All @@ -424,11 +442,11 @@ return [
],
],
'middleware_pipeline' => [
'pre_routing' => [
[ 'middleware' => Helper\BodyParams\BodyParamsMiddleware::class ],
/* ... */
[
'middleware' => Helper\BodyParams\BodyParamsMiddleware::class,
'priority' => 1000,
],
'post_routing' => [
'routing' => [
/* ... */
],
],
Expand Down
19 changes: 0 additions & 19 deletions src/Exception/MissingSubjectException.php

This file was deleted.

16 changes: 6 additions & 10 deletions src/UrlHelper.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@
use Zend\Expressive\Router\Exception\RuntimeException as RouterException;
use Zend\Expressive\Router\RouterInterface;
use Zend\Expressive\Router\RouteResult;
use Zend\Expressive\Router\RouteResultObserverInterface;

class UrlHelper implements RouteResultObserverInterface
class UrlHelper
{
/**
* @var string
Expand Down Expand Up @@ -95,14 +94,11 @@ public function generate($route = null, array $params = [])
}

/**
* {@inheritDoc}
*/
public function update(RouteResult $result)
{
$this->result = $result;
}

/**
* Inject a route result.
*
* When the route result is injected, the helper will use it to seed default
* parameters if the URL being generated is for the route that was matched.
*
* @param RouteResult $result
*/
public function setRouteResult(RouteResult $result)
Expand Down
25 changes: 11 additions & 14 deletions src/UrlHelperMiddleware.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@

use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Zend\Expressive\Router\RouteResultSubjectInterface;
use Zend\Expressive\Router\RouteResult;

/**
* Pipeline middleware for attaching a UrlHelper to a
* RouteResultSubjectInterface instance.
* Pipeline middleware for injecting a UrlHelper with a RouteResult.
*/
class UrlHelperMiddleware
{
Expand All @@ -22,25 +21,18 @@ class UrlHelperMiddleware
*/
private $helper;

/**
* @var RouteResultSubjectInterface
*/
private $subject;

/**
* @param UrlHelper $helper
* @param RouteResultSubjectInterface $subject
*/
public function __construct(UrlHelper $helper, RouteResultSubjectInterface $subject)
public function __construct(UrlHelper $helper)
{
$this->helper = $helper;
$this->subject = $subject;
}

/**
* Attach the UrlHelper instance as an observer to the RouteResultSubjectInterface
* Inject the UrlHelper instance with a RouteResult, if present as a request attribute.
*
* Attaches the helper, and then dispatches the next middleware.
* Injects the helper, and then dispatches the next middleware.
*
* @param ServerRequestInterface $request
* @param ResponseInterface $response
Expand All @@ -49,7 +41,12 @@ public function __construct(UrlHelper $helper, RouteResultSubjectInterface $subj
*/
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
$this->subject->attachRouteResultObserver($this->helper);
$result = $request->getAttribute(RouteResult::class, false);

if ($result instanceof RouteResult) {
$this->helper->setRouteResult($result);
}

return $next($request, $response);
}
}
44 changes: 1 addition & 43 deletions src/UrlHelperMiddlewareFactory.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@
namespace Zend\Expressive\Helper;

use Interop\Container\ContainerInterface;
use Zend\Expressive\Application;
use Zend\Expressive\Router\RouteResultSubjectInterface;

class UrlHelperMiddlewareFactory
{
Expand All @@ -20,8 +18,6 @@ class UrlHelperMiddlewareFactory
* @return UrlHelperMiddleware
* @throws Exception\MissingHelperException if the UrlHelper service is
* missing
* @throws Exception\MissingSubjectException if the
* RouteResultSubjectInterface service is missing
*/
public function __invoke(ContainerInterface $container)
{
Expand All @@ -33,44 +29,6 @@ public function __invoke(ContainerInterface $container)
));
}

$subjectService = $this->getSubjectService($container);

return new UrlHelperMiddleware(
$container->get(UrlHelper::class),
$container->get($subjectService)
);
}

/**
* Determine the name of the service returning the RouteResultSubjectInterface instance.
*
* Checks against:
*
* - RouteResultSubjectInterface
* - Application
*
* returning the first that is found in the container.
*
* If neither is found, raises an exception.
*
* @param ContainerInterface $container
* @return string
* @throws Exception\MissingSubjectException
*/
private function getSubjectService(ContainerInterface $container)
{
if ($container->has(RouteResultSubjectInterface::class)) {
return RouteResultSubjectInterface::class;
}

if ($container->has(Application::class)) {
return Application::class;
}

throw new Exception\MissingSubjectException(sprintf(
'%s requires a %s service at instantiation; none found',
UrlHelperMiddleware::class,
UrlHelper::class
));
return new UrlHelperMiddleware($container->get(UrlHelper::class));
}
}
33 changes: 1 addition & 32 deletions test/UrlHelperMiddlewareFactoryTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,10 @@
use Interop\Container\ContainerInterface;
use PHPUnit_Framework_TestCase as TestCase;
use Prophecy\Prophecy\ObjectProphecy;
use Zend\Expressive\Application;
use Zend\Expressive\Helper\Exception\MissingHelperException;
use Zend\Expressive\Helper\Exception\MissingSubjectException;
use Zend\Expressive\Helper\UrlHelper;
use Zend\Expressive\Helper\UrlHelperMiddleware;
use Zend\Expressive\Helper\UrlHelperMiddlewareFactory;
use Zend\Expressive\Router\RouteResultSubjectInterface;

class UrlHelperMiddlewareFactoryTest extends TestCase
{
Expand All @@ -34,33 +31,15 @@ public function injectContainer($name, $service)
$this->container->get($name)->willReturn($service);
}

public function testFactoryCreatesAndReturnsMiddlewareWhenHelperAndSubjectArePresentInContainer()
public function testFactoryCreatesAndReturnsMiddlewareWhenHelperIsPresentInContainer()
{
$helper = $this->prophesize(UrlHelper::class)->reveal();
$subject = $this->prophesize(RouteResultSubjectInterface::class)->reveal();
$this->injectContainer(UrlHelper::class, $helper);
$this->injectContainer(RouteResultSubjectInterface::class, $subject);

$factory = new UrlHelperMiddlewareFactory();
$middleware =$factory($this->container->reveal());
$this->assertInstanceOf(UrlHelperMiddleware::class, $middleware);
$this->assertAttributeSame($helper, 'helper', $middleware);
$this->assertAttributeSame($subject, 'subject', $middleware);
}

public function testFactoryCreatesAndReturnsMiddlewareWhenHelperAndApplicationArePresentInContainer()
{
$helper = $this->prophesize(UrlHelper::class)->reveal();
$subject = $this->prophesize(RouteResultSubjectInterface::class)->reveal();
$this->injectContainer(UrlHelper::class, $helper);
$this->container->has(RouteResultSubjectInterface::class)->willReturn(false);
$this->injectContainer(Application::class, $subject);

$factory = new UrlHelperMiddlewareFactory();
$middleware =$factory($this->container->reveal());
$this->assertInstanceOf(UrlHelperMiddleware::class, $middleware);
$this->assertAttributeSame($helper, 'helper', $middleware);
$this->assertAttributeSame($subject, 'subject', $middleware);
}

public function testFactoryRaisesExceptionWhenContainerDoesNotContainHelper()
Expand All @@ -74,14 +53,4 @@ public function testFactoryRaisesExceptionWhenContainerDoesNotContainHelper()
$this->setExpectedException(MissingHelperException::class);
$middleware =$factory($this->container->reveal());
}

public function testFactoryRaisesExceptionWhenContainerDoesNotContainSubject()
{
$this->injectContainer(UrlHelper::class, $this->prophesize(UrlHelper::class));
$this->container->has(RouteResultSubjectInterface::class)->willReturn(false);
$this->container->has(Application::class)->willReturn(false);
$factory = new UrlHelperMiddlewareFactory();
$this->setExpectedException(MissingSubjectException::class);
$middleware =$factory($this->container->reveal());
}
}
Loading

0 comments on commit 52b0f79

Please sign in to comment.