Controllers
Controllers are thin HTTP adapters - validate / authorize / call services / return a response. Same advice as Nest or Express: don't bury domain logic forever in the controller… but for learning, it's fine to start there.
Generate one
bash
php bin/console make:controller PostController
# interactive MakerBundle questions - accept defaults while learningThin example
php
<?php
namespace App\Controller;
use App\Repository\PostRepository;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class PostController extends AbstractController
{
public function __construct(
private PostRepository $posts,
) {}
#[Route('/posts', name: 'app_posts_index')]
public function index(): Response
{
return $this->render('posts/index.html.twig', [
'posts' => $this->posts->findBy([], ['createdAt' => 'DESC']),
]);
}
}Comments for the code above:
AbstractControllergives helpers:render,redirectToRoute,json,createForm,getUser, …- Constructor DI is the default style - no Facades
- Return type
Response(or a subclass) keeps intent clear
Invokable controllers
Single-action class - nice for dedicated endpoints:
php
<?php
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
class HealthController
{
#[Route('/health', name: 'app_health')]
public function __invoke(): Response
{
return new Response('ok');
}
}You don't have to extend AbstractController - only when you want its shortcuts.
Dependency injection
php
<?php
public function __construct(
private PostRepository $posts,
private MailerInterface $mailer,
) {}Symfony resolves these from the container - the DI story from the PHP tutorial runway chapter, now automatic.
What to return
| Intent | Typical return |
|---|---|
| HTML page | $this->render('…', $data) |
| Redirect after POST | $this->redirectToRoute('…') |
| JSON | $this->json($data) / JsonResponse |
| 404 | throw $this->createNotFoundException() |
| Download | $this->file($path) |
Keep controllers boring. Boring is good.
Best,
-- ł.