Skip to content

Commit 7c12571

Browse files
committed
Fix #25 Inject request attributes
Currently route placeholders are injected in controllers. This was wrongly called "request attribute" in the documentation. This adds the ability to inject (actual) request attributes in controller parameters too. Route placeholders take priority over request attributes. Example: ```php $app->add(function ($request, $response, $next) { $request = $request->withAttribute('name', 'Bob'); return $next($request, $response); }); $app->get('/', function ($name, ResponseInterface $response) { $response->getBody()->write('Hello ' . $name); return $response; }); ``` Will show `Hello Bob`.
1 parent 434ad3f commit 7c12571

File tree

3 files changed

+65
-7
lines changed

3 files changed

+65
-7
lines changed

README.md

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,10 @@ By default, Slim controllers have a strict signature: `$request, $response, $arg
5454

5555
Controller parameters can be any of these things:
5656

57-
- request or response injection (parameters must be named `$request` or `$response`)
58-
- request attribute injection
59-
- service injection (by type-hint)
57+
- the request or response (parameters must be named `$request` or `$response`)
58+
- route placeholders
59+
- request attributes
60+
- services (injected by type-hint)
6061

6162
You can mix all these types of parameters together too. They will be matched by priority in the order of the list above.
6263

@@ -72,7 +73,7 @@ $app->get('/', function (ResponseInterface $response, ServerRequestInterface $re
7273

7374
As you can see, the order of the parameters doesn't matter. That allows to skip injecting the `$request` if it's not needed for example.
7475

75-
#### Request attribute injection
76+
#### Route placeholder injection
7677

7778
```php
7879
$app->get('/hello/{name}', function ($name, ResponseInterface $response) {
@@ -83,6 +84,22 @@ $app->get('/hello/{name}', function ($name, ResponseInterface $response) {
8384

8485
As you can see above, the route's URL contains a `name` placeholder. By simply adding a parameter **with the same name** to the controller, PHP-DI will directly inject it.
8586

87+
#### Request attribute injection
88+
89+
```php
90+
$app->add(function ($request, $response, $next) {
91+
$request = $request->withAttribute('name', 'Bob');
92+
return $next($request, $response);
93+
});
94+
95+
$app->get('/', function ($name, ResponseInterface $response) {
96+
$response->getBody()->write('Hello ' . $name);
97+
return $response;
98+
});
99+
```
100+
101+
As you can see above, a middleware sets a `name` attribute. By simply adding a parameter **with the same name** to the controller, PHP-DI will directly inject it.
102+
86103
#### Service injection
87104

88105
To inject services into your controllers, you can write them as classes. But if you want to write a micro-application using closures, you don't have to give up dependency injection either.

src/ControllerInvoker.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,9 @@ public function __invoke(
4444
// Inject the route arguments by name
4545
$parameters += $routeArguments;
4646

47+
// Inject the attributes defined on the request
48+
$parameters += $request->getAttributes();
49+
4750
return $this->invoker->call($callable, $parameters);
4851
}
4952
}

tests/RoutingTest.php

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ public function injects_request_and_response()
3030
/**
3131
* @test
3232
*/
33-
public function injects_request_path_parameters()
33+
public function injects_route_placeholder()
3434
{
3535
$app = new App;
3636
$app->get('/{name}', function ($name, $response) {
@@ -45,7 +45,7 @@ public function injects_request_path_parameters()
4545
/**
4646
* @test
4747
*/
48-
public function injects_optional_path_parameter()
48+
public function injects_optional_route_placeholder()
4949
{
5050
$app = new App;
5151
$app->get('/[{name}]', function ($response, $name = null) {
@@ -60,7 +60,7 @@ public function injects_optional_path_parameter()
6060
/**
6161
* @test
6262
*/
63-
public function injects_default_value_in_optional_path_parameter()
63+
public function injects_default_value_in_optional_route_placeholder()
6464
{
6565
$app = new App;
6666
$app->get('/[{name}]', function ($response, $name = 'john doe') {
@@ -72,6 +72,44 @@ public function injects_default_value_in_optional_path_parameter()
7272
$this->assertEquals('Hello john doe', (string) $response->getBody());
7373
}
7474

75+
/**
76+
* @test
77+
*/
78+
public function injects_request_attribute()
79+
{
80+
$app = new App;
81+
// Let's add a middleware that adds a request attribute
82+
$app->add(function (ServerRequestInterface $request, $response, $next) {
83+
return $next($request->withAttribute('name', 'Bob'), $response);
84+
});
85+
$app->get('/', function ($name, $response) {
86+
$response->getBody()->write('Hello ' . $name);
87+
return $response;
88+
});
89+
90+
$response = $app->callMiddlewareStack(RequestFactory::create('/'), new Response);
91+
$this->assertEquals('Hello Bob', $response->getBody()->__toString());
92+
}
93+
94+
/**
95+
* @test
96+
*/
97+
public function injects_route_placeholder_over_request_attribute()
98+
{
99+
$app = new App;
100+
$app->add(function (ServerRequestInterface $request, $response, $next) {
101+
return $next($request->withAttribute('name', 'Bob'), $response);
102+
});
103+
$app->get('/{name}', function ($name, $response) {
104+
$response->getBody()->write('Hello ' . $name);
105+
return $response;
106+
});
107+
108+
$response = $app->callMiddlewareStack(RequestFactory::create('/matt'), new Response);
109+
// The route placeholder has priority over the request attribute
110+
$this->assertEquals('Hello matt', (string) $response->getBody());
111+
}
112+
75113
/**
76114
* @test
77115
*/

0 commit comments

Comments
 (0)