Módulo 00: Introducción

Objetivos del módulo

  • Entender qué es un framework y qué problemas resuelve frente a escribir PHP “a mano”
  • Conocer qué es Symfony: un conjunto de componentes reutilizables y, a la vez, un framework full-stack
  • Repasar la historia de Symfony, desde SensioLabs y Fabien Potencier hasta Symfony 8
  • Descubrir qué proyectos famosos se apoyan en componentes de Symfony
  • Comprender la política de versiones y soporte (versiones menores, mayores y LTS)
  • Visualizar el ciclo petición-respuesta y el papel del HttpKernel y sus eventos
  • Relacionar el patrón MVC con la forma de trabajar de Symfony
  • Conocer la filosofía del framework: bundles, Flex, recipes y configuración por atributos
  • Comparar Symfony con Laravel de forma equilibrada
  • Saber qué aplicación construiremos a lo largo del curso

1. ¿Qué es un framework y por qué usarlo?

📘 Concepto: Un framework es un conjunto de herramientas, librerías y convenciones que te proporcionan la estructura básica de una aplicación. Tú escribes el código específico de tu negocio y el framework se encarga de todo lo demás: enrutar peticiones, cargar configuración, conectar con la base de datos, gestionar sesiones, proteger formularios, etc.

Una analogía: construir una casa

Imagina que quieres construir una casa. Puedes:

  1. Hacerlo todo tú: fabricar los ladrillos, cortar la madera, diseñar la instalación eléctrica desde cero… Es posible, pero lento, y lo más probable es que cometas errores que otros ya resolvieron hace décadas.
  2. Usar una estructura prefabricada: los cimientos, las vigas y las tuberías ya vienen hechos y probados. Tú decides la distribución de las habitaciones, la decoración y los acabados.

Un framework es esa estructura prefabricada. No te quita libertad para decidir cómo es tu casa, pero te ahorra reinventar la rueda.

PHP “a pelo” frente a un framework

Seguro que has escrito alguna vez algo parecido a esto:

<?php
// articulo.php — PHP sin framework
declare(strict_types=1);

$pdo = new PDO('mysql:host=localhost;dbname=blog', 'root', '');
$id = $_GET['id'] ?? null;

if (!ctype_digit((string) $id)) {
    http_response_code(404);
    echo 'Artículo no encontrado';
    exit;
}

$stmt = $pdo->prepare('SELECT * FROM article WHERE id = ?');
$stmt->execute([$id]);
$article = $stmt->fetch(PDO::FETCH_ASSOC);

if (!$article) {
    http_response_code(404);
    echo 'Artículo no encontrado';
    exit;
}
?>
<h1><?= htmlspecialchars($article['title']) ?></h1>
<p><?= nl2br(htmlspecialchars($article['content'])) ?></p>

Funciona, pero mezcla en un solo fichero la conexión a la base de datos, la validación de la entrada, la consulta SQL, la gestión de errores y el HTML. Cuando la aplicación tenga 50 páginas, esto se vuelve inmanejable.

Con Symfony, el mismo caso de uso queda así:

<?php
// src/Controller/ArticleController.php — con Symfony
declare(strict_types=1);

namespace App\Controller;

use App\Entity\Article;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

final class ArticleController extends AbstractController
{
    #[Route('/articulos/{id}', name: 'article_show', requirements: ['id' => '\d+'])]
    public function show(Article $article): Response
    {
        // Symfony ya ha validado el {id}, ha buscado el artículo en la BD
        // y, si no existe, ha devuelto un 404 automáticamente.
        return $this->render('article/show.html.twig', [
            'article' => $article,
        ]);
    }
}

Y la plantilla (que escapa el HTML automáticamente):

{# templates/article/show.html.twig #}
<h1>{{ article.title }}</h1>
<p>{{ article.content|nl2br }}</p>

Ventajas de usar un framework

Ventaja Qué significa en la práctica
Productividad Rutas, formularios, validación, seguridad o envío de emails ya están resueltos
Seguridad Protección CSRF, escape automático contra XSS, consultas parametrizadas, hashing de contraseñas
Mantenibilidad Todos los proyectos siguen la misma estructura: cualquier desarrollador Symfony se orienta rápido
Calidad Código probado por miles de proyectos y una comunidad enorme
Testabilidad Arquitectura pensada para escribir tests unitarios y funcionales
Empleabilidad Las empresas buscan perfiles que dominen frameworks concretos

⚠️ Importante: Un framework no sustituye a saber PHP. Al contrario: Symfony usa intensivamente POO, interfaces, namespaces, atributos y tipado estricto. Cuanto mejor domines PHP 8, más fácil te resultará este curso.


2. ¿Qué es Symfony?

📘 Concepto: Symfony es dos cosas a la vez:

  1. Un conjunto de varias decenas de componentes PHP desacoplados y reutilizables (HttpFoundation, Routing, Console, Form, Validator, Mailer…), que puedes usar en cualquier proyecto PHP.
  2. Un framework full-stack que une esos componentes mediante el FrameworkBundle para que construyas aplicaciones web completas.

Componentes: piezas de LEGO

Cada componente resuelve un problema concreto y puede instalarse por separado con Composer. Por ejemplo, si solo necesitas crear una herramienta de línea de comandos, puedes usar únicamente el componente Console:

composer require symfony/console
<?php
// saludar.php — usando SOLO el componente Console, sin framework
declare(strict_types=1);

require __DIR__ . '/vendor/autoload.php';

use Symfony\Component\Console\Application;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

$app = new Application('Saludador', '1.0');

$app->register('saludar')
    ->addArgument('nombre', InputArgument::REQUIRED, 'A quién saludar')
    ->setCode(function (InputInterface $input, OutputInterface $output): int {
        $output->writeln(sprintf('¡Hola, %s!', $input->getArgument('nombre')));
        return Command::SUCCESS;
    });

$app->run();
php saludar.php saludar Ana
# ¡Hola, Ana!

Algunos componentes importantes

Componente Para qué sirve
HttpFoundation Representa la petición (Request) y la respuesta (Response) como objetos
HttpKernel Convierte una Request en una Response mediante eventos
Routing Asocia URLs con controladores
DependencyInjection Contenedor de servicios y autowiring
EventDispatcher Sistema de eventos y suscriptores
Console Comandos de terminal (bin/console)
Form Creación y procesamiento de formularios
Validator Validación de datos mediante restricciones
Security Autenticación y autorización
Serializer Conversión de objetos a JSON/XML/CSV y viceversa
Mailer Envío de correos electrónicos
Messenger Colas de mensajes y procesamiento asíncrono
String Manipulación de cadenas Unicode y generación de slugs
Yaml Lectura y escritura de ficheros YAML
AssetMapper Gestión de CSS y JavaScript sin Node.js

El framework full-stack

Cuando creas un proyecto Symfony, no instalas todos los componentes: instalas un esqueleto mínimo y vas añadiendo lo que necesitas. El FrameworkBundle es el pegamento que configura los componentes y los registra en el contenedor de servicios para que trabajen juntos.

flowchart TB
    subgraph FS["Symfony full-stack"]
        FB["FrameworkBundle (pegamento)"]
        FB --> HK["HttpKernel"]
        FB --> RT["Routing"]
        FB --> DI["DependencyInjection"]
        FB --> ED["EventDispatcher"]
        FB --> CO["Console"]
        TB["TwigBundle"] --> TW["Twig"]
        DB["DoctrineBundle"] --> ORM["Doctrine ORM"]
        SB["SecurityBundle"] --> SEC["Security"]
    end
    APP["Tu aplicación (src/)"] --> FS

💡 Consejo: Aprender Symfony es una inversión doble: lo que aprendas de sus componentes te servirá también en Laravel, Drupal o cualquier proyecto PHP que los use.


3. Historia de Symfony

Symfony nació en SensioLabs (entonces Sensio), una agencia web francesa, de la mano de Fabien Potencier. Necesitaban una base común para los proyectos de sus clientes y decidieron publicarla como software libre.

Año Versión Hito
2005 Symfony (pre-1.0) Se publica como proyecto de código abierto
2007 Symfony 1.0 Primera versión estable; muy inspirada en Ruby on Rails
2011 Symfony 2.0 Reescritura completa: componentes desacoplados, contenedor de servicios, bundles, Twig
2015 Symfony 3.0 Limpieza de código obsoleto y política de versiones definitiva
2017 Symfony 4.0 Llega Symfony Flex: proyectos mínimos que crecen con recipes
2019 Symfony 5.0 Componentes nuevos (String, Notifier…) y mejoras de rendimiento
2021 Symfony 6.0 Requiere PHP 8: tipado estricto en todo el código y atributos PHP
2023 Symfony 7.0 Requiere PHP 8.2; adiós a las anotaciones de Doctrine en favor de atributos
2025 Symfony 8.0 Requiere PHP 8.4; eliminado el formato de configuración XML y gran limpieza de código obsoleto
2026 Symfony 8.1 Versión estable actual (mayo de 2026), la que usamos en este curso

📘 Concepto: El salto de Symfony 1 a Symfony 2 fue una ruptura total: no había compatibilidad entre ambas. Desde Symfony 2, en cambio, las actualizaciones son progresivas y predecibles gracias a la política de versiones que verás en la siguiente sección.

Hoy Symfony es mantenido por una comunidad de miles de contribuidores, con SensioLabs como empresa principal detrás del proyecto (formación, certificación oficial y consultoría).


4. Proyectos que usan Symfony

Uno de los grandes éxitos de Symfony es que sus componentes están por todas partes, incluso en proyectos que compiten con él.

Proyecto Qué usa de Symfony
Laravel HttpFoundation, HttpKernel, Routing, Console, Process, Finder, Mailer, VarDumper…
Drupal (8+) HttpFoundation, HttpKernel, Routing, DependencyInjection, EventDispatcher, Yaml…
Composer Console, Process, Filesystem, Finder
phpBB Varios componentes (DependencyInjection, EventDispatcher, Routing…)
PrestaShop El back-office moderno está construido con el framework Symfony
Shopware Construido sobre el framework Symfony completo
Sylius Plataforma de comercio electrónico construida sobre Symfony
API Platform Framework para APIs construido sobre Symfony
PHPUnit, PHPStan, Behat… Console y otros componentes de utilidad

💡 Consejo: Cuando en Laravel escribes $request->query('page'), por debajo estás usando el objeto Request de Symfony HttpFoundation. Muchas de las cosas que aprenderás aquí te sonarán si vienes de Laravel.


5. Política de versiones y soporte

Symfony sigue un calendario fijo y predecible, algo muy valorado por las empresas porque permite planificar actualizaciones.

Reglas del calendario

  • Se publica una versión menor cada 6 meses: en mayo y en noviembre.
  • Cada 2 años (en noviembre) se publica una versión mayor (X.0) junto con la última versión menor de la rama anterior (X.4).
  • La versión X.4 de cada rama es LTS (Long Term Support): 3 años de corrección de errores y 4 de parches de seguridad.
  • Las versiones normales (no LTS) tienen soporte de 8 meses: el tiempo justo para actualizar a la siguiente.
  • La versión X.0 tiene exactamente las mismas funcionalidades que la X.4 anterior, pero sin el código obsoleto (deprecations).
timeline
    title Calendario de Symfony
    Nov 2025 : Symfony 7.4 LTS : Symfony 8.0
    May 2026 : Symfony 8.1
    Nov 2026 : Symfony 8.2 (prevista)
    May 2027 : Symfony 8.3 (prevista)
    Nov 2027 : Symfony 8.4 LTS (prevista) : Symfony 9.0 (prevista)

Versiones actuales (octubre de 2026)

Versión Publicación Fin de corrección de errores Fin de parches de seguridad PHP mínimo Estado
7.4 LTS Noviembre 2025 Noviembre 2028 Noviembre 2029 8.2 Mantenida (LTS)
8.0 Noviembre 2025 Julio 2026 Julio 2026 8.4 Sin soporte
8.1 Mayo 2026 Enero 2027 Enero 2027 8.4 Estable actual (la del curso)
8.2 Noviembre 2026 (prevista) Julio 2027 Julio 2027 8.4 En desarrollo

💡 Consejo: Puedes consultar siempre las fechas actualizadas en symfony.com/releases. La regla general es:

  • Proyecto nuevo o que se actualiza a menudo → última versión estable (8.1).
  • Proyecto que se va a tocar poco durante años (una intranet, un cliente con poco presupuesto de mantenimiento) → la LTS (7.4).

¿Qué es una deprecation?

📘 Concepto: Cuando Symfony quiere eliminar o cambiar una funcionalidad, primero la marca como obsoleta (deprecated) en una versión menor. Sigue funcionando, pero el Profiler te avisa. En la siguiente versión mayor, se elimina. Así, si tu aplicación en 7.4 no tiene avisos de obsolescencia, actualizar a 8.0 es prácticamente inmediato.

Esto se conoce como la promesa de compatibilidad hacia atrás (Backward Compatibility Promise): dentro de una misma versión mayor (de 8.0 a 8.4), actualizar nunca rompe tu código.


6. El ciclo petición-respuesta

Toda la web funciona con el mismo esquema: el navegador envía una petición HTTP y el servidor devuelve una respuesta HTTP. Symfony modela esto de forma literal: tu aplicación es una función que recibe un objeto Request y devuelve un objeto Response.

📘 Concepto: El front controller es el único fichero PHP accesible públicamente: public/index.php. Todas las peticiones pasan por él (el servidor web redirige cualquier URL a este fichero). Él arranca el Kernel, que se encarga del resto.

El front controller

Este es el public/index.php que genera Symfony 8:

<?php

use App\Kernel;

require_once dirname(__DIR__).'/vendor/autoload_runtime.php';

return static function (array $context) {
    return new Kernel($context['APP_ENV'], (bool) $context['APP_DEBUG']);
};

Es muy corto porque el componente Runtime (autoload_runtime.php) se encarga de crear la Request a partir de las superglobales ($_GET, $_POST, $_SERVER…), pasársela al Kernel, enviar la Response al navegador y ejecutar las tareas finales.

El recorrido completo

sequenceDiagram
    participant N as Navegador
    participant FC as public/index.php
    participant K as Kernel (HttpKernel)
    participant R as Router
    participant C as Controlador
    participant T as Twig / Servicios
    N->>FC: GET /articulos/hola-symfony
    FC->>K: handle(Request)
    K->>K: evento kernel.request
    K->>R: ¿qué controlador atiende esta URL?
    R-->>K: ArticleController::show, slug=hola-symfony
    K->>K: evento kernel.controller
    K->>K: resolver argumentos (kernel.controller_arguments)
    K->>C: show(Article $article)
    C->>T: render('article/show.html.twig')
    T-->>C: HTML
    C-->>K: Response
    K->>K: evento kernel.response
    K-->>FC: Response
    FC-->>N: HTTP 200 + HTML
    FC->>K: terminate() → evento kernel.terminate

Los eventos del HttpKernel

El HttpKernel no hace casi nada por sí mismo: lanza eventos y distintas partes del framework (y tu código) reaccionan a ellos. Así es como funciona, por ejemplo, el enrutado, la seguridad o la barra de depuración.

Evento Cuándo se lanza Ejemplo de uso
kernel.request Nada más llegar la petición El Router determina el controlador; el firewall de seguridad comprueba el usuario; se fija el idioma
kernel.controller Tras decidir qué controlador se ejecuta Procesar atributos como #[IsGranted]
kernel.controller_arguments Tras resolver los argumentos del controlador Modificar argumentos antes de llamar al controlador
kernel.view Si el controlador no devuelve una Response Convertir el valor devuelto en una respuesta (lo usa, por ejemplo, #[Template])
kernel.response Cuando ya hay una Response Añadir cabeceras, inyectar la Web Debug Toolbar
kernel.finish_request Al terminar de procesar la petición Restaurar el estado (idioma, contexto del router)
kernel.terminate Después de enviar la respuesta al navegador Enviar emails o tareas lentas sin que el usuario espere
kernel.exception Si se produce una excepción en cualquier punto Mostrar la página de error o convertirla en JSON

💡 Consejo: No necesitas memorizar estos eventos ahora. Volveremos a ellos en el módulo 11 (Eventos, Messenger y Mailer), donde crearás tus propios listeners. De momento quédate con la idea: Request → Kernel → Router → Controller → Response.

Un Kernel en miniatura

Para que veas que no hay magia, este es el concepto reducido a su mínima expresión con los componentes de Symfony:

<?php
declare(strict_types=1);

use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

// 1. Crear la petición a partir de las superglobales
$request = Request::createFromGlobals();

// 2. "Router" muy simplificado
$response = match ($request->getPathInfo()) {
    '/' => new Response('<h1>Portada del blog</h1>'),
    '/sobre-nosotros' => new Response('<h1>Sobre nosotros</h1>'),
    default => new Response('<h1>Página no encontrada</h1>', Response::HTTP_NOT_FOUND),
};

// 3. Enviar la respuesta al navegador (cabeceras + contenido)
$response->send();

Symfony hace exactamente esto, pero de forma extensible, configurable, cacheada y segura.


7. MVC en Symfony

📘 Concepto: MVC (Modelo-Vista-Controlador) es un patrón de arquitectura que separa la aplicación en tres responsabilidades:

  • Modelo: los datos y la lógica de negocio.
  • Vista: la presentación (HTML, JSON…).
  • Controlador: recibe la petición, coordina al modelo y elige la vista.

Analogía: un restaurante

  • El cliente (navegador) hace un pedido al camarero.
  • El camarero (controlador) no cocina: transmite el pedido a la cocina y luego sirve el plato.
  • La cocina (modelo y servicios) prepara la comida con los ingredientes de la despensa (base de datos).
  • La presentación del plato (vista) es cómo llega a la mesa.

Cómo encaja en Symfony

Capa MVC En Symfony Dónde vive
Modelo Entidades Doctrine, repositorios y servicios con la lógica de negocio src/Entity/, src/Repository/, src/Service/
Vista Plantillas Twig (o JSON en APIs) templates/
Controlador Clases con métodos asociados a rutas src/Controller/
flowchart LR
    U["Usuario"] -->|"Petición HTTP"| C["Controlador<br/>src/Controller"]
    C -->|"pide datos"| M["Modelo<br/>Entidades + Repositorios + Servicios"]
    M <-->|"SQL vía Doctrine"| DB[("Base de datos")]
    M -->|"devuelve objetos"| C
    C -->|"pasa variables"| V["Vista<br/>templates/*.html.twig"]
    V -->|"HTML"| C
    C -->|"Response"| U

⚠️ Importante: Symfony se define a sí mismo como un framework HTTP (Request/Response) más que como un framework MVC estricto. No te obliga a seguir MVC: puedes devolver JSON, archivos o streams. Pero para aplicaciones web clásicas, MVC es la forma natural de organizarse y la que seguiremos.

Buena práctica: controladores delgados

Un error habitual de principiante es meter toda la lógica en el controlador. La recomendación de Symfony es:

  • Controladores delgados: leen la petición, llaman a servicios y devuelven una respuesta.
  • Servicios gruesos: contienen la lógica de negocio reutilizable y testeable.
// ❌ Mal: el controlador calcula, consulta, envía emails...
// ✅ Bien: el controlador delega
#[Route('/articulos/{slug}/publicar', name: 'article_publish', methods: ['POST'])]
public function publish(Article $article, ArticlePublisher $publisher): Response
{
    $publisher->publish($article);   // toda la lógica está en el servicio

    $this->addFlash('success', 'Artículo publicado');
    return $this->redirectToRoute('article_show', ['slug' => $article->getSlug()]);
}

8. La filosofía de Symfony

Bundles

📘 Concepto: Un bundle es un paquete que integra una funcionalidad en el framework: registra servicios, aporta configuración, plantillas, comandos… Es el equivalente a un plugin.

Ejemplos: FrameworkBundle, TwigBundle, SecurityBundle, DoctrineBundle, MakerBundle, WebProfilerBundle. Los bundles activos se listan en config/bundles.php:

<?php
// config/bundles.php (fragmento)
return [
    Symfony\Bundle\FrameworkBundle\FrameworkBundle::class => ['all' => true],
    Symfony\Bundle\TwigBundle\TwigBundle::class => ['all' => true],
    Doctrine\Bundle\DoctrineBundle\DoctrineBundle::class => ['all' => true],
    Symfony\Bundle\WebProfilerBundle\WebProfilerBundle::class => ['dev' => true, 'test' => true],
    Symfony\Bundle\MakerBundle\MakerBundle::class => ['dev' => true],
];

⚠️ Importante: Desde Symfony 4, tu propia aplicación ya no se organiza en bundles. Todo tu código va en src/ con el namespace App\. Los bundles quedan para código reutilizable entre proyectos.

Symfony Flex y las recipes

📘 Concepto: Symfony Flex es un plugin de Composer que automatiza la instalación de paquetes. Cuando haces composer require de un paquete que tiene una recipe (receta), Flex ejecuta esa receta: registra el bundle, crea ficheros de configuración por defecto, añade variables a .env, etc.

composer require mailer
# Flex traduce el alias "mailer" a symfony/mailer, lo instala y aplica la recipe:
#   - crea config/packages/mailer.yaml
#   - añade MAILER_DSN a .env

Gracias a Flex, un proyecto Symfony empieza siendo mínimo y crece solo con lo que necesitas. Lo verás en detalle en el módulo 01.

Configuración por atributos

Symfony moderno apuesta por atributos PHP 8 para la configuración que está ligada al código, y YAML para la configuración global de la aplicación:

#[Route('/articulos', name: 'article_index')]           // Rutas
#[ORM\Entity(repositoryClass: ArticleRepository::class)] // Mapeo de base de datos
#[Assert\NotBlank]                                        // Validación
#[IsGranted('ROLE_ADMIN')]                                // Seguridad
#[AsCommand(name: 'app:publicar-articulos')]              // Comandos de consola
# config/packages/twig.yaml — configuración global en YAML
twig:
    file_name_pattern: '*.twig'
    form_themes: ['bootstrap_5_layout.html.twig']

⚠️ Importante: El formato de configuración XML quedó obsoleto en Symfony 7.4 y se eliminó en Symfony 8.0. Si encuentras tutoriales antiguos con ficheros .xml de configuración o con anotaciones en comentarios (@Route, @ORM\Column), están desactualizados: usa siempre YAML y atributos PHP.

Otros principios

Principio Qué significa
Explícito mejor que mágico Symfony evita las “fachadas” estáticas: inyectas lo que usas, y se ve en el constructor
Inyección de dependencias Todo es un servicio en un contenedor; el autowiring lo conecta automáticamente
Estándares PHP Sigue los PSR (PSR-4, PSR-3 logs, PSR-6/16 caché, PSR-11 contenedor, PSR-14 eventos…)
Rendimiento El contenedor y las rutas se compilan a PHP plano y se cachean
Estabilidad Promesa de compatibilidad y calendario fijo de versiones

9. Symfony frente a Laravel

Symfony y Laravel son los dos frameworks PHP más populares. Ambos son excelentes y la elección suele depender del equipo y del tipo de proyecto.

Aspecto Symfony Laravel
Filosofía Explícito, configurable, arquitectura desacoplada Convención sobre configuración, productividad inmediata
Curva de aprendizaje Algo más pronunciada al principio Más suave para empezar
ORM Doctrine (patrón Data Mapper: entidades sin lógica de persistencia) Eloquent (patrón Active Record: el modelo se guarda a sí mismo)
Plantillas Twig Blade
Acceso a servicios Inyección de dependencias y autowiring Fachadas, helpers globales e inyección
Configuración Atributos PHP + YAML Ficheros PHP en config/
Frontend por defecto AssetMapper (sin Node), Stimulus, Turbo Vite (con Node), Livewire o Inertia
Soporte a largo plazo Versiones LTS con 3 + 1 años de soporte Versiones anuales con soporte más corto
Ecosistema Componentes usados en todo PHP, API Platform, Sylius Ecosistema oficial muy amplio (Forge, Vapor, Nova, Cashier…)
Encaja muy bien en Proyectos grandes, empresariales y de larga vida; APIs complejas MVPs, startups, proyectos que necesitan salir rápido

💡 Consejo: No hay un “ganador”. Como ya viste, Laravel usa por debajo muchos componentes de Symfony. Si aprendes bien uno, aprender el otro te costará mucho menos. Lo importante son los conceptos: HTTP, MVC, ORM, inyección de dependencias, testing.

Data Mapper frente a Active Record

Esta es quizá la diferencia más visible en el día a día:

// Laravel (Active Record): el modelo sabe guardarse
$article = new Article();
$article->title = 'Hola';
$article->save();

// Symfony + Doctrine (Data Mapper): la entidad es un objeto PHP "normal"
// y es el EntityManager quien se encarga de persistirla
$article = new Article();
$article->setTitle('Hola');
$entityManager->persist($article);
$entityManager->flush();

10. Qué construiremos en el curso

A lo largo del curso desarrollarás paso a paso un blog / gestor de artículos completo. Cada módulo añadirá una pieza nueva a la misma aplicación, así verás cómo encaja todo.

El modelo de datos

erDiagram
    USER ||--o{ ARTICLE : escribe
    USER ||--o{ COMMENT : escribe
    CATEGORY ||--o{ ARTICLE : agrupa
    ARTICLE ||--o{ COMMENT : recibe
    USER {
        int id
        string email
        string password
        json roles
    }
    CATEGORY {
        int id
        string name
        string slug
    }
    ARTICLE {
        int id
        string title
        string slug
        text content
        datetime publishedAt
    }
    COMMENT {
        int id
        text content
        datetime createdAt
    }

Hoja de ruta

Módulos Lo que añadirás al blog
01 Crear el proyecto blog y tu primer controlador
02-04 Rutas del blog (portada, listado, detalle por slug, “Sobre nosotros”), plantillas Twig con Bootstrap y un poco de interactividad con Stimulus
05-06 Entidades Article, Category, Comment y User con Doctrine, relaciones y consultas
07-08 Formularios para crear y editar artículos y comentarios, con validación
09 Servicios propios (por ejemplo, un ArticlePublisher)
10 Registro, login, roles (ROLE_USER, ROLE_ADMIN) y voters para que solo el autor edite su artículo
11 Notificar por email cuando llega un comentario nuevo
12 Una API REST de artículos
13 Tests unitarios y funcionales
14 Comandos de consola (por ejemplo, publicar artículos programados)
15-16 Despliegue en producción y proyecto final

✏️ Ejercicio: Antes de seguir, piensa en tres funcionalidades extra que te gustaría que tuviera tu blog (etiquetas, buscador, artículos destacados…). Anótalas: al final del curso tendrás las herramientas para implementarlas.


11. Ejercicios

Ejercicio 1: Framework sí o no

Para cada uno de estos proyectos, razona en dos o tres líneas si usarías Symfony full-stack, solo algunos componentes de Symfony o PHP sin framework:

  • Un script que se ejecuta una vez al día para borrar ficheros temporales.
  • Una tienda online con pagos, usuarios y panel de administración.
  • Una herramienta CLI interna para importar CSV a una base de datos.
  • Una página estática de “Próximamente”.

Ejercicio 2: Usa un componente suelto

En una carpeta vacía (fuera de cualquier proyecto Symfony), instala con Composer el componente symfony/http-foundation. Crea un index.php que, usando Request::createFromGlobals(), lea el parámetro nombre de la URL y devuelva una Response con “Hola, {nombre}” o “Hola, desconocido” si no existe. Pruébalo con php -S localhost:8080.

Ejercicio 3: Mini router con match

Amplía el ejercicio anterior para que responda a tres rutas: / (portada del blog), /articulos (listado ficticio con tres títulos en una lista HTML) y /sobre-nosotros. Cualquier otra ruta debe devolver un código 404. Comprueba el código de estado con las herramientas de desarrollo del navegador (pestaña Red).

Ejercicio 4: Calendario de versiones

Consulta symfony.com/releases y responde:

  • ¿Cuál será la próxima versión LTS y cuándo se publicará?
  • Si hoy empiezas un proyecto con Symfony 8.1, ¿antes de qué fecha deberías actualizar a 8.2 para seguir recibiendo parches?
  • Una empresa tiene una aplicación en Symfony 6.4. ¿Hasta cuándo recibe parches de seguridad?

Ejercicio 5: Ordena el ciclo

Ordena estos pasos del ciclo petición-respuesta y asocia cada uno con el evento del kernel correspondiente (si lo hay): “se envía la respuesta”, “el Router busca la ruta”, “se ejecuta el controlador”, “se añade la barra de depuración”, “se envía un email tras responder”, “se resuelven los argumentos del controlador”.

Ejercicio 6: Componentes en Laravel

Busca el fichero composer.json del repositorio laravel/framework en GitHub y haz una lista de todos los paquetes symfony/* que requiere. Para tres de ellos, explica brevemente para qué los usa Laravel.


Resumen

Concepto Descripción
Framework Estructura y herramientas reutilizables para construir aplicaciones
Componentes Symfony Decenas de librerías PHP desacopladas e instalables por separado
Framework full-stack Los componentes integrados mediante FrameworkBundle
Fabien Potencier / SensioLabs Creador y empresa detrás de Symfony (2005)
Versión menor Cada 6 meses (mayo y noviembre)
Versión LTS La X.4 de cada rama: 3 años de errores + 1 más de seguridad
Symfony 8.1 Versión estable actual, requiere PHP >= 8.4
Symfony 7.4 Versión LTS actual (soporte de seguridad hasta noviembre de 2029)
Front controller public/index.php, punto de entrada único
HttpKernel Convierte Request en Response lanzando eventos
Eventos del kernel kernel.request, kernel.controller, kernel.view, kernel.response, kernel.terminate, kernel.exception
MVC Modelo (entidades/servicios), Vista (Twig), Controlador (src/Controller)
Bundle Paquete que integra una funcionalidad en el framework
Symfony Flex + recipes Plugin de Composer que configura automáticamente los paquetes
Atributos PHP Forma moderna de configurar rutas, entidades, validación, seguridad…
Doctrine vs Eloquent Data Mapper (Symfony) frente a Active Record (Laravel)