Route Parameters and Constraints
Route Parameters and Constraints
~13 Min. Lesezeit Zuletzt aktualisiert am August 8, 2026
To display a SINGLE project, we need dynamic parts in the URL – route parameters. This chapter covers them systematically, including the ordering trap from chapter 7.
Simple route parameters
#[Route('/projects/{id}', name: 'project_show', methods: ['GET'])]
public function show(int $id): Response
{
return new Response(sprintf('Project details for ID %d', $id));
}{id} in the path AND int $id as the method's parameter – Symfony matches the placeholder's name to the method parameter's name and passes the value automatically. The PHP type (int) is automatically converted from the URL's string value in the process.
Requirements: constraining route parameters
Without a constraint, {id} would match ANY string – even /projects/abc, which causes an error once PHP tries to convert 'abc' to int. With requirements, this can be restricted BEFORE the controller is even reached:
#[Route('/projects/{id}', name: 'project_show', requirements: ['id' => '\d+'], methods: ['GET'])]
public function show(int $id): Response
{
// ...
}'\d+' is a regular expression: one or more digits. /projects/abc no longer matches this route – Symfony instead looks for a DIFFERENT matching route, or returns 404 if none exists.
The ordering trap from chapter 7, now solved
// CORRECT order: the more specific route first
#[Route('/projects/new', name: 'project_new', methods: ['GET', 'POST'])]
public function new(): Response { /* ... */ }
#[Route('/projects/{id}', name: 'project_show', requirements: ['id' => '\d+'], methods: ['GET'])]
public function show(int $id): Response { /* ... */ }With requirements: ['id' => '\d+'], the order here would ACTUALLY not matter anymore, since /projects/new doesn't even match the digits pattern in the first place – an added safety-net effect of requirements, independent of declaration order.
Optional parameters with default values
#[Route('/projects/{id}/tasks/{status}', name: 'project_tasks', requirements: ['id' => '\d+'])]
public function tasks(int $id, string $status = 'all'): Response
{
return new Response(sprintf('Tasks of project %d, status: %s', $id, $status));
}A default value on the method parameter (string $status = 'all') makes this part of the URL implicitly optional – both /projects/1/tasks/ AND /projects/1/tasks/open match the same route.
Multiple HTTP methods on the same route
For forms (block 3), a route is often needed for TWO purposes: DISPLAYING the form (GET) and PROCESSING it after submission (POST) – both on the SAME route:
#[Route('/projects/new', name: 'project_new', methods: ['GET', 'POST'])]
public function new(Request $request): Response
{
if ($request->isMethod('POST')) {
// Process the form (chapter 15)
}
// Show the form (chapter 15)
return new Response('Create a new project');
}Tipp: debug:router from chapter 7 shows multiple HTTP methods per route comma-separated – a good way to check whether a route was REALLY registered for both methods.