Controllers
A Symfony controller is nothing exotic — it's just a plain PHP class. Each public method on that
class is called an action, and an action is matched to a request through the
#[Route] attribute covered on the previous Routing
page. Symfony doesn't force any particular base class on you, but almost every controller extends
AbstractController because of the convenience it provides.
Why Extend AbstractController?
AbstractController is a set of helper methods layered on top of the framework's core
request/response handling. Extending it is optional — you could build a Response object by hand —
but the helpers below cover almost everything a typical action needs to do.
| Method | What it does |
|---|---|
$this->render() | Renders a Twig template and wraps it in a Response — see the upcoming Twig Templates page |
$this->redirectToRoute() | Sends the browser to another named route instead of rendering a template |
$this->json() | Encodes any array or object as JSON and returns it with the right headers — handy for API-style actions |
redirectToRoute() and json()
Both helpers take the same shape you already saw with generateUrl() on the routing
page — a route name plus an optional array of parameters:
// Redirect back to the article list after an action completes
return $this->redirectToRoute('article_index');
// Return a JSON response instead of an HTML page
return $this->json(['id' => $id, 'title' => 'Hello World']);
Getting Services Into a Controller
Real actions usually need more than routing and templates — they need a Repository to fetch data, or a service to send an email. Symfony's container can hand these to your controller automatically through autowiring: type-hint the service you want, either as a constructor argument or directly on the action method, and Symfony supplies it without any manual wiring. This is a big enough topic to get its own page later — Services & Dependency Injection — so we'll keep it brief here.
A Full Worked Example
Here's a small but complete controller with two actions: index() lists articles, and
show(int $id) displays one, using the same routing style from the previous page.
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Routing\Annotation\Route;
class ArticleController extends AbstractController
{
#[Route('/articles', name: 'article_index')]
public function index()
{
return $this->render('article/index.html.twig');
}
#[Route('/articles/{id<\d+>}', name: 'article_show')]
public function show(int $id)
{
return $this->render('article/show.html.twig', ['id' => $id]);
}
}
Both actions end with $this->render(), which hands a template name (and, for
show(), the matched $id) off to Twig. That rendering step is exactly
where the next page picks up.
article/show.html.twig.