Skip to content

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 learning

Thin 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:

  • AbstractController gives 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 ​

IntentTypical return
HTML page$this->render('…', $data)
Redirect after POST$this->redirectToRoute('…')
JSON$this->json($data) / JsonResponse
404throw $this->createNotFoundException()
Download$this->file($path)

Keep controllers boring. Boring is good.

Best,

-- ł.

Continues after /lab/php-tutorial-for-fe-devs/