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

Commit 8ba871f

Browse files
committed
Versions documentation
This patch versions the documentation. V1 docs were imported from the v1.1.1 tag, and pushed under a v1 subdirectory of `docs/book/`. A sibling `v2/` subdirectory was also created, and the existing v2 docs were moved under that directory. The v1-specific migration documentation was removed from the v2 tree, leaving us with a single migration document for that tree. v2 documentation was scrubbed of references to "version 1" and/or "since v2"; users can go to the v1 documentation for that information. All existing pages were updated to render as redirects, using a combination of `<noscript>` tags and JS redirects. The mkdocs configuration was updated: - to rewrite all existing mappings to the v2 subdirectory - to add links for the v1 docs - to add hidden mappings for the existing redirect pages I've tested builds locally, and all works as expected. Existing URIs redirect to the correct v2 variants, and the navigation now uses v2 links.
1 parent 4abd9d3 commit 8ba871f

File tree

154 files changed

+19332
-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

+19332
-12109
lines changed
Lines changed: 8 additions & 210 deletions
Original file line numberDiff line numberDiff line change
@@ -1,210 +1,8 @@
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+
var uri = new URL(window.location.href);
5+
uri.pathname = 'v2/cookbook/autowiring-routes-and-pipelines/';
6+
window.location = uri.href;
7+
});
8+
</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)