Drupal - How do I alter the route defined by another module?
Allow views to override existing routing items just uses existing functionality.
If you want to change the information attached to a route, not the menu, like the actual used controller, or the requirements (permission/role etc.) you can use an event provided by Drupal:
<?php
use Drupal\Core\Routing\RouteBuildEvent;
use Drupal\Core\Routing\RoutingEvents;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class RouteSubscriber implements EventSubscriberInterface {
/**
* {@inheritdoc}
*/
public static function getSubscribedEvents() {
$events[RoutingEvents::ALTER] = 'alterRoutes';
return $events;
}
/**
* Alters existing routes.
*
* @param \Drupal\Core\Routing\RouteBuildEvent $event
* The route building event.
*/
protected function alterRoutes(RouteBuildEvent $event) {
// Fetch the collection which can be altered.
$collection = $event->getRouteCollection();
// The event is fired multiple times so ensure that the user_page route
// is available.
if ($route = $collection->get('user_page')) {
// As example add a new requirement.
$route->setRequirement('_role', 'anonymous');
}
}
}
Additionally you have to register a service with the tag 'event_subscriber' for this class.
Since I asked this question, Drupal 8 core changed, and some of the issues about routes have been fixed.
hook_menu()
is not used from Drupal 8 anymore; the routes a module uses are defined in a .routing.yml file (e.g. user.routing.yml). Alter hooks are still used, but since hook_menu()
is not used anymore from Drupal core, Drupal core doesn't invoke hook_menu_alter()
too.
The steps necessary to alter a route defined from other modules are the following:
Define a service tagged as event_subscriber. (The tag is the important part, or the service would not work as expected.)
services: mymodule.route_subscriber: class: Drupal\mymodule\Routing\RouteSubscriber tags: - { name: event_subscriber }
Create a class that extends
RouteSubscriberBase
.namespace Drupal\mymodule\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 the route associated with the user profile page (/user, /user/{uid}). if ($route = $collection->get('user.page')) { $route->setDefault('_controller', '\Drupal\mymodule\Controller\UserController::userPage'); } } }
For real implementation examples of
alterRoutes()
, seeAdminRouteSubscriber::alterRoutes()
orRouteSubscriber::alterRoutes()
.protected function alterRoutes(RouteCollection $collection) { foreach ($collection->all() as $route) { if (strpos($route->getPath(), '/admin') === 0 && !$route->hasOption('_admin_route') && static::isHtmlRoute($route)) { $route->setOption('_admin_route', TRUE); } } }
protected function alterRoutes(RouteCollection $collection) { $mappers = $this->mapperManager->getMappers($collection); foreach ($mappers as $mapper) { $collection->add($mapper->getOverviewRouteName(), $mapper->getOverviewRoute()); $collection->add($mapper->getAddRouteName(), $mapper->getAddRoute()); $collection->add($mapper->getEditRouteName(), $mapper->getEditRoute()); $collection->add($mapper->getDeleteRouteName(), $mapper ->getDeleteRoute()); } }
Notice that compared with earlier Drupal 8 releases, some details are changed.
- The route name is changed from user_page to user.page
- The requirements for that route changed from
_access: 'TRUE'
to_user_is_logged_in: 'TRUE'
- The property to set the controller of a route changed from _content to _controller
See Altering existing routes and adding new routes based on dynamic ones; it worked nicely for me to remove /search handled by the Search module. In my case, I used the following code.
<?php
/**
* @file
* Contains \Drupal\ua_sc_module\Routing\SearchAlterRouteSubscriber.
*/
namespace Drupal\ua_sc_module\Routing;
use Drupal\Core\Routing\RouteSubscriberBase;
use Symfony\Component\Routing\RouteCollection;
/**
* Listens to the dynamic route events.
*/
class SearchAlterRouteSubscriber extends RouteSubscriberBase {
/**
* {@inheritdoc}
*/
protected function alterRoutes(RouteCollection $collection) {
// Remove the /search route.
$collection->remove('search.view');
}
}