Route specific configuration in Slim
A friend emailed me recently asking about route specific configuration in Slim. He wants to be able to set properties when creating the route that he can pick up when the route is matched. The way to do this is using route arguments. I've written about route arguments before in the context of setting default values for a route parameter, but you can also use them to set new data for use later.
In this post, I'm going to look at how to use them for configuration data, such as setting required ACL permissions for routes.
Setting per-route data
To recap, we set an argument on a route using setArgument() and like this:
$app->get('/widgets/{id}', HelloAction::class) ->setArgument('permission', 'canReadWidgets');
We have added a new argument, permission, to the route's arguments and given it a value of canReadWidgets. As the name "permission" is not in the path arguments, it cannot be overridden.
Access the per-route configuration data
We want to access the value of the permission in middleware, so let's use group middleware:
$app->group('', function ($app) { $app->get('/widgets/{id}', HelloAction::class) ->setArgument('permission', 'canReadWidgets'); // other routes that need an ACL check here })->setMiddleware(AclCheckMiddleware::class);
We can now use getArgument() to retrieve the permission and see if the user is allowed to continue.
$route = $request->getAttribute('route'); $permission = $route->getArgument('permission', 'canDoNothing');
That is, we retrieve the Route object from the Request and then grab the argument. The second parameter to getArgument() is the default value to return if there is no argument set on the route.
To see it in context, the bare-bones AclCheckMiddleware code might look something like this:
class AclCheckMiddleware { public function __invoke($request, $response, $next) { $user = $request->getAttribute('user'); // set in an earlier middleware // retrieve the configured permission value $route = $request->getAttribute('route'); $permission = $route->getArgument('permission', 'canDoNothing'); if ($this->user->isAllowed($permission) === false) { return new Response(StatusCode::HTTP_FORBIDDEN); } return $next($request, $response); } }
Note that for group or per-route middleware this works out-of-the-box. If the middleware is added using $app->add(), then you'll need to set the configuration setting determineRouteBeforeAppMiddleware to true so that the route attribute exists when the middleware is run.
That’s it
There you have it. The same mechanism used for setting a default route argument can also be used for per-route settings. This is incredibility useful for situations like ACL permissions checks and I'm confident that there are many more equally useful things that can be done.