How to implement custom access checking on routes in Drupal 8

Drupal 8’s access checking for routes is configurable in routing.yml for various implementations, with the simplest being to set the requirements parameter to pre-set values for roles, permissions, or a controller method.

For more involved access checking, we can take advantage of a dedicated service.  The steps:

1. Create a new Access service.

2. Add the new service entry to services.yml

3. Configure the route in routing.yml to use the access checker service.

An access checking service is stored in src/Access. As these are services, you can wire them with other services and pass slugs from the route to the method handling the granting of the access.

For this example, I want to allow access to an edit form for a product if that product is owned by the current user, or if the current user is a marketplace manager.  The route configuration is:

mymod.card.edit:
  path: '/my-store/products/edit-card/{product_id}'
  defaults:
    _form: '\Drupal\mymod\Form\CardEditForm'
  requirements:
    _mymod_card_edit_access: 'TRUE'

Take note of the product_id slug, and the _mymod_card_edit_access parameter, as the slug will be passed to the service, and the access parameter binds this route to the specific access checking service tagged on the applies_to parameter.

This example has a store manager service that handles the access checking on products.  We'll inject this store manager service into the access checker service.

mymod.card_edit_access_checker:
  class: Drupal\mymod\Access\CardEditAccessChecker
  arguments: ['@mymod.store_mgr']
  tags:
    - { name: access_check, applies_to: _mymod_card_edit_access }

For the service object code, override the access() method, with any parameters named the same as any slugs in route path.  In this example it’s product_id: