I am creating a controller class and trying to add a method on my controller to load a object based on its route ID. I've got that all written and ready to work EXCEPT I don't know how to grab the route parameters from the request. There's also no documented way for middleware to access those parameters.
$app->group('/users', function () {
$controller = new MyController();
$this->get('/', [$controller, 'index']);
$this->get('/{id}', [$controller, 'show'])->add($controller);
});
class MyController
{
// Fulfills invoke status for Middleware, see http://www.slimframework.com/docs/concepts/middleware.html#invokable-class-middleware-example
public function __invoke(ServerRequestInterface $request, ResponseInterface $response, callable $next)
{
// how do I get $routeParams???
$userId = $routeParams['id'];
$user = $userMapper->findById($userId);
if ($user) {
$request = $request->withAttribute('user', $user);
return $next($request, $response);
} else {
// setup $response to contain a 404 response, don't call $next
return $response;
}
}
public function show(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$user = $request->getAttribute('user'); // already loaded by middleware
return $response->getBody()->write(json_encode($user)); // varies, but for example
}
I think I've figured out a solution to my problem; however, I still would like to know how a middleware would access router parameters or what people think on that being poor middleware design
For now I'm just doing the __call() magic method on my controller and making my show action private. Something like:
$app->group('/users', function () {
$controller = new MyController();
$this->get('/', [$controller, 'index']);
$this->get('/{id}', [$controller, 'show']);
});
class MyController
{
public function __call($name, array $arguments)
{
list($request, $response, $args) = $arguments;
$user = $userMapper->findById($args['id']);
if ($user) {
$request = $request->withAttribute('user', $user);
return call_user_func([$this, $name], $request, $response, $args);
} else {
// $response now contains 404 response, don't call method
return $response;
}
}
private function show(ServerRequestInterface $request, ResponseInterface $response, array $args)
{
$user = $request->getAttribute('user'); // already loaded by middleware
return $response->getBody()->write(json_encode($user)); // varies, but for example
}
}
You'll find the route parameters in the third element of the 'routeInfo' attribute. We'd love a PR documenting this :)
$routeParams = $request->getAttribute('routeInfo')[2];
As a side note, this option does not work for application middleware
Agree with @bradynpoulsen that it doesn't work for middleware. Would also be able to access route arguments from within middleware
By default, the route hasn't been worked out when app middleware is running, which is why it's not working for you.
If you need this, then you can enable this setting:
$settings = [
'settings' => [
'determineRouteBeforeAppMiddleware' => true,
],
];
$app = new Slim\App($settings);
@akrabat - this does solve the problem. Thanks!
This works @kevinmlong:
$settings = [
'settings' => [
'determineRouteBeforeAppMiddleware' => true,
],
];
$app = new \Slim\App($settings);
$app->get("/{name}", function ($request, $response, $args) {
return $response->write("Hello {$args['name']}");
});
$app->add(function ($request, $response, $next) {
var_dump($request->getAttribute('routeInfo')[2]);
return $next($request, $response);
});
Navigating to http://develop-testbed.slim3.dev/rob gives me:
/www/dev/slim3/develop-testbed/public/index-scratch.php:23:
array (size=1)
'name' => string 'rob' (length=3)
Hello rob
@akrabat - I sent the other comment to quickly. I suppose I deleted it after you saw it. Thanks!
A related issue ....
In my middleware, I am calling
$next($request,$response);
-- doing stuff --
As it goes through the routing methods, it returns a response. In some cases it changes the status to a different code (e.g. 400) like below:
$newResponse = $this->response->withStatus(404)
->withHeader('Content-Type', 'application/json')
->write(...stuff...);
return $newResponse;
when the application returns to the middleware, the response object has a code of 200. When returned, it has the right body, but the wrong status. Thoughts?
@kevinmlong $this->response is probably stale. Where is that coming from?
It's in the route method
$app->get('path to route', function ($request, $response, $args) {
do some stuff
$newResponse = $this->response->withStatus(404)
->withHeader('Content-Type', 'application/json')
->write(...stuff...);
return $newResponse;
}
The code then returns to the middleware. The code response here is 200 instead of 404. If I return the response, I get the right body from the write(..stuff..) but the code is not 404.
@kevinmlong you need to use $response instead of $this->response. $this->response is a stale response object while $response is the current one from this request. You can even do exactly what @akrabat did in his example.
$app->get('path to route', function ($request, $response, $args) {
return $response->withStatus(404)
->withHeader('Content-Type', 'application/json')
->write(...stuff...);
});
@silentworks - that doesn't work. it still returns to the middleware with a 200 code.
@silentworks - the response object is getting updated, since the body is being updated correctly.
@silentworks - I figured it out. Need to change the middleware to:
public function __invoke($request, $response, $next){
// do some stuff
$newResponse = $next($request,$response);
// do more stuff
return $newResponse;
}
Add following line in configuration
$config['determineRouteBeforeAppMiddleware'] = true;
In middleware
$route = $request->getAttribute('route');
$arguments = $route->getArguments();
print_r($arguments);
Most helpful comment
Add following line in configuration
$config['determineRouteBeforeAppMiddleware'] = true;
In middleware
$route = $request->getAttribute('route');
$arguments = $route->getArguments();
print_r($arguments);