Encodera

Thanks for reading!

Donate to the site — we work hard for you.

Contents

Route parameters and access

A route parameter is the changing part of an address: the year in /blog/year/2026 or the node number in /blog/5/info. One route with a parameter replaces any number of similar pages. The same route description says who may open the page.

A parameter in the path

A parameter is written in curly braces. Add a route to blog.routing.yml:

# web/modules/custom/blog/blog.routing.yml
blog.year:
  path: '/blog/year/{year}'
  defaults:
    _controller: '\Drupal\blog\Controller\BlogController::year'
    _title: 'Archive'
  requirements:
    _permission: 'access content'

The value arrives in the method argument with the same name:

// web/modules/custom/blog/src/Controller/BlogController.php
public function year(string $year): array
{
    return [
        '#markup' => $this->t('Articles from @year.', ['@year' => $year]),
    ];
}

After drush cr the address /blog/year/2026 shows "Articles from 2026.". The argument name must match the name in the braces: {year} and $year.

Validating a parameter

Right now /blog/year/abc matches too. To make the route work only for four digits, add a regular expression under the parameter name in requirements:

# web/modules/custom/blog/blog.routing.yml
  requirements:
    _permission: 'access content'
    year: '\d{4}'

Now /blog/year/abc returns a 404 error and the controller method is not called.

A default value

If defaults holds a value for the parameter, it can be left out of the address:

# web/modules/custom/blog/blog.routing.yml
  defaults:
    _controller: '\Drupal\blog\Controller\BlogController::year'
    _title: 'Archive'
    year: '2026'

The address /blog/year opens the archive for 2026.

A node in a parameter

If a parameter is named after an entity type, for example node, Drupal loads the node by the number from the address itself:

# web/modules/custom/blog/blog.routing.yml
blog.info:
  path: '/blog/{node}/info'
  defaults:
    _controller: '\Drupal\blog\Controller\BlogController::info'
    _title: 'Article details'
  requirements:
    _entity_access: 'node.view'
// web/modules/custom/blog/src/Controller/BlogController.php
use Drupal\node\NodeInterface;
 
public function info(NodeInterface $node): array
{
    return ['#markup' => $node->label()];
}

The address /blog/1/info shows the title of node number 1. If there is no such node, Drupal returns 404. The _entity_access: 'node.view' condition lets in only those who are allowed to see this node.

// in any PHP class
use Drupal\Core\Url;
 
$url = Url::fromRoute('blog.year', ['year' => 2025])->toString(); // /blog/year/2025

Parameter values are passed as the second argument. In Twig it is path('blog.year', {'year': 2025}).

Access conditions

Entry in requirementsWho can open the page
_permission: 'access content'Those who have this permission
_role: 'administrator'Users with the role
_user_is_logged_in: 'TRUE'Logged-in users only
_entity_access: 'node.update'Those who may edit the node from the address
_access: 'TRUE'Everyone, without a check

Your own permissions are declared in the blog.permissions.yml file — a separate lesson covers that.

Common mistake

Drupal treats a route without an access condition as closed: the page answers "Access denied" even to the administrator. And the _access: 'TRUE' entry used to "fix" it opens the page to everyone — including an internal one. Name a specific permission through _permission, and keep open access only for truly public pages.

Thanks for reading!

Donate to the site — we work hard for you.