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
HttpKernely 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:
- 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.
- 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:
- Un conjunto de varias decenas de componentes PHP desacoplados y reutilizables (HttpFoundation, Routing, Console, Form, Validator, Mailer…), que puedes usar en cualquier proyecto PHP.
- Un framework full-stack que une esos componentes mediante el
FrameworkBundlepara 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 objetoRequestde 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 namespaceApp\. 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 requirede 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
.xmlde 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) |