What it is
Symfony is a set of reusable PHP components and a full-stack framework, favoured for large, long-lived enterprise applications.
Controllers are classes with attribute-based routing. Services are autowired from the container, and Doctrine provides the ORM.
Installation
composer create-project symfony/skeleton my-appGetting started
The smallest useful thing you can do with it, and what each part means.
php
<?php
#[Route('/books')]
final class BookController extends AbstractController
{
public function __construct(
private readonly BookRepository $books, // autowired by type
) {}
#[Route('/{id}', methods: ['GET'])]
public function show(int $id): JsonResponse
{
$book = $this->books->find($id)
?? throw $this->createNotFoundException("Book $id not found");
return $this->json($book, context: ['groups' => ['book:read']]);
}
#[Route('', methods: ['POST'])]
public function create(
#[MapRequestPayload] CreateBookDto $dto, // decoded and validated
): JsonResponse {
return $this->json($this->books->create($dto), Response::HTTP_CREATED);
}
}Advanced usage
Where the library earns its place over a simpler alternative.
php
<?php
#[ORM\Entity(repositoryClass: BookRepository::class)]
#[ORM\Index(columns: ['year'])]
class Book
{
#[ORM\Id, ORM\GeneratedValue, ORM\Column]
private ?int $id = null;
#[ORM\Column(length: 200)]
#[Assert\NotBlank, Assert\Length(max: 200)]
private string $title;
#[ORM\ManyToOne(inversedBy: 'books')]
private ?Author $author = null;
}
// Asynchronous message handling
#[AsMessageHandler]
final class SendWelcomeEmailHandler
{
public function __invoke(SendWelcomeEmail $message): void
{
$this->mailer->send(/* … */);
}
}
$this->bus->dispatch(new SendWelcomeEmail($userId));Errors and fixes
The failures you are most likely to hit, and what actually resolves them.
- Cannot autowire service: argument references class but no such service exists
- The class is outside the autoconfigured paths or is not concrete. Register it explicitly, or bind the interface to an implementation in services.yaml.
- Doctrine schema is out of sync
- Generate and run a migration with make:migration and doctrine:migrations:migrate rather than using schema:update in production.
Best practices
- Prefer constructor injection with typed properties; autowiring resolves them.
- Use MapRequestPayload with DTOs rather than reading the raw request in controllers.
- Cache the container and routes in production (composer dump-env prod).
- Choose Symfony for large, long-lived applications; Laravel is faster for smaller ones.
Background
Why it exists, and what it was reacting to.
Symfony's components underpin much of the PHP ecosystem — Laravel, Drupal and Composer itself all use them. The framework is more explicit and less convention-driven than Laravel, which suits large teams.
