Routing
Attribute routes are your React Router / Vue Router config - but for the server. You've already built the toy version.
Basic routes
php
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class HelloController extends AbstractController
{
#[Route('/', name: 'app_home')]
public function home(): Response
{
return $this->render('home.html.twig');
}
#[Route('/hello/{name}', name: 'app_hello')]
public function hello(string $name): Response
{
return new Response("Hello, {$name}");
}
#[Route('/posts', name: 'app_posts_index', methods: ['GET'])]
public function index(): Response
{
// …
}
}Comments for the code above:
{name}is the same idea as your mini-router paramsname: 'app_posts_index'gives you stable links (path('app_posts_index')in Twig /$this->generateUrl(...)) so URLs can change without hunting stringsmethods:restricts verbs - browser forms only do GET/POST natively
Path requirements & defaults
php
<?php
#[Route('/posts/{id}', name: 'app_posts_show', requirements: ['id' => '\d+'])]
public function show(int $id): Response
{
// …
}As you can see, requirements are just regex constraints on placeholders.
Prefixes via class attributes
php
<?php
#[Route('/dashboard')]
class DashboardController extends AbstractController
{
#[Route('', name: 'app_dashboard')]
public function index(): Response { /* … */ }
#[Route('/settings', name: 'app_dashboard_settings')]
public function settings(): Response { /* … */ }
}Inspecting routes
bash
php bin/console debug:router
php bin/console debug:router app_posts_showWhen a URL 404s mysteriously, debug:router is the first stop - same energy as php artisan route:list.
YAML / XML routes?
Still exist. Modern apps lean on attributes on controllers. If you open an older codebase and see config/routes.yaml full of path definitions - same router, different syntax.
Next: controllers as thin HTTP adapters.
Best,
-- ł.