The Drupal 8 routing system builds a great deal on the Symfony framework. To define and use routes, you don't necessarily need to know the Symfony concepts, but for more advanced use cases, knowing the background helps a lot.
Drupal 8 uses the Symfony HTTP Kernel, a system which gets the request and asks other systems to produce the requested output (a response object) and sends the response back to the client. The output is generated by a piece of code called the controller. In theory the controller can be either a pure PHP 4 function, a method on an object or even an anonymous function.
Symfony routing compared to Drupal routing
As a module developer you define the list of routes and the corresponding controllers, for example:
Older Drupal 8 examples and Symfony documentation use 'pattern' instead of 'path', but since Symfony 2.2 (now incorporated in Drupal) the 'path' key is used in the routing file.
The key concept is the controller which gets some parameters from the system and converts them to the response. In this example you have the parameter 'taxonomy_vocabulary'. Everything without an underscore is considered to be a parameter to the controller. If you want to specify a default value, you should put it into the defaults array. Properties that have nothing to do with the controller parameters are considered to be internal and have an underscore as prefix.
Symfony also allows you to define regular expressions to validate that the incoming parameter is valid (using 'requirements'). In the code above it would only match to numbers.
Once Symfony determines which controller is active on the current request, it asks the so called controller resolver to create an instance of the controller, which is then executed via call_user_func_array(). See the getController() and getArguments() methods on ControllerResolver.
Drupal's extensions to Symfony routing
The above is basically what Symfony gives us for request handling. Drupal though is slightly more complicated:
- You can check access to the route. For example calling user_access() was very common in Drupal 7 and below.
- You want to convert the taxonomy_vocabulary to its actual entity object in the request
- You don't want to generate the full page response, but just the "main content".
The "underscore keys" in the routing file inform these systems.
Old content to be reworked below this line. vvvvvv
The system that knows which controller is responsible for the current request is the routing system.
This documentation describes how the routing system in Drupal 8 works.
- To learn how to create router entries, see https://drupal.org/node/2092643
- Read the symfony documentation: http://symfony.com/doc/current/book/routing.html
// @todo Additional to high level overview we should also give the service IDs so people can debug it.
// @todo Maybe some of these sections should be moved to extra handbooks.
// @todo How to mention that everything is based on events.
The HTTP Kernel (service: http_kernel Drupal\Core\HttpKernel)
The HTTP Kernel is the root which handles how to convert an incoming request to an outgoing response. Therefore it asks the routing component to find out which code should be run on a certain request.
This code corresponds to the hook_menu() page callback in Drupal 7. In Drupal 8 this is called the controller.
The router determines which code should be run to generate the response.
In detail there are a list of components involved so let's describe all of them and how they work together:
The Route Provider (service: router.route_provider Drupal\Core\Routing\RouteProvider)
The route provider gives you all controllers which potentially match on the current request.
Therefore it takes the path and finds matching routes in the database. (see section route builder how the route items get into the database)
Important method: getRouteCollectionForRequest
As a controller could be different depending on a POST and GET request, you might get multiple routes for the same URL.
... Nested matcher ... (service: router.matcher, router.matcher.final_matcher)
As the route provider returns multiple possible controllers, there is a component which returns the first matching controller based upon other criteria than just the URL. (GET, vs. POST as example).
Important method: matchRequest