Altering existing routes and adding new routes based on dynamic ones

Last updated on
27 July 2020

Any route - whether statically defined in a YAML file, as seen in the introductory example, or a dynamic route as described in Providing dynamic routes - can be altered. You can do so by modifying a RouteCollection using an EventSubscriber triggered by the RoutingEvents::ALTER event.

Altering existing routes

After building routes (e.g. when a module is enabled or when caches are cleared), the RoutingEvents::ALTER event triggers the route alter process. The \Drupal\Core\Routing\RouteSubscriberBase class contains an event listener that listens to this event. You can alter existing routes by implementing the alterRoutes(RouteCollection $collection) method of this class.

This example alters two different routes of the User module. Use a src/Routing/RouteSubscriber.php file in your module.

namespace Drupal\example\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route events.
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    // Change path '/user/login' to '/login'.
    if ($route = $collection->get('user.login')) {
      $route->setPath('/login');
    }
    // Always deny access to '/user/logout'.
    // Note that the second parameter of setRequirement() is a string.
    if ($route = $collection->get('user.logout')) {
      $route->setRequirement('_access', 'FALSE');
    }
  }

}

The \Drupal\example\Routing\RouteSubscriber::alterRoutes method is an event subscriber because it extends RouteSubscriberBase. Therefore, the class must be registered as an event subscriber service.

Use a example.services.yml file in your module (if the module is named example).

services:
  example.route_subscriber:
    class: Drupal\example\Routing\RouteSubscriber
    tags:
      - { name: event_subscriber }

Adding custom access checks on dynamic routes

Route::setRequirement() can be used to set custom access checks on any route within the alterRoutes function. If you are using an Access Check service that has arguments defining extra services as requirements, the service name should be used in the second parameter rather than the class name. Below, standardAccessCheck does not have specific services defined and servicesAccessCheck does.

services:
  example.standard_access_checker:
    class: Drupal\example\Access\StandardAccessCheck
    arguments: ['@current_user']
    tags:
      - { name: access_check, applies_to: _custom_access }
  example.services_access_checker:
    class: Drupal\example\Access\ServicesAccessCheck
    arguments: ['@mymodule.service']
    tags:
      - { name: access_check }

The above custom access checks can both be defined using the service name or class name, however, ServiceAccessCheck must use the service name. This is to ensure the services defined in services.yml are injected into the construct function when the access check is called.

namespace Drupal\example\Routing;

use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;

/**
 * Listens to the dynamic route events.
 */
class RouteSubscriber extends RouteSubscriberBase {

  /**
   * {@inheritdoc}
   */
  protected function alterRoutes(RouteCollection $collection) {
    // Define custom access for '/user/login'.
    if ($route = $collection->get('user.login')) {
      $route->setRequirement('_custom_access', 'Drupal\example\Access\StandardAccessCheck::access');
    }
    // Define custom access for '/user/logout'.
    if ($route = $collection->get('user.logout')) {
      $route->setRequirement('_custom_access', 'example.services_access_checker::access');
    }
  }

}

Adding routes based on existing dynamic routes

You can use the alterRoutes() method to add dynamic routes as well. If your dynamic routes are standalone, the preferred method is not to implement this class and event subscriber but instead use the simpler route_callbacks solution. However, if the dynamic routes are dependent on other dynamic routes, you'll need to implement a class extending from RouteSubscriberBase. Make sure to adjust the weight of the event subscription in a getSubscribedEvents() method implementation. A real example can be found in the configuration translation RouteSubscriber.

Help improve this page

Page status: No known problems

You can: