@@ -63,23 +63,20 @@ class ConfigProvider
63
63
public function getDependencies() : array
64
64
{
65
65
return [
66
- 'delegators' => [
67
- // module class name => delegators
68
- Router::class => [
69
- RoutesDelegatorFactory::class,
70
- ],
71
- ],
72
66
'factories' => [
73
- // module class name => factory
74
- LinkGenerator::class => new LinkGeneratorFactory(UrlGenerator::class),
75
- ResourceGenerator::class => new ResourceGeneratorFactory(LinkGenerator::class),
76
- Router::class => FastRouteRouterFactory::class,
77
- UrlHelper::class => new UrlHelperFactory('/api', Router::class),
78
- UrlHelperMiddleware::class => new UrlHelperMiddlewareFactory(UrlHelper::class),
79
- UrlGenerator::class => new ExpressiveUrlGeneratorFactory(UrlHelper::class),
67
+ // module-specific class name => factory
68
+ LinkGenerator::class => new LinkGeneratorFactory(UrlGenerator::class),
69
+ ResourceGenerator::class => new ResourceGeneratorFactory(LinkGenerator::class),
70
+ Router::class => FastRouteRouterFactory::class,
71
+ UrlHelper::class => new UrlHelperFactory('/api', Router::class),
72
+ UrlHelperMiddleware::class => new UrlHelperMiddlewareFactory(UrlHelper::class),
73
+ UrlGenerator::class => new ExpressiveUrlGeneratorFactory(UrlHelper::class),
80
74
81
75
// Our handler:
82
- CreateBookHandler::class => CreateBookHandlerFactory::class,
76
+ CreateBookHandler::class => CreateBookHandlerFactory::class,
77
+
78
+ // And our pipeline:
79
+ Pipeline::class => PipelineFactory::class,
83
80
],
84
81
];
85
82
}
@@ -174,105 +171,98 @@ class CreateBookHandlerFactory
174
171
}
175
172
```
176
173
177
- ## Creating path-segregated routes
174
+ You can create any number of such handlers for your module; the above
175
+ demonstrates how and where injection of the alternate resource generator occurs.
176
+
177
+ ## Creating our pipeline and routes
178
+
179
+ Now we can create our pipeline and routes.
180
+
181
+ Generally when piping to an application instance, we can specify a class name of
182
+ middleware to pipe, or an array of middleware:
183
+
184
+ ``` php
185
+ // in config/pipeline.php:
186
+ $app->pipe('/api', [
187
+ \Zend\ProblemDetails\ProblemDetailsMiddleware::class,
188
+ \Api\RouteMiddleware::class, // module-specific routing middleware!
189
+ ImplicitHeadMiddleware::class,
190
+ ImplicitOptionsMiddleware::class,
191
+ MethodNotAllowedMiddleware::class,
192
+ \Api\UrlHelperMiddleware::class, // module-specific URL helper middleware!
193
+ DispatchMiddleware::class,
194
+ \Zend\ProblemDetails\ProblemDetailsNotFoundHandler::class,
195
+ ]);
196
+ ```
197
+
198
+ However, we have both the pipeline _ and_ routes, and we likely want to indicate
199
+ the exact behavior of this pipeline. Additionally, we may want to re-use this
200
+ pipeline in other applications; pushing this into the application configuration
201
+ makes that more error-prone.
178
202
179
- Finally, we need to create a route to it. We can do that by creating a delegator
180
- factory (which we have already referenced above):
203
+ As such, we will create a factory that generates and returns a
204
+ ` Zend\Stratigility\MiddlewarePipe ` instance that is fully configured for our
205
+ module. As part of this functionality, we will also add our module-specific
206
+ routing.
181
207
182
208
``` php
183
- // In src/Api/RoutesDelegatorFactory .php:
209
+ // In src/Api/PipelineFactory .php:
184
210
namespace Api;
185
211
186
212
use Psr\Container\ContainerInterface;
187
213
use Zend\Expressive\MiddlewareFactory;
188
- use Zend\Expressive\Router\RouteCollector;
189
- use Zend\Expressive\Router\RouterInterface;
190
-
191
- /**
192
- * Add routes to the router.
193
- *
194
- * This delegator decorates creation of the router, and is used to
195
- * inject routes into it via a `RouteCollector` instance, using a combination of
196
- * the HTTP method name as the instance method, a path, a middleware/handler, and
197
- * optionally a name.
198
- *
199
- * You will need to use the MiddlewareFactory to prepare your middleware,
200
- * as the `RouteCollector` expects valid middleware instances.
201
- */
202
- class RoutesDelegatorFactory
214
+ use Zend\Expressive\Router\Middleware as RouterMiddleware;
215
+ use Zend\ProblemDetails\ProblemDetailsMiddleware;
216
+ use Zend\ProblemDetails\ProblemDetailsNotFoundHandler;
217
+ use Zend\Stratigility\MiddlewarePipe;
218
+
219
+ class PipelineFactory
203
220
{
204
- public function __invoke(ContainerInterface $container, string $serviceName, callable $routerFactory ) : RouterInterface
221
+ public function __invoke(ContainerInterface $container) : MiddlewarePipe
205
222
{
206
- $router = $routerFactory();
207
- $routes = new RouteCollector($router);
208
223
$factory = $container->get(MiddlewareFactory::class);
209
224
210
- // Add routing here:
225
+ // First, create our middleware pipeline
226
+ $pipeline = new MiddlewarePipe();
227
+ $pipeline->pipe($factory->lazy(ProblemDetailsMiddleware::class));
228
+ $pipeline->pipe($factory->lazy(RouteMiddleware::class)); // module-specific!
229
+ $pipeline->pipe($factory->lazy(RouterMiddleware\ImplicitHeadMiddleware::class));
230
+ $pipeline->pipe($factory->lazy(RouterMiddleware\ImplicitOptionsMiddleware::class));
231
+ $pipeline->pipe($factory->lazy(RouterMiddleware\MethodNotAllowedMiddleware::class));
232
+ $pipeline->pipe($factory->lazy(UrlHelperMiddlweare::class)); // module-specific!
233
+ $pipeline->pipe($factory->lazy(RouterMiddleware\DispatchMiddleware::class));
234
+ $pipeline->pipe($factory->lazy(ProblemDetailsNotFoundHandler::class));
235
+
236
+ // Second, we'll create our routes
237
+ $router = $container->get(Router::class); // Retrieve our module-specific router
238
+ $routes = new RouteCollector($router); // Create a route collector to simplify routing
239
+
240
+ // Start routing:
211
241
$routes->post('/books', $factory->lazy(CreateBookHandler::class));
212
-
213
- // Return the router at the end !
214
- return $router ;
242
+
243
+ // Return the pipeline now that we're done !
244
+ return $pipeline ;
215
245
}
216
246
}
217
247
```
218
248
219
- Note that the routing does ** not** include the string ` /api ` ; this is because
220
- that string will be stripped when we path-segregate our API middleware pipeline.
221
- All routing will be _ relative_ to that path.
249
+ Note that the routing definitions do ** not** include the prefix ` /api ` ; this is
250
+ because that prefix will be stripped when we path-segregate our API middleware
251
+ pipeline. All routing will be _ relative_ to that path.
222
252
223
253
## Creating a path-segregated pipeline
224
254
225
- Finally, we will create our path-segregated middleware pipeline :
255
+ Finally, we will attach our pipeline to the application, using path segregation :
226
256
227
257
``` php
228
258
// in config/pipeline.php:
229
- $app->pipe('/api', [
230
- \Zend\ProblemDetails\ProblemDetailsMiddleware::class,
231
- \Api\RouteMiddleware::class, // module-specific routing middleware!
232
- ImplicitHeadMiddleware::class,
233
- ImplicitOptionsMiddleware::class,
234
- MethodNotAllowedMiddleware::class,
235
- \Api\UrlHelperMiddleware::class, // module-specific URL helper middleware!
236
- DispatchMiddleware::class,
237
- \Zend\ProblemDetails\ProblemDetailsNotFoundHandler::class,
238
- ]);
259
+ $app->pipe('/api', \Api\Pipeline::class);
239
260
```
240
261
241
- > You might want to create the above as a middleware pipeline _ service_ via a
242
- > factory:
243
- >
244
- > ``` php
245
- > namespace Api;
246
- >
247
- > use Psr\Container\ContainerInterface;
248
- > use Zend\Expressive\MiddlewareFactory;
249
- > use Zend\Expressive\Router\Middleware as RouterMiddleware;
250
- > use Zend\ProblemDetails\ProblemDetailsMiddleware;
251
- > use Zend\ProblemDetails\ProblemDetailsNotFoundHandler;
252
- > use Zend\Stratigility\MiddlewarePipe;
253
- >
254
- > class PipelineFactory
255
- > {
256
- > public function __invoke(ContainerInterface $container) : MiddlewarePipe
257
- > {
258
- > $factory = $container->get(MiddlewareFactory::class);
259
- > $pipeline = new MiddlewarePipe();
260
- > $pipeline->pipe($factory->lazy(ProblemDetailsMiddleware::class));
261
- > $pipeline->pipe($factory->lazy(RouteMiddleware::class)); // module-specific!
262
- > $pipeline->pipe($factory->lazy(RouterMiddleware\ImplicitHeadMiddleware::class));
263
- > $pipeline->pipe($factory->lazy(RouterMiddleware\ImplicitOptionsMiddleware::class));
264
- > $pipeline->pipe($factory->lazy(RouterMiddleware\MethodNotAllowedMiddleware::class));
265
- > $pipeline->pipe($factory->lazy(UrlHelperMiddlweare::class)); // module-specific!
266
- > $pipeline->pipe($factory->lazy(RouterMiddleware\DispatchMiddleware::class));
267
- > $pipeline->pipe($factory->lazy(ProblemDetailsNotFoundHandler::class));
268
- > return $pipeline;
269
- > }
270
- > }
271
- > ```
272
- >
273
- > Such an approach keeps the pipeline definition in the module, which allows you
274
- > to better re-use it later.
262
+ This statement tells the application to pipe the pipeline returned by our
263
+ ` PipelineFactory ` under the path ` /api ` ; that path will be stripped from
264
+ requests when passed to the underlying middleware.
275
265
276
- The above approach will allow you to create a custom pipeline that can be
277
- dropped into an existing application, and allows defining per-module routing and
278
- dispatch relative to a given path.
266
+ At this point, we now have a re-usable module, complete with its own routing,
267
+ with URI generation that will include the base path under which we have
268
+ segregated the pipeline!
0 commit comments