Módulo 01: Instalación y estructura
Objetivos del módulo
- Preparar el entorno: PHP 8.4+, extensiones necesarias y Composer
- Instalar Symfony CLI en Windows, macOS y Linux y comprobar los requisitos
- Crear el proyecto del blog con
symfony new --webappy con Composer - Entender la diferencia entre el esqueleto mínimo (skeleton) y la aplicación web (webapp)
- Arrancar el servidor local, con HTTPS incluido
- Conocer la estructura de directorios de un proyecto Symfony
- Comprender Symfony Flex, las recipes y el fichero
symfony.lock - Manejar los entornos
dev,testyprody los ficheros.env - Usar la consola
bin/console, la Web Debug Toolbar y el Profiler - Generar código con Maker Bundle
- Levantar una base de datos con Docker o usar MySQL/MariaDB local
- Configurar VS Code o PhpStorm y crear tu primer “Hola mundo”
1. Requisitos
Symfony 8.1 necesita:
| Requisito | Versión / detalle |
|---|---|
| PHP | 8.4 o superior (recomendado PHP 8.5) |
| Extensiones PHP obligatorias | ctype, iconv, pcre, session, simplexml, tokenizer (casi siempre vienen activadas) |
| Extensiones recomendadas | intl, mbstring, pdo_mysql / pdo_pgsql / pdo_sqlite, xml, curl, zip, openssl, fileinfo |
| Composer | 2.x |
| Symfony CLI | Opcional pero muy recomendable |
| Git | Recomendable (Symfony CLI inicializa un repositorio en cada proyecto nuevo) |
| Docker | Opcional, para levantar la base de datos |
Comprueba lo que ya tienes instalado:
php -v
# PHP 8.5.x (cli) ...
php -m
# Lista de extensiones activas: ctype, curl, iconv, intl, mbstring, pdo_mysql...
composer --version
# Composer version 2.x.x
⚠️ Importante: Si
php -vmuestra una versión inferior a 8.4, Composer se negará a instalar Symfony 8 con un error del tipo “symfony/framework-bundle v8.1.x requires php >=8.4”. Actualiza PHP antes de seguir. Si necesitas trabajar con PHP 8.2 o 8.3, usa Symfony 7.4 LTS.
Instalar PHP y Composer
Windows (con Scoop, un gestor de paquetes para la terminal):
# Instalar Scoop (solo la primera vez)
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
Invoke-RestMethod -Uri https://get.scoop.sh | Invoke-Expression
# Instalar PHP y Composer
scoop install php composer git
💡 Consejo: En Windows, las extensiones se activan en el
php.ini. Ejecutaphp --inipara ver qué fichero se está usando y descomenta (quita el;) líneas comoextension=intl,extension=pdo_mysql,extension=pdo_sqlite,extension=mbstring,extension=openssloextension=zip.
macOS (con Homebrew):
brew install php composer
Linux (Debian/Ubuntu). Si tu distribución no trae PHP 8.4+, puedes usar el repositorio de Ondřej Surý en Ubuntu:
sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install php8.5-cli php8.5-xml php8.5-intl php8.5-mbstring \
php8.5-curl php8.5-zip php8.5-mysql php8.5-sqlite3 unzip
# Composer: sigue las instrucciones de https://getcomposer.org/download/
2. Symfony CLI
📘 Concepto: Symfony CLI es un programa (el ejecutable
symfony) que facilita el desarrollo: crea proyectos, comprueba requisitos, levanta un servidor web local con HTTPS, detecta tus contenedores Docker y ejecuta la consola y Composer con la versión correcta de PHP. No forma parte de tu proyecto: se instala una vez en tu ordenador.
Instalación
Windows (Scoop):
scoop install symfony-cli
macOS (Homebrew):
brew install symfony-cli/tap/symfony-cli
Linux Debian/Ubuntu (APT):
curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.deb.sh' | sudo -E bash
sudo apt install symfony-cli
Linux Fedora/RHEL (DNF):
curl -1sLf 'https://dl.cloudsmith.io/public/symfony/stable/setup.rpm.sh' | sudo -E bash
sudo dnf install symfony-cli
Cualquier sistema (instalador genérico):
curl -sS https://get.symfony.com/cli/installer | bash
Comprueba la instalación:
symfony version
# Symfony CLI version 5.x.x
Comprobar los requisitos
symfony check:requirements
Salida aproximada:
Symfony Requirements Checker
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> PHP is using the following php.ini file:
C:\Users\TuUsuario\scoop\apps\php\current\cli\php.ini
> Checking Symfony requirements:
.............................W.......
[OK]
Your system is ready to run Symfony projects
Optional recommendations to improve your setup
----------------------------------------------
* intl extension should be available
> Install and enable the intl extension (used for validators).
💡 Consejo: Atiende también a las recomendaciones opcionales (marcadas con
W). La extensiónintl, por ejemplo, es necesaria para formatear fechas y números según el idioma, algo que usaremos en el blog.
3. Crear el proyecto del blog
Con Symfony CLI (recomendado)
symfony new blog --webapp
Esto descarga la última versión estable (8.1), instala todos los paquetes típicos de una aplicación web y crea un repositorio Git con un primer commit. Si quieres fijar la versión:
# Versión concreta
symfony new blog --version="8.1.*" --webapp
# Última LTS (7.4)
symfony new blog --version=lts --webapp
Con Composer (equivalente)
composer create-project symfony/skeleton:"8.1.*" blog
cd blog
composer require webapp
📘 Concepto:
symfony newno es más que un atajo: por debajo ejecutacomposer create-project symfony/skeletony, con--webapp, añade el paquetesymfony/webapp-pack. El resultado es idéntico.
Skeleton frente a webapp
symfony/skeleton (sin --webapp) | --webapp (skeleton + webapp-pack) | |
|---|---|---|
| Pensado para | Microservicios, APIs, aplicaciones de consola | Aplicaciones web tradicionales con HTML |
| Paquetes | Lo mínimo: FrameworkBundle, Console, Dotenv, Flex, Runtime, YAML | Además: Twig, Doctrine ORM, formularios, validación, seguridad, Mailer, Messenger, Serializer, AssetMapper, Stimulus, Turbo, traducciones… |
| Herramientas de desarrollo | Ninguna | Maker Bundle, Web Profiler, Debug Bundle, PHPUnit |
Tamaño de vendor/ | Pequeño | Mayor |
| Directorios | bin, config, public, src, var, vendor | Además: assets, migrations, templates, tests, translations |
💡 Consejo: Para el blog del curso usaremos
--webapp. Cuando hagas una API pura (módulo 12), quizá prefieras el skeleton e ir añadiendo solo lo necesario. Nada te impide además empezar con el skeleton y hacercomposer require twigmás tarde: así es como funciona Flex.
4. El servidor web local
Entra en la carpeta del proyecto y arranca el servidor:
cd blog
symfony server:start
[OK] Web server listening
The Web server is using PHP CLI 8.5.x
http://127.0.0.1:8000
Abre esa URL en el navegador y verás la página de bienvenida de Symfony.
Comandos útiles del servidor
| Comando | Qué hace |
|---|---|
symfony server:start | Arranca el servidor y muestra los logs en la terminal (Ctrl+C para parar) |
symfony server:start -d | Lo arranca en segundo plano (daemon); también symfony serve -d |
symfony server:start --open | Arranca y abre el navegador |
symfony open:local | Abre el proyecto en el navegador |
symfony server:log | Muestra los logs del servidor en segundo plano |
symfony server:status | Indica si el servidor está en marcha |
symfony server:list | Lista todos los servidores locales activos |
symfony server:stop | Detiene el servidor en segundo plano |
HTTPS en local
Muchas funcionalidades modernas del navegador (cookies seguras, service workers, HTTP/2) exigen HTTPS. Symfony CLI crea una autoridad de certificación local para que tengas HTTPS sin avisos:
symfony server:ca:install
Solo tienes que hacerlo una vez por ordenador. A partir de entonces, el servidor estará disponible en https://127.0.0.1:8000.
⚠️ Importante: El servidor de Symfony CLI es solo para desarrollo. En producción usarás Nginx/Apache con PHP-FPM, FrankenPHP o un contenedor Docker (lo verás en el módulo 15).
¿Y si no uso Symfony CLI?
Puedes usar el servidor integrado de PHP apuntando a public/:
php -S 127.0.0.1:8000 -t public
Funciona, pero pierdes el HTTPS, la integración con Docker y la detección automática de la versión de PHP.
5. Estructura de directorios
Tras crear el proyecto con --webapp, verás algo así:
blog/
├── assets/ ← JavaScript y CSS (AssetMapper)
├── bin/
│ └── console ← la consola de Symfony
├── config/
│ ├── packages/ ← configuración de cada bundle (YAML)
│ ├── routes/ ← importación de rutas de bundles
│ ├── bundles.php
│ ├── routes.yaml
│ └── services.yaml
├── migrations/ ← migraciones de base de datos
├── public/
│ └── index.php ← front controller
├── src/
│ ├── Controller/
│ ├── Entity/
│ ├── Repository/
│ └── Kernel.php
├── templates/
│ └── base.html.twig
├── tests/
├── translations/
├── var/ ← caché y logs (no se versiona)
├── vendor/ ← dependencias de Composer (no se versiona)
├── .env
├── .env.dev
├── .env.test
├── compose.yaml
├── compose.override.yaml
├── composer.json
├── composer.lock
├── importmap.php
├── phpunit.dist.xml
└── symfony.lock
| Directorio / fichero | Contenido | ¿Lo tocas? |
|---|---|---|
assets/ | JavaScript (app.js), CSS (styles/app.css) y controladores Stimulus | Sí, a menudo |
bin/ | Ejecutables, sobre todo bin/console | Casi nunca |
config/ | Configuración: packages/ (un YAML por bundle), routes.yaml, services.yaml, bundles.php | Sí |
migrations/ | Clases PHP generadas por Doctrine para cambiar el esquema de la BD | Las generas y revisas |
public/ | Raíz web: lo único accesible desde el navegador (index.php, imágenes públicas, assets compilados) | Poco |
src/ | Tu código PHP (namespace App\): controladores, entidades, servicios… | Constantemente |
templates/ | Plantillas Twig | Constantemente |
tests/ | Tests de PHPUnit | Sí |
translations/ | Ficheros de traducción (YAML, XLIFF…) | Si tu app es multidioma |
var/ | Caché (var/cache/), logs (var/log/) y otros datos generados | No (se puede borrar) |
vendor/ | Dependencias instaladas por Composer | Nunca |
.env | Variables de entorno por defecto (sí se versiona) | Sí, con cuidado |
compose.yaml | Servicios Docker (base de datos, servidor de correo de pruebas…) | A veces |
importmap.php | Paquetes JavaScript del frontend (AssetMapper) | Mediante comandos |
symfony.lock | Registro de las recipes aplicadas por Flex (sí se versiona) | Nunca a mano |
⚠️ Importante: El servidor web debe apuntar siempre a
public/, nunca a la raíz del proyecto. Si apuntases a la raíz, cualquiera podría descargar tu.envcon las contraseñas.
💡 Consejo: En Symfony 8.1 la recipe del framework también crea un fichero
AGENTS.md(y unCLAUDE.mdque lo referencia) con indicaciones para asistentes de IA sobre las convenciones del proyecto. Puedes editarlo o eliminarlo si no lo usas.
Una mirada a config/services.yaml
# config/services.yaml
parameters:
services:
_defaults:
autowire: true # Inyecta dependencias automáticamente
autoconfigure: true # Registra automáticamente comandos, subscribers, extensiones Twig...
# Cada clase de src/ se convierte en un servicio
App\:
resource: '../src/'
Gracias a estas pocas líneas, cualquier clase que crees en src/ puede inyectarse en controladores y otros servicios sin configurar nada. Lo estudiarás a fondo en el módulo 09.
Y config/routes.yaml
# config/routes.yaml
controllers:
resource: routing.controllers
Esta entrada le dice a Symfony que cargue las rutas definidas con el atributo #[Route] en tus controladores.
6. Symfony Flex y las recipes
📘 Concepto: Symfony Flex es un plugin de Composer. Cada vez que instalas o eliminas un paquete, Flex comprueba si existe una recipe (receta) para él en el repositorio oficial (
symfony/recipes) o en el de la comunidad (symfony/recipes-contrib) y la aplica.
Una recipe puede:
- Registrar el bundle en
config/bundles.php - Copiar ficheros de configuración en
config/packages/ - Añadir variables a
.env(por ejemplo,DATABASE_URLoMAILER_DSN) - Añadir entradas a
.gitignore - Crear carpetas (
src/Entity/,templates/…) - Añadir servicios a
compose.yaml
Alias
Flex permite usar alias cortos:
composer require twig # symfony/twig-pack
composer require orm # symfony/orm-pack
composer require mailer # symfony/mailer
composer require --dev maker # symfony/maker-bundle
composer require --dev debug # symfony/debug-pack
📘 Concepto: Un pack es un paquete “vacío” que solo sirve para instalar varias dependencias de una vez. Por ejemplo,
symfony/orm-packinstala Doctrine ORM, DoctrineBundle y Doctrine Migrations.
symfony.lock
Flex guarda en symfony.lock qué recipe (y qué versión de ella) se aplicó a cada paquete. Debe versionarse en Git para que todo el equipo tenga el mismo estado.
# Ver las recipes instaladas y si tienen actualizaciones
composer recipes
# Ver los detalles de una
composer recipes symfony/framework-bundle
# Actualizar una recipe (te mostrará los cambios para que los revises con Git)
composer recipes:update symfony/framework-bundle
💡 Consejo: Cuando actualices de versión de Symfony (por ejemplo de 8.1 a 8.2), ejecuta
composer recipes:updatepaquete a paquete y revisa elgit diff: así incorporas los cambios de configuración recomendados.
7. Entornos y ficheros .env
Los tres entornos
📘 Concepto: Un entorno es un conjunto de configuración. La misma aplicación se comporta distinto según el entorno, que se controla con la variable
APP_ENV.
| Entorno | Uso | Características |
|---|---|---|
dev | Desarrollo en tu ordenador | Errores detallados, Web Debug Toolbar, caché que se regenera sola, Maker Bundle |
test | Ejecución de tests automáticos | Base de datos de pruebas, sin toolbar, emails que no se envían |
prod | Servidor de producción | Máximo rendimiento, errores genéricos para el usuario, logs |
La configuración específica de un entorno se escribe con la clave when@entorno:
# config/packages/framework.yaml
framework:
secret: '%env(APP_SECRET)%'
session: true
when@test:
framework:
test: true
session:
storage_factory_id: session.storage.factory.mock_file
Los ficheros .env
Symfony carga las variables de entorno desde varios ficheros, en este orden (los posteriores sobrescriben a los anteriores):
| Fichero | ¿Se versiona? | Para qué |
|---|---|---|
.env | Sí | Valores por defecto para todos (sin contraseñas reales) |
.env.local | No | Tus valores locales: contraseña de tu BD, claves de API… (se ignora en el entorno test) |
.env.dev, .env.test, .env.prod | Sí | Valores por defecto específicos de un entorno |
.env.dev.local, .env.test.local… | No | Valores locales específicos de un entorno |
⚠️ Importante: Las variables de entorno reales del sistema (las que define el servidor o Docker) tienen siempre prioridad sobre cualquier fichero
.env. Y nunca subas contraseñas reales a.env: van en.env.localo, en producción, en los secrets de Symfony (módulo 15).
Ejemplo de .env en el blog:
# .env
APP_ENV=dev
APP_SECRET=
APP_SHARE_DIR=var/share
DEFAULT_URI=http://localhost
DATABASE_URL="postgresql://app:!ChangeMe!@127.0.0.1:5432/app?serverVersion=16&charset=utf8"
MAILER_DSN=null://null
Y tu configuración personal:
# .env.local (no se sube a Git)
DATABASE_URL="mysql://root:mi_password@127.0.0.1:3306/blog?serverVersion=8.4.0&charset=utf8mb4"
Para ver qué valor final tiene cada variable y de qué fichero sale:
php bin/console debug:dotenv
💡 Consejo: El
APP_SECRETde desarrollo se genera automáticamente en.env.deval crear el proyecto. En producción debes definir uno propio, largo y aleatorio.
8. La consola: bin/console
📘 Concepto:
bin/consolees la herramienta de línea de comandos de tu aplicación. Desde ella creas código, limpias la caché, ejecutas migraciones, depuras rutas y servicios, y lanzas tus propios comandos.
php bin/console # lista todos los comandos disponibles
php bin/console list make # lista solo los del grupo "make"
php bin/console help cache:clear # ayuda de un comando
Con Symfony CLI puedes escribir symfony console en lugar de php bin/console. La ventaja es que usa la versión de PHP del proyecto y inyecta automáticamente las variables de los contenedores Docker (como DATABASE_URL).
Comandos que usarás a diario
| Comando | Para qué |
|---|---|
about | Información del proyecto: versión de Symfony, PHP, entorno, rutas de caché y logs |
cache:clear | Vacía la caché del entorno actual (--env=prod para producción) |
debug:router | Lista todas las rutas de la aplicación |
router:match /ruta | Indica qué ruta coincide con una URL |
debug:container | Lista los servicios del contenedor (admite búsqueda: debug:container mailer) |
debug:autowiring | Lista los tipos que puedes inyectar con autowiring |
debug:config framework | Muestra la configuración actual de un bundle |
config:dump-reference twig | Muestra todas las opciones posibles de un bundle |
debug:twig | Lista filtros, funciones y variables globales de Twig |
debug:dotenv | Muestra las variables de entorno y su origen |
lint:twig, lint:yaml, lint:container | Comprueban errores de sintaxis y configuración |
make:* | Generadores de código (Maker Bundle) |
doctrine:* | Base de datos y migraciones |
php bin/console about
-------------------- ----------------------------------
Symfony
-------------------- ----------------------------------
Version 8.1.x
Long-Term Support No
End of maintenance 01/2027
End of life 01/2027
-------------------- ----------------------------------
Kernel
-------------------- ----------------------------------
Type App\Kernel
Environment dev
Debug true
...
-------------------- ----------------------------------
PHP
-------------------- ----------------------------------
Version 8.5.x
...
¿Cuándo limpiar la caché?
En dev, Symfony detecta los cambios en tus ficheros y regenera la caché automáticamente. Aun así, si algo “raro” ocurre tras instalar un paquete o cambiar configuración:
php bin/console cache:clear
💡 Consejo: Si
cache:clearfalla porque la propia caché está corrupta, puedes borrar la carpeta a mano:rm -rf var/cache/*(en PowerShell:Remove-Item -Recurse -Force var\cache\*).
9. Web Debug Toolbar y Profiler
En el entorno dev, cada página HTML muestra en la parte inferior la Web Debug Toolbar, una barra con información de la petición:
| Panel | Qué te dice |
|---|---|
| Código de estado y ruta | Código HTTP, nombre de la ruta y controlador ejecutado |
| Tiempo y memoria | Cuánto tardó la página y cuánta memoria consumió |
| Logs | Mensajes de log, avisos y deprecations |
| Doctrine | Número de consultas SQL y su tiempo (con un icono de alerta si hay demasiadas) |
| Twig | Plantillas renderizadas y tiempo de renderizado |
| Seguridad | Usuario autenticado y sus roles |
| Formularios, Mailer, Messenger… | Información de cada componente usado |
Al hacer clic en cualquier panel se abre el Profiler (/_profiler), que guarda el historial detallado de las últimas peticiones: incluso de las peticiones AJAX, las redirecciones o las llamadas a tu API.
dump() y dd()
El componente VarDumper te da dos funciones mucho mejores que var_dump():
public function index(): Response
{
$articles = ['Hola Symfony', 'Rutas y controladores', 'Twig a fondo'];
dump($articles); // aparece en la toolbar (icono de la mira)
// dd($articles); // "dump and die": muestra y detiene la ejecución
return $this->render('home/index.html.twig', ['articles' => $articles]);
}
⚠️ Importante: La toolbar solo aparece si la respuesta es HTML completo (con
<body>). Si devuelvesnew Response('Hola')sin etiquetas HTML, no se mostrará. El Profiler, en cambio, sí registra la petición.
10. Maker Bundle
📘 Concepto: Maker Bundle es un generador de código para el entorno de desarrollo. En lugar de escribir a mano el esqueleto de un controlador, una entidad o un formulario, lo generas con un comando y luego lo adaptas.
Con --webapp ya está instalado. Si no:
composer require --dev symfony/maker-bundle
Los makers más usados
| Comando | Genera | Módulo |
|---|---|---|
make:controller | Controlador + plantilla Twig | 02 |
make:twig-extension | Extensión de Twig (filtros/funciones) | 03 |
make:twig-component | Twig Component | 03 |
make:stimulus-controller | Controlador de Stimulus | 04 |
make:entity | Entidad Doctrine y su repositorio (o añade campos) | 05 |
make:migration | Migración a partir de los cambios en las entidades | 05 |
make:crud | CRUD completo: controlador, formulario y plantillas | 05 |
make:fixtures | Clase de datos de prueba | 06 |
make:form | Clase de formulario | 07 |
make:validator | Restricción de validación propia | 08 |
make:user | Entidad de usuario para la seguridad | 10 |
make:security:form-login | Login con formulario | 10 |
make:registration-form | Formulario de registro | 10 |
make:voter | Voter de seguridad | 10 |
make:listener | Event listener o subscriber | 11 |
make:message | Mensaje y handler de Messenger | 11 |
make:test | Test unitario o funcional | 13 |
make:command | Comando de consola | 14 |
php bin/console list make # lista completa en tu versión
11. Base de datos para desarrollo
El blog necesitará una base de datos a partir del módulo 05. Tienes dos opciones.
Opción A: Docker (recomendada)
Al instalar Doctrine (incluido en --webapp), su recipe añade un servicio de PostgreSQL al fichero compose.yaml:
# compose.yaml (generado por la recipe de Doctrine)
services:
###> doctrine/doctrine-bundle ###
database:
image: postgres:${POSTGRES_VERSION:-16}-alpine
environment:
POSTGRES_DB: ${POSTGRES_DB:-app}
# You should definitely change the password in production
POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:-!ChangeMe!}
POSTGRES_USER: ${POSTGRES_USER:-app}
healthcheck:
test: ["CMD", "pg_isready", "-d", "${POSTGRES_DB:-app}", "-U", "${POSTGRES_USER:-app}"]
timeout: 5s
retries: 5
start_period: 60s
volumes:
- database_data:/var/lib/postgresql/data:rw
###< doctrine/doctrine-bundle ###
volumes:
###> doctrine/doctrine-bundle ###
database_data:
###< doctrine/doctrine-bundle ###
Y en compose.override.yaml (solo para desarrollo) expone el puerto:
# compose.override.yaml
services:
###> doctrine/doctrine-bundle ###
database:
ports:
- "5432"
###< doctrine/doctrine-bundle ###
📘 Concepto: Los comentarios
###> paquete ###y###< paquete ###marcan el bloque que gestiona la recipe. Flex los usa para saber qué eliminar si desinstalas el paquete. No los borres.
Arranca el contenedor:
docker compose up -d
docker compose ps # comprueba que "database" está "healthy"
💡 Consejo: La línea
"5432"(sin puerto del host) hace que Docker asigne un puerto aleatorio. No pasa nada: si usassymfony server:startysymfony console, Symfony CLI detecta el contenedor y defineDATABASE_URLautomáticamente con el puerto correcto. Compruébalo consymfony var:export --multiline.
¿Prefieres MySQL o MariaDB en Docker?
Puedes sustituir el servicio por este:
# compose.yaml (alternativa con MySQL)
services:
database:
image: mysql:8.4
environment:
MYSQL_DATABASE: blog
MYSQL_USER: app
MYSQL_PASSWORD: '!ChangeMe!'
MYSQL_ROOT_PASSWORD: root
ports:
- "3306:3306"
volumes:
- database_data:/var/lib/mysql
volumes:
database_data:
Opción B: MySQL / MariaDB instalados en local
Si ya tienes MySQL o MariaDB (por ejemplo con XAMPP, Laragon o el instalador oficial), solo tienes que configurar DATABASE_URL en .env.local:
# .env.local — MySQL
DATABASE_URL="mysql://root:@127.0.0.1:3306/blog?serverVersion=8.4.0&charset=utf8mb4"
# .env.local — MariaDB
# DATABASE_URL="mysql://root:@127.0.0.1:3306/blog?serverVersion=11.4.2-MariaDB&charset=utf8mb4"
# .env.local — SQLite (sin servidor: un fichero en var/)
# DATABASE_URL="sqlite:///%kernel.project_dir%/var/data_%kernel.environment%.db"
⚠️ Importante: Indica siempre
serverVersioncon la versión real de tu servidor. Doctrine la usa para generar el SQL adecuado; si no coincide, las migraciones pueden salir distintas en cada máquina.
Comprueba la conexión creando la base de datos:
php bin/console doctrine:database:create
# Created database "blog" for connection named default
12. Configurar el editor
PhpStorm
- Instala el plugin Symfony Support (Settings → Plugins → Marketplace). Activa el plugin para el proyecto cuando te lo sugiera.
- Te da autocompletado de nombres de rutas, plantillas, servicios y parámetros; navegación con Ctrl+clic desde
render('article/show.html.twig')a la plantilla; y soporte para Twig y YAML. - Marca
var/como Excluded para que no indexe la caché. - Configura el intérprete de PHP (Settings → PHP) con la versión 8.4 o superior.
Visual Studio Code
Extensiones recomendadas:
| Extensión | Para qué |
|---|---|
| PHP Intelephense | Autocompletado, navegación y análisis de PHP |
| Una extensión de Twig (resaltado y formato) | Sintaxis de plantillas .twig |
| YAML (Red Hat) | Valida los YAML de configuración usando los esquemas que Symfony declara al inicio de services.yaml y routes.yaml |
| PHP Debug (Xdebug) | Depuración paso a paso |
| EditorConfig | Respeta el .editorconfig que genera Symfony |
💡 Consejo: Fíjate en la primera línea de
config/services.yaml:# yaml-language-server: $schema=.... Esa línea permite que la extensión YAML de VS Code te avise de errores y te autocomplete opciones mientras escribes.
13. Tu primer controlador: “Hola mundo”
Paso 1: generar el controlador
php bin/console make:controller Home
created: src/Controller/HomeController.php
created: templates/home/index.html.twig
Success!
Next: Open your new controller class and add some pages!
Paso 2: revisar el código generado
<?php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class HomeController extends AbstractController
{
#[Route('/home', name: 'app_home')]
public function index(): Response
{
return $this->render('home/index.html.twig', [
'controller_name' => 'HomeController',
]);
}
}
Paso 3: adaptarlo a la portada del blog
Cambia la ruta a / y pasa algunos datos a la plantilla:
<?php
// src/Controller/HomeController.php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class HomeController extends AbstractController
{
#[Route('/', name: 'app_home')]
public function index(): Response
{
// De momento, datos de ejemplo. En el módulo 05 vendrán de la base de datos.
$articles = [
'Hola, Symfony 8',
'Rutas y controladores con atributos',
'Plantillas Twig desde cero',
];
return $this->render('home/index.html.twig', [
'blog_name' => 'Blog de Symfony',
'articles' => $articles,
]);
}
}
Paso 4: la plantilla
{# templates/home/index.html.twig #}
{% extends 'base.html.twig' %}
{% block title %}{{ blog_name }}{% endblock %}
{% block body %}
<h1>¡Hola mundo desde {{ blog_name }}!</h1>
<h2>Últimos artículos</h2>
<ul>
{% for title in articles %}
<li>{{ title }}</li>
{% endfor %}
</ul>
{% endblock %}
Visita https://127.0.0.1:8000/: verás el título, la lista de artículos y la Web Debug Toolbar abajo.
Paso 5: comprobar la ruta
php bin/console debug:router
-------------------------- -------- -------- ------ -----------------------------------
Name Method Scheme Host Path
-------------------------- -------- -------- ------ -----------------------------------
_preview_error ANY ANY ANY /_error/{code}.{_format}
_wdt ANY ANY ANY /_wdt/{token}
_profiler_home ANY ANY ANY /_profiler/
...
app_home ANY ANY ANY /
-------------------------- -------- -------- ------ -----------------------------------
Un controlador aún más simple
Un controlador no necesita plantilla. Basta con devolver un objeto Response:
<?php
// src/Controller/HelloController.php
declare(strict_types=1);
namespace App\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;
final class HelloController
{
#[Route('/hola/{nombre}', name: 'app_hello')]
public function hello(string $nombre = 'mundo'): Response
{
return new Response(sprintf(
'<html><body><h1>¡Hola, %s!</h1></body></html>',
htmlspecialchars($nombre)
));
}
}
/hola/Ana → “¡Hola, Ana!”. /hola → “¡Hola, mundo!”.
✏️ Ejercicio: Crea la ruta
/hola/{nombre}y prueba qué ocurre si quitashtmlspecialchars()y visitas/hola/<b>Ana</b>. Así entenderás por qué Twig escapa todo por defecto.
Errores comunes al empezar
| Error | Causa | Solución |
|---|---|---|
No route found for "GET /home" | La ruta no existe o tiene otro path | Revisa debug:router |
Unable to find template "home/index.html.twig" | La plantilla no está en templates/ o el nombre no coincide | Comprueba la ruta y las mayúsculas |
The controller must return a "Response" object | El método no devuelve nada o devuelve un array | Devuelve $this->render(...), $this->json(...) o una Response |
| Página en blanco sin toolbar | La respuesta no es HTML completo | Usa una plantilla que extienda base.html.twig |
Could not open input file: bin/console | No estás en la carpeta del proyecto | Haz cd blog |
Port 8000 is already in use | Ya hay un servidor en marcha | symfony server:stop o symfony server:start --port=8001 |
14. Ejercicios
Ejercicio 1: Entorno preparado
Instala PHP 8.4+ (mejor 8.5), Composer y Symfony CLI en tu sistema. Ejecuta symfony check:requirements y resuelve todas las recomendaciones, incluida la extensión intl. Anota la ruta de tu php.ini.
Ejercicio 2: Dos proyectos, dos sabores
Crea dos proyectos: prueba-skeleton con symfony new prueba-skeleton y prueba-webapp con symfony new prueba-webapp --webapp. Compara: carpetas creadas, número de bundles en config/bundles.php, ficheros en config/packages/ y tamaño de vendor/. Escribe una tabla con las diferencias.
Ejercicio 3: Flex en acción
En prueba-skeleton, ejecuta composer require twig y observa con git status y git diff qué ficheros ha creado o modificado la recipe. Después ejecuta composer remove twig y comprueba que Flex deshace los cambios.
Ejercicio 4: El proyecto del blog
Crea el proyecto definitivo blog con --webapp, arráncalo con HTTPS en segundo plano y ábrelo con symfony open:local. Levanta la base de datos con docker compose up -d (o configura tu MySQL local en .env.local) y ejecuta symfony console doctrine:database:create.
Ejercicio 5: Explorando la consola
Ejecuta y anota para qué sirve la salida de: about, debug:router, debug:autowiring --all, debug:config twig, debug:dotenv y debug:container --parameters. ¿Qué entorno y qué versión de PHP reporta about?
Ejercicio 6: Hola mundo del blog
Con make:controller, crea HomeController con la ruta / (nombre app_home) que muestre el nombre del blog y una lista de cinco títulos de artículos. Usa dump() para inspeccionar el array en la Web Debug Toolbar y abre el Profiler para ver qué plantillas se han renderizado.
Ejercicio 7: Variables de entorno
Añade en .env la variable BLOG_NAME="Mi blog" y en .env.local sobrescríbela con tu nombre. Usa debug:dotenv para comprobar qué valor gana. (En el módulo 09 aprenderás a inyectarla en tus servicios.)
Resumen
| Concepto | Comando / detalle |
|---|---|
| Requisitos | PHP >= 8.4, Composer 2, extensiones ctype, iconv, pcre, session, simplexml, tokenizer (+ intl, pdo_*) |
| Instalar Symfony CLI | scoop install symfony-cli / brew install symfony-cli/tap/symfony-cli / apt install symfony-cli |
| Comprobar requisitos | symfony check:requirements |
| Proyecto web | symfony new blog --webapp |
| Equivalente Composer | composer create-project symfony/skeleton blog + composer require webapp |
| Servidor local | symfony server:start, -d, symfony open:local, symfony server:stop |
| HTTPS local | symfony server:ca:install (una vez) |
| Código propio | src/ (namespace App\) y templates/ |
| Raíz web | public/ (front controller index.php) |
| Flex | Plugin de Composer que aplica recipes; estado en symfony.lock |
| Entornos | dev, test, prod (APP_ENV), configuración con when@entorno |
Ficheros .env | .env (versionado) → .env.local (privado) → .env.{entorno} → .env.{entorno}.local |
| Consola | php bin/console o symfony console |
| Depurar | debug:router, debug:container, debug:autowiring, about, cache:clear |
| Toolbar y Profiler | Solo en dev; /_profiler; dump() y dd() |
| Maker Bundle | make:controller, make:entity, make:crud… |
| Base de datos | docker compose up -d o DATABASE_URL en .env.local |