Skip to content
This repository was archived by the owner on Jan 29, 2020. It is now read-only.

Commit fa05240

Browse files
committed
Merge branch 'feature/versioned-documentation'
Close #575
2 parents 4abd9d3 + a334dff commit fa05240

File tree

154 files changed

+19226
-12109
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

154 files changed

+19226
-12109
lines changed
Lines changed: 6 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -1,210 +1,6 @@
1-
# How can I autowire routes and pipelines?
2-
3-
Expressive 2.0 switches to _programmatic_ pipelines and routes, versus
4-
_configuration-driven_ pipelines and routing. One drawback is that with
5-
configuration-driven approaches, users could provide configuration via a module
6-
`ConfigProvider`, and automatically expose new pipeline middleware or routes;
7-
with a programmatic approach, this is no longer possible.
8-
9-
Or is it?
10-
11-
## Delegator Factories
12-
13-
One possibility, available since the Expressive 2.X skeleton application, is to
14-
use _delegator factories_ on the `Zend\Expressive\Application` instance in order
15-
to inject these items.
16-
17-
A _delegator factory_ is a factory that _delegates_ creation of an instance to a
18-
callback, and then operates on that instance for the purpose of altering the
19-
instance or providing a replacement (e.g., a decorator or proxy). The delegate
20-
callback usually wraps a service factory, or, because delegator factories
21-
_also_ return an instance, additional delegator factories. As such, you assign
22-
delegator _factories_, plural, to instances, allowing multiple delegator
23-
factories to intercept processing of the service initialization.
24-
25-
For the purposes of this particular example, we will use delegator factories to
26-
both _pipe_ middleware as well as _route_ middleware.
27-
28-
To demonstrate, we'll take the default pipeline and routing from the skeleton
29-
application, and provide it via a delegator factory instead.
30-
31-
First, we'll create the class `App\Factory\PipelineAndRoutesDelegator`, with
32-
the following contents:
33-
34-
```php
35-
<?php
36-
37-
namespace App\Factory;
38-
39-
use App\Action;
40-
use Psr\Container\ContainerInterface;
41-
use Zend\Expressive\Application;
42-
use Zend\Expressive\Helper\ServerUrlMiddleware;
43-
use Zend\Expressive\Helper\UrlHelperMiddleware;
44-
use Zend\Expressive\Middleware\ImplicitHeadMiddleware;
45-
use Zend\Expressive\Middleware\ImplicitOptionsMiddleware;
46-
use Zend\Expressive\Middleware\NotFoundHandler;
47-
use Zend\Stratigility\Middleware\ErrorHandler;
48-
49-
class PipelineAndRoutesDelegator
50-
{
51-
/**
52-
* @param ContainerInterface $container
53-
* @param string $serviceName Name of the service being created.
54-
* @param callable $callback Creates and returns the service.
55-
* @return Application
56-
*/
57-
public function __invoke(ContainerInterface $container, $serviceName, callable $callback)
58-
{
59-
/** @var $app Application */
60-
$app = $callback();
61-
62-
// Setup pipeline:
63-
$app->pipe(ErrorHandler::class);
64-
$app->pipe(ServerUrlMiddleware::class);
65-
$app->pipeRoutingMiddleware();
66-
$app->pipe(ImplicitHeadMiddleware::class);
67-
$app->pipe(ImplicitOptionsMiddleware::class);
68-
$app->pipe(UrlHelperMiddleware::class);
69-
$app->pipeDispatchMiddleware();
70-
$app->pipe(NotFoundHandler::class);
71-
72-
// Setup routes:
73-
$app->get('/', Action\HomePageAction::class, 'home');
74-
$app->get('/api/ping', Action\PingAction::class, 'api.ping');
75-
76-
return $app;
77-
}
78-
}
79-
```
80-
81-
> ### Where to put the factory
82-
>
83-
> You will place the factory class in one of the following locations:
84-
>
85-
> - `src/App/Factory/PipelineAndRoutesDelegator.php` if using the default, flat,
86-
> application structure.
87-
> - `src/App/src/Factory/PipelineAndRoutesDelegator.php` if using the
88-
> recommended, modular, application structure.
89-
90-
Once you've created this, edit the class `App\ConfigProvider`; in it, we'll
91-
update the `getDependencies()` method to add the delegator factory:
92-
93-
```php
94-
public function getDependencies()
95-
{
96-
return [
97-
/* . . . */
98-
'delegators' => [
99-
\Zend\Expressive\Application::class => [
100-
Factory\PipelineAndRoutesDelegator::class,
101-
],
102-
],
103-
];
104-
}
105-
```
106-
107-
> ### Where is the ConfigProvider class?
108-
>
109-
> The `ConfigProvider` class is in one of the following locations:
110-
>
111-
> - `src/App/ConfigProvider.php` if using the default, flat, application
112-
> structure.
113-
> - `src/App/src/ConfigProvider.php` using the recommended, modular, application
114-
> structure.
115-
116-
> ### Why is an array assigned?
117-
>
118-
> As noted above in the description of delegator factories, since each delegator
119-
> factory returns an instance, you can nest multiple delegator factories in
120-
> order to shape initialization of a service. As such, they are assigned as an
121-
> _array_ to the service.
122-
123-
Once you've done this, you can remove:
124-
125-
- `config/pipeline.php`
126-
- `config/routes.php`
127-
- The following lines from `public/index.php`:
128-
129-
```php
130-
// Import programmatic/declarative middleware pipeline and routing
131-
// configuration statements
132-
require 'config/pipeline.php';
133-
require 'config/routes.php';
134-
```
135-
136-
If you reload your application at this point, you should see that everything
137-
continues to work as expected!
138-
139-
## Caution: pipelines
140-
141-
Using delegator factories is a nice way to keep your routing and pipeline
142-
configuration close to the modules in which they are defined. However, there is
143-
a caveat: you likely should **not** register pipeline middleware in a delegator
144-
factory _other than within your root application module_.
145-
146-
The reason for this is simple: pipelines are linear, and specific to your
147-
application. If one module pipes in middleware, there's no guarantee it will be
148-
piped before or after your main pipeline, and no way to pipe the middleware at a
149-
position in the middle of the pipeline!
150-
151-
As such:
152-
153-
- Use a `config/pipeline.php` file for your pipeline, **OR**
154-
- Ensure you only define the pipeline in a **single** delegator factory on your
155-
`Application` instance.
156-
157-
## Caution: third-party, distributed modules
158-
159-
If you are developing a module to distribute as a package via
160-
[Composer](https://getcomposer.org/), **you should not autowire any delegator
161-
factories that inject pipeline middleware or routes in the `Application`**.
162-
163-
Why?
164-
165-
As noted in the above section, pipelines should be created exactly once, at
166-
the application level. Registering pipeline middleware within a distributable
167-
package will very likely not have the intended consequences.
168-
169-
If you ship with pipeline middleware, we suggest that you:
170-
171-
- Document the middleware, and where you anticipate it being used in the
172-
middleware pipeline.
173-
- Document how to add the middleware service to dependency configuration, or
174-
provide the dependency configuration via your module's `ConfigProvider`.
175-
176-
With regards to routes, there are other considerations:
177-
178-
- Routes defined by the package might conflict with the application, or with
179-
other packages used by the application.
180-
181-
- Routing definitions are typically highly specific to the router implementation
182-
in use. As an example, each of the currently supported router implementations
183-
has a different syntax for placeholders:
184-
185-
- `/user/:id` + "constraints" configuration to define constraints (zend-router)
186-
- `/user/{id}` + "tokens" configuration to define constraints (Aura.Router)
187-
- `/user/{id:\d+}` (FastRoute)
188-
189-
- Your application may have specific routing considerations or design.
190-
191-
You could, of course, detect what router is in use, and provide routing for each
192-
known, supported router implementation within your delegator factory. We even
193-
recommend doing exactly that. However, we note that such an approach does not
194-
solve the other two points above.
195-
196-
However, we still recommend _shipping_ a delegator factory that would register
197-
your routes, since routes *are* often a part of module design; just **do not
198-
autowire** that delegator factory. This way, end-users who *can* use the
199-
defaults do not need to cut-and-paste routing definitions from your
200-
documentation into their own applications; they will instead opt-in to your
201-
delegator factory by wiring it into their own configuration.
202-
203-
## Synopsis
204-
205-
- We recommend using delegator factories for the purpose of autowiring routes,
206-
and, with caveats, pipeline middleware:
207-
- The pipeline should be created exactly once, so calls to `pipe()` should
208-
occur in exactly _one_ delegator factory.
209-
- Distributable packages should create a delegator factory for _routes only_,
210-
but _should not_ register the delegator factory by default.
1+
<noscript><meta http-equiv="refresh" content="0; url=v2/cookbook/autowiring-routes-and-pipelines/"></noscript>
2+
<script>
3+
document.addEventListener("DOMContentLoaded", function (event) {
4+
window.location.pathname = 'v2/cookbook/autowiring-routes-and-pipelines/';
5+
});
6+
</script>
Lines changed: 8 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -1,45 +1,8 @@
1-
# How can I prepend a common path to all my routes?
2-
3-
You may have multiple middlewares providing their own functionality:
4-
5-
```php
6-
$middleware1 = new UserMiddleware();
7-
$middleware2 = new ProjectMiddleware();
8-
9-
$app = AppFactory::create();
10-
$app->pipe($middleware1);
11-
$app->pipe($middleware2);
12-
13-
$app->run();
14-
```
15-
16-
Let's assume the above represents an API.
17-
18-
As your application progresses, you may have a mixture of different content, and now want to have
19-
the above segregated under the path `/api`.
20-
21-
This is essentially the same problem as addressed in the
22-
["Segregating your application to a subpath"](../reference/usage-examples.md#segregating-your-application-to-a-subpath) example.
23-
24-
To accomplish it:
25-
26-
- Create a new application.
27-
- Pipe the previous application to the new one, under the path `/api`.
28-
29-
```php
30-
$middleware1 = new UserMiddleware();
31-
$middleware2 = new ProjectMiddleware();
32-
33-
$api = AppFactory::create();
34-
$api->pipe($middleware1);
35-
$api->pipe($middleware2);
36-
37-
$app = AppFactory::create();
38-
$app->pipe('/api', $api);
39-
40-
$app->run();
41-
```
42-
43-
The above works, because every `Application` instance is itself middleware, and, more specifically,
44-
an instance of [Stratigility's `MiddlewarePipe`](https://github.com/zendframework/zend-stratigility/blob/master/doc/book/middleware.md),
45-
which provides the ability to compose middleware.
1+
<noscript><meta http-equiv="refresh" content="0; url=v2/cookbook/common-prefix-for-routes/"></noscript>
2+
<script>
3+
document.addEventListener("DOMContentLoaded", function (event) {
4+
var uri = new URL(window.location.href);
5+
uri.pathname = 'v2/cookbook/common-prefix-for-routes/';
6+
window.location = uri.href;
7+
});
8+
</script>

0 commit comments

Comments
 (0)