Drupal\Core\Routing\Attribute\Route extends the Symfony #[Route] attribute and
adds a title property. The property sets the _title route default.
The title can be a plain string, a TranslatableMarkup object, or a static closure that is evaluated on each request. Closures can be used to replace the _title_callback default so a separate title method is no longer needed to generate dynamic page titles.
Before
use Symfony\Component\Routing\Attribute\Route;
#[Route(
path: '/admin/config/system/site-information',
name: 'system.site_information_settings',
defaults: ['_title' => 'Basic site settings'],
)]
A dynamic title needed a separate _title_callback default:
block_content.add_form:
path: '/block/add/{block_content_type}'
defaults:
_controller: '\Drupal\block_content\Controller\BlockContentController::addForm'
_title_callback: '\Drupal\block_content\Controller\BlockContentController::getAddFormTitle'
After
use Drupal\Core\Routing\Attribute\Route;
#[Route(
path: '/admin/config/system/site-information',
name: 'system.site_information_settings',
title: new TranslatableMarkup('Basic site settings'),
)]
A closure keeps the dynamic title generator next to the route:
#[Route(
path: '/block/add/{block_content_type}',
name: 'block_content.add_form',
title: static function (BlockContentTypeInterface $block_content_type) {
return new TranslatableMarkup('Add %type content block', [
'%type' => $block_content_type->label(),
]);
},
)]
Title closures
Closure arguments are resolved in the same way as _title_callback arguments, from upcasted route parameters, the request and the route match. A closure has no access to the container. You can continue to use _title_callback when the title depends on injected services.
Title closures only work on method-level #[Route] attributes. Class-level #[Route] attributes with title closures throw an InvalidArgumentException.