Routing in Symfony 7
Symfony 7 uses PHP 8 attributes to define routes. This is far more readable than YAML or XML.
Basic controller
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class BlogController extends AbstractController
{
#[Route('/blog', name: 'app_blog_index', methods: ['GET'])]
public function index(): Response
{
return $this->render('blog/index.html.twig');
}
#[Route('/blog/{slug}', name: 'app_blog_show', methods: ['GET'])]
public function show(string $slug): Response
{
// $slug is automatically injected from the URL
return $this->render('blog/show.html.twig', ['slug' => $slug]);
}
}
Parameter requirements
You can restrict the format of parameters using requirements:
#[Route(
'/blog/{id}',
name: 'blog_show_by_id',
requirements: ['id' => '\d+'],
methods: ['GET']
)]
public function showById(int $id): Response
{
// ...
}
Route groups
The #[Route] attribute can also be set on the class — all routes in the controller inherit the prefix:
#[Route('/api/v1', name: 'api_v1_')]
class ApiController extends AbstractController
{
#[Route('/users', name: 'users')] // → /api/v1/users
public function users(): JsonResponse { /* ... */ }
}
Dependency Injection in the controller
Constructor injection (recommended)
class ArticleController extends AbstractController
{
public function __construct(
private readonly ArticleRepository $articles,
private readonly SluggerInterface $slugger,
) {}
#[Route('/articles')]
public function index(): Response
{
$all = $this->articles->findPublished();
return $this->render('article/index.html.twig', ['articles' => $all]);
}
}
Action injection (for one-off dependencies)
#[Route('/articles/new')]
public function new(Request $request, EntityManagerInterface $em): Response
{
// $request and $em injected only into this action
}
Returning data
| Method | Class | When to use |
|---|---|---|
render() |
Response |
HTML pages (Twig) |
json() |
JsonResponse |
API endpoints |
redirect() |
RedirectResponse |
Redirects |
file() |
BinaryFileResponse |
File downloads |
JSON example
#[Route('/api/articles', format: 'json')]
public function list(ArticleRepository $repo): JsonResponse
{
return $this->json(
$repo->findAll(),
Response::HTTP_OK,
[],
['groups' => 'article:list']
);
}
Debugging routes
# list all routes
php bin/console debug:router
# details of a specific route
php bin/console debug:router app_blog_show
# match a URL to a route
php bin/console router:match /blog/my-article
Tip: In the development environment, the Symfony Profiler bar (at the bottom of the page) shows the current route, controller and execution time.