We've seen so far how to create access rules on our own routes. However, it would not be Drupal if it wasn't also easy to alter existing routes and change their access rules to whatever we want. This is yet another small extension point with which our custom modules can contribute to an existing functionality.
Altering route access is done by altering the routes themselves. Of course, access is not the only reason why routes may be altered, as you can change just about anything else on the definition. So, with this occasion, we will see how you can alter routes for any purpose you might need.
Routes can be altered by subscribing to an event, just as we've seen in Chapter 2, Creating Your First Module, when we subscribed to the kernel.request event. This event is dispatched at the moment all the routes are being built and before they get cached. So, the alteration will not happen dynamically (upon someone accessing the route), but only when they all get rebuilt. So, let's take a look at how we can subscribe to that event.
Unlike most other subscribers, the EventSubscriberInterface class for routes typically goes in the Routing namespace of the module so that's where we'll put it. Moreover, the event we're listening to is RoutingEvents::ALTER. However, the routing system provides us with a base subscriber class we can extend and which contains all this boilerplate code, leaving us to do only the alterations themselves.
And these alterations can look like this:
namespace Drupal\hello_world\Routing;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Subscribes to route events for the Hello World module.
*/
class HelloWorldRouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
$route = $collection->get('user.register');
if (!$route) {
return;
}
// Example 1:
// We deny access to the Register page in all cases. With this requirement,
// it doesn't matter anymore what other access requirements exist or if they
// evaluate positively.
$route->setRequirement('_access', 'FALSE');
// Example 2:
// We check for the presence of a specific access requirement and if it exists,
// we clear all the access requirements on the route and set our own.
if ($route->hasRequirement('_access_user_register')) {
$route->setRequirements([]);
$route->setRequirement('_user_types_access_check', 'TRUE');
}
}
}
We extended RouteSubscriberBase, which subscribes to the event and provides us with the alterRoutes() method and a collection of all the routes on the site. I encourage you to look into the RouteCollection class, as it's a very handy one to know when working with routes. One important feature is that we can retrieve routes based on their name, which we did in the preceding example.
Then, we will work with Route objects like we did a bit earlier. We can see two examples, all with comments I will not repeat here. The second example does not make any sense in a real-world scenario, as we cannot have logged-in users register for new accounts anyway. However, it serves to illustrate how we can add our own access checker to an existing route.
Similar to how we manipulate access requirements, we can change a lot of other things: options, parameters, controller, and even the actual route path. For this, I encourage you to familiarize yourself with the Route class methods and see what you can set on the new route. Couple this information with the documentation (https://www.drupal.org/docs/8/api/routing-system/structure-of-routes) on all the things you can add to routes for a better understanding.
The only thing left for this to work is to register the subscriber as a tagged service, just like we did in Chapter 2, Creating Your First Module:
hello_world.route_subscriber:
class: Drupal\hello_world\Routing\HelloWorldRouteSubscriber
tags:
- { name: event_subscriber }
Now, we are done with altering our routes.