New in Symfony 7.1: Mapped Route Parameters


Contributed by Nicolas Grekas
in #54720
and #54455.

Symfony maps route parameters to controller arguments automatically since day one.
You only have to use the same name in both places and Symfony does the necessary
data type conversion:

#[Route('/document/{id}')]
public function showDocument(int $id): Response
{
// here, $id is an integer variable with the same value as
// the string value included in the URL after '/document/'
// ...
}

When using Doctrine entities, this feature is even more convenient because it
can transform the route parameters into entities via database queries:

// ...
use App\Entity\Document;

#[Route('/document/{id}')]
public function showDocument(Document $document): Response
{
// using the $id value of the route, Symfony makes a database query
// to find an entity of type Document and whose id matches that value
// (and throws a 404 error if the entity is not found)
// ...
}

This feature works by using all the route parameters in the database query:

#[Route('/document/{slug}/{id}-{name}/')]
public function showDocument(Document $document): Response
{
// the database query in this case would be:
// $repository = $entityManager->getRepository(Document::class);
// $document = $repository->findOneBy(['slug' => 'the slug', 'id' => 'the id', 'name' => 'the name']);
}

This becomes problematic when the controller arguments contain more than one
entity. All route parameters will be used to find all entities, resulting in errors.
You can solve this with the #[MapEntity] attribute but in Symfony 7.1 we're
introducing Mapped Route Parameters as an alternative.
When adding route parameters, you can now define the mapping between the
route parameter and the controller argument:

#[Route('/document/{slug:category}/{id:document}-{name:document}/')]
public function showDocument(Document $document, Category $category): Response
{
// the database queries in this case would be:
// $document = $documentRepository->findOneBy(['id' => 'the id', 'name' => 'the name']);
// $category = $categoryRepository->findOneBy(['slug' => 'the slug']);
}

This explicit mapping configuration solves the issues when using multiple entities
as controller arguments and is more concise than using the #[MapEntity] attribute.
This mapping also comes in handy when you can't change the route parameter names
but want to use different controller argument names:

#[Route('/voucher/{voucher:voucherCode}/{action:userAction}/')]
public function handleVoucher(string $voucherCode, string $userAction): Response

Given that this new feature provides a nice DX (developer experience) and is very
concise, we've decided to deprecate the automatic mapping of route parameters into
Doctrine entities
starting from Symfony 7.1. This means that the following example
is now deprecated:

#[Route('/document/{id}')]
public function showDocument(Document $document): Response

To fix this deprecation, you can use the #[MapEntity] attribute:

use Symfony\Bridge\Doctrine\Attribute\MapEntity;
// ...

#[Route('/document/{id}')]
public function showDocument(
#[MapEntity(id: 'id')] Document $document,
): Response

Or you can use the new mapped route parameters:

#[Route('/document/{id:document}')]
public function showDocument(Document $document): Response

Sponsor the Symfony project.