Menu system in Drupal 8 has been changed completely. One of the biggest changes being removal of hook_menu(). Now the menu items, its page callbacks/access callbacks... are all defined in yml files(*.routing.yml). There has been a shift in terminology as well from path -> routes.
What are access Callbacks?
Access callbacks are functions returning TRUE if the user has access rights to this menu item, and FALSE if not. Most of the access control can be achieved using Drupal permission system itself, but cases wherein we need to control the access to a menu link dynamically, access callbacks come into play.
e.g., Consider a case where in we need to allow the access to a menu only if it has a CSRF token attached with it.
How do we create access Callbacks in Drupal 7
The example below will validate for CSRF token in the url attached as ?token=<token_value>. If the token is valid, the requesting user will be granted access else not.
How to port it to Drupal 8
- Convert menu into routes
- Create a service for custom access check
- Create access callback class defined in the service.
Covert menu into routes
The menu definition goes completely into d8_demo.routing.yml as shown below.
What is _access_check_token here?
We will take a look at it in the next step.
Details on the attributes used above:
path: Drupal 8 has replaced the use of path over route names. So, wherever in a module a path was being used, it has been replaced with route names. The index for the items array in hook_menu() moves under path attribute. Dynamic paths like d8-demo/% get replaced as d8-demo/{arg}. For entity paths like node/%nodewhich covert the entity id into the object before passing it to the page callback, it becomes node/{node}.
defaults: Page callbacks are converted into controllers now. These controllers could be of type _content, _controller, _form, _entity_view, _entity_list or _entity_form. For more details on which type of controller should be used where check the documentation here.
requirements: Determines what conditions must be met in order to grant access to the route. For more details, check the documentation here. We will be talking about_access here.
options (optional): Additional options on how the route should interact. For more details, check the documentation here.
Create a service for custom access check
arguments: injects other services that this custom service requires. In our case, since we need to validate CSRF token in the path, we need to usecsrf_token service.
For details on rest of these attributes mean here, read my previous blog post on porting hook_init().
Adding access callback
The access callback class declared above in services.yml, needs to be implementAccessInterface.
AccessInterface requires us to define our logic into access(Route $route, Request $request, AccountInterface $account). In our case since we need to validate the csrf_token, we injected csrf_token service into our custom access check. Hence, its available in the constructor for the class. For those who are not familiar with the term dependency injection, watch this excellent video on how its done in Drupal8.
Conclusion
In this post, we touchbased on how to replace hook_menu() with a *.routing.yml file. We also learned on how to declare access callbacks in Drupal8 and then moving the code for access callback into access Controllers.
Next article, we’ll have a look at How to port Blocks to Drupal 8.