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

Commit 37a3618

Browse files
committed
Merge branch 'hotfix/239'
Close #239
2 parents 6be6281 + 08d5228 commit 37a3618

File tree

4 files changed

+162
-2
lines changed

4 files changed

+162
-2
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ Fifth release candidate.
1414
- [#238](https://github.com/zendframework/zend-expressive/pull/238) adds a
1515
cookbook recipe detailing how to handle serving an Expressive application from
1616
a subdirectory of your web root.
17+
- [#239](https://github.com/zendframework/zend-expressive/pull/239) adds a
18+
cookbook recipe detailing how to create modular Expressive applications.
1719

1820
### Deprecated
1921

doc/book/cookbook/bookdown.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
{"Setting custom 404 page handling": "custom-404-page-handling.md"},
77
{"Registering custom view helpers when using zend-view": "using-custom-view-helpers.md"},
88
{"Using zend-form view helpers": "using-zend-form-view-helpers.md"},
9-
{"Using Expressive from a subdirectory": "using-a-base-path.md"}
9+
{"Using Expressive from a subdirectory": "using-a-base-path.md"},
10+
{"Building modular applications": "modular-layout.md"}
1011
]
1112
}
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
# How can I make my application modular?
2+
3+
Zend Framework 2 applications have a concept of modules, independent units that
4+
can provide configuration, services, and hooks into its MVC lifecycle. This
5+
functionality is provided by zend-modulemanager.
6+
7+
While zend-modulemanager could be used with Expressive, we suggest another
8+
approach: modules that are based only on configuration. This powerful approach
9+
doesn't affect performance, and offers extensive flexibility: each module can
10+
provide its own services (with factories), default configuration, and routes.
11+
12+
This cookbook will show how to organize modules using
13+
[mtymek/expressive-config-manager](https://github.com/mtymek/expressive-config-manager),
14+
a lightweight library that aggregates and merges configuration, optionally caching it.
15+
16+
## Install the configuration manager
17+
18+
The configuration manager is available in Packagist:
19+
20+
```bash
21+
$ composer require mtymek/expressive-config-manager
22+
```
23+
24+
## Generate your config
25+
26+
The default Expressive skeleton installs a `config/config.php` file, which
27+
aggregates all configuration. When using the configuration manager, you will
28+
need to replace the contents of that file with the following code:
29+
30+
```php
31+
<?php
32+
33+
use Zend\Expressive\ConfigManager\ConfigManager;
34+
use Zend\Expressive\ConfigManager\PhpFileProvider;
35+
36+
$configManager = new ConfigManager([
37+
new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
38+
]);
39+
40+
return new ArrayObject($configManager->getMergedConfig());
41+
```
42+
43+
If you open your application in a browser, it should still work in exactly the
44+
same way as it was before. Now you can start adding your modules.
45+
46+
## First module
47+
48+
`ConfigManager` does not force you to use any particular structure for your
49+
module; its only requirement is to expose default configuration using a "config
50+
provider", which is simply an invokable class that returns a configuration
51+
array.
52+
53+
For instance, this is how your module could provide its own routes:
54+
55+
```php
56+
namespace App;
57+
58+
class AppConfig
59+
{
60+
public function __invoke()
61+
{
62+
return [
63+
'routes' => [
64+
[
65+
'name' => 'api.list-transactions',
66+
'path' => '/api/transactions',
67+
'middleware' => App\Action\ListTransactionsAction::class,
68+
'allowed_methods' => ['GET'],
69+
],
70+
[
71+
'name' => 'api.refund-transaction',
72+
'path' => '/api/refund',
73+
'middleware' => App\Action\RefundAction::class,
74+
'allowed_methods' => ['POST'],
75+
],
76+
],
77+
];
78+
}
79+
}
80+
```
81+
82+
## Enabling the module
83+
84+
Finally, you can enable your module by adding a reference to your config class
85+
within the arguments of the `ConfigManager` constructor in the `config/config.php`
86+
file:
87+
88+
```php
89+
$configManager = new ConfigManager([
90+
App\AppConfig::class,
91+
new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
92+
]);
93+
```
94+
95+
## Caching configuration
96+
97+
In order to provide configuration caching, two things must occur:
98+
99+
- First, you must define a `config_cache_enabled` key in your configuration
100+
somewhere.
101+
- Second, you must pass a second argument to the `ConfigManager`, the location
102+
of the cache file to use.
103+
104+
The `config_cache_enabled` key can be defined in any of your configuration
105+
providers, including the autoloaded configuration files. We recommend defining
106+
them in two locations:
107+
108+
- `config/autoload/global.php` should define the value to `true`, as the
109+
production setting.
110+
- `config/autoload/local.php` should also define the setting, and use a value
111+
appropriate to the current environment. In development, for instance, this
112+
would be `false`.
113+
114+
```php
115+
// config/autoload/global.php
116+
117+
return [
118+
'config_cache_enabled' => true,
119+
/* ... */
120+
];
121+
122+
// config/autoload/local.php
123+
124+
return [
125+
'config_cache_enabled' => false, // <- development!
126+
/* ... */
127+
];
128+
```
129+
130+
You would then alter your `config/config.php` file to add the second argument.
131+
The following example builds on the previous, and demonstrates having the
132+
`AppConfig` entry enabled. The configuration will be cached to
133+
`data/config-cache.php` in the application root:
134+
135+
```php
136+
$configManager = new ConfigManager([
137+
App\AppConfig::class,
138+
new PhpFileProvider('config/autoload/{{,*.}global,{,*.}local}.php'),
139+
], 'data/config-cache.php');
140+
```
141+
142+
When the configuration cache path is present, if the `config_cache_enabled` flag
143+
is enabled, then configuration will be read from the cached configuration,
144+
instead of parsing and merging the various configuration sources.
145+
146+
## Final notes
147+
148+
This approach may look simple, but it is flexible and powerful:
149+
150+
- You pass a list of config providers to the `ConfigManager` constructor.
151+
- Configuration is merged in the same order as it is passed, with later entries
152+
having precedence.
153+
- You can override module configuration using `*.global.php` and `*.local.php` files.
154+
- If cached config is found, `ConfigManager` does not iterate over provider list.
155+
156+
For more details, please refer to the
157+
[Config Manager Documentation](https://github.com/mtymek/expressive-config-manager#expressive-configuration-manager).

mkdocs.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pages:
1313
- { Helpers: [{ Introduction: helpers/intro.md }, { UrlHelper: helpers/url-helper.md }, { ServerUrlHelper: helpers/server-url-helper.md }] }
1414
- { Emitters: emitters.md }
1515
- { Examples: usage-examples.md }
16-
- { Cookbook: [{ 'Prepending a common path to all routes': cookbook/common-prefix-for-routes.md }, { 'Route-specific middleware pipelines': cookbook/route-specific-pipeline.md }, { 'Setting custom 404 page handling': cookbook/custom-404-page-handling.md }, { 'Registering custom view helpers when using zend-view': cookbook/using-custom-view-helpers.md }, { 'Using zend-form view helpers': cookbook/using-zend-form-view-helpers.md }, { 'Using Expressive from a subdirectory': cookbook/using-a-base-path.md }] }
16+
- { Cookbook: [{ 'Prepending a common path to all routes': cookbook/common-prefix-for-routes.md }, { 'Route-specific middleware pipelines': cookbook/route-specific-pipeline.md }, { 'Setting custom 404 page handling': cookbook/custom-404-page-handling.md }, { 'Registering custom view helpers when using zend-view': cookbook/using-custom-view-helpers.md }, { 'Using zend-form view helpers': cookbook/using-zend-form-view-helpers.md }, { 'Using Expressive from a subdirectory': cookbook/using-a-base-path.md }, { 'Building modular applications': cookbook/modular-layout.md }] }
1717
- { 'Expressive Projects': expressive-projects.md }
1818
site_name: zend-expressive
1919
site_description: 'zend-expressive: PSR-7 Middleware Microframework'

0 commit comments

Comments
 (0)