Vous êtes sur la page 1sur 161

Desarrollo fcil con Symfony

Aprende a desarrollar con Symfony y otras herramientas


que te apoyarn y facilitarn en gran medida tu trabajo
como desarrollador web

Jon Torrado
Este libro est a la venta en http://leanpub.com/aprendesymfony2

Esta versin se public en 2017-05-01

This is a Leanpub book. Leanpub empowers authors and publishers with the Lean Publishing
process. Lean Publishing is the act of publishing an in-progress ebook using lightweight tools and
many iterations to get reader feedback, pivot until you have the right book and build traction once
you do.

2014 - 2017 Jon Torrado


Twitea sobre el libro!
Por favor ayuda a Jon Torrado hablando sobre el libro en Twitter!
El hashtag sugerido para este libro es #aprendesymfony2.
Descubre lo que otra gente est diciendo sobre el libro haciendo click en este enlace para buscar el
hashtag en Twitter:
https://twitter.com/search?q=#aprendesymfony2
Tambin por Jon Torrado
Easy development with Symfony
A todos aquellos que han aportado algo de conocimiento en mi vida, a todas esas personas que
cambiaron el rumbo de mi carrera profesional y que han hecho que ahora sea la persona que soy,
tanto en lo personal como en lo profesional. En este libro escribo un cachito de cada uno de vosotros.
Gracias.
ndice general

Introduccin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
Por qu escribo este libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
Qu vas a aprender . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
A quin est dirigido . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Cmo se divide el libro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3
Nota del autor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3

Captulo 1: instalacin y configuracin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4


El instalador de Symfony . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4
Composer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5
Base de datos MySQL . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 6
Los permisos de escritura . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
El servidor web . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7
Entorno de desarrollo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Truco 1 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 8
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 9

Captulo 2: los bundles de terceros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10


Buscando nuestro bundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Instalando un nuevo bundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 10
Truco 2 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

Captulo 3: Admin Bundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13


Instalando el Admin Bundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 13
Creando nuestra Admin Class . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 16
Truco 3 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Otras posibilidades . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

Captulo 4: User Bundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21


Instalando User Bundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 21
Creando la estructura de usuarios . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 26
NDICE GENERAL

Creando nuestro usuario admin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 27


Truco 4 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 28

Captulo 5: MopaBootstrap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Instalando MopaBootstrapBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Preparando la plantilla base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Truco 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38

Captulo 6: Gulp (1) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39


Instalacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 39
Creando nuestro primer SCSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 40
Minificando nuestro CSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 42
Autoprefixer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43
Gulp watch . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Tarea por defecto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 45
Truco 6 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 46
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 47

Captulo 7: Assetic con JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48


Instalacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 48
Configuracin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 49
Minificando nuestro JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 50
Recomendaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Truco 7 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 53

Captulo 8: ejemplo completo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54


El flujo de la aplicacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Creando la ruta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54
Creando el mtodo action . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 55
Nuestra propia home . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 56
El carrusel de imgenes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 57
Los claims . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 63
Formulario de contacto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 65
Enviando un email . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72
Truco 8 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 74
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 75

Captulo 9: Doctrine Extensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76


Instalacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
Filtro timestampable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 76
NDICE GENERAL

Filtro slugabble . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Filtro softdeleteable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Otros filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Truco 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84

Captulo 10: LiipImagineBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85


Instalacin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85
Configuracin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 86
Uso con Twig . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 89
Uso con PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 90
Truco 10 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 91
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 92

Captulo 11: otros bundles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93


EWZRecaptchaBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 93
AcceleratorCacheBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
FOSRestBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
NelmioApiDocBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94
DoctrineFixturesBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
DoctrineMigrationsBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
FOSJSRoutingBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 95
HWIOAuthBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
KnpSnappyBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 96
JMSTranslationBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Truco 11 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 97
Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 98

Captulo 12: Gulp (2) . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99


Sass Lint . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 99
JSCS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 104
PHP CS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 106
PHP CBF . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107
Ejecutar comandos PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 108
Truco 12 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 109
Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 110

Captulo 13: configuracin adicional de Symfony . . . . . . . . . . . . . . . . . . . . . . . 111


ParamConverter . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 111
Plantilla sin controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 113
Redireccionamientos sin controller . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 114
Dump autoload . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
Hide logs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115
NDICE GENERAL

Enviar errores 500 por email . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 116


Configuracin para PhpStorm . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Sacando las sesiones a otra carpeta . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 117
Truco 13 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 118
Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 119

Captulo 14: modificando SonataUserBundle . . . . . . . . . . . . . . . . . . . . . . . . . . 120


Modificando FOSUserBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Sobrescribiendo las plantillas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 120
Sobrescribiendo los controladores . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122
Modificando el login y el registro . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123
Modificando el registro (2) y los emails . . . . . . . . . . . . . . . . . . . . . . . . . . . . 124
Otras modificaciones . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Truco 14 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 125
Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 126

Captulo 15: despliegue . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127


Magallanes . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Ansible . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 127
Capifony . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Capistrano . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128
Desplegando con capistrano-symfony . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129
Capistrano-symfony: resultado final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133
Truco 15 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 134
Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 135

Captulo 16: testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 136


Qu es eso del testing? Me sirve eso a m? . . . . . . . . . . . . . . . . . . . . . . . . . 136
TDD vs BDD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 137
Herramientas de testing en PHP . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 138
Integracin continua . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 145
Truco 16 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 146
Conclusin . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 149

Eplogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Bower . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Gassetic . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
Gulp con JavaScript . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 150
PostCSS . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Desarrollos hechos con Symfony . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
DDD . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Si te ha gustado . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 151
Agradecimientos al lector . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152
Introduccin
Desde que comenc mi carrera profesional, prcticamente todo lo que he hecho ha estado relacionado
con el mundo de la web. Al terminar la universidad, mis conocimientos sobre el desarrollo en este
campo eran muy limitados. Tan solo conoca un puado de elementos HTML y algo un poco ms
sofisticado llamado Struts, un framework MVC en Java. Tuve que aprender en un tiempo rcord
para estar a la altura del entorno en el que me estaba introduciendo, y esto fue posible gracias a la
amplia documentacin y ayuda que se puede encontrar por Internet.
Comenc utilizando una serie de CMS para diferentes empresas, como Moodle o Drupal, para
posteriormente embarcarme en el desarrollo a medida. Y tan a medida! Tuve que mantener una web
que estaba realizada de 0, sin ayuda de ningn framework ni nada por el estilo. Y menos mal que tuve
que hacerlo! Este fue el proyecto que cambi mi rumbo profesional ya que pude sumergirme en las
entraas de todo tipo de libreras y cdigos espaguetti pertenecientes a gente realmente interesante.
Tras una serie de giros algo inesperados, comenc mi viaje en el mundo de Symfony. Gracias a su
excelente documentacin, la cual aconsejo leer encarecidamente ms de una vez al ao, y de bundles
de terceros que mantiene una comunidad realmente activa, pronto estaba desarrollando webs
increblemente potentes con un esfuerzo ridculo comparado a lo que haba estado desarrollando
hasta ese momento.
Tras varios aos desarrollando webs bajo Symfony, considero ste un momento oportuno para
escribir el libro que te encuentras leyendo. Aqu voy a recopilar lo que considero ms importante para
comenzar a desarrollar un proyecto en Symfony y procurar que seas capaz de utilizar tecnologas
que facilitarn tu trabajo en una medida que jams habas imaginado (o por lo menos yo no lo
haba hecho). Tampoco te creas que voy a escribir el libro definitivo y que dominar el mundo,
ya que da a da sigo aprendiendo en un sector en el que ocurre de todo a diario y en el que es
completamente imposible seguir el frentico ritmo cambiante en el que se encuentra. Pero intentar
estar a la altura, ya que soy de Bilbao1 . Tan solo quiero pedirte una cosa antes de leerte este libro:
lee la documentacin de Symfony. La documentacin est genial e incluso traducida al castellano,
as que no voy a repetir ninguna parte que ya se encuentra perfectamente explicada en otro sitio.
Tampoco hace falta que seas un profesional de la temtica, pero al menos que sepas qu son y como
funcionan las rutas, los controladores y las plantillas, adems de saber qu es la consola de Symfony
(en resumen, los 8 primeros captulos del libro de la documentacin). Ests deseando empezar? Yo
tambin, as que vamos al lo.
1
http://www.frikipedia.es/friki/Bilbao
Introduccin 2

Por qu escribo este libro


Una de las partes ms importantes de mi vida es la que dedico a la enseanza. Poder expandir tu
conocimiento a otras personas y hacer crecer este sector entre todos es algo que considero vital para
cualquier temtica, no solo esta. Creo que en la sociedad actual necesitamos de profesores excelentes
que traten de mejorarse cada da ya que, dando lo mejor de ellos, conseguirn que sus alumnos den
lo mejor de s mismos.
Esto que te acabo de contar lo conecto con dos conceptos japoneses muy interesantes. El primero
de ellos (aunque es una leyenda urbana, permteme ser feliz contndotelo) es que, en su cultura, los
profesores son los nicos que no tienen que hacer una reverencia ante el emperador, porque ste
considera que sin ellos no existira un emperador.
Por otro lado est uno de los conceptos que desde que lo le han aplicado una mejora sustancial en
mi vida: Kaizen. Este concepto japons viene a resumirse como el proceso de mejora que debemos
aplicarnos cada uno de nosotros a diario.
Todo lo que voy a escribir en este libro lo he aprendido gratuitamente durante muchas semanas,
incluso aos, y decido compartirlo contigo para que no tengas que gastar tanto tiempo en aprenderlo
t. Adems considero que ponerle un precio a algo que he conseguido gratis y que actualmente me
da de comer es bastante absurdo. Es por esto que este libro es completamente gratuito, pero si al
finalizarlo te gusta y quieres agradecerme el trabajo que he realizado, puedes comprarlo por el precio
que consideres sabiendo que ests ayudando a una buena causa, ya que donar el 50% de lo que
pagues por el libro a la plataforma de crowdfunding watchi2 que proporciona todo tipo de ayuda
mdica a gente de todo el mundo que lo necesita. Aprendes, disfrutas y ayudas a una buena causa.

Qu vas a aprender
Durante el libro realizaremos una serie de instalaciones y configuraciones de forma prctica para
disponer de un proyecto base que te servir de cara al futuro para cualquiera de tus proyectos.
Algunas cosas puede que no sean necesarias en algunos de ellos; otras puede que haya aadir y que
tengas que descubrir por tu cuenta. Intentar cubrir la mayora de elementos que se utilizan a da de
hoy tanto de Symfony como del desarrollo web en general y que podamos incorporar a este proyecto
base.
No vas a aprender a utilizar Symfony desde 0. Considero que la documentacin oficial de Symfony
es estupenda para este cometido. Pero tras leer esta parte bsica compuesta por los 8 primeros
captulos de su documentacin, mi objetivo es que sepas utilizar Symfony de una manera ms
profesional y orientada al producto final.
Hay algo que me he dejado fuera del tintero? No pasa nada, tienes mi Twitter para hacerme llegar
lo que creas conveniente e ir recopilando cada una de las cosas que me digis. Procurar mantener
el libro actualizado para que sirva como documentacin extra de tus proyectos.
2
https://leanpub.com/causes/watsi
Introduccin 3

A quin est dirigido


El libro est dirigido a cualquier persona que est interesada en aprender a desarrollar con Symfony,
sepa previamente utilizar este framework o no. Incluso a las personas que no han programado en
PHP o ni siquiera han programado. Lgicamente, depende del punto en el que te encuentres, puede
que te cueste ms o menos captar algunas de las cuestiones que plantear durante el libro, pero esto
no significa que ests fuera del target ni mucho menos.
En cuanto al nivel del lector, me arriesgar a decir que es un libro para principiante - intermedio.
Si ya llevas mucho tiempo con Symfony y en el desarrollo web, es posible que no aprendas nada.
An as, tambin cabe la posibilidad de que alguno de los captulos dedicados a tecnologas externas
sean del inters de profesionales del sector, pero desde luego no est pensado para ese nivel de
desarrollador.
Ganas y fuerza de voluntad, es lo nico que necesitas para adentrarte en este mundo, nimo!

Cmo se divide el libro


Cada captulo contendr una temtica concreta explicada de la forma en la que yo trabajo. Adems,
en cada uno de ellos, dedicar una seccin para revelarte un truco que o bien te ayudar a ser
ms eficiente en el trabajo, o te permitir hacer las cosas mejor mediante atajos y triquiuelas que
circulan por la red.
Empezaremos la casa por el tejado y, tras instalar el framework lo llenaremos de un montn de
bundles de terceros antes de tocar ni una sola lnea de cdigo. Estos bundles nos permitirn tener
una base prefabricada para nuestros proyectos. Es posible que algunos de ellos no los necesites, pero
aunque ste sea el caso, es muy interesante que dediques algo de tiempo a saber cmo estn hechos
por dentro. sto es algo que dejo en tus manos, ya que como desarrollador has de ser inquieto y
mirar ms all del funciona y ya est.
Es hora de empezar a trabajar. Comenzamos con la instalacin y sus buenas prcticas.

Nota del autor


Es posible que este libro tenga elementos desactualizados y por ello te salgan errores. Te invito
a que contactes conmigo para cualquier duda, te responder con la mayor brevedad. Asimismo,
disponemos de un Standard de Symfony que mantenemos desde LIN3S, actualizado y funcionando,
y que puedes ver aqu3 ;
3
https://github.com/LIN3S/SymfonyStandard
Captulo 1: instalacin y
configuracin
S que esta parte est muy bien cubierta en la documentacin oficial4 de Symfony, pero existen
ciertos conceptos que estn separados en varios captulos. Algunos de ellos se pueden incluso realizar
de varias formas distintas. Y la pregunta es, cul es la mejor? La verdad es que muchas veces durante
este libro te responder que no hay una forma mejor o peor, sino que debes escoger la forma con
la que te sientas ms cmodo.
Existen unas buenas prcticas que normalmente los desarrolladores intentamos seguir, y junto a esas
buenas prcticas me gustara aadir las buenas prcticas de Jon (ya te dije que soy de Bilbao) que
compartir contigo en este libro. Antes de poner los pasos que normalmente sigo yo, debes saber
que utilizo prcticamente siempre Linux y/o OSX, por lo que si usas Windows tendrs que adaptar
lo que escribo durante los diferentes captulos para que funcione en tu sistema operativo. Pero no te
asustes, todo lo relacionado con Windows est disponible en la documentacin oficial.
El objetivo de este captulo es aprender a instalar un proyecto Symfony, y a su vez dejar preparado
nuestro sistema operativo para que las futuras instalaciones del framework sean increblemente ms
sencillas. Empecemos!

El instalador de Symfony
Desde hace ya un tiempo, Symfony cuenta con su propio instalador en un archivo .phar 5 . Antes de
disponer de este instalador, tenas que descargarte un archivo comprimido desde la web oficial de
Symfony y descomprimirlo en la carpeta sobre la que ibas a desarrollar; posteriormente aadieron
el proyecto en Packagist 6 y el gestor de dependencias Composer 7 hizo de instalador. Actualmente, lo
mejor que puedes hacer es mantener el instalador de Symfony como binario de tu sistema operativo
para que puedas ejecutarlo como cualquier otro comando. Para ello, utiliza los siguientes comandos
o los correspondientes a tu sistema operativo:

1 sudo mkdir /usr/local/bin


2 sudo curl -LsS http://symfony.com/installer -o /usr/local/bin/symfony
3 sudo chmod a+x /usr/local/bin/symfony
4
http://symfony.com/doc/current/setup.html
5
http://php.net/manual/es/phar.using.intro.php
6
https://packagist.org/
7
https://getcomposer.org/
Captulo 1: instalacin y configuracin 5

Una vez realizado, podrs ejecutar el comando symfony desde la ruta que quieras. Como consejo me
gustara decirte que procures mantener el instalador de Symfony actualizado ejecutando el siguiente
comando:

1 sudo symfony self-update

Genial! Ahora que ya dispones del instalador de forma global, ya puedes crear tu primer proyecto
Symfony. Para ello, ejecuta el siguiente comando:

1 symfony new aupa_bilbao

El comando anterior crear la carpeta aupa_bilbao con todo lo necesario para comenzar a trabajar
sin perder un solo minuto, todo ello automgicamente. Si has ledo la documentacin de Symfony
o has utilizado Composer con anterioridad, deberas saber que la carpeta vendor, situada en la raz,
contiene todas las dependencias necesarias de tu proyecto. Estas dependencias se definen en el
archivo tambin situado en la raz composer.json, archivo que utilizaremos en varias ocasiones
durante todo el libro. Sin embargo, el otro archivo situado a la par cuyo nombre es composer.lock
muestra las versiones bloqueadas, versiones exactas que se descargarn al ejecutar composer
install.
En este punto, ya hemos hablado varias veces sobre Composer, pero, qu es Composer?

Composer
Composer es la herramienta por excelencia para la gestin de dependencias de PHP que nos ayuda a
introducir paquetes o libreras de terceros en nuestro proyecto y as poder utilizarlos de una forma
sencilla. Si has utilizado npm de Node o bundler de Ruby, esto es muy similar; si no es el caso y
nunca los has usado, no te preocupes puesto que en este libro tendrs la oportunidad de hacerlo.
Al igual que el instalador de Symfony, lo mejor es disponer de Composer como comando del sistema
operativo y, de esta forma, poder ejecutarlo desde cualquier ruta. Para esto, ejecuta los siguientes
comandos:

1 curl -sS https://getcomposer.org/installer | php


2 sudo mv composer.phar /usr/local/bin/composer

En cuanto al proyecto Symfony, gracias a su propio instalador, ya tenemos los paquetes base
descargados en la carpeta vendor y los archivos composer.json y composer.lock bien definidos.
Pero si el proyecto sobre el que ests trabajando es un proyecto descargado o ests desplegando el
proyecto en un servidor, lo correcto es que ejecutes el comando composer install en la raz del
mismo. Este comando instalar las versiones concretas especificadas en el archivo composer.lock y
que, a su vez, deberan ser las versiones que estn probadas y carecen de fallos o incompatibilidades.
Captulo 1: instalacin y configuracin 6

Sin embargo, si lanzas el comando composer update, el proceso ignorar el archivo de bloqueo y
actualizar a la ltima versin posible que hayas definido en el archivo composer.json, actualizando
el archivo de bloqueo cuando termine el proceso de descarga.
Oye! No te olvides de actualizar tus dependencias, cada semana se corrigen bugs de seguridad o se
aaden nuevas funcionalidades que seguro que aportan valor a tu proyecto. Y, por supuesto, mantn
tambin tu Composer actualizado con el siguiente comando:

1 sudo composer self-update

Base de datos MySQL


Todava hay das en los que me encuentro proyectos en codificacin latin1. Te aconsejo que evites
esta codificacin y utilices UTF-8 para cada uno de tus proyectos. Con Symfony, no necesitas crear
la base de datos manualmente ya que la consola la crear por ti, pero, para utilizar la codificacin
correcta por defecto, debers realizar uno de las dos siguientes posibilidades. La primera es desde
la configuracin de Symfony. Necesitas disponer de doctrine-bundle en, al menos, la versin 1.6
(mira tu composer.json):

1 "doctrine/doctrine-bundle": "^1.6",

Tras esto, hay que configurar el framework de Symfony para usar la codificacin correcta. Edita el
archivo app/config/config.yml:

1 doctrine:
2 dbal:
3 # ...
4 default_table_options:
5 charset: utf8mb4
6 collate: utf8mb4_unicode_ci
7 engine: InnoDB

La otra posibilidad es editando el archivo my.cnf. Este archivo contiene todos los parmetros de
configuracin para MySQL, y es aqu donde debes aadir las prximas dos lneas a la seccin
[mysqld]:

1 [mysqld]
2 collation-server = utf8mb4_unicode_ci
3 character-set-server = utf8mb4

Hecho esto, solo te queda decirle a Symfony cules son tus datos de conexin a la base de datos.
Dirgete al archivo app/config/parameters.yml que automticamente te ha creado el instalador, y
edita los parmetros que se encuentran en l introduciendo los datos que corresponden a tu mquina.
Una vez modificados, lanza el siguiente comando para crear la base de datos:
Captulo 1: instalacin y configuracin 7

1 bin/console doctrine:database:create

Si usas Windows, tienes que escribir php antes del comando.

Buen trabajo! Ya tienes tu base de datos creada para empezar a trabajar. Solo te queda saber una
cosilla ms: Symfony hace uso de una serie de directorios a los que hay que darles permiso de
escritura. Dichos directorios los usar tanto tu servidor web como la consola del framework, por lo
que ambos usuarios del sistema operativo deben tener permiso para escribir en ellos.

Los permisos de escritura


Este es un error que me encuentro en muchos despliegues en local, y es que mucha gente se olvida
de darle permiso de escritura a la carpeta var, donde est la cach, por ejemplo.
En este enlace8 puedes ver cmo debes darle permiso a dicha carpeta, y si usas un sistema Linux
basado en Ubuntu, los siguientes comandos son los que te sirven:

1 HTTPDUSER=`ps axo user,comm | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]gin\


2 x' | grep -v root | head -1 | cut -d\ -f1`
3 sudo setfacl -R -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX var
4 sudo setfacl -dR -m u:"$HTTPDUSER":rwX -m u:`whoami`:rwX var

La primera lnea almacena en HTTPDUSER el usuario que est ejecutando nuestro servidor web, ya
que dependiendo del sistema operativo y del servidor, este usuario vara. Las otras dos lneas se
encargarn de darle a ese usuario y al usuario de la consola (es decir, el usuario con el que ejecutars
los comandos en la terminal) permisos para escribir en dicha carpeta.
Otorgados los permisos pertinentes, ya ests a punto de terminar con la instalacin.

El servidor web
Tienes la posibilidad de trabajar con Apache de forma nativa creando un VirtualHost en tu
ordenador. No entra dentro del alcance de este libro el mostrar cmo se hace ya que existe una
forma mucho ms sencilla que, aunque es menos potente, didcticamente nos vale. Si quieres saber
cmo llegar a buen puerto con esta primera opcin, visita este enlace9 .
En este momento, y suponiendo que tienes PHP instalado en tu ordenador, te vale con ejecutar el
siguiente comando en la raz del proyecto:

8
http://symfony.com/doc/current/setup/file_permissions.html
9
http://symfony.com/doc/current/setup/web_server_configuration.html
Captulo 1: instalacin y configuracin 8

1 bin/console server:run

Tal y como te muestra la terminal, ya puedes acceder a tu proyecto Symfony navegando a la URL
http://127.0.0.1:8000/. Lamentablemente, todava no hay (casi) nada que ver, pero lo solucionaremos
muy pronto.

Entorno de desarrollo
Si vas a desarrollar Symfony, te aconsejo que dispongas de un entorno de desarrollo. A da de hoy, el
IDE que mejor trabaja con Symfony es PhpStorm10 . Existen muchos otros como NetBeans o Aptana,
e incluso editores como Sublime o Brackets que funcionan a la perfeccin. Prubalos y escoge el que
mejor se adapte a tu forma de trabajar, aunque personalmente te aconsejo PhpStorm por algn que
otro truco que saldr en futuros captulos.

Truco 1
En cada captulo te dar un consejo que sin duda te ayudar bastante. Depende de ti el utilizar
estos consejos o no (salos insensat@!). A partir de ahora, utilizaremos muy a menudo la consola
de Symfony. Como has podido ver, para ejecutar los diferentes comandos has de introducir
bin/console cada vez que vayas a ejecutar cualquiera de ellos, y si adems quieres que el entorno
de ejecucin sea prod en vez de dev, que es el entorno por defecto para los comandos, debers aadir
--env=prod al final de cada uno de los comandos.
Con el siguiente truco dars un respiro a tu teclado y, por descontado, a tus manos:

1 alias dev = php bin/console --env=dev


2 alias prod = php bin/console --env=prod

Estos alias no se mantienen tras reiniciar la mquina, por lo que busca en Internet cmo guardar
dichos alias para que permanezcan en el tiempo (no te lo iba a dar todo hecho, no?).
Adems del alias, tampoco hace falta introducir el comando completo: con tal de que sea
completamente discriminatorio vale. Qu quiere decir esto? Como un ejemplo vale ms que mil
palabras, a continuacin te aclaro las ideas:

1 dev s:r

El comando anterior ejecutar php bin/console --env=dev server:run. Por qu? Porque los
comandos que empiezan por s son server y swiftmailer, pero dentro de stos solo hay uno que
empiece por r que es server:run.
Enhorabuena! Ya eres ms eficiente que muchos de los desarrolladores Symfony, los cuales
desconocen este pequeo truco :)
10
https://www.jetbrains.com/phpstorm/
Captulo 1: instalacin y configuracin 9

Resumen
En este captulo hemos aprendido la forma correcta de desplegar un proyecto Symfony nuevo para
comenzar a trabajar con l. Gracias a la instalacin global de su instalador y el gestor de dependencias
Composer, las posteriores nuevas instalaciones se harn en cuestin de minutos, incluso segundos!
Captulo 2: los bundles de terceros
Una de las grandes maravillas que tiene Symfony y que comparte con otras muchas aplicaciones
de cdigo abierto es que la comunidad aporta elementos que enriquecen el framework. En Symfony
existen varias formas de colaborar, pero la creacin y mantenimiento de bundles que dotan de
funcionalidades extra a las que ya trae por defecto es sin duda una de las piezas clave para que
su utilizacin est tan extendida. Durante este captulo, aprenderemos las bases para instalar
bundles de terceros, aunque luego cada uno suele disponer de algunas secciones de configuracin
que difieren entre ellos. Empecemos!

Buscando nuestro bundle


Antes de comenzar a desarrollar una nueva funcionalidad en tu proyecto, debes conocer este
concepto: DRY. Estas siglas significan Dont Repeat Yourself, es decir, no te repitas o, como
decimos, no reinventes la rueda. Muchos de los desarrollos que vas a codificar para tu nueva
web seguramente ya estn realizados por otras personas: gestin de usuarios, manejo de imgenes,
conexin y uso de bases de datos NoSQL por qu rehacer lo que otros ya han hecho y mantienen
por ti?
Es por esta razn que, si sabes que lo que deseas incorporar en tu cdigo es lo suficientemente
genrico para que otros lo hayan hecho antes, te recomiendo que visites knpbundles.com11 y busques
en este sitio web lo que necesitas. Por supuesto que puedes buscar en Google o en Packagist lo-que-
necesito symfony bundle, pero aqu podrs navegar entre los diferentes bundles que utilizan el resto
de personas, filtrarlos segn varios criterios y, gracias a esto, quizs puedas nutrirte de algo que no
tenas previsto pero que es realmente interesante y te encaja perfectamente, no crees?
Parece que ya te voy convenciendo y me voy ganando un pequeo pedazo de tu corazn de
desarrollador, as que vamos a ver cmo tienes que hacer para instalar cualquier bundle en tu
proyecto.

Instalando un nuevo bundle


Ya has encontrado lo que necesitas y es hora de empezar a usarlo en tu proyecto. Durante este libro,
instalaremos varios bundles de terceros para terminar con un proyecto base slido y potente, pero
antes de instalar nada creo conveniente escribir sobre estos pasos genricos para que los tengas en
cuenta a la hora de introducir cualquier bundle que consideres oportuno y de utilidad.
11
http://knpbundles.com/
Captulo 2: los bundles de terceros 11

Lo primero que debes hacer es actualizar el archivo de descripcin de dependencias composer.json


para que al lanzar comando de actualizacin sea capaz de descargar las nuevas dependencias en la
carpeta vendor. Bien en la documentacin del bundle o en Packagist encontrars todo lo que tienes
que introducir en este archivo. Ten cuidado con las versiones tanto de las dependencias como del
propio Symfony, ya que no todos los bundles soportan todas las versiones de Symfony y viceversa.
Es posible que alguna vez te toque indagar un poco en la documentacin para seleccionar la versin
correcta del bundle segn tus necesidades.
Actualizado el archivo, debes lanzar el comando de actualizacin de Composer para que instale las
nuevas dependencias:

1 composer update

El siguiente paso es configurar el bundle. Al igual que con la versin de Composer, la mayora
de los bundles disponen una configuracin especfica que debers aadir en el archivo app/-
config/config.yml. La configuracin concreta debe proporcionrtela el creador del bundle y te
permitir parametrizar tu nueva dependencia para que se adapte a tu proyecto y funcione como t
quieres que lo haga. Cuidado: tambin hay bundles que requieren que modifiques otros archivos de
configuracin del framework como el sistema de rutas, los servicios o la configuracin relativa a la
seguridad. Al final, todos son archivos de configuracin en los que debers pegar una serie de lneas
que previamente habrs copiado de un archivo README o de documentacin.
Hecho esto, el ltimo paso es activar el bundle. Parece una chorrada pero cuando trabajas a diario
con esto, es una accin tan repetitiva que en algunas ocasiones te olvidas de realizarlo. Para activar las
nuevas dependencias, debes editar el archivo app/AppKernel.php y aadir las lneas de instanciacin
necesarias que tambin se te habrn proporcionado en la documentacin correspondiente. Si por
alguna razn no las encuentras en la documentacin, puedes dirigirte a la carpeta vendor y buscar
el archivo principal del bundle para encontrar el nombre especfico de la clase a instanciar.
Todo listo! Tatate estos pasos en el cdigo fuente de tu cerebro, aunque de momento puedes echar
de vez en cuando una ojeada a este captulo. Son muy sencillos y te abrirn muchas posibilidades en
el mundo de Symfony, todava no te lo crees? No te preocupes, en los prximos captulos, sin tocar
ni una sola lnea de cdigo, vers como tienes un Symfony con todo lo que siempre has deseado.

Truco 2
No se si llamar a esto un truco o un consejo, pero sin duda es algo que debes tener en cuenta en
tu da a da como desarrollador o desarrolladora. Como hemos visto a lo largo de este captulo, la
web de knpbundles.com es un buen punto de referencia para bundles de terceros de Symfony. No te
limites a buscar aqu nicamente lo que necesitas y cuando lo necesitas, sino que debes visitar esta
web con asiduidad para saber lo que se est moviendo y lo que otros estn utilizando. S curioso/a
y mantente a la ltima conociendo gran parte del trabajo de la comunidad. Claramente ganars una
ventaja competitiva sobre todos los que estn aislados de esta comunidad o se mantienen en su zona
Captulo 2: los bundles de terceros 12

de confort reutilizando lo que llevan haciendo durante aos. En el mundo en el que has decidido
embarcarte, un ao significan tantos cambios y modificaciones que, o te subes al carro, o te quedas
atrs y no te dicen ni hasta luego.

Resumen
En este captulo hemos aprendido las bases para instalar casi cualquier bundle realizado por
la comunidad. Hemos establecido una serie de pasos genricos que te servirn en la instalacin
y configuracin de la mayora de los desarrollos de la comunidad. En el siguiente captulo
comenzaremos instalando uno de los bundles ms potentes y ms utilizados en el mundo Symfony:
el Admin Bundle del proyecto Sonata.
Captulo 3: Admin Bundle
Como hemos visto en captulos anteriores, existe una comunidad activa que aporta innumerables
elementos interesantes a la atmsfera de Symfony. Algunos lo hacen con ms ahnco que otros,
y el proyecto Sonata es sin duda uno de los grandes a tener en cuenta. Dentro de su stack de
bundles12 podrs encontrar un gran abanico de soluciones geniales. En el enlace anterior puedes
leer informacin acerca de los bundles en los que trabajan activamente para que vayas llenando tus
neuronas de informacin. Adems, durante este libro probaremos algunos de estos bundles para que
veas cmo se instalan, cmo se configuran y, por supuesto, cmo se usan. El resto de ellos quedan
en tu tejado. Vamos all!

Instalando el Admin Bundle


Instalar el Admin Bundle es bastante sencillo a da de hoy ya que Sonata ha generado un desarrollo
padre llamado core bundle que facilita en gran medida la instalacin de los diferentes bundles
que pertenecen a su stack de proyectos. Para este ejemplo didctico, vamos a suponer que usamos
Doctrine ORM con una base de datos MySQL, pero tienes que saber que puedes utilizarlo con una
base de datos MongoDB que tan de moda se encuentra actualmente.
Lo primero que vamos a hacer es actualizar nuestras dependencias editando el archivo compo-
ser.json. Para ello, podemos hacerlo manualmente o podemos ejecutar el siguiente comando desde
la ruta raz de nuestro proyecto:

1 composer require sonata-project/admin-bundle ^3.16

Ten cuidado con las versiones y comprueba siempre el repositorio de GitHub de Sonata Admin para
la versin que necesitas, siempre escogiendo la ltima rama estable posible.
Como consejo te dir que ests atento cada vez que instales un nuevo bundle, porque muchas veces
vers lneas como:

1 name1/bundle1 suggests installing name2/bundle2

Es recomendable que al menos te dirijas a la documentacin de ese bundle recomendado porque


puede que sea muy interesante para tu proyecto ya que se encuentra estrechamente relacionado con
algo que acabas de instalar.
Hecho esto, el siguiente bundle a instalar es la conexin de nuestro admin con nuestra base de datos
MySQL:
12
https://sonata-project.org/bundles/
Captulo 3: Admin Bundle 14

1 composer require sonata-project/doctrine-orm-admin-bundle ^3.1

Lo siguiente que tenemos que hacer es activar el bundle y sus dependencias. En el captulo
anterior dejbamos esto para el ltimo paso. Por qu? Porque si activas el bundle sin terminar
de configurarlo, te dar error (siempre y cuando lleve una configuracin asociada, por supuesto),
por lo que si te quedas a medias en alguno de los pasos que vamos a realizar a continuacin
y quieres seguir trabajando con lo que estabas, tendrs que volver a desactivarlo y terminar la
instalacin ms adelante. Como vamos a hacerlo todo seguido, realmente el orden no importa, pero
que sepas que puedes dejar este paso para ms adelante. Para activar todo lo necesario, edita el
archivo app/AppKernel.php, aadiendo las siguientes lneas justo antes del AppBundle:

1 new Knp\Bundle\MenuBundle\KnpMenuBundle(),
2 new Sonata\CoreBundle\SonataCoreBundle(),
3 new Sonata\BlockBundle\SonataBlockBundle(),
4 new Sonata\DoctrineORMAdminBundle\SonataDoctrineORMAdminBundle(),
5 new Sonata\AdminBundle\SonataAdminBundle(),

Para el siguiente paso tenemos que configurar los diferentes bundles que hemos activado. Todo
lo necesario est en la documentacin oficial, y como deca uno de mis profesores: un buen
programador sabe copiar y pegar con gusto. Edita el archivo app/config/config.yml y aade la
siguiente configuracin:

1 sonata_block:
2 default_contexts: [cms]
3 blocks:
4 sonata.block.service.text:
5 sonata.admin.block.admin_list:
6 contexts: [admin]
7 sonata.admin.block.search_result:
8 contexts: [admin]

Adems de esto, tenemos que activar el translator. Para ello, en el mismo config.yml, debes eliminar
la # que precede a translator en la parte superior del archivo. Lo has encontrado? Entonces
seguimos.
En este momento, realizamos la primera prueba de fuego: copiamos los assets a la carpeta pblica
y limpiamos la cach. Si alguno de los siguientes comandos te da un error tengo que anunciarte que
te has equivocado en alguno de los pasos previos que hemos hecho:
Captulo 3: Admin Bundle 15

1 bin/console assets:install web --symlink


2 bin/console cache:clear

Para entender las lneas anteriores deberas leerte la documentacin de Symfony (todava no te la
has ledo?!). Para los ms rezagados: la primera lnea del cdigo anterior copia los assets (recursos
como archivos JavaScript, CSS, imgenes) desde carpetas no pblicas (src/...) a la carpeta pblica
web, en este caso creando un enlace simblico (la mejor opcin para cuando estamos en proceso de
desarrollo, en produccin no deberas escribir este ltimo parmetro); la segunda lnea simplemente
limpia la cache para regenerar elementos necesarios para un renderizado correcto.
Estupendo! Ya tenemos el admin instalado pero todava no es funcional. La siguiente parte de la
configuracin nos permitir acceder a l a travs de nuestro navegador. Por eso, necesitamos editar
el archivo app/config/routing.yml y aadir las rutas que vienen en el vendor y harn accesible el
desarrollo. Aade la siguiente configuracin en la parte superior del archivo:

1 admin:
2 resource: '@SonataAdminBundle/Resources/config/routing/sonata_admin.xml'
3 prefix: /admin
4
5 _sonata_admin:
6 resource: .
7 type: sonata_admin
8 prefix: /admin

Sabes qu ya dispones un admin que funciona? Si tienes el servidor web levantado, tan solo
dirgete a http://127.0.0.1:8000/admin y podrs ver su solitario y despoblado panel de administracin.
Supongo que aqu ya te han surgido varias dudas:

Cmo introduzco elementos en este panel de administracin?


Ni siquiera tengo usuario y he podido entrar, dnde est la seguridad?
Es muy feo! Cmo modifico los estilos?

Tranquilo joven padawan, cada cosa llegar a su tiempo y de momento, este solo es el de responder
al primer punto que te planteo, ya que en el siguiente captulo instalaremos un bundle que hace
uso de este administrador. Con l, automgicamente dispondremos de:

Gestin de usuarios: creacin, email de activacin, recuperacin de contrasea, login,


Administracin de usuarios: desde el panel de administracin, edicin de roles,
Seguridad en el admin: respondiendo a uno de los puntos anteriores.
Etc.

Lo mejor de todo esto es que la instalacin del bundle de usuarios son tan solo 10 minutos, y la
potencia que te otorga es impresionante. Nunca ms te preocupars de hacer la gestin de usuarios
para ninguna de tus webs.
Captulo 3: Admin Bundle 16

Creando nuestra Admin Class


De nada sirve tener un admin si no podemos introducir nuestros propios elementos. El ejemplo que
vamos a realizar a continuacin te permitir realizar el CRUD (Create, Read, Update, Delete) de una
entidad ejemplo aadiendo un sistema de filtrado y ordenacin.
Para comenzar, debes crear la clase Product.php tal y como se indica en el captulo de Doctrine de la
documentacin13 oficial de Symfony:

1 <?php
2 // src/AppBundle/Entity/Product.php
3
4 namespace AppBundle\Entity;
5
6 use Doctrine\ORM\Mapping as ORM;
7
8 /**
9 * @ORM\Entity
10 * @ORM\Table(name="product")
11 */
12 class Product
13 {
14 /**
15 * @ORM\Column(type="integer")
16 * @ORM\Id
17 * @ORM\GeneratedValue(strategy="AUTO")
18 */
19 protected $id;
20
21 /**
22 * @ORM\Column(type="string", length=100)
23 */
24 protected $name;
25
26 /**
27 * @ORM\Column(type="decimal", scale=2)
28 */
29 protected $price;
30
31 /**
32 * @ORM\Column(type="text")
13
http://symfony.com/doc/current/doctrine.html#add-mapping-information
Captulo 3: Admin Bundle 17

33 */
34 protected $description;
35 }

Recuerda generar los getters y setters y actualizar tu base de datos tras crear o modificar alguna de
tus entidades:

1 bin/console doctrine:generate:entities AppBundle


2 bin/console doctrine:schema:update --force

Lo siguiente que tienes que hacer es declarar la clase del admin que realizar el CRUD para esta
entidad. Si vienes de versiones anteriores de Symfony, sabrs que la inyeccin de dependencias
de cada bundle cargaba un archivo services.yml. Personalmente, me gusta modificar dicho archivo
para cargar un archivo admin.yml que tambin carga servicios, pero nos permite tener el cdigo
bien ordenadito: cada cosa en su sitio. En el truco de este captulo comparto contigo cmo lograr
este objetivo, pero para este ejemplo, debers modificar el archivo app/config/services.yml para
cargar desde ah nuestra admin class:

1 services:
2 sonata.admin.product:
3 class: AppBundle\Admin\ProductAdmin
4 tags:
5 - { name: sonata.admin, manager_type: orm, group: "Content", label: \
6 "Product" }
7 arguments: [~, AppBundle\Entity\Product ~]

Excelente. Como puedes deducir del cdigo anterior, nos hace falta crear una clase ProductAdmin
que es un copy&paste de la clase que puedes leer en la documentacin, modificando nicamente
los campos de la entidad:

1 <?php
2 // src/AppBundle/Admin/ProductAdmin.php
3
4 namespace AppBundle\Admin;
5
6 use Sonata\AdminBundle\Admin\AbstractAdmin;
7 use Sonata\AdminBundle\Datagrid\ListMapper;
8 use Sonata\AdminBundle\Datagrid\DatagridMapper;
9 use Sonata\AdminBundle\Form\FormMapper;
10
11 class ProductAdmin extends AbstractAdmin
Captulo 3: Admin Bundle 18

12 {
13 // Fields to be shown on create/edit forms
14 protected function configureFormFields(FormMapper $formMapper)
15 {
16 $formMapper
17 ->add('name')
18 ->add('price')
19 ->add('description')
20 ;
21 }
22
23 // Fields to be shown on filter forms
24 protected function configureDatagridFilters(DatagridMapper $datagridMapper)
25 {
26 $datagridMapper
27 ->add('name')
28 ->add('price')
29 ;
30 }
31
32 // Fields to be shown on lists
33 protected function configureListFields(ListMapper $listMapper)
34 {
35 $listMapper
36 ->addIdentifier('id')
37 ->add('name')
38 ->add('price')
39 ->add('description')
40 ->add('_action', 'actions', array(
41 'actions' => array(
42 'edit' => array()
43 )))
44 ;
45 }
46 }

Lo tenemos! Si vuelves a visitar http://127.0.0.1:8000/admin podrs ver la parte correspondiente a


los productos. Prueba a crear, editar, filtrar y todo el resto de acciones que te permite el admin por
defecto.
A partir de aqu, aumentar tu panel de administracin es pan comido: creas tu clase admin y la
activas en el archivo services.yml, sabiendo que tanto la clase PHP como la declaracin del servicio
es prcticamente un copy&paste exacto de cualquier otro admin que ya hayas creado previamente.
Captulo 3: Admin Bundle 19

En este ejemplo te he aadido algunas modificaciones con respecto a la documentacin oficial, como
el botn de editar en el listado. Con este admin podrs incluso hacer tus propios botones asociados a
tus controladores, por lo que usa el admin para lo que se adapte a ti, y adapta el admin para el resto
de acciones que te falten. Cmo hacer esto? Documentacin y a probar, nimo! Por de pronto, aqu
te dejo la configuracin base14 de Sonata Admin Bundle, para que cambies el ttulo, el logo u otros
elementos que necesites.

Truco 3
Es posible que te guste tener la parte del admin completamente centralizada en tu bundle y no quieras
depender del archivo de servicios que est en la carpeta app. Esto se consigue con la inyeccin de
dependencias de una forma muy simple. Si creas un nuevo bundle, esta clase ya te viene creada por
defecto. Puedes probarlo con este comando:

1 bin/console generate:bundle

Pero sin duda, la mejor forma de aprender es trasteando (y rompiendo). Para ello, te dejo la
documentacin necesaria en este15 enlace. Recuerda modificar la parte de XML por YAML:

1 $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Res\


2 ources/config'));
3 $loader->load('services.yml');
4 $loader->load('admin.yml');

Nota: el enlace anterior podra modificar su contenido debido a que la documentacin


del bundle se encuentra en reestructuracin.

Otras posibilidades
Existen otras posibilidades para realizar paneles de administracin en Symfony con una gran
facilidad. Javier Eguiluz16 dispone de una de ellas, actualmente con ms estrellas en GitHub que
el propio Sonata admin. Es recomendable que lo utilices y veas si te gusta y te es ms fcil de usar
para tus proyectos: https://github.com/javiereguiluz/EasyAdminBundle17 .
14
https://sonata-project.org/bundles/admin/master/doc/reference/configuration.html
15
http://symfony.com/doc/current/bundles/extension.html
16
https://github.com/javiereguiluz
17
https://github.com/javiereguiluz/EasyAdminBundle
Captulo 3: Admin Bundle 20

Resumen
En este captulo hemos aprendido a instalar el Admin Bundle del proyecto Sonata y a crear nuestra
primera clase aplicada al administrador. Durante el siguiente captulo instalaremos la gestin de
usuarios basada en este administrador que nos dar control completo sobre ellos adems de securizar
el recin instalado admin.
Captulo 4: User Bundle
En este captulo extenderemos el admin que hemos creado en el captulo anterior y adems
le daremos una vuelta de tuerca ms a nuestro proyecto base. Con User Bundle de Sonata
dispondremos de la gestin ms completa de usuarios gracias a la incorporacin de un bundle muy
conocido llamado FOSUserBundle. Sonata simplemente proporciona una capa sobre este bundle que
mejora la integracin con su admin y aade alguna que otra serie de aspectos extra. Al igual que
en el captulo anterior, nuestros pasos sern los siguientes: instalar, configurar y aprender a utilizar.
Al lo!

Instalando User Bundle


En la propia documentacin18 del bundle podemos leer que para poder instalarlo tenemos que
instalar previamente Sonata Admin y Sonata Easy Extends. El primero de ellos ya lo tenemos;
el segundo es tremendamente sencillo de instalar. Lo primero que hay que hacer es aadir la
dependencia al archivo composer.json con el siguiente comando:

1 composer require sonata-project/easy-extends-bundle

El siguiente y ltimo paso es activarlo en app/AppKernel.php. Yo lo escribo antes de activar el


AppBundle, pero organiza tus bundles como ms cmodo/a te sientas:

1 new Sonata\EasyExtendsBundle\SonataEasyExtendsBundle(),

Ya tenemos los requisitos previos correctamente instalados, por lo que podemos empezar a instalar
Sonata User Bundle. Para que esto funcione en Symfony 3, todava no est aprobado el pull request
con el cdigo final, por lo que hay que editar el archivo de composer composer.json manualmente
y arriba del todo aadir lo siguiente:

18
https://sonata-project.org/bundles/user/master/doc/reference/installation.html
Captulo 4: User Bundle 22

1 "repositories": [
2 {
3 "url": "https://github.com/jordisala1991/SonataUserBundle",
4 "type": "git"
5 }
6 ],

Y posteriormente, aadir su fork a nuestro composer:

1 "sonata-project/user-bundle": "dev-feature/fos_user_2"

NOTA: cuando esto sea estable, editar el libro con las versiones finales.
Como has ledo en la introduccin, este bundle no es ms que una capa situada por encima de
FOSUserBundle. Lo que necesitas ahora es activar entonces ambos bundles, como siempre, en
app/AppKernel.php:

1 new FOS\UserBundle\FOSUserBundle(),
2 new Sonata\UserBundle\SonataUserBundle(),

A continuacin vienen una serie de configuraciones con las que es muy fcil perderse. Lo primero
es aadir lo siguiente a app/config/config.yml:

1 fos_user:
2 db_driver: orm
3 firewall_name: main
4 user_class: Sonata\UserBundle\Entity\BaseUser
5 group:
6 group_class: Sonata\UserBundle\Entity\BaseGroup
7 group_manager: sonata.user.orm.group_manager
8 service:
9 user_manager: sonata.user.orm.user_manager

Adems, dentro de la configuracin de doctrine y a su vez dentro de dbal, hay que aadir las ltimas
dos lneas del bloque de configuracin que est a continuacin. Ten cuidado con la indentacin ya
que es un archivo YAML:
Captulo 4: User Bundle 23

1 doctrine:
2 #Aqu en medio hay configuracin
3 dbal:
4 types:
5 json: Sonata\Doctrine\Types\JsonType

Asegrate adems de que el parmetro auto_mapping dentro de orm lo tienes activado, en el mismo
apartado de doctrine.
Por ltimo, modifica la parte de sonata_block para aadir las ltimas lneas. ste es el resultado
final:

1 sonata_block:
2 default_contexts: [cms]
3 blocks:
4 sonata.user.block.menu:
5 sonata.user.block.account:
6 sonata.block.service.text:
7 sonata.admin.block.admin_list:
8 contexts: [admin]
9 sonata.admin.block.search_result:
10 contexts: [admin]

Estupendo! Ahora cargaremos las rutas que nos permitirn registrarnos, solicitar una nueva
contrasea o ver nuestro perfil dentro de la plataforma adems de las rutas que tienen que ver
con la seguridad entre Sonata User y Sonata Admin (y sin codificar ni una lnea!). Tan solo hace
falta aadir las siguientes lneas al archivo app/config/routing.yml:

1 # Sonata User
2 sonata_user_admin_security:
3 resource: '@SonataUserBundle/Resources/config/routing/admin_security.xml'
4 prefix: /admin
5
6 sonata_user_admin_resetting:
7 resource: '@SonataUserBundle/Resources/config/routing/admin_resetting.xml'
8 prefix: /admin/resetting
9
10 # FOS User
11 fos_user:
12 resource: '@FOSUserBundle/Resources/config/routing/all.xml'
13
14 fos_user_group:
15 resource: '@FOSUserBundle/Resources/config/routing/group.xml'
16 prefix: /group
Captulo 4: User Bundle 24

Y por ltimo (de momento, no cantes victoria), tienes que configurar la seguridad de tu sitio web. El
archivo que se encarga de la mencionada seguridad es app/config/security.yml, y puedes vaciarlo
entero tranquilamente e introducir el cdigo que muy amablemente te dejo a continuacin:

1 security:
2 encoders:
3 FOS\UserBundle\Model\UserInterface: bcrypt
4
5 role_hierarchy:
6 ROLE_ADMIN: [ROLE_USER, ROLE_SONATA_ADMIN]
7 ROLE_SUPER_ADMIN: [ROLE_ADMIN, ROLE_ALLOWED_TO_SWITCH]
8 SONATA:
9 - ROLE_SONATA_PAGE_ADMIN_PAGE_EDIT
10
11 providers:
12 fos_userbundle:
13 id: fos_user.user_manager
14
15 firewalls:
16 # Disabling the security for the web debug toolbar, the profiler and Ass\
17 etic.
18 dev:
19 pattern: ^/(_(profiler|wdt)|css|images|js)/
20 security: false
21
22 # -> custom firewall for the admin area of the URL
23 admin:
24 pattern: /admin(.*)
25 context: user
26 form_login:
27 provider: fos_userbundle
28 login_path: /admin/login
29 use_forward: false
30 check_path: /admin/login_check
31 failure_path: null
32 logout:
33 path: /admin/logout
34 anonymous: true
35
36 # -> end custom configuration
37
38 # default login area for standard users
39
Captulo 4: User Bundle 25

40 # This firewall is used to handle the public login area


41 # This part is handled by the FOS User Bundle
42 main:
43 pattern: .*
44 context: user
45 form_login:
46 provider: fos_userbundle
47 login_path: /login
48 use_forward: false
49 check_path: /login_check
50 failure_path: null
51 logout: true
52 anonymous: true
53
54 access_control:
55 # URL of FOSUserBundle which need to be available to anonymous users
56 - { path: ^/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
57 - { path: ^/register, role: IS_AUTHENTICATED_ANONYMOUSLY }
58 - { path: ^/resetting, role: IS_AUTHENTICATED_ANONYMOUSLY }
59
60 # Admin login page needs to be access without credential
61 - { path: ^/admin/login$, role: IS_AUTHENTICATED_ANONYMOUSLY }
62 - { path: ^/admin/logout$, role: IS_AUTHENTICATED_ANONYMOUSLY }
63 - { path: ^/admin/login_check$, role: IS_AUTHENTICATED_ANONYMOUSLY }
64
65 # Secured part of the site
66 # This config requires being logged for the whole site and having the ad\
67 min role for the admin part.
68 # Change these rules to adapt them to your needs
69 - { path: ^/admin/, role: [ROLE_ADMIN, ROLE_SONATA_ADMIN] }
70 - { path: ^/.*, role: IS_AUTHENTICATED_ANONYMOUSLY }

Te recomiendo que lo mires y lo entiendas ya que no es complicado, y recuerda siempre contrastar


cada cosa que no entiendas con la documentacin oficial de Symfony, en la que seguro que
encontrars respuestas. Pero ahora, es momento de ver un poco de magia. Con tu servidor
levantado:

1 bin/console server:run

Dirgete a http://127.0.0.1:8000/app_dev.php/admin/. Deberas ver una genial pantalla de login para


poder acceder a tu admin, y deberas ver tambin un fantstico error de base de datos cuando intentas
interactuar con dicho formulario de login. Aqu pueden pasar varias cosas:
Captulo 4: User Bundle 26

Lo ves todo bien, incluso el error de base de datos: no hay problema, vas por buen camino.
Ves el formulario y el error, pero los estilos no estn nada bien: prueba a limpiar la cache y
a instalar los assets.
No veo nada y/o tengo errores en la consola: un, dos, tres empieza otra vez.

Hasta aqu la configuracin del administrador con Sonata User Bundle. El siguiente paso ser generar
la estructura para usuarios y grupos y poder interactuar con este formulario de login: vamos a crear
nuestro usuario admin.

Creando la estructura de usuarios


La verdad es que el ttulo de la seccin da un poco de miedo, pero ya nos vamos conociendo y no te
voy a dejar que malgastes tu preciado tiempo creando lneas de cdigo que otros ya han hecho por
ti. Te habrs dado cuenta en la seccin anterior que uno de los requisitos era Sonata Easy Extends,
as que es hora de utilizarlo para este menester.
El paso inicial es crear tu propio bundle que usars para todo lo que tenga que ver con el sistema de
usuarios. Para ello, copia y pega con gusto:

1 bin/console sonata:easy-extends:generate SonataUserBundle -d src

El comando anterior te generar un nuevo bundle dentro de la carpeta src, y como es nuevo, tienes
que activarlo. Abre y modifica el archivo app/AppKernel.php aadiendo:

1 new Application\Sonata\UserBundle\ApplicationSonataUserBundle(),

Ahora, tienes que cambiar un pequeo pedazo de la configuracin que aadiste en la seccin anterior,
la que tiene que ver con las clases tanto del usuario como del grupo. Modifica los parmetros user_-
class y group_class dentro de la seccin fos_user en el archivo app/config/config.yml, tal y
como te dejo en el trozo de configuracin siguiente:

1 fos_user:
2 db_driver: orm # can be orm or odm
3 firewall_name: main
4 user_class: Application\Sonata\UserBundle\Entity\User
5 group:
6 group_class: Application\Sonata\UserBundle\Entity\Group
7 group_manager: sonata.user.orm.group_manager
8 service:
9 user_manager: sonata.user.orm.user_manager

Ya lo tienes! No te lo crees? Ejecuta el siguiente comando para ver lo que automticamente se va


a aadir a la estructrura de tu base de datos:
Captulo 4: User Bundle 27

1 bin/console doctrine:schema:update --dump-sql

Cuando hayas terminado de celebrarlo, ejecuta los cambios en tu base de datos para que se cree la
estructura:

1 bin/console doctrine:schema:update --force

Ahora puedes dirigirte al formulario y no debera darte el error de base de datos pero no tienes el
permiso necesario para entrar porque ni siquiera dispones de un usuario. Vamos a solucionar esto.

Creando nuestro usuario admin


Como profesional de la consola de Symfony que eres, sabrs que puedes crear tus propios comandos.
Esto es lo que ha hecho FOSUserBundle que puedas crear tu primer usuario y que no tengas que
hacer malabares a la hora de crearlos. Lanza el siguiente comando para insertar nuestro primer
usuario en la base de datos:

1 bin/console fos:user:create

Tras seguir los pasos, ya tendrs un usuario capaz de conectarse a travs de la web, pero si lo intentas,
vers que no tienes permiso para ver el admin. Por qu? Si vuelves un poco hacia atrs y ves la
seccin de access_control situada en el archivo de seguridad, vers que necesitas el rol ROLE_-
ADMIN para poder acceder. Aprovecharemos este momento para crear un super admin asignando
al recien creado usuario el rol ROLE_SUPER_ADMIN :

1 bin/console fos:user:promote

Tras asignarle el rol que te he dicho, vers que si te has conectado anteriormente, sigues sin
poder acceder. Esto se debe a que, una vez que has hecho login, los roles del usuario se seria-
lizan en la sesin, as que tienes que hacer logout en http://127.0.0.1:8000/logout y volver a ir a
http://127.0.0.1:8000/admin.
Qu te parece? Solo con estos primeros captulos de este libro debera haberte conquistado. Si no
es as, no te preocupes, todava me quedan muchos captulos para llegar a lo ms profundo de tu
corazn y ganarte esa batalla.
Es hora de que pares de leer un rato y trastees con lo que acabamos de instalar. Sobre todo, mira
todas y cada una de las rutas que hemos cargado dentro de app/config/routing.yml como el login,
el registro, la pgina de perfil, etc. Puedes empezar por esta ltima en http://127.0.0.1:8000/app_-
dev.php/profile.
Captulo 4: User Bundle 28

Truco 4
Mucha gente se olvida de que Sonata User Bundle no es ms que una capa por encima de
FOSUserBundle. Esto significa que cualquier configuracin que vale para el bundle de FOS, tambin
nos sirve para el recin instalado bundle. Por ejemplo, la activacin de cuentas por email que puedes
ver en este19 enlace. Tan solo con aadir este pedazo a tu configuracin, tus usuarios tendrn que
confirmar su cuenta a travs de un email que lo crear de forma automtica este bundle y del que
t no tendrs que preocuparte jams, a menos que quieras adaptar la plantilla del email.

1 fos_user:
2 # ...
3 registration:
4 confirmation:
5 enabled: true

Adems, s que ests pensando que esas pginas blancas con formularios son bastante feas, y es que
en realidad no creo que le valgan a nadie como producto final. Es por eso que puedes sobrescribirlas
para que se adapten a tu maquetacin base adems de aadir y/o modificar los elementos de cada
una de ellas a tu gusto. Toda esta configuracin est aqu20 , aunque te recomiendo que apuntes esa
direccin ya que en captulos posteriores aprenderemos sobre la maquetacin base tanto a nivel de
nuestro proyecto como para modificar las plantillas que nos vienen gratis de estos bundles instalados.

Resumen
En este captulo hemos instalado y configurado Sonata User Bundle, un bundle que nos permite
disponer de una gestin completa de usuarios tanto a nivel de base de datos como a nivel de
estructura web y de administracin. Sin duda, la instalacin de estos bundles de Sonata permiten
un ahorro de tiempo considerable en nuestros proyectos para poder dedicarnos a la parte ms
importante del mismo, nicamente teniendo que preocuparnos de sobrescribir las partes que
necesitemos modificar.
19
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/emails.rst
20
https://github.com/FriendsOfSymfony/FOSUserBundle/blob/master/Resources/doc/index.rst#next-steps
Captulo 5: MopaBootstrap
Ya tenemos nuestro admin y nuestro sistema de usuarios montado y funcionando. Pero, como ya me
has podido notar, el diseo base de los bundles instalados hasta el momento es demasiado simple.
Es hora de darle vida y color a nuestro proyecto y preparar una buena base para el futuro del mismo.
Podramos empezar a meter hojas de estilos desde 0, o incluso descargarnos algn framework CSS
e insertarlo en nuestro proyecto como assets en nuestras plantillas (si no te suena esto, es hora de
que pares y te leas los captulos correspondientes de la documentacin). Pero tengo que proponerte
algo mucho mejor que deriva de las siguientes cuestiones:

Empezar a picar CSS desde 0 es directamente un suicidio. Sera un buen ejercicio didctico
pero si quieres aprender CSS desde el principio, debers aprenderlo fuera de este libro ya que
es algo que est fuera del alcance del mismo.
Introducir un framework CSS como asset es perfectamente vlido, pero para qu vamos a
hacerlo de esta forma si disponemos de un gestor de dependencias?
CSS en qu siglo vives? No se te ocurra volver a usar CSS sin un preprocesador. Trabajar
sin variables o funciones te ralentizar de una forma espectacular.

Entonces qu es lo que vamos a hacer? Utilizaremos Composer para instalar MopaBootstrap-


Bundle, un bundle que nos permitir tener una plantilla base de la que extender y en la que ya
dispondremos Bootstrap listo para usar. Adems contiene una serie de componentes extra y permite
tener los archivos sassy (.scss) para poder trabajar directamente con ellos. Cmo? Para esto existen
varias formas de recorrer el camino.
Symfony traa una por defecto hasta su versin 2.7: Assetic. Tras un tiempo utilizando esta
tecnologa, me di cuenta de las carencias y la lentitud que arrastraba en el desarrollo. Tras una
serie de cambios intermedios, termin utilizando Gulp y un mundo nuevo de posibilidades se abri
para mi. En captulos posteriores abrir tambin este mundo para ti, pero empecemos la faena de
este captulo e instalemos MopaBootstrap.
Por cierto: esto es solo un ejemplo, y si te gusta Foundation ms que Bootstrap tendrs que rebuscar
un poco por Internet y adaptar las siguientes secciones, pero seguro que lo consigues!

NOTA: si eres un desarrollador con experiencia, deberas saber que acoplar tu cdigo
a un bundle tan grande como MopaBootstrap no es la mejor decisin. Este captulo
te ayudar mucho s ests comenzando con el diseo frontend o, quizs, si eres un
desarrollador intermedio. En el ltimo captulo te mostrar como manejar este trmino
de acoplamiento.
Captulo 5: MopaBootstrap 30

Instalando MopaBootstrapBundle
Como siempre, lo primero que tenemos que hacer es editar el archivo de definicin de dependencias
composer.json. Esta vez, en lugar de usar el comando composer require, editamos el archivo
directamente, para que veas que tambin se puede hacer de esta forma, y aadimos las siguientes
lneas al final de la seccin require:

1 "mopa/bootstrap-bundle": "^3.1",
2 "twbs/bootstrap-sass": "^3.3.0"

Adems, en este mismo archivo, debes aadir unos hooks al final del mismo. No son ms que
commands de Symfony que se ejecutarn cuando Composer ha terminado de realizar el composer
install o el composer update. En el trozo de cdigo que te ves a continuacin solo te dejo la lnea
que hay que aadir a cada seccin:

1 {
2 "scripts": {
3 "post-install-cmd": [
4 ...
5 "Mopa\\Bundle\\BootstrapBundle\\Composer\\ScriptHandler::postInstall\
6 SymlinkTwitterBootstrapSass"
7 ],
8 "post-update-cmd": [
9 ...
10 "Mopa\\Bundle\\BootstrapBundle\\Composer\\ScriptHandler::postInstall\
11 SymlinkTwitterBootstrapSass"
12 ]
13 }
14 }

Antes de descargar nada, como al terminar de instalar los bundles se van a ejecutar los hooks
nos tenemos que asegurar de que el bundle est activado y configurado. Editamos el archivo
app/AppKernel.php para activar el bundle:

1 new Mopa\Bundle\BootstrapBundle\MopaBootstrapBundle(),

Y modificamos tambin app/config/config.yml para introducir la configuracin que nos indican


en la documentacin:
Captulo 5: MopaBootstrap 31

1 mopa_bootstrap:
2 form: ~ # Adds twig form theme support
3 menu: ~ # enables twig helpers for menu

Excelente! Ahora ya puedes lanzar el comando de actualizacin para que se descarguen las nuevas
dependencias y se ejecute el hook que hemos introducido anteriormente:

1 composer update

Es posible que veas otros bundles actualizarse. Esto es bueno, sobre todo teniendo en cuenta que en
el ejemplo que estamos haciendo trabajamos con ramas dev-master, es decir, que estn en desarrollo,
por lo que deberan actualizarse cada poco tiempo. Al final de la ejecucin deberas poder leer al
final algo como

1 Checking Symlink ... Creating Symlink: [...]/vendor/mopa/bootstrap-bundle/Mopa/B\


2 undle/BootstrapBundle/Resources/public/bootstrap-sass ... OK

Si es as, todo marcha por buen camino.

Preparando la plantilla base


Como ya s que te has ledo el captulo de templating21 de la documentacin de Symfony, voy a
ir directo al grano. Antes de nada, tengo que hablar sobre las buenas prcticas del framework.
Esta es una de esas normas que sorprende a mucha gente que estaba acostumbrada a trabajar
sobre el bundle directamente y es que las plantillas de tu proyecto deberan estar situadas
dentro de app/Resources/views. Si te diriges a esta carpeta, vers que ya dispones de la plantilla
base.html.twig y de default/index.html.twig. Y esto? Symfony por defecto tiene el AppBundle
creado, y dentro de ste, un controller con la ruta / cargada. Ya hemos visto esta pgina con
anterioridad:

1 http://127.0.0.1:8000/

Si has abierto el cdigo del controller, vers que la ruta se carga por anotaciones. Para todo lo
que hagas t y para lo que hars conmigo en captulos posteriores, te recomiendo que utilices
anotaciones; para cargar rutas de terceros, realizar redirecciones o cargar una plantilla sin necesidad
de un controlador, debers utilizar archivos de rutas como lo hemos hecho hasta ahora.
En este captulo solo vamos a editar la plantilla base para que extienda de la que nos proporciona
MopaBootstrap y explicaremos como funciona por dentro. En el siguiente captulo dejaremos
preparada la parte de Sass con Gulp para poder meter mano a los estilos. Editamos el archivo
app/Resources/views/base.html.twig y lo dejamos tal que as:
21
http://symfony.com/doc/current/templating.html
Captulo 5: MopaBootstrap 32

1 {% extends 'MopaBootstrapBundle::base.html.twig' %}

Cmo? Eso es todo? En efecto, y si vuelves a visitar homepage vers que todo se ha roto ms
o menos. Si abres el cdigo fuente, observars que ya dispones de un title, que existe el meta de
viewport y algn que otro detalle. Pero, tirando un poco ms del hilo, vers que la plantilla base que
ests extendiendo contiene todo esto:

1 {% from 'MopaBootstrapBundle::flash.html.twig' import session_flash %}


2
3 <!DOCTYPE html>
4
5 {% block html_tag %}
6 <html lang="{{ app.request.locale }}">
7 {% endblock html_tag %}
8
9 {% block head %}
10 <head>
11 <meta charset="{{ _charset }}" />
12 {% block head_style %}
13 {# Override this block to add your own files! #}
14 {# To use this without less or sass use the base.html.twig template as your \
15 base
16 # Be sure you understand whats going on: have a look into
17 # https://github.com/phiamo/MopaBootstrapBundle/blob/master/Resources/doc/c\
18 ss-vs-less.md
19 #}
20 {% endblock head_style %}
21
22 <meta name="viewport" content="width=device-width, initial-scale=1.0">
23
24 {% block head_script %}
25 {# Overwrite this block to add your own js here, to get them generated into \
26 final files
27 {% javascripts
28 'http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js'
29 %}
30 <script type="text/javascript" src="{{ asset_url }}"></script>
31 {% endjavascripts %}
32 #}
33 {% endblock head_script %}
34
35 <title>{% block title %}Mopa Bootstrap Bundle{% endblock title %}</title>
Captulo 5: MopaBootstrap 33

36 {% block favicon %}<link rel="shortcut icon" href="{{ asset('favicon.ico') }\


37 }" />{% endblock %}
38 {% block head_bottom %}
39 {% endblock head_bottom %}
40 </head>
41 {% endblock head %}
42
43 {% block body_tag %}
44 <body>
45 {% endblock body_tag %}
46
47 {% block body_start %}
48 {% endblock body_start %}
49
50 {% block body %}
51 {% block navbar %}
52 <!-- No navbar included here to reduce dependencies, see https://github.com/\
53 phiamo/MopaBootstrapSandboxBundle/blob/master/Resources/views/base.html.twig for\
54 howto include it -->
55 {% endblock navbar %}
56
57 {% block container %}
58 {% block container_div_start %}<div class="{% block container_class %}contai\
59 ner{% endblock container_class %}">{% endblock container_div_start %}
60 {% block header %}
61 {% endblock header %}
62
63 {% block content_div_start %}<div class="content">{% endblock content_di\
64 v_start %}
65 {% block page_header %}
66 <div class="page-header">
67 <h1>{% block headline %}Mopa Bootstrap Bundle{% endblock headl\
68 ine %}</h1>
69 </div>
70 {% endblock page_header %}
71
72 {% block flashes %}
73 {% if app.session.flashbag.peekAll|length > 0 %}
74 <div class="row">
75 <div class="col-sm-12">
76 {{ session_flash() }}
77 </div>
Captulo 5: MopaBootstrap 34

78 </div>
79 {% endif %}
80 {% endblock flashes %}
81
82 {% block content_row %}
83 <div class="row">
84 {% block content %}
85 <div class="col-sm-9">
86 {% block content_content %}
87 <strong>Hier knnte Ihre Werbung stehen ... </strong>
88 {% endblock content_content %}
89 </div>
90 <div class="col-sm-3">
91 {% block content_sidebar %}
92 <h2>Sidebar</h2>
93 {% endblock content_sidebar %}
94 </div>
95 {% endblock content %}
96 </div>
97 {% endblock content_row %}
98
99 {% block content_div_end %}</div>{% endblock content_div_end %}
100
101 {% block footer_tag_start %}
102 <footer>
103 {% endblock footer_tag_start %}
104
105 {% block footer %}
106 <p>&copy; <a href="http://www.mohrenweiserpartner.de" target="_blank">Mo\
107 hrenweiser & Partner</a> 2011-2015</p>
108 {% endblock footer %}
109
110 {% block footer_tag_end %}
111 </footer>
112 {% endblock footer_tag_end %}
113 {% block container_div_end %}</div><!-- /container -->{% endblock container_\
114 div_end %}
115 {% endblock container %}
116
117 {% block body_end_before_js %}
118 {% endblock body_end_before_js %}
119
Captulo 5: MopaBootstrap 35

120 {% block foot_script %}


121 {# To only use a subset or add more js overwrite and copy paste this block
122 To speed up page loads save a copy of jQuery in your project and override th\
123 is block to include the correct path
124 Otherwise the regeneration is done on every load in dev more with use_contro\
125 ller: true
126 #}
127 {% block foot_script_assetic %}
128 {# Please add the javascripts you need in your project #}
129 {% endblock foot_script_assetic %}
130
131 <script type="text/javascript">
132 $(document).ready(function () {
133 $('[data-toggle="tooltip"]').tooltip();
134 $('[data-toggle="popover"]').popover();
135 });
136 </script>
137 {% endblock foot_script %}
138 {% endblock body %}
139
140 {% block body_end %}
141 {% endblock body_end %}
142 </body>
143 </html>

Vaya! Qu es todo esto? Vayamos por cachos. Cada etiqueta {% block %} te va a permitir a ti
sobrescribir la parte tiene dentro o aadir ms cosas. Esto har que podamos aadir nuestros CSS y
JS, modificar los meta, sobrescribir simplemente la etiqueta <body> para aadirle, por ejemplo, una
clase, etc. La base est muy bien hecha, y debera servirte para casi cualquier ocasin. No te sirve?
Sintete libre de sobrescribir todo lo que quieras o incluso de hacerte la tuya propia. Y.. todo el resto?
Blocks, blocks y ms blocks, algunos con algo de maquetacin por defecto que puedes utilizar; otros
con un pequeo trozo de JavaScript que activarn los tooltips y popovers de Bootstrap; un montn de
cdigo base que te dejo que leas con calma. Antes de nada, te propongo un cambio para dejar una base
limpia en funcionamiento. Editamos el archivo app/Resources/views/default/index.html.twig
y lo dejamos tal que as:

1 {% extends 'base.html.twig' %}
2
3 {% block content_content %}
4 Homepage.
5 {% endblock %}
Captulo 5: MopaBootstrap 36

Si vuelves a recargar la pgina, qu es lo que ha pasado? Ahora hemos sobreescrito un bloque central
y ya podemos ver un encabezado y un pie. Es hora de cambiar los textos que no queremos, por lo que
editamos la plantilla base app/Resources/views/base.html.twig y la adaptamos a nuestro gusto:

1 {% extends 'MopaBootstrapBundle::base.html.twig' %}
2
3 {% block title %}Aupa Bilbao!{% endblock %}
4
5 {% block headline %}Aupa Bilbao!{% endblock headline %}
6
7 {% block footer %}
8 <p>Footer sexy 2016</p>
9 {% endblock footer %}

Recargamos la pgina y et voil! Ya hemos modificado las partes que no eran de nuestra web para
que se adapte a nuestras necesidades (didcticas, por supuesto). Pero te habrs dado cuenta, donde
estn los estilos de Bootstrap? Tenemos que introducirlos con Sass.
Existe una plantilla base base_sass_3.2 pero hace uso de Assetic para la precompilacin de SCSS a
CSS. Nosotros iremos un peldao ms arriba aadiendo Gulp, que adems de esta precompilacin,
nos dar un montn de funcionalidades extra ms que deseables.

Truco 5
MopaBootstrap dispone de un sandbox22 que puedes desplegar en tu ordenador o que puedes visitar
online y ver lo que tiene. En este truco te voy a ayudar a hacer tu propio men gracias al bundle
knp-menu que lo tienes instalado como dependencia de Sonata. Puede que te sea til o puede que
quieras gestionar este truco a travs de tus plantillas manualmente, lo dejo a tu eleccin.
Lo primero que vamos a hacer es crear la carpeta src/AppBundle/Menu, y dentro de ella crear el
archivo Builder.php con el siguiente contenido:

22
http://bootstrap.mohrenweiserpartner.de/
Captulo 5: MopaBootstrap 37

1 <?php
2
3 namespace AppBundle\Menu;
4
5 use Knp\Menu\FactoryInterface;
6
7 class Builder
8 {
9 public function mainMenu(FactoryInterface $factory, array $options)
10 {
11 $menu = $factory->createItem('root', array(
12 'navbar' => true,
13 ));
14 $menu->addChild('Home', array(
15 'icon' => 'home',
16 'route' => 'homepage',
17 ));
18 $dropdown = $menu->addChild('Subpaginas', array(
19 'dropdown' => true,
20 'caret' => true,
21 ));
22 $dropdown->addChild('Subpagina 1', array('route' => 'homepage'));
23 $dropdown->addChild('Subpagina 2', array('route' => 'homepage'));
24 $dropdown->addChild('Subpagina 3', array('route' => 'homepage'));
25
26 return $menu;
27 }
28 }

El siguiente paso es introducir nuestro men en la plantilla base.html.twig. El bloque de cdigo


que ves a continuacin es el que necesitas, y si quieres mantener tu plantilla ordenada, ponlo entre
el bloque title y el headline:

1 {% block navbar %}
2 {% embed '@MopaBootstrap/Navbar/navbar.html.twig' with {'fixedTop': true } %}
3 {% block brand %}
4 <a class="navbar-brand" href="#">Aupa</a>
5 {% endblock %}
6
7 {% block menu %}
8 {{ mopa_bootstrap_menu('AppBundle:Builder:mainMenu', {'automenu':'na\
9 vbar', 'currentClass': ''}) }}
Captulo 5: MopaBootstrap 38

10 {% endblock %}
11 {% endembed %}
12 {% endblock navbar %}

Magnfico! Ahora si recargas la homepage podrs ver el men que hemos creado con un montn
de enlaces y subenlaces que te llevan a la home por defecto. Este men es muy feo, nos encargamos
de darle magia de estilos en el siguiente captulo.

Resumen
En este captulo hemos aprendido a instalar MopaBootstrapBundle y a crear una pequea plantilla
base todava sin estilos. Hemos descartado el uso de la plantilla base de Sass porque funciona con
Assetic. Con el objetivo de que aprendas a utilizar las ltimas tecnologas (o al menos, mientras
escriba este libro), usaremos Gulp para compilar nuestros archivos SCSS y ms adelante le daremos
unas cuantas vitaminas para que nos sea de mucha ms utilidad.
Captulo 6: Gulp (1)
Cada vez nuestro proyecto base va tomando ms forma. Pero, si tuviera que destacar un captulo
importante hasta ahora, probablemente sera este. Curiosamente, es el primero en el que no
trabajaremos con Symfony como tal, sino que nos apoyaremos de herramientas externas para hacer
la precompilacin de SCSS a CSS y para vitaminar este proceso con bastantes mejoras.
Si no has trabajado con ningn preprocesador hasta ahora, no te preocupes: SCSS es un superset
de CSS3. Qu significa esto? Todo lo que antes funcionaba en CSS ahora te funciona en SCSS.
Entonces de qu te sirve? En este enlace23 te dejo una pequea gua para que veas la potencia
de Sass y vayas familiarizndote con variables, mixins y el resto de la terminologa. Una vez que
empieces a usarlo, no querrs volver atrs.
En este captulo vamos a aprender a crear nuestros archivos SCSS y utilizarlos mientras estamos
desarrollando de tal forma que no tengamos que estar precompilando cada vez que hagamos un
cambio. Adems, en el truco te explicar como se suele organizar la estructura de SCSS para trabajar
de una forma ptima.

Instalacin
Como todo captulo, tenemos que instalar los requisitos necesarios para que las herramientas
funcionen. Lo primero que debes saber es que para instalar Gulp24 necesitas haber instalado
previamente Node.js25 y npm26 en tu ordenador. El primero de ellos sirve para disponer de un entorno
JavaScript en el lado del servidor gracias al motor V8 de Google, mientras que el segundo es un
gestor de paquetes para Node y muchas otras herramientas. Estos son los comandos para un sistema
operativo basado en Ubuntu:

1 sudo apt-get install nodejs-legacy npm

Nada que te asuste de momento, verdad? Lo siguiente que haremos es instalar Gulp desde npm. Este
gestor de paquetes tiene la posibilidad de instalar un paquete a nivel de proyecto o a nivel global. En
el caso de Gulp, necesitamos tenerlo instalado a nivel global para poder ejecutarlo como comando,
pero tambin a nivel local como dependencia de lo que realizaremos en nuestro gulpfile.js. Para
instalarlo de formal global, ejecutamos el siguiente comando:

23
http://sass-lang.com/guide
24
http://gulpjs.com/
25
https://nodejs.org/
26
https://www.npmjs.com/
Captulo 6: Gulp (1) 40

1 sudo npm install gulp -g

Ya tenemos todo lo necesario para poder meternos en terreno ms pantanoso. Pero puede que
pienses que para transfomar tus archivos SCSS a CSS necesites tener Ruby instalado. Con LibSass
tan solo necesitas tener Node.js instalado, y adems es mucho ms rpido el proceso que con Ruby.
Instalaremos algunas gemas de Ruby ms adelante pero, por ahora, tenemos todo lo que necesitamos
instalado.
Siguiente paso: crear nuestro primer archivo SCSS.

Creando nuestro primer SCSS


Existen muchos sitios en los que puedes crear la carpeta SCSS. Segn las buenas prcticas, los
assets deben ir en la carpeta pblica web, pero nicamente los que sean pblicos. De esta forma,
yo suelo crear una carpeta app/Resources/assets/scss en la que trabajo y una tarea de gulp que
enviar el resultado a la carpeta pblica web/css. Para comenzar, creamos nuestro archivo global
app/Resources/assets/scss/app.scss con el siguiente contenido:

1 @import '../../../../web/bundles/mopabootstrap/sass/mopabootstrapbundle-3.2';

Por si eres nuevo en Sass, esta lnea lo que hace es importar otro archivo SCSS en el que est todo
lo necesario para que funcione Bootstrap gracias al bundle que instalamos en el captulo anterior.
Por supuesto, ms adelante iremos aadiendo ms lneas a este archivo.
Creado nuestro ultra-complejo SCSS, es hora de transformarlo para que lo entienda nuestro
navegador. Usaremos una tarea de Gulp para este objetivo, pero primero, hay que ir dando una
serie de pasos que nos ayudarn en un futuro tanto a nosotros como a otros posibles desarrolladores
que quieran desplegar y trabajar con este proyecto. Introducimos el siguiente comando en la raz de
nuestro proyecto y vamos rellenando los datos:

1 npm init

Este comando crear un archivo package.json en la raz, con el siguiente contenido, en mi caso:
Captulo 6: Gulp (1) 41

1 {
2 "name": "aupa_bilbao",
3 "version": "0.0.1",
4 "description": "Aupa Bilbao",
5 "main": "gulpfile.js",
6 "author": "Jon Torrado",
7 "license": "MIT"
8 }

Te suena? Aqu definiremos los paquetes NPM necesarios para que funcionen nuestras tareas de
Gulp. De esta forma, cuando lo despliegues en otro PC u otra persona quiera trabajar en el proyecto,
bastar con ejecutar npm install para que npm lea este archivo e instale los paquetes necesarios.
Pero qu es lo que necesitamos? Ya veo que tienes ganas. El primer paquete, aunque ya lo tenemos
instalado a nivel global, es necesario que est instalado como local como te he dicho anteriormente:

1 npm install gulp --save-dev

Has visto el cambio en package.json? Esto se debe al --save-dev aadido en la parte final del
comando y que permite, adems de instalar el paquete, guardar la dependencia en el archivo de
paquetes. Como ya te he comentado, si un nuevo desarrollador quiere desplegarlo en su ordenador,
tan solo necesitar tener npm instalado y realizar npm install en la raz, y ya est! Ahora,
instalamos el resto de paquetes que vas a necesitar:

1 npm install gulp-sass --save-dev

Ya tenemos todo lo necesario instalado. ltimo paso: definir la tarea de Gulp que realizar la
compilacin. Para ello, crea un archivo gulpfile.js en la raz del proyecto con este contenido:

1 var gulp = require('gulp'),


2 sass = require('gulp-sass');
3
4 gulp.task('sass', function () {
5 return gulp.src('./app/Resources/assets/scss/app.scss')
6 .pipe(sass())
7 .pipe(gulp.dest('./web/css/'));
8 });

Lo conseguimos! Ejecuta la tarea, que hemos llamado sass, para tener tu archivo CSS que s
entender nuestro navegador:
Captulo 6: Gulp (1) 42

1 gulp sass

Ya tienes disponible tu archivo web/css/app.css listo para ser incluido en tu plantilla base.
Editamos app/Resources/views/base.html.twig y aadimos el CSS en la parte superior, siempre
teniendo en cuenta los bloques que podemos sobrescribir de la plantilla base que extendemos de
MopaBootstrap:

1 {% block head_style %}
2 <link href="{{ asset('css/app.css') }}" type="text/css" rel="stylesheet" />
3 {% endblock head_style %}

Es hora de ver lo que hemos creado. Abre tu navegador y dirgete a http://127.0.0.1:8000/. Lo ves?
Ya tenemos los estilos que nos regala Bootstrap y todo en su sitio pero todava no hay interaccin.
Por qu? Sencillo: nos falta de incluir los archivos JavaScript. Esto tardar un rato ms, porque
ahora tengo que ayudarte a ser un maestro/a jedi de Gulp.

Minificando nuestro CSS


Una de las cosas ms interesantes y fciles que se puede aplicar para reducir el tamao de nuestros
assets es minificar nuestras hojas de estilos. En este caso, ser nuestra hoja de estilos, porque con
Sass conseguiremos tener una nica hoja de estilos que combinar todas las anteriores (en el truco
se ver mejor esto). Minificar esta gigantesca hoja de estilos significa quitarle todos los caracteres
que no se necesitan para nada, incluso los saltos de lnea. Entonces no vas a poder leer la hoja de
estilos resultante? En realidad, tampoco lo necesitas, y con el devtools de Chrome o el Firebug es
sufience, pero te voy a dar la posibilidad de tener ambas hojas de estilo: la normal y la minificada.
Empezamos instalando los paquetes necesarios para conseguir el objetivo que acabamos de comen-
tar:

1 npm install gulp-rename gulp-cssnano --save-dev

Despus, actualizamos nuestro gulpfile.js, que ahora tendr esta pinta:


Captulo 6: Gulp (1) 43

1 var gulp = require('gulp'),


2 sass = require('gulp-sass'),
3 rename = require('gulp-rename'),
4 cssnano = require('gulp-minify-css');
5
6 gulp.task('sass', function () {
7 return gulp.src('./app/Resources/assets/scss/app.scss')
8 .pipe(sass())
9 .pipe(gulp.dest('./web/css/'))
10 .pipe(rename({suffix: '.min'}))
11 .pipe(cssnano())
12 .pipe(gulp.dest('./web/css/'));
13 });

Ves lo que hemos hecho? Primero transformamos SCSS a CSS y lo dejamos como app.css.
Posteriormente encadenamos esta misma salida para renombrar el archivo aadindole .min por
detrs, minificarlo y dejarlo en la misma carpeta, disponiendo finalmente dos archivos: app.css
y app.min.css. Eh! No te olvides de cambiar tu app/Resources/views/base.html.twig para que
sirva el CSS minificado:

1 {% block head_style %}
2 {% if app.debug %}
3 <link href="{{ asset('css/app.css') }}" type="text/css" rel="stylesheet" />
4 {% else %}
5 <link href="{{ asset('css/app.min.css') }}" type="text/css" rel="stylesheet"\
6 />
7 {% endif %}
8 {% endblock head_style %}

He aadido una pequea lgica para que sirva diferentes archivos dependiendo de si estamos en
app_dev.php o no. Tras esto, todava nos queda una cosilla ms que podemos hacer antes de servir
el CSS y esta te va a gustar mucho mucho!

Autoprefixer
Es posible que ya conozcas esta herramienta, pero si no es as, te doy la bienvenida a una nueva forma
de aprovechar tu tiempo a la hora de escribir tu cdigo SCSS. Existen una serie de caractersticas CSS
que para que funcionen en todos los navegadores tienes que aadir lo que se llaman vendor prefixes.
Como un ejemplo vale ms que mil palabras, te ilustro con la funcionalidad del borde redondeado:
Captulo 6: Gulp (1) 44

1 -webkit-border-radius: 5px;
2 -moz-border-radius: 5px;
3 border-radius: 5px;

Adems de estos vendor prefixes, tambin existen los de Internet Explorer y Opera. La lista completa
es la siguiente:

Android, Chrome, iOS, Safari: -webkit-


Firefox: -moz-
Internet Explorer: -ms-
Opera: -o-

Mucha gente soluciona esto con mixins de Sass, pero la solucin que te propongo es mucho mejor. Y
si te digo que no tienes que escribir ms que la lnea CSS sin vendor prefixes? Siguiendo el ejemplo
anterior, border-radius: 5px; sera suficiente, ya que Gulp se encargar de reescribir nuestro CSS
y aadir todos los prefixes por nosotros. Para conseguir esta varita mgica, aadimos el siguiente
paquete:

1 npm install gulp-autoprefixer --save-dev

Por supuesto, tenemos que modificar la tarea de Gulp. Editamos gulpfile.js con la nueva funciona-
lidad:

1 var gulp = require('gulp'),


2 sass = require('gulp-sass'),
3 autoprefixer = require('gulp-autoprefixer'),
4 rename = require('gulp-rename'),
5 cssnano = require('gulp-minify-css');
6
7 gulp.task('sass', function () {
8 return gulp.src('./app/Resources/assets/scss/app.scss')
9 .pipe(sass({sourceComments: 'map'}))
10 .pipe(autoprefixer())
11 .pipe(gulp.dest('./web/css/'))
12 .pipe(rename({suffix: '.min'}))
13 .pipe(cssnano())
14 .pipe(gulp.dest('./web/css/'));
15 });

Ahora, cada vez que lancemos gulp sass transformaremos SCSS en CSS, aadiremos los prefijos
al archivo app.css, minificaremos este archivo y lo dejaremos como app.min.css, no te parece
genial? Solo queda una cosa por resolver: a nadie le gustara tener que estar lanzando esta tarea
cada vez que modificamos alguno de nuestros archivos SCSS mientras estamos desarrollando. Gulp
soluciona esto por defecto: la tarea watch.
Captulo 6: Gulp (1) 45

Gulp watch
Gulp trae una funcionalidad por defecto a la que le dices: oye Gulp, cada vez que modifique algo
de esta ruta, llama a esta tarea. Ves por dnde voy? Lo que vamos a hacer es decirle a Gulp
que cada vez que modifiquemos un archivo SCSS llame a la tarea que compila que hemos creado
anteriormente. Editamos el archivo gulpfile.js y aadimos la siguiente tarea:

1 gulp.task('watch', function () {
2 gulp.watch('./app/Resources/assets/scss/**/*.scss', ['sass']);
3 });

Esta tarea, a la que por supuesto hemos llamado watch, es capaz de detectar las modificaciones en
todos los archivos .scss dentro de la carpeta app/Resources/assets/scss y sus subdirectorios y
llama a la tarea sass creada anteriormente. Ahora, tan solo tienes que escribir gulp watch en la
terminal, editar cualquier archivo y guardarlo, prubalo!
Nota: si usas Ubuntu y te da un error, aade este comando:

1 echo fs.inotify.max_user_watches=524288 | sudo tee -a /etc/sysctl.conf && sudo s\


2 ysctl -p

Tarea por defecto


Si no le pasas ningn parmetro a Gulp, es decir, si solo escribes gulp en la terminal, se ejecutar
la tarea por defecto. Lo ms correcto es definir una que pueda ser interesante. Yo por ejemplo
suelo poner que ejecute la compilacin y empiece a escuchar. De esta forma, cuando me pongo
a desarrollar, ejecuto gulp y al lo. Aqu te dejo el cdigo de ejemplo, pero sintete libre de modificar
esta tarea default:

1 gulp.task('default', ['sass', 'watch']);

Con esto hemos terminado la primera parte de Gulp, ya que en en captulos posteriores le vamos
a dar una vuelta de tuerca ms a nuestro gulpfile.js para que nos proporcione muchas ms
funcionalidades. No te olvides que esto es un ejemplo didctico, as que te aconsejo que mires lo
que estn haciendo otros con Gulp. Para empezar, deberas mirar postcss27 . Como ejercicio, deberas
intentar cambiar autoprefixer por cssnext.
27
https://github.com/postcss/postcss
Captulo 6: Gulp (1) 46

Truco 6
Tener nuestra aplicacin bien organizada es de vital importancia para cualquier implicado en ella.
En este truco te voy a proponer una forma de estructurar los archivos SCSS en tu proyecto Symfony,
pero quiero que sepas que no es la nica ni mucho menos la mejor. sala, comprueba cmo te sientes
trabajando de esta forma y adptala a ti y a tus proyectos.
Lo primero que debes saber es cmo combina Sass los archivos. Te habrs dado cuenta de que,
cuando hemos lanzado la tranformacin de Sass con Gulp, solo se ha producido un archivo. Sass
va coger todos los archivos de las carpetas que le indiquemos y generar un archivo CSS final por
cada archivo que no empiece con barra baja. Por eso debes tener un archivo app.scss y el resto
de archivos deben estar incluidos en ste y nombrados como _archivo.scss. A la hora de incluir el
archivo en app.scss no hace falta que escribas la barra baja, ni tampoco la terminacin .scss.
Dicho esto, o ms bien, escrito esto, pasamos a hablar de la estructura de archivos. En la carpeta
app/Resources/assets/scss solo debera haber un archivo: app.scss. El resto, irn dentro de
carpetas. La carpeta base contendr estilos que se aplican de forma global a toda la plataforma.
Aqu te dejo un ejemplo:

_colors.scss: todas las variables de colores que se van a utilizar en la aplicacin.


_common.scss: estilos globales, como por ejemplo los h1, los a, etc.
_fonts.scss: inclusin de fuentes. Si adems de incluir quieres meter algn estilo general, o
variables con los tamaos de fuentes, puedes renombrarlo a _typography.scss.

Existen ms posibles ficheros, pero te dejo que los vayas encajando segn te vayan surgiendo.
La siguiente carpeta a comentar es components, donde tendremos un archivo SCSS por cada
componente de nuestra aplicacin. Qu es un componente? Para que algo sea reutilizable entre
pginas, lo ms correcto es hacer un componente que se pueda incluir en cualquier sitio y no dependa
ms que de si mismo: un carrousel, botones, dropdowns, tablas de precio, etc. Cada uno de estos
tendr su archivo correspondiente en esta carpeta.
En el directorio helpers tendremos archivos SCSS que nos ayudarn con elementos de posiciona-
miento o tamaos. Por ejemplo:

_align.scss: aunque esto ya lo trae Bootstrap, sirve para crear las clases de alinear a la derecha,
izquierda, centrado y justificado. De esta forma podemos aplicar las clases a los elementos y
no aplicar el estilo concreto al elemento.
_dividers.scss: en este archivo meteremos todo lo que tiene que ver con borders u otros
elementos de divisin entre componentes.
_icons.scss: para posicionar y dimensionar correctamente los iconos o imgenes que usamos
en la aplicacin.
_spacing.scss: para incluir espaciados en los elementos sin tener que aplicar estilos concretos,
sino llamando a los mixins de este archivo (busca por Google, este apartado es muy
interesante).
Captulo 6: Gulp (1) 47

La penltima carpeta a comentar es layout. Como seguro que has adivinado, en esta carpeta habr
al menos 3 archivos: los correspondientes al header, content y footer. Adems, puedes crear nuevos
archivos para modificar los estilos de alguno de otros elementos para mvil o escritorio.
Por ltimo, el directorio pages donde irn los estilos especficos de una pgina que no encajan en
ninguna de las carpetas anteriores. El claro ejemplo de archivo que cuadra aqu es _404.scss, lo ves?
Con esto hemos terminado el truco y uno de los captulos ms interesantes del libro. Realiza pruebas
y sintete cmodo con esto, avanzars muchsimo ms rpido que como lo hacas hasta ahora (y si
no es as, mndame un correo para que yo aprenda como lo haces :P).

Resumen
En este captulo hemos aprendido a utilizar las tareas de Gulp para transformar los archivos SCSS a
CSS. Adems, hemos creado una tarea watch que nos permitir trabajar en caliente con los archivos
mientras que el sistema es capaz de actualizar el CSS resultante de forma automtica. En el siguiente
captulo utilizamos la herramienta previamente (hasta Symfony 2.7) incluida en Symfony llamada
Assetic para trabajar con los JavaScripts, pero que sepas que tambin podras utilizar Gulp sin
problema alguno.
Captulo 7: Assetic con JavaScript
Es hora de interactuar con el usuario, o al menos de que la parte de JavaScript del framework
Bootstrap que estamos utilizando en nuestro proyecto. Podramos utilizar Gulp de una forma similar
a la que la hemos usado en el captulo anterior para la transformacin de Sass a CSS. De hecho, es
algo que recomiendo hacer encarecidamente. Sin embargo, Assetic es una buena opcin en este
punto por motivos didcticos.
En este captulo vamos a aprender a trabajar con archivos JavaScript de una forma sencilla a la par
de correcta. Adems, en el truco, te explicar una de las formas de trabajar con Symfony que yo suelo
utilizar en mis desarrollos. Como siempre, no es la nica ni mucho menos la mejor, es simplemente
una ms que a mi me funciona bien, y espero que a ti tambin.

NOTA: esto es solo un captulo didctico. La recomendacin actual es utilizar Gulp para
trabajar tambin los archivos JavaScript. Se ver en el ltimo captulo.

Instalacin
A partir de la versin 2.8 de Symfony, Assetic no viene instalado por defecto. Por lo que si te
encuentras en este versin o superior, lo primero que tienes que hacer es descargar la dependencia
de Composer:

1 composer require symfony/assetic-bundle

Hecho esto, hay que activar el bundle. Para ello, edita el archivo app/AppKernel.php aadiendo la
siguiente lnea:

1 new Symfony\Bundle\AsseticBundle\AsseticBundle(),

Por ltimo, aade la siguiente configuracin en app/config/config.yml:


Captulo 7: Assetic con JavaScript 49

1 assetic:
2 debug: '%kernel.debug%'
3 use_controller: '%kernel.debug%'
4 filters:
5 cssrewrite: ~

Listo! Es hora de realizar la segunda parte de la configuracin.

Configuracin
Antes de comenzar a configurar nada, me gustara recordarte que Assetic que tambin sirve para la
transformacin de Sass a CSS, pero es infinitamente ms lento y te har perder mucho tiempo en
comparacin con Gulp. An as, te invito a que lo pruebes y me comentes los resultados que obtienes
con ambos e incluso con otras posibles alternativas.
Lo primero que vamos a realizar es descargar jQuery, ya que Bootstrap lo necesita como dependencia
para el correcto funcionamiento del framework. En este caso, Bootstrap 3 necesita de jQuery
1.x, versin que podrs encontrar en http://jquery.com/download/28 . Puedes descargarte la versin
uncompressed, ya que nos encargaremos ms adelante de minificarla nosotros mismos. El archivo
descargado debes dejarlo en la carpeta pblica web/js.

NOTA: aqu existen varias posibilidades. Podramos usar npm para descargar jQuery,
pero tendramos que realizar una tarea de Gulp para que copie el archivo JavaScript
a la carpeta pblica web. Tambin podramos utilizar otra tecnologa como Bower que
permite decirle dnde descargar las dependencias que queremos. Se ver ms adelante.
De momento, utilizaremos la forma fcil y ms adelante lo iremos complicando.

El resto de archivos ya los tenemos disponibles, as que vamos manos a la obra. Editamos el archivo
app/Resources/views/base.html.twig y aadimos la etiqueta correspondiente de Assetic para
JavaScript en el bloque preparado para tal en la plantilla base de MopaBootstrap:

28
http://jquery.com/download/
Captulo 7: Assetic con JavaScript 50

1 {% block head_script %}
2 {% javascripts output='js/compressed/app.min.js'
3 'js/jquery.js'
4 'bundles/mopabootstrap/js/modernizr-2.7.1-respond-1.4.2.min.js'
5 'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/toolt\
6 ip.js'
7 'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/*.js'
8 %}
9 <script type="text/javascript" src="{{ asset_url }}"></script>
10 {% endjavascripts %}
11 {% endblock head_script %}

Revisa que los nombres de los archivos coincidan con lo que tienes en tu proyecto. Como puedes
ver en el cdigo anterior, lo primero que hacemos es declarar la salida del JavaScript, que estar
en la ruta js/compressed/app.min.js. De momento no est minificado a pesar de que ponga .min
en el nombre, pero ya vamos preparando el terreno para llegar a este puerto ms adelante. Las
siguientes lneas incluyen los diferentes archivos JavaScript de nuestra aplicacin que sern unidos
en un solo archivo final, teniendo en cuenta que popover necesita tener cargado antes tooltip.
Es por esta razn que escribimos manualmente esa dependencia antes, ya que el asterisco carga en
orden alfabtico (esto te lo chivo yo, pero te habras dado cuenta por el error que sale en la consola
de tu navegador). La lnea intermedia no es ms que la salida HTML que producir el bloque.
Hora de probar lo que acabamos de introducir. Dirgete a http://127.0.0.1:8000/ y pulsa sobre el
desplegable superior, funciona? Espero que s, porque sino tendrs que rehacer alguno de los puntos
que hemos tratado en estos tres ltimos captulos.

Minificando nuestro JavaScript


Nuestro siguiente paso ser minificar el JavaScript resultante para hacer que nuestros assets ocupen
menos. Existen varias formas de lograr este objetivo, todas ellas descritas en las recetas de Assetic
que te dejo en este enlace29 . Nosotros, para nuestro ejemplo, vamos a utilizar UglifyJS.
En este caso, necesitamos instalar el binario de uglifyjs, por lo que introducimos el siguiente
comando:

1 npm install uglify-js --save-dev

El siguiente paso es configurar el filtro de Twig que posteriormente utilizaremos para minificar. Para
configurarlo, editamos el archivo app/config/config.yml y en la seccin de assetic introducimos
lo siguiente:

29
https://symfony.com/doc/current/cookbook/assetic/index.html
Captulo 7: Assetic con JavaScript 51

1 assetic:
2 debug: "%kernel.debug%"
3 use_controller: false
4 bundles: [ ]
5 #node: /usr/bin/nodejs
6 filters:
7 cssrewrite: ~
8 uglifyjs2:
9 bin: "%kernel.root_dir%/../node_modules/uglify-js/bin/uglifyjs"

Tienes comentado el binario de node por si ocurre que tu sistema operativo dispone de una ruta
distinta a la habitual y tienes que decirle a Assetic donde est ese binario.
Por ltimo, modificamos nuestro archivo app/Resources/views/base.html.twig para que la sec-
cin de javascripts utilice el recin creado filtro:

1 {% block head_script %}
2 {% javascripts filter='uglifyjs2' output='js/compressed/app.min.js'
3 'js/jquery.js'
4 'bundles/mopabootstrap/js/modernizr-2.7.1-respond-1.4.2.min.js'
5 'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/toolt\
6 ip.js'
7 'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/popov\
8 er.js'
9 'bundles/mopabootstrap/bootstrap-sass/assets/javascripts/bootstrap/*.js'
10 %}
11 <script type="text/javascript" src="{{ asset_url }}"></script>
12 {% endjavascripts %}
13 {% endblock head_script %}

Ya lo tenemos! Recarga la pgina que hemos estado utilizando como ejemplo durante todos estos
captulos y vers los archivos JavaScript minificados automticamente. Pero ten cuidado: esto solo
ocurre en modo desarrollo, es decir, cuando ests con navegando con app_dev.php (por defecto
con el built-in server). Si quieres generar el JavaScript minificado final para que funcione en modo
produccin, debers ejecutar el siguiente comando en una terminal:

1 bin/console assetic:dump --no-debug

No te gusta tenerlo todo minificado en modo desarrollo? No te preocupes, puedes desactivar el


filtro nicamente para desarrollo de la siguiente forma:
Captulo 7: Assetic con JavaScript 52

1 filter='?uglifyjs2'

Aadiendo una ? delante del filtro hace que Assetic lo ignore cuando estemos trabajando en
desarrollo.
Con todo lo que hemos realizado durante estos captulos, ya puedes empezar a trabajar de una
forma completa con tu herramienta. Es por esto que en el siguiente captulo realizaremos un ejemplo
completo de una pgina de Symfony, desde la declaracin de la ruta al renderizado de la plantilla.
Pero antes me gustara recomendarte algunos temas de inters relacionados con este captulo.

Recomendaciones
Quiero aprovechar esta seccin del captulo para hablar de dos temas que son muy aconsejables
de, al menos, leer. El primero de ellos tiene que ver con Assetic. Al igual que sirve para minificar
CSS y JS, tambin sirve para minificar imgenes. Gracias a jpegoptim se pueden hacer algunos
malabarismos para tratar las imgenes antes de ser servidas por el servidor web. En este enlace30 te
dejo la receta en la que se habla de ello.
Por otro lado, est el tema de los archivos JavaScript. Si tu aplicacin hace un uso intenso de estos
archivos, deberas mirar RequireJS: un archivo JavaScript cargador de mdulos bajo demanda y con
dependencias. Sin duda tus pginas tendrn un antes y un despus al utilizar una tecnologa como
esta, que adems es compatible con prcticamente todos los navegadores actuales. Visita su pgina
web aqu31 y comienza a usarlo.

Truco 7
En el truco de este captulo voy a contarte como trabajaba yo en algunos (no en todos) de mis
proyectos mientras estaban en desarrollo. Como siempre, cada uno tenemos nuestras formas y
nuestras manas, nuestros trucos y nuestros defectos, pero gracias a esta riqueza de diferencias
tenemos un sector que crece a la velocidad de la luz. Si alguien te dice que su forma de trabajar
es la mejor miente, y por eso yo jams dir eso en ningn sitio.
Si abres la plantilla base de MopaBootsrap vers que hay un bloque tal que as:

1 {% block foot_script_assetic %}
2 {% endblock foot_script_assetic %}

Como su propio nombre indica, este bloque nos permite introducir elementos JavaScript antes del
footer. Aunque el nombre del bloque contenga la palabra assetic, obviamente no estamos obligados
30
https://symfony.com/doc/current/cookbook/assetic/jpeg_optimize.html
31
http://requirejs.org/
Captulo 7: Assetic con JavaScript 53

a utilizar dicha tecnologa, as que nos aprovecharemos de este bloque para insertar cdigo JS dentro
de la misma plantilla.
En cada archivo Twig puedes aadir el cdigo JS que hace referencia al HTML que esa plantilla
renderiza, por ejemplo:

1 {% block foot_script_assetic %}
2 <script>
3 $(document).ready(function() {
4 $('#form').submit(function() {
5 console.log('Submit!');
6 });
7 });
8 </script>
9 {% endblock foot_script_assetic %}

Lgicamente, este cdigo no se minificar puesto que no est en ningn archivo JavaScript, pero
nos permite desarrollar de una forma ms sencilla ya que este cdigo JS y la pgina HTML en
la que debe funcionar estn en el mismo sitio. Cuando hayas terminado y sea una versin ms o
menos definitiva, acurdate de crear el archivo JavaScript correspondiente y copiar el cdigo que has
realizado en la plantilla a este nuevo archivo. Adems, tienes que editar el bloque %javascripts%
de Assetic y aadir tu archivo JS recin creado que deberas haber colocado en web/js. Al terminar,
tendremos varios archivos JavaScript bien organizados y un nico app.min.js final con todo lo
necesario para que nuestro proyecto funcione.

Resumen
En este captulo hemos aprendido a incluir los ficheros JavaScript necesarios por Bootstrap y a
trabajar con nuestros propios ficheros JS. En este punto ya tenemos una base solida para poder
empezar a desarrollar nuestro proyecto en Symfony, as que en el siguiente captulo realizaremos
un ejemplo completo: desde la ruta hasta el renderizado de la plantilla.
Captulo 8: ejemplo completo
Ya disponemos las suficientes herramientas para poder empezar a construir nuestro proyecto web
completo. Por supuesto, aprenderemos muchas ms herramientas de las que nutrirnos para hacer
dicho proyecto ms slido y completo, pero es hora de parar un poco, reflexionar sobre lo que
hemos aprendido hasta ahora y realizar un ejemplo completo para afianzar nuestro conocimientos.
En este captulo veremos como trabajar con Symfony desde que nuestra aplicacin recibe la
request hasta que devolvemos un objeto response. Aprovecharemos tambin para hablar de
buenas practicas en cada una de las secciones, tanto las oficiales como las mas personales.

El flujo de la aplicacin
Mucha gente dice que Symfony es un framework MVC; otros decimos que es un framework request-
response. Esta ltima asignacin me gusta ms porque se asemeja a la forma de programar nuestra
aplicacin.
En este captulo vamos a realizar una home, con un carrusel, unos claims para de esta forma aprender
a trabajar con el grid de Bootstrap. Adems, crearemos un formulario de contacto para trabajar con
tanto con forms de Symfony como con los servicios.
Si fuera de este ejemplo, en alguno de tus proyectos, ya tienes claro lo que vas a hacer, debes seguir
estos pasos:

1. Crear la ruta correspondiente


2. Crear la funcin (action) que ejecuta la ruta dentro de un controller
3. Crear el cdigo necesario dentro de esa funcin que generar lo necesario para la vista
4. Crear la plantilla Twig que renderice lo generado en el controller

Vayamos poco a poco resolviendo cada uno de los puntos anteriores.

Creando la ruta
En Symfony existen varias formas de crear las rutas. Si ests generando un bundle para que otros
lo incorporen en su proyecto, te aconsejo que utilices el formato YAML para tus rutas, ya que
sern ms sencillas de incluir por otros; sin embargo, para tu propia aplicacin, te aconsejo que
uses anotaciones. Incluso en mi propio trabajo hay gente que difiere conmigo y prefiere trabajar
siempre con YAML, as que te digo lo mismo de siempre: escoge con lo que ms cmodo te sientas,
ya que no hay una mejor ni peor.
Un ejemplo de ruta YAML sera lo siguiente, situado en un archivo routing.yml:
Captulo 8: ejemplo completo 55

1 home:
2 path: /home
3 defaults: { _controller: AppBundle:Home:index }

Del cdigo anterior, extraemos lo siguiente:

La ruta se llamar home


La URL ser /home
La funcin a ejecutar ser indexAction y estar en el controller HomeController dentro del
AppBundle.

Ahora, si te quieres ahorrar esto e ir directamente al cdigo del controller, puedes hacerlo gracias
a las anotaciones. Las anotaciones nos permiten declarar la ruta en el mismo controller justo sobre
la funcin a ejecutar, lo que hace que nuestro cdigo est algo menos desacoplado pero que sea
mucho ms fcil de leer.
La misma ruta anterior en el HomeController que comentaba anteriormente sera:

1 /**
2 * @Route("/home", name="home")
3 */

Tambin debes saber que con anotaciones no necesitas darle un nombre a la ruta, y esta coger
su nombre de la siguiente forma: nombrebundle_nombrecontroller_nombreaccion. En este caso, el
nombre generado sera app_home_index, sencillo, no? Siempre puedes ver todas las rutas con el
siguiente comando de Symfony.

1 bin/console debug:router

Para que las anotaciones funcionen, asegrate que tu controller es capaz de leerlas con el correspon-
diente use en la parte superior:

1 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;

Ya tenemos nuestra ruta lista, siguiente paso: el mtodo action.

Creando el mtodo action


Con anotaciones esto es ms sencillo ya que nada ms declarar la ruta tienes que declarar la funcin
que ejecuta esa ruta. Siguiendo el ejemplo anterior, nuestra action quedara de la siguiente forma:
Captulo 8: ejemplo completo 56

1 /**
2 * @Route("/home", name="home")
3 */
4 public function indexAction()
5 {
6
7 }

De momento no te voy a echar la bronca por la gua de estilos de PHP, pero tendrs un captulo
ms adelante que se encargar de sacarte los colores. En cuanto al controller, doy por supuesto que
sabes crear uno. Symfony trae un DefaultController creado gratuitamente, por lo que solo tienes que
copiarlo y renombrarlo a HomeController y quitar las funciones que ya estaba hechas.
En cuanto a nuestro ejemplo didctico, la ruta y la funcin action ya estn listas, es hora de empezar
a rellenar este mtodo de cdigo PHP.

Nuestra propia home


Antes de empezar a codificar PHP como locos, hay que sentarse y pensar lo que queremos mostrar
en pantalla y cmo queremos mostrarlo. Como te he dicho en la introduccin del captulo, nuestra
home personal tendr:

Carrusel de imgenes
Claims
Formulario de contacto

He querido que nuestra home tenga estos elementos porque cada uno de ellos utilizar una tcnica
distinta que nos ser de gran utilidad didctica.
El carrusel de imgenes har uso de los repositorios de Symfony para obtener las imgenes
(destacados, por ejemplo) y posteriormente del componente carousel que nos proporciona Bootstrap.
Los claims los haremos estticos pero utilizando la estructura de componentes que comentamos en
el captulo 6. Haremos uso del grid de Bootstrap y explicar brevemente como hacer tu contenido
responsive.
El formulario de contacto har uso de los formularios de Symfony adems de los servicios. Para
complicarlo un poco, lo meteremos en un modal de Bootstrap que incluiremos mediante la llamada
a un controller desde la plantilla Twig.
Saber entender y manejar todo lo que te acabo de proponer en este apartado es bsico para que
desarrolles aplicaciones en Symfony de una forma correcta. No tengas prisa de continuar al siguiente
apartado y crea una base solida en tu cabeza para que te sea mucho ms sencillo continuar.
Captulo 8: ejemplo completo 57

En la ltima parte de esta seccin, me gustara hablarte sobre los controladores. Mucha gente cree
que los controladores son cajones de sastre en los que escribir un chorro de PHP que luego es
ingestionable y, por supuesto, no reutilizable. Hay que evitar esto a toda costa, ya que realizarlo de
una forma correcta supondr muy pocos minutos de tu tiempo a corto plazo pero muchsimo tiempo
ganado a medio plazo. Comencemos con el carrusel de imgenes.

El carrusel de imgenes
Para el carrusel de imgenes, primero vamos a crear la entidad que guardar lo que necesitemos. Yo
simplemente voy a crear dos campos adems del id que deberan tener todas tus entidades, pero
te propongo que lo compliques todo lo que quieras.
Para crear una nueva entidad, la forma ms sencilla de hacerlo es con el siguiente comando de
Symfony:

1 bin/console doctrine:generate:entity

Cuando te pida los datos, introduces lo siguiente:

The Entity shortcut name: AppBundle:FeaturedImage


Configuration format: annotation
Field name: url / Field type: string / Field length: 255 / Is nullable: false / Unique: false
Field name: active / Field type: boolean / Is nullable: false / Unique: false

En la segunda pregunta, tambin puedes utilizar cualquiera de los otros formatos propuestos. Es
muy parecido al sistema de rutas, ya que si utilizas YAML, por ejemplo, te crear un archivo
[bundle]/Resources/config/doctrine/FeaturedImage.orm.yml donde estar toda la configura-
cin. Para este ejemplo, al igual que en mis proyectos, usaremos anotaciones.
Podrs ver dos nuevos archivos en tu Bundle:

src/AppBundle/Entity/FeaturedImage.php
src/AppBundle/Repository/FeaturedImageRepository.php

En el repositorio colocaremos todas las consultas a base de datos que tengan como base la entidad
FeaturedImage. Con el ejemplo que realizaremos ms adelante lo vers mejor.
Para modificar la estructura de base de datos, recuerda que debes ejecutar el siguiente comando en
la terminal:
Captulo 8: ejemplo completo 58

1 bin/console doctrine:schema:update --force

Si no ests seguro de qu es lo que va a pasar al ejecutarlo, puedes cambiar el parmetro --force


por --dump-sql y echar un ojo a las sentencias que se van a ejecutar antes de realizar los cambios.
Siguiente paso: crear la clase admin. Aqu te dejo el cdigo de mi src/AppBundle/Admin/Feature-
dImageAdmin.php:

1 <?php
2
3 namespace AppBundle\Admin;
4
5 use Sonata\AdminBundle\Admin\AbstractAdmin;
6 use Sonata\AdminBundle\Datagrid\ListMapper;
7 use Sonata\AdminBundle\Datagrid\DatagridMapper;
8 use Sonata\AdminBundle\Form\FormMapper;
9
10 class FeaturedImageAdmin extends AbstractAdmin
11 {
12 /**
13 * {@inheritdoc}
14 */
15 protected function configureFormFields(FormMapper $formMapper)
16 {
17 $formMapper
18 ->add('url', 'url')
19 ->add('active')
20 ;
21 }
22
23 /**
24 * {@inheritdoc}
25 */
26 protected function configureDatagridFilters(DatagridMapper $datagridMapper)
27 {
28 $datagridMapper
29 ->add('url')
30 ->add('active')
31 ;
32 }
33
34 /**
35 * {@inheritdoc}
Captulo 8: ejemplo completo 59

36 */
37 protected function configureListFields(ListMapper $listMapper)
38 {
39 $listMapper
40 ->addIdentifier('id')
41 ->add('url')
42 ->add('active', null, array('editable' => true))
43 ->add('_action', 'actions', array(
44 'actions' => array(
45 'edit' => array()
46 )))
47 ;
48 }
49 }

Te he aadido algunos trucos ms a este admin que no estaban en captulos anteriores:

En el formulario, he aadido el segundo parmetro a la url indicando de qu tipo tiene que


ser el input type. De esta forma, nos aseguramos de que lo que se va a introducir ser una
URL. Aprovecha para leerte el captulo de validaciones32 en la documentacin de Symfony.
En el listado, Sonata permite pasarle un parmetro editable que te permitir modificar los
campos booleanos con un simple clic sin entrar en el formulario.

No te olvides de aadir el servicio en app/config/services.yml:

1 sonata.admin.featured_image:
2 class: AppBundle\Admin\FeaturedImageAdmin
3 tags:
4 - { name: sonata.admin, manager_type: orm, group: "Content", label: "Fea\
5 tured Image" }
6 arguments: [~, AppBundle\Entity\FeaturedImage, ~]

Es hora de aadir unas cuantas imgenes. Para nuestro ejemplo, buscamos en Google imgenes de
ejemplo de por ejemplo 1280x550px. Te dejo en este33 enlace una bsqueda de imgenes para que
insertes unas cuantas desde el admin.
Ya hemos insertado las imgenes, por lo que nuestro siguiente paso es recuperarlas desde la base
de datos. Para lograr esto, creamos una funcin en el archivo FeaturedImageRepository.php que
obtendr las imgenes necesarias para el carrusel. Es posible que el da de maana las imgenes
que aparezcan en el carrusel sigan un algoritmo distinto, por eso en este caso aconsejo llamar a
32
https://symfony.com/doc/current/book/validation.html
33
https://www.google.es/search?q=landscape&espv=2&biw=1680&bih=913&tbs=isz:ex,iszw:1280,iszh:550&tbm=isch&source=lnt
Captulo 8: ejemplo completo 60

la funcin algo as como findCarouselImages en vez de findLastActiveImages (con esto quiero


decir que cabe la posibilidad de que en un futuro las imgenes que vayan al carrusel sean las ltimas
activas y que hayan sido subidas entre tales fechas, y es ms fcil mantener el nombre de la funcin
y modificar el algoritmo que cambiar todo en todos los sitios).
Editamos src/AppBundle/Repository/FeaturedImageRepository.php y aadimos la funcin que
se ajuste a nuestras necesidades, como por ejemplo:

1 <?php
2
3 namespace AppBundle\Repository;
4
5 use Doctrine\ORM\EntityRepository;
6
7 /**
8 * FeaturedImageRepository
9 *
10 * This class was generated by the Doctrine ORM. Add your own custom
11 * repository methods below.
12 */
13 class FeaturedImageRepository extends EntityRepository
14 {
15 public function findCarouselImages()
16 {
17 return $this->createQueryBuilder('f')
18 ->where('f.active = :active')
19 ->setParameter('active', true)
20 ->setMaxResults(5)
21 ->getQuery()
22 ->getResult();
23 }
24
25 }

Atencin: los repositorios sirven para hacer de intermediarios con la base de datos, asi que no uses
los repositorios para crear lgica de negocio. sta debe ir en un servicio siempre que tenga sentido
o sino en el propio Controller. Esta funcin es un ejemplo, ya que se podra hacer con el repositorio
por defecto:

1 $em->getRepository('AppBundle:FeaturedImage')->findBy(['active' => true], ['id' \


2 => DESC], 5);

Por ltimo, nuestro src/AppBundle/HomeController.php debera llamar a esta funcin para obtener
las imgenes y pasrselas a la plantilla correspondiente. Aqu te dejo el cdigo:
Captulo 8: ejemplo completo 61

1 <?php
2
3 namespace AppBundle\Controller;
4
5 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
6 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7
8 class HomeController extends Controller
9 {
10 /**
11 * @Route("/home", name="home")
12 */
13 public function indexAction()
14 {
15 $em = $this->getDoctrine()->getManager();
16 $fimageRepository = $em->getRepository("AppBundle:FeaturedImage");
17 $featuredImages = $fimageRepository->findCarouselImages();
18
19 return $this->render('home/index.html.twig', array(
20 'featuredImages' => $featuredImages
21 ));
22 }
23 }

Ya estamos muy cerca de terminar. Nuestro siguiente paso es crear la plantilla app/Resource-
s/views/home/index.html.twig, hacer que extienda de nuestra plantilla base y rellenar el hueco
necesario.

1 {% extends 'base.html.twig' %}
2
3 {% block container %}
4 <div class="container">
5 <h1>Aqu va el carrusel</h1>
6 </div>
7 {% endblock %}

Vaya, qu he hecho aqu? Resulta que nuestra plantilla base contiene un encabezado, un sidebar
y un footer que no me interesan en absoluto para esta home. Lo que he hecho es buscar un bloque
superior a todos ellos y sobrescribirlo a mi gusto. En este caso, el bloque container cuadra bastante
bien y nos permite rescribir la pgina completa.
Es posible que ya te hayas dado cuenta del error y que hayas intentado solucionar el problema
derivado de la barra superior. Al tratarse de un elemento fixed no ocupa espacio como tal, y nuestro
Captulo 8: ejemplo completo 62

comienzo de pgina se encuentra tapado por esta barra. En la propia documentacin de Bootstrap
tienes como arreglarlo. Crea el archivo app/Resources/assets/scss/base/_common.scss con el
siguiente contenido:

1 body {
2 padding-top: 55px;
3 }

Y por supuesto, edita app/Resources/assets/scss/app.scss para aadir este nuevo archivo:

1 @import '../../../../web/bundles/mopabootstrap/sass/mopabootstrapbundle-3.2';
2 @import 'base/common';

Recuerda tambin, si no tienes lanzado la tarea gulp watch, lanzar el comando de Gulp que convierte
nuestros archivos SCSS en CSS. Una vez que lo hagas, puedes dirigirte a http://127.0.0.1:8000/home
y ver como ha quedado.
Para finalizar, tenemos que dibujar nuestro carrusel. El cdigo de Bootstrap lo tienes en este34
enlace. Cpialo, pegalo en tu plantilla Twig e intenta adaptarlo para que se vean las imgenes que
hemos sacado de la base de datos. Si no lo consigues, aqu te dejo la solucin:

1 {% extends 'base.html.twig' %}
2
3 {% block container %}
4 <div class="container">
5 <div id="carousel-example-generic" class="carousel slide" data-ride="car\
6 ousel">
7 <ol class="carousel-indicators">
8 {% for i in 1..featuredImages|length %}
9 <li data-target="#carousel-example-generic" data-slide-to="{{ i-\
10 1 }}"{% if loop.first %} class="active"{% endif %}></li>
11 {% endfor %}
12 </ol>
13 <div class="carousel-inner" role="listbox">
14 {% for featuredImage in featuredImages %}
15 <div class="item{% if loop.first %} active{% endif %}">
16 <img src="{{ featuredImage.url }}" alt="">
17 <div class="carousel-caption">
18 Caption de la imagen {{ loop.index }}
19 </div>
34
http://getbootstrap.com/javascript/#carousel
Captulo 8: ejemplo completo 63

20 </div>
21 {% endfor %}
22 </div>
23
24 <!-- Controls -->
25 <a class="left carousel-control" href="#carousel-example-generic" ro\
26 le="button" data-slide="prev">
27 <span class="glyphicon glyphicon-chevron-left" aria-hidden="true\
28 "></span>
29 <span class="sr-only">Anterior</span>
30 </a>
31 <a class="right carousel-control" href="#carousel-example-generic" r\
32 ole="button" data-slide="next">
33 <span class="glyphicon glyphicon-chevron-right" aria-hidden="tru\
34 e"></span>
35 <span class="sr-only">Siguiente</span>
36 </a>
37 </div>
38 </div>
39 {% endblock %}

Impresionante! Con este primer ejemplo hemos aprendido a crear una ruta y su funcin a ejecutar,
un entidad con un repositorio para devolver los datos necesarios, enviar a la plantilla la informacin
necesaria y renderizar el elemento Carousel de Bootstrap. Para mejorar an ms esta parte,
podramos encapsular la parte de Twig en un componente reutilizable, pero en la siguiente seccin
del captulo aprenders a hacer esto.

Los claims
Con esta parte del ejemplo aprenderemos a controlar un poco el grid de Bootstrap y a gestionar
nuestra pgina en base a componentes. Este no es un libro de Bootstrap, as que comentar por
encima algunos elementos a tener en cuenta, pero dejo en tu tejado la parte de aprender a utilizarlo.
Para ser ms giles, no vamos a obtener los claims desde base de datos, as que directamente editamos
la plantilla que hemos utilizado anteriormente y, antes de cerrar el div principal introducimos tres
claims de esta forma:
Captulo 8: ejemplo completo 64

1 </div>{# cierra carousel-example-generic #}


2 <section id="claims" class="row">
3 {% for i in 1..3 %}
4 {{ include('component/claim.html.twig') }}
5 {% endfor %}
6 </section>

Como ves, lo que hemos hecho es sacar ese componente a una plantilla separada para que podamos
incluirla desde cualquier punto de nuestra aplicacin. El cdigo del claim lo he sacado de uno de los
ejemplos de Bootstrap y lo he adaptado un poco, pero puedes poner el que quieras:

1 <div class="claim col-md-4 text-center">


2 <img class="img-circle" src="data:image/gif;base64,R0lGODlhAQABAIAAAHd3dwAAA\
3 CH5BAAAAAAALAAAAAABAAEAAAICRAEAOw==" alt="Generic placeholder image" width="140"\
4 height="140">
5 <h2 class="claim__title">Claim</h2>
6 <p class="claim__text">Donec sed odio dui. Etiam porta sem malesuada magna m\
7 ollis euismod. Nullam id dolor id nibh ultricies vehicula ut id elit. Morbi leo \
8 risus, porta ac consectetur ac, vestibulum at eros. Praesent commodo cursus magn\
9 a.</p>
10 <p><a class="btn btn-default" href="#" role="button">View details </a></p>
11 </div>

Ahora me paro un poco para explicarte brevemente el tema del grid system:

Para poder introducir columnas (todas las clases que empiezan por col-) debes insertar
previamente una clase row.
Al ser mobile first, tienes que escribir las clases de ms concretas que necesites primero a
menos concretas (ms pequea, es decir, col-xs- hasta col-lg-).

Las posibles clases las tienes en este35 enlace.


Refresca la pgina para ver el resultado, qu te parece? A mi tambin me parece que le falta algo
de color, as que vamos a crear el archivo SCSS correspondiente app/Resources/assets/scss/-
component/_claim.scss:

35
http://getbootstrap.com/css/#grid-options
Captulo 8: ejemplo completo 65

1 .claim {
2 margin: 10px 0;
3
4 &__title {
5 color: #f22;
6 }
7
8 &__text {
9 font-style: italic;
10 }
11 }

Te dejo como tarea que quites ese color en hexadecimal de ah y crees el correspondiente
_colors.scss para declarar la variable que contenga ese valor y la utilices. Cuidado! No te olvides
de actualizar tu app/Resources/assets/scss/app.scss. Refresca de nuevo y visualiza la pgina.
Ves a dnde quiero llegar con esto? Hemos hecho que nuestro componente sea prefectamente
reutilizable en cualquier sitio de nuestra aplicacin, y si tuviramos una gua de estilos con los
colores corporativos, simplemente nos tenemos que dedicar a asignar variables de colores a los
diferentes componentes en vez de estar repitiendo hexadecimales por todos los sitios. Porque y si
de repente cambian los colores corporativos? Tendras que hacer un buscar y reemplazar por todo
el proyecto. En cambio, con Sass, vas a la variable, cambias su valor hexadecimal (o rgb, hsl, ) y
listo!
Es hora de meternos en terreno ms pantanoso: formularios y servicios.

Formulario de contacto
Como su nombre dice, lo primero que vamos a hacer en esta seccin es el formulario de Symfony.
Es muy aconsejable que domines este36 captulo de la documentacin ya que la potencia de los
formularios en Symfony es brutal. Por otro lado, MopaBootstrap tiene una forma de renderizar los
formularios muy sencilla que puedes leer aqu37 . En este ejemplo renderizaremos nuestro formulario
manualmente, pero puede ser un buen ejercicio aprender a mejorar esta parte.
Lo primero que tenemos que hacer es crear nuestro formulario en el archivo src/AppBundle/For-
m/Type/ContactType.php. A continuacin te dejo el cdigo que utilizaremos para el ejemplo:

36
https://symfony.com/doc/current/forms.html
37
http://bootstrap.mohrenweiserpartner.de/mopa/bootstrap/forms/examples
Captulo 8: ejemplo completo 66

1 <?php
2
3 namespace AppBundle\Form\Type;
4
5 use Symfony\Component\Form\AbstractType;
6 use Symfony\Component\Form\FormBuilderInterface;
7 use Symfony\Component\OptionsResolver\OptionsResolver;
8 use Symfony\Component\Validator\Constraints\Collection;
9 use Symfony\Component\Validator\Constraints\NotBlank;
10 use Symfony\Component\Validator\Constraints\Length;
11 use Symfony\Component\Validator\Constraints\Email;
12
13 class ContactType extends AbstractType
14 {
15 public function buildForm(FormBuilderInterface $builder, array $options)
16 {
17 $builder
18 ->add('name', 'text', array(
19 'label' => 'Nombre',
20 'attr' => array(
21 'placeholder' => 'Introduce tu nombre',
22 'autocomplete' => 'off'
23 )
24 ))
25 ->add('email', 'email', array(
26 'attr' => array(
27 'placeholder' => 'Para contactar contigo',
28 'autocomplete' => 'off'
29 )
30 ))
31 ->add('message', 'textarea', array(
32 'label' => 'Mensaje',
33 'attr' => array(
34 'cols' => 90,
35 'rows' => 10,
36 'placeholder' => 'Qu necesitas saber?'
37 )
38 ))
39 ;
40 }
41
42 public function configureOptions(OptionsResolver $resolver)
Captulo 8: ejemplo completo 67

43 {
44 $collectionConstraint = new Collection(array(
45 'name' => array(
46 new NotBlank(array('message' => 'El nombre no puede estar vaco'\
47 ))
48 ),
49 'email' => array(
50 new NotBlank(array('message' => 'El email no puede estar vaco')\
51 ),
52 new Email(array('message' => 'Email incorrecto'))
53 ),
54 'message' => array(
55 new NotBlank(array('message' => 'El mensaje debe ser ms largo')\
56 ),
57 new Length(array('min' => 5))
58 )
59 ));
60
61 $resolver->setDefaults(array(
62 'constraints' => $collectionConstraint
63 ));
64 }
65
66 public function getBlockPrefix()
67 {
68 return 'contact';
69 }
70 }

Como ves, he aadido algunos elementos de validacin.


El siguiente paso es crear un ContactController con la funcin necesaria para cargar la plantilla que
renderizar el formulario recin creado. Esto lo puedes leer en el captulo de formularios que te he
enlazado anteriormente:
Captulo 8: ejemplo completo 68

1 <?php
2
3 namespace AppBundle\Controller;
4
5 use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
6 use Symfony\Bundle\FrameworkBundle\Controller\Controller;
7 use Symfony\Component\HttpFoundation\Request;
8 use AppBundle\Form\Type\ContactType;
9
10 class ContactController extends Controller
11 {
12 /**
13 * @Route("/contacto")
14 */
15 public function contactAction(Request $request)
16 {
17 $form = $this->createForm(new ContactType());
18 $form->handleRequest($request);
19 if ($form->isSubmitted() && $form->isValid()) {
20 // Formulario enviado
21 die;
22 }
23
24 return $this->render('contact/contact.html.twig', array(
25 'contactform' => $form->createView()
26 ));
27 }
28 }

En el cdigo anterior puedes ver que, de momento, no hacemos nada cuando se enve el formulario,
y que, si el formulario no ha sido enviado, renderizamos una plantilla de contacto. Ten cuidado si
has creado el controller de cero y mira los use que he utilizado en la parte superior.
La plantilla que usamos recibe el formulario de contacto que tenemos que poner bonito y usable
para nuestros usuarios. Vamos all!

1 {% extends 'base.html.twig' %}
2
3 {% block content_content %}
4 {{ include('component/contact_form.html.twig') }}
5 {% endblock %}

Otro componente! Ms adelante embeberemos este formulario en un modal de Bootstrap, y


Captulo 8: ejemplo completo 69

tenerlo todo en componentes nos premitir reutilizar el cdigo sin repetirlo. La plantilla contact_-
form.html.twig contiene lo siguiente:

1 {% form_theme contactform 'bootstrap_3_layout.html.twig' %}


2
3 {{ form_start(contactform, {'attr': {'class': ''}}) }}
4 {{ form_row(contactform.name) }}
5 {{ form_row(contactform.email) }}
6 {{ form_row(contactform.message) }}
7 <div class="form-group">
8 <button type="submit" class="btn btn-success">Enviar</button>
9 </div>
10 {{ form_end(contactform) }}

Vaya! Es as de simple? Definitivamente, lo es. Pero, si ves la primera lnea de esta plantilla, vers
que el formulario se est renderizando con las clases de Bootstrap. Debes activar esta caracterstica
en tu archivo app/config/config.yml:

1 twig:
2 debug: "%kernel.debug%"
3 strict_variables: "%kernel.debug%"
4 form:
5 resources: ['bootstrap_3_layout.html.twig']

Puedes leer sobre la funcionalidad the Bootstrap theming en este link38 , y aqu39 tienes la receta sobre
la personalizacin de formularios.
Ya hemos avanzado bastante en nuestro objetivo, pero todava nos quedan dos objetivos por lograr:

Hacer que nuestro formulario aparezca en un modal de Boostrap


Enviar nuestro email, claro!

Para lo primero, tienes que saber cmo funcionan los modales de Bootstrap, algo que puedes leer
en su documentacin40 . Ya? Lo has entendido bien? Pues vamos a ello.
La idea es introducir un enlace en el footer que ponga contacto, pero en vez de llevar a la pgina
de contacto, lo que har es sacar un modal con el formulario. Lgicamente, para renderizar el
formulario, siempre que se cargue la plantilla base la variable contactform deber existir. Esto
solo se puede hacer de dos formas:
38
http://symfony.com/blog/new-in-symfony-2-6-bootstrap-form-theme
39
http://symfony.com/doc/current/cookbook/form/form_customization.html
40
http://getbootstrap.com/javascript/#modals
Captulo 8: ejemplo completo 70

Crear el formulario en cada una de tus actions, algo que est mal
Hacer que tu plantilla base llame a una action que pintar este modal con el formulario

Como ests de acuerdo conmigo, vamos con el segundo punto. Lo primero es hacer el cdigo
del modal como un componente para que nos valga para cualquier punto de la aplicacin. Mi
app/Resources/views/component/modal.html.twig tiene lo siguiente:

1 <div class="modal fade" id="{{ identifier }}" tabindex="-1" role="dialog">


2 <div class="modal-dialog" role="document">
3 <div class="modal-content">
4 <div class="modal-header">
5 <button type="button" class="close" data-dismiss="modal" aria-la\
6 bel="Close"><span aria-hidden="true"></span></button>
7 <h4 class="modal-title">{{ title }}</h4>
8 </div>
9 <div class="modal-body">
10 {{ content|raw }}
11 </div>
12 </div>
13 </div>
14 </div>

He simplificado bastante el ejemplo de la pgina de Bootstrap para que luego puedas mejorarlo a
tu gusto. Como ves, este componente recibe tres variables: el identificador del modal, para poder
abrirlo desde donde necesitemos; el ttulo del modal; y el contenido del modal. Esta ltima variable
lleva el filtro *raw** para que no escape el HTML que puede que metamos en ella.
El siguiente paso es modificar nuestro ContactController para renderizar el modal si es una
subrequest (una plantilla ha llamado al controller) o renderizar la pgina si no lo es. A continuacin
te dejo las modificaciones a realizar:

1 /**
2 * @Route("/contacto")
3 */
4 public function contactAction(Request $request) {
5 $form = $this->createForm(new ContactType(), null, array(
6 'action' => $this->generateUrl('app_contact_contact'),
7 ));
8 $form->handleRequest($request);
9 if ($form->isSubmitted() && $form->isValid()) {
10 // Formulario enviado
11 die;
Captulo 8: ejemplo completo 71

12 }
13
14 $requestStack = $this->get('request_stack')->getParentRequest();
15 if ($requestStack) {
16 $modalContent = $this->renderView('component/contact_form.html.twig', ar\
17 ray(
18 'contactform' => $form->createView()
19 ));
20
21 return $this->render('component/modal.html.twig', array(
22 'identifier' => 'contactModal',
23 'title' => 'Formulario de contacto',
24 'content' => $modalContent
25 ));
26 }
27
28 return $this->render('contact/contact.html.twig', array(
29 'contactform' => $form->createView()
30 ));
31 }

Como ves, he realizado un pequeo truco para saber si se trata de una subrequest a travs del servicio
request_stack. Esto nos permitir identificar lo que se tiene que devolver como respuesta. En el
caso de ser una subrequest, primero cargamos el contenido del formulario en una variable poniendo
renderView en vez de render, ya que render devuelve un objeto response mientras que renderView
devuelve nicamente el HTML renderizado. Tambien he aadido el parmetro action para forzar
que se enve el formulario (POST) a la misma funcin de un controller, siempre usando rutas de
Symfony.
El ltimo paso es editar nuestro base.html.twig y aadir lo siguiente en el footer:

1 {% block footer %}
2 <p>Footer sexy 2015 - <a href="javascript:void(0)" data-toggle="modal" data-targ\
3 et="#contactModal">Contacto</a></p>
4 {% endblock footer %}
5
6 {% block body_end %}
7 {{ render(controller('AppBundle:Contact:contact')) }}
8 {% endblock body_end %}

En ese bloque de la plantilla base, justo antes de cerrar el body, se cargar el modal con nuestro
formulario en cada una de las pginas. Quieres probarlo? Recarga la pgina y pulsa sobre el enlace
de contacto que tienes en el footer.
Captulo 8: ejemplo completo 72

Ya tenemos toda la parte de visualizacin realizada. Es hora de darle funcionalidad a nuestro


formulario de contacto.

Enviando un email
En esta seccin vamos a aprender dos cosas: enviar un email (esta era fcil de adivinar) y crear y
usar un servicio en Symfony.
Lo primero que vamos a hacer es crear el servicio que nos sirva para mandar correos electrnicos
desde nuestra aplicacin. Es muy parecido a un controller, as que seguro que no tienes problema
alguno en entender lo que te escribo a continuacin. Este es el cdigo de src/AppBundle/Servi-
ce/AppMailer.php que te servir por ahora:

1 <?php
2
3 namespace AppBundle\Service;
4
5 class AppMailer {
6
7 private $mailer;
8
9 public function __construct($_mailer) {
10 $this->mailer = $_mailer;
11 }
12
13 public function sendEmail($to, $subject, $text) {
14 $message = \Swift_Message::newInstance()
15 ->setSubject($subject)
16 ->setFrom('noreply@dominio.com')
17 ->setTo($to)
18 ->setBody($text)
19 ;
20 return $this->mailer->send($message);
21 }
22
23 }

Como puedes ver, tenemos un constructor que recibe servicios a utilizar dentro de esta clase y una
funcin para enviar un email segn los parmetros que recibe. Swift Mailer viene ya con Symfony,
as que no te preocupes por ello.
Lo siguiente que tenemos que hacer es declarar el servicio, en el que solo hay que poner la clase
y los argumentos que recibir el constructor de dicha clase. En nuestro services.yml aadimos lo
siguiente:
Captulo 8: ejemplo completo 73

1 appmailer:
2 class: AppBundle\Service\AppMailer
3 arguments: ["@mailer"]

Ya solo nos queda actualizar nuestro ContactController para que enve un sencillo email:

1 if ($form->isValid()) {
2 $emailService = $this->get('aupa.email_service');
3 $text = "De: " . $form->get('name')->getData()
4 . " Email: " . $form->get('email')->getData()
5 . " Mensaje: " . $form->get('message')->getData();
6 $emailService->sendEmail(
7 'tuemail@tudominio.com',
8 'Formulario de contacto',
9 $text
10 );
11
12 return $this->redirectToRoute('app_contact_contact');
13 }

Si quieres probarlo, puedes editar tu archivo app/config/config_dev.yml y aadir tu cuenta de


Gmail de la siguiente forma:

1 swiftmailer:
2 transport: gmail
3 username: your_gmail_username
4 password: your_gmail_password

Te dejo como ejercicio que mejores el contenido del email enviado y generes algo ms sofisticado
con HTML a travs de una plantilla Twig. Tienes la documentacin sobre emails en Symfony en este
enlace41 .
Con esto hemos terminado nuestro completo ejemplo de Symfony en el que hemos tocado un poco
todas las patas base del framework, y con ello, te has convertido en un jedi Symfony. An as tengo
que decirte que tras varios aos trabajando con este framework, yo a da de hoy sigo aprendiendo,
y eso es maravilloso.
41
http://symfony.com/doc/current/email.html
Captulo 8: ejemplo completo 74

Truco 8
Es posible que alguna vez tengas que renderizar una pgina la cual no tiene absolutamente nada
de base de datos ni lgica de negocio, es decir, el controlador solo sirve para renderizar la plantilla.
En este caso, Symfony nos da la posibilidad de renderizar una plantilla sin necesidad de controller
alguno. Para ello, necesitas una ruta especial que no se puede hacer con anotaciones. Edita el
archivo routing.yml e introduce la siguiente ruta YAML:

1 politica_privacidad:
2 path: /privacidad
3 defaults:
4 _controller: FrameworkBundle:Template:template
5 template: static/privacy.html.twig

As de simple, nos hemos quitamos de varias lneas de cdigo que solo necesitbamos para cargar
una plantilla. Pero si eres un usuario avanzado de Symfony te habrs dado cuenta de que existe un
problema: como cacheo la pgina sin el Controller? Tranquilo, tiene solucin:

1 politica_privacidad:
2 path: /privacidad
3 defaults:
4 _controller: FrameworkBundle:Template:template
5 template: static/privacy.html.twig
6 maxAge: 86400
7 sharedAge: 86400

Como este captulo ha sido largo, te voy a dar otro truco. Imagnate que migras algunas de tus URLs
y, por temas de SEO, necesitas hacer redirects. Se puede hacer de muchas formas sin necesidad de
Symfony, pero como este libro no trata de Apache, entre otros, te explicar cmo se hace en Symfony.
Al igual que en el caso anterior, puedes recoger la ruta antigua con un controller y lanzar un return
$this->redirect('...'). Sin embargo, con YAML podemos hacer lo siguiente:

1 wpadmin:
2 path: /wp-admin
3 defaults:
4 _controller: FrameworkBundle:Redirect:redirect
5 route: sonata_admin_dashboard
6 permanent: true

Con la ruta anterior hemos hecho que tras nuestra ficticia migracin de WordPress (wp-admin)
a Symfony, los usuarios que se dirijan a /wp-admin sern redirigidos a nuestro admin hecho con
Sonata.
Captulo 8: ejemplo completo 75

Resumen
En este captulo hemos realizado un ejemplo muy completo de varios elementos tanto Symfony
como Bootstrap y Sass. En l hemos aprendido a crear nuevas rutas, a crear repositorios para
todo lo que tenga que ver con base de datos, a crear servicios para desacoplar funcionalidad
y que sea reutilizable, a controlar Twig, sus plantillas y varios elementos que estn relacionados
con la maquetacin de nuestra pgina basada en Bootstrap. En el siguiente captulo veremos otras
herramientas que potenciarn an ms nuestro desarrollo.
Captulo 9: Doctrine Extensions
Este es el primer captulo en el que empezamos a hablar sobre bundles en los que puedes apoyarte
a la hora de desarrollar tus proyectos pero que no forman parte por defecto del proyecto base
que estamos haciendo. He querido dedicarle un captulo especfico a un bundle muy interesante:
Doctrine Extensions. Con este aadido podrs vitaminar tus entidades con funcionalidades extra
que normalmente se repiten entre proyectos. Almacenar la ltima vez que se actualizado una entidad,
calcular el slug a travs de una funcin especfica o incluso caractersticas ms complejas como
traducciones de campos o almacenamiento de versiones quedan hechos de forma automtica con
este bundle. Como siempre, comenzamos con la instalacin.

Instalacin
Si buscas el nombre de este bundle tal cual, vers que existe y que puedes instalarlo. Sin embargo,
tendrs que crear t mismo los servicios asociados a cada filtro. La mayora de ellos son relativamente
simples para el 99% de los casos, y por eso STOF cre un bundle por encima de Doctrine Extensions
con todo lo necesario para empezar a usar dichos filtros.
Lo primero que tenemos que hacer es aadir la correspondiente lnea al archivo de definicin
dependencias composer.json:

1 composer require stof/doctrine-extensions-bundle

Adems, dentro de app/AppKernel.php activamos el bundle para comenzar a utilizarlo:

1 new Stof\DoctrineExtensionsBundle\StofDoctrineExtensionsBundle(),

Ya esta todo listo para poder usarlo salvo la configuracin. Este bundle es un poco especial, as
que configuraremos lo necesario segn lo vayamos utilizando. A continuacin te explicar como
funcionan tres de sus filtros, el resto te dir cules son para que los pruebes y veas si pueden servirte
o no en tus futuros proyectos.

Filtro timestampable
Comenzaremos por uno de los filtros ms sencillos que tiene. Lo primero que debemos saber es qu
hace este filtro. Si vas a la documentacin del mismo, la cual te dejo al final de esta seccin, vers
que el filtro te permite establecer una fecha en un campo concreto de la entidad cuando sta:
Captulo 9: Doctrine Extensions 77

Se cree (nueva entidad)


Se actualice
Se actualicen uno o varios campos en concreto
Se actualice un campo a un valor concreto

Lo que tenemos que hacer en este momento es activar el filtro. Para ello, editamos el archivo
app/config/config.yml y aadimos una nueva configuracin:

1 stof_doctrine_extensions:
2 orm:
3 default:
4 timestampable: true

A partir de ahora, solo tendremos que aadir a la lista de esta configuracin los nuevos filtros. A mi
me gusta mantenerlos por orden alfabtico, pero eres libre de tenerlos como mejor te convenga.
Activado el filtro, es hora de empezar a utilizarlo. Modificamos la entidad producto para que sea
capaz de almacenar cundo se crea, cundo se actualiza y cundo cambia el nombre de un producto.
Esto cdigo de a continuacin es lo que yo he aadido en en archivo src/AppBundle/Entity/Pro-
duct.php antes de los getters y setters:

1 use Gedmo\Mapping\Annotation as Gedmo;


2 use Doctrine\ORM\Mapping as ORM;
3
4 //...
5
6 /**
7 * @Gedmo\Timestampable(on="create")
8 * @ORM\Column(type="datetime")
9 */
10 private $created;
11
12 /**
13 * @Gedmo\Timestampable(on="update")
14 * @ORM\Column(type="datetime")
15 */
16 private $updated;
17
18 /**
19 * @ORM\Column(name="name_changed", type="datetime", nullable=true)
20 * @Gedmo\Timestampable(on="change", field={"name"})
21 */
22 private $nameChanged;
Captulo 9: Doctrine Extensions 78

Comprendes lo que he realizado? Lo primero de todo es aadir el use en la parte superior que nos
permite utilizar las anotaciones @Gedmo. Posteriormente hemos creado una variable de tipo datetime
para cada uno de las necesidades planteadas. Recuerda generar los getters y setters para esta entidad:

1 bin/console doctrine:generate:entities AppBundle:Product

Y, por supuesto, como hemos aadido nuevas columnas, tenemos que actualizar nuestro esquema
de base de datos:

1 bin/console doctrine:schema:update --force

Todo listo para la accin, salvo por un pequeo detalle: cmo vamos a ver si est funcionando o no?
Existen varias formas, pero ya que hemos construido un estupendo admin, vamos a usarlo. Editamos
src/AppBundle/Admin/ProductAdmin.php y en la funcin de listado aadimos las nuevas columnas:

1 protected function configureListFields(ListMapper $listMapper)


2 {
3 $listMapper
4 ->addIdentifier('id')
5 ->add('name')
6 ->add('price')
7 ->add('description')
8 ->add('created')
9 ->add('updated')
10 ->add('priceChanged')
11 ->add('_action', 'actions', array(
12 'actions' => array(
13 'edit' => array()
14 )))
15 ;
16 }

Para probarlo, navegamos a http://127.0.0.1:8000/admin y creamos un nuevo producto y lo editamos


de varias formas, para que veas los campos como se van actualizando.
La documentacin de este filtro la tienes disponible en este42 enlace. Pero te voy a contar un pequeo
truco antes de tiempo, y es que Symfony ya tiene una herramienta para modificar los valores de
tu entidad cuando ocurran ciertos eventos. Esta funcionalidad se llama Lifecycle Callbacks, y no
estara de ms que te leyeses cmo funciona en este43 enlace antes de que te acomodes a Gedmo.
42
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/timestampable.md#timestampable-entity-example
43
http://symfony.com/doc/current/book/doctrine.html#lifecycle-callbacks
Captulo 9: Doctrine Extensions 79

Filtro slugabble
Con este filtro podrs realizar tus slugs de forma tan sencilla como en la actualizacin de fechas
del ejemplo anterior. Por si no sabes lo que es un slug, te lo explico con un ejemplo. Si tienes el
producto en tu base de datos con el nombre Jamn pata negra, lo correcto para que una URL se
considere SEO friendly es que sea algo tipo /productos/jamon-pata-negra. Esta cadena de texto
sin espacios, en minscula, sin acentos y caracteres especiales se llama slug.
Vamos a activar el filtro para verlo en funcionamiento. Editamos el archivo de configuracin
app/config/config.yml y aadimos el nuevo filtro:

1 stof_doctrine_extensions:
2 orm:
3 default:
4 sluggable: true
5 timestampable: true

Posteriormente, editamos nuestra entidad producto y le aadimos el campo slug justo debajo el
nombre, as lo mantenemos ordenado:

1 /**
2 * @Gedmo\Slug(fields={"name"})
3 * @ORM\Column(length=105, unique=true)
4 */
5 private $slug;

Generamos los getters y setters con el mismo comando que utilizamos antes. Para mantener el orden
de la entidad, al igual que con las variables, deberas ordenar estos getters y setters. Puedes borrarlos
todos manualmente y regenerarlos de nuevo, o una vez generados los nuevos cortarlos y pegarlos
segn corresponda, pero te aconsejo que los mantengas en el mismo orden que las variables.

1 bin/console doctrine:generate:entities AppBundle:Product

Por ltimo, actualizamos la base de datos pero antes debes borrar los productos que ya tienes
creados, porque al haber establecido el slug como campo nico, cuando actualices el esquema, todos
los producto tendrn el slug vaco y te saltar un error del tipo el campo est duplicado. Puedes
eliminarlos desde el propio admin. Una vez borrados, ejecuta el comando de actualizacin de la
estructura de base de datos en la terminal:
Captulo 9: Doctrine Extensions 80

1 bin/console doctrine:schema:update --force

Vamos a verlo en funcionamiento. De nuevo, editamos nuestro ProductAdmin para aadir el nuevo
campo en el listado:

1 protected function configureListFields(ListMapper $listMapper)


2 {
3 $listMapper
4 ->addIdentifier('id')
5 ->add('name')
6 ->add('slug')
7 ->add('price')
8 ->add('description')
9 ->add('created')
10 ->add('updated')
11 ->add('nameChanged')
12 ->add('_action', 'actions', array(
13 'actions' => array(
14 'edit' => array()
15 )))
16 ;
17 }

Crea un nuevo producto y visualiza el slug generado, qu te parece? Si te das cuenta, cuando
actualizas el nombre se actualiza el slug. Pensando ya en la web, esto producira un error 404 si la
URL ya la hemos compartido, por ejemplo, en las redes sociales. Para que esto no ocurra, existe una
opcin para desactivar la actualizacin del slug cuando la entidad se modifique. A continuacin te
explico como realizar esta y otras configuraciones modificando la anotacin que hemos aadido con
los siguientes parmetros:

fields: es un campo requerido en el que le indicas el campo o los campos que quieres que
formen el slug
updatable: es un campo opcional para indicarle si quieres que se actualice el slug o no cuando
se modifiquen los campos del parmetro fields (por defecto a true)
unique: es un campo opcional que indica si los slugs deben ser nicos o no (por defecto a true)
unique_base: es un campo opcional que junto con unique indica el campo a comprobar para
que el slug sea nico. (por defecto es null)
separator: es un campo opcional para modificar el separador de palabras (por defecto -)
prefix: es un campo opcional que indica el prefijo que se le aadir al slug (por defecto )
suffix: es un campo opcional que indica el sufijo que se le aadir al slug (por defecto )
Captulo 9: Doctrine Extensions 81

style: es un campo opcional que puede ser default para todas las letras en minsculas -
camel para tener la primera letra de cada palabra mayscula - upper para tener las letras
de las palabras en maysculas - lower para tener las letras de las palabras en minsculas
handlers: es un parmetro opcional para indicarle una lista de handlers

Todo te habr sonado sencillo hasta que has llegado a los handlers. Para qu sirven? En la mayora
de tus proyectos no necesitars esta caracterstica, pero te puede ser de gran ayuda.
Existen varios handlers por defecto que te permitirn generar slugs de una forma ms concreta. Por
ejemplo: si tienes los productos relacionados con una categora, es posible que te guste tener en el
slug algo como /categoria/producto. Te gustara hacer un ejemplo? Venga, este ejercicio te dejo
que lo trabajes con la documentacin de aqu44 .
Por otro lado, tienes toda la documentacin de este filtro en este45 enlace.

Filtro softdeleteable
Cuntas veces te han dicho que no hay que borrar de la base de datos, sino marcarlo como
borrado? Con este filtro podemos realizar tal cosa sin tener que preocuparnos de aadir en todas
nuestras consultas que devuelva los no borrados, ya que lo hace por defecto. No entiendes bien?
Enseguida lo entenders.
Lo primero es activar el filtro. Modificamos la configuracin editando el archivo app/config/con-
fig.yml:

1 stof_doctrine_extensions:
2 orm:
3 default:
4 sluggable: true
5 softdeleteable: true
6 timestampable: true

Con esta configuracin conseguiremos que al borrar una entidad nos la marque como borrada. Pero
tambin nos gustara que al realizar la consulta dame todos los productos no devuelva los productos
que estn marcados como borrados. Para conseguir esto, hace falta aadir un filtro de orm. A
continuacin te dejo la configuracin. Solo tienes que aadir la parte de filters en adelante, lo
dems ya est creado. Bscalo en app/config/config.yml:

44
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sluggable.md#some-other-configuration-options-for-slug-annotation
45
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sluggable.md
Captulo 9: Doctrine Extensions 82

1 doctrine:
2 orm:
3 filters:
4 softdeleteable:
5 class: Gedmo\SoftDeleteable\Filter\SoftDeleteableFilter
6 enabled: true

Configurado el filtro, aadimos una nueva propiedad a nuestra entidad producto para marcar cuando
ste sea borrado:

1 <?php
2
3 namespace AppBundle\Entity;
4
5 use Gedmo\Mapping\Annotation as Gedmo;
6 use Doctrine\ORM\Mapping as ORM;
7
8 /**
9 * @ORM\Entity
10 * @ORM\Table(name="product")
11 * @Gedmo\SoftDeleteable(fieldName="deletedAt", timeAware=false)
12 */
13 class Product
14 {
15 //...
16
17 /**
18 * @ORM\Column(name="deleted_at", type="datetime", nullable=true)
19 */
20 private $deletedAt;
21 }

Te has dado cuenta? Esta anotacin es algo diferente, y se hace a nivel de entidad en vez de campo.
Es lo que permitir a Doctrine saber cundo devolver una entidad o no adems de marcarla como
borrada en la base de datos. Como siempre, genera los getters y setters y actualiza el esquema de tu
base de datos:

1 bin/console doctrine:generate:entities AppBundle:Product


2 bin/console doctrine:schema:update --force

Ya puedes ir al admin y borrar uno de tus productos. Al hacerlo, no podrs volver a visualizarlo en
el admin, pero si vas a la base de datos a travs del PhpMyAdmin o cualquier otro software que
utilices, vers que sigue ah. Esto se debe a la segunda configuracin que hemos aadido. Es decir,
si ejecutas el cdigo que te dejo a continuacin, vers que no te devuelve los productos borrados:
Captulo 9: Doctrine Extensions 83

1 $products = $em->getRepository("AppBundle:Product")->findAll();

Si por cualquier razn quieres que te devuelva todos, incluso los borrados, puedes desactivar el filtro
de orm de la siguiente forma:

1 $em->getFilters()->disable('softdeleteable');
2 $products = $em->getRepository("AppBundle:Product")->findAll();

Tienes toda la documentacin de este filtro en este46 enlace.

Otros filtros
Existen muchos otros filtros que puedes utilizar gracias a este bundle. Puedes encontrars cmo
configurarlos y cmo utilizarlos en este47 enlace. Te recomiendo encarecidamente que te leas las
posibilidades de cada uno de ellos para que aprendas cunto cdigo puedes ahorrarte gracias al
trabajo de otros. Pero, para dejarte con buen sabor de boca, aqu te dejo explicados brevemente el
resto de filtros que no hemos visto:

blameable: al igual que timestampable sirve para marcar una entidad tras un evento pero con
un string en vez de un datetime. Si est en un campo de texto, introducir el username del
usuario; si es una relacin, crear la conexin entre entidades. Documentacin48 .
iptraceable: lo mismo que el caso anterior, solo que establece un string que es la IP del usuario.
Documentacin49 .
loggable: traza los cambios de tu entidad y permite gestionar versiones de sta. Documenta-
cin50 .
sortable: mantiene un campo posicin para ordenar entidades. Documentacin51 .
translatable: permite traducir campos especficos a diferentes idiomas, cargando adems
la traduccin concreta del usuario de forma automtica segn el idioma de navegacin.
Documentacin52 .
tree: permite implementar el comportamiendo Nested-Set en la entidad, soportando varias
estrategias. Documentacin53 .
uploadable: permite gestionar la persistencia de archivos con Doctrine como mover, renom-
brar o eliminar estos archivos de forma automtica. Documentacin54 .
46
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/softdeleteable.md
47
https://github.com/stof/StofDoctrineExtensionsBundle/blob/master/Resources/doc/index.rst
48
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/blameable.md
49
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/ip_traceable.md
50
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/loggable.md
51
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/sortable.md
52
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/translatable.md
53
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/tree.md
54
https://github.com/Atlantic18/DoctrineExtensions/blob/master/doc/uploadable.md
Captulo 9: Doctrine Extensions 84

Truco 9
En este captulo hemos visto el tema de los slugs, y como seguro que ya te ha pasado o ya ests viendo
el futuro, muchas de tus rutas empezarn por dame el objeto cuyo slug es este. Los desarrolladores
de Symfony vieron esta repeticin de cdigo y crearon una forma que nos permite saltarnos este
paso. Para ello, solo debes modificar la declaracin de tu action para que reciba el objeto y no el
string del slug. Aqu te dejo un ejemplo:

1 /**
2 * @Route("/products/{slug}")
3 */
4 public function indexAction(Product $product)
5 {
6 // ...
7 }

Como ves, en vez de recibir el slug directamente le estoy diciendo a Symfony que lo busque por
mi. Ah, otra cosa! Si no lo encuentra, automticamente se lanzar una excepcin de pgina no
encontrada sin tener que tengas que lanzarla manualmente.
Cunto cdigo te acabo de ahorrar? Esto no es nada. Esta tcnica se llama ParamConverter y la
vamos a utilizar mucho ms a fondo en el captulo 13, ya que con unas simples (aunque a veces no
tan simples) anotaciones, conseguiremos tener listos nuestros objetos nada ms comenzar el cdigo
de nuestro action.

Resumen
En este captulo hemos aprendido a aadir una serie de mejoras a nuestras entidades que a
travs de filtros de Doctrine nos otorgan una funcionalidad bsica pero que se repeta en todos
nuestros proyectos. Gracias a unas simples anotaciones y a una configuracin muy corta, hemos
proporcionado dichas funcionalidades a nuestro proyecto y eliminado (o ahorrado) muchas lneas
de cdigo.
En el siguiente captulo nos movemos a algo un poco ms visual y trabajaremos con un bundle que
nos permite gestionar imgenes antes de servirlas a nuestros usuarios.
Captulo 10: LiipImagineBundle
Puede que este sea un captulo un poco ms breve que los anteriores, pero me gustara dedicarle
un captulo completo al tratamiento de imgenes. Por Internet puedes encontrar varios bundles
que se encargan de gestionar las imgenes dentro de Symfony, pero tras probar varios de ellos
personalmente, el que mejor me ha funcionado es LiipImagineBundle. En este captulo te explicar
como instalarlo, configurarlo y trabajar con l. Empecemos!

Instalacin
La instalacin es tan simple como ejecutar el siguiente comando:

1 composer require liip/imagine-bundle

Y posteriormente activar el bundle dentro del AppKernel.php

1 public function registerBundles()


2 {
3 $bundles = array(
4 // ...
5
6 new Liip\ImagineBundle\LiipImagineBundle(),
7 );
8
9 // ...
10 }

Por ltimo, hay que aadir un archivo de rutas en app/config/routing.yml ya que sin ellas, nuestros
filtros no funcionarn:

1 _liip_imagine:
2 resource: "@LiipImagineBundle/Resources/config/routing.xml"

Con esto ya est todo, pero que es eso de los filtros? Los filtros no son ms que configuraciones
del bundle que nos permitirn aplicar transformaciones a las imgenes, bien a travs de un filtro de
Twig (no confundir el concepto de filtro de ambas temticas) o bien aplicando el filtro a la imagen
desde nuestro cdigo PHP. En la siguiente seccin te explico cmo se hace.
Captulo 10: LiipImagineBundle 86

Configuracin
Por defecto, este bundle crea las imgenes modificadas en web/media/cache, por lo que tenemos que
permitir a nuestro servidor web que escriba en este carpeta. Aqu te dejo los comandos que yo he
utilizado para darle permisos nicamente al servidor web, por lo que si te interesa disponer de otros
permisos puedes saltarte estos comandos para ejecutar los tuyos propios:

1 mkdir -p web/media/cache
2 HTTPDUSER=`ps aux | grep -E '[a]pache|[h]ttpd|[_]www|[w]ww-data|[n]ginx' | grep \
3 -v root | head -1 | cut -d\ -f1`
4 sudo chown $HTTPDUSER:$HTTPDUSER web/media/cache

La configuracin que necesita esta bundle a nivel de Symfony para funcionar es ninguna. Y lo
pongo entre comillas puesto que es algo engaoso. Realmente, el bundle tal y como est funciona y
no da error, pero no hace nada. Tu trabajo ahora ser editar el archivo de configuracin app/con-
fig/config.yml y aadir los filtros que consideres necesarios para tu proyecto. A continuacin te
voy a explicar los que vienen por defecto en el bundle, pero debes saber que tienes la posibilidad de
crear tus propios filtros.
Thumbnail Este filtro crea una miniatura de una imagen concreta. En la configuracin puede
recibir dos modos diferentes: el modo inset permite hacer un redimensionado relativo, nunca
excediendo los lmites dados en la configuracin; el modo outbound corta la imagen si tras realizar
este redimensionado relativo las dimensiones no son las correctas. Quizs lo veas mejor con una
configuracin ejemplo del filtro, como ya sabes, situada en app/config/config.yml:

1 liip_imagine:
2 filter_sets:
3 my_thumb_in:
4 filters:
5 thumbnail: { size: [32, 32], mode: inset } # Transforma 50x40 a \
6 32x26, sin cortar
7 my_thumb_out:
8 filters:
9 thumbnail: { size: [32, 32], mode: outbound } # Transforma 50x40\
10 a 32x32, cortando el ancho

Ambas opciones admiten un parmetro extra allow_upscale para permitir redimensionar la imagen
a mayor tamao. Por defecto, este parmetro es false.
Relative resize Este filtro permite modificar el tamao de una imagen segn cuatro posiblidades.
Con el siguiente ejemplo de configuracin te explico su funcionamiento:
Captulo 10: LiipImagineBundle 87

1 liip_imagine:
2 filter_sets:
3 my_heighten:
4 filters:
5 relative_resize: { heighten: 60 } # Transforma 50x40 a 75x60
6 my_widen:
7 filters:
8 relative_resize: { widen: 32 } # Transforma 50x40 a 32x26
9 my_increase:
10 filters:
11 relative_resize: { increase: 10 } # Transforma 50x40 a 60x50
12 my_widen:
13 filters:
14 relative_resize: { scale: 2.5 } # Transforma 50x40 a 125x100

Upscale Este filtro te permite redimensionar la imagen a un tamao superior:

1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 upscale: { min: [800, 600] }

Crop Este filtro te permite recortar la imagen desde un punto concreto, un tamao exacto:

1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 crop: { start: [10, 20], size: [120, 90] }

Strip Este filtro te permite eliminar todos los perfiles y comentarios asociados a una imagen:

1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 strip: ~

Background Este filtro te permite establecer un color de fondo a una imagen. Por defecto, el color
es #FFF (blanco):
Captulo 10: LiipImagineBundle 88

1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 background: { color: '#00FFFF' }

Tambin se le puede pasar un tamao, con lo que crear una nueva imagen con el tamao dado:

1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 background: { size: [1026, 684], color: '#00FFFF' }

Watermark Este filtro es genial porque te permite aadir una marca de agua a tus imgenes
manteniendo el ratio:

1 liip_image:
2 filter_sets:
3 my_image:
4 filters:
5 watermark:
6 # Ruta relativa al archivo de marca de agua (desde "%kernel.\
7 root_dir%/")
8 image: Resources/data/watermark.png
9 # Tamao de la marca de agua relativo al tamao de la imagen\
10 original
11 size: 0.5
12 # Position: a escoger entre topleft,top,topright,left,center\
13 ,right,bottomleft,bottom,bottomright
14 position: center

Auto rotate Este filtro rota la imagen basndose en su informacin EXIF. Si vas a encadenar filtros
(lo explico ms adelante), deberas llamar primero a este filtro antes que al resto:

1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 auto_rotate: ~

Rotate Este filtro rota la imagen basndose en un ngulo (grados) especfico:


Captulo 10: LiipImagineBundle 89

1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 rotate: { angle: 90 }

Interlace Este filtro establece la carga progresiva de la imagen:

1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 interlace:
6 # Los modos pueden ser none, line, plane, partition
7 mode: line

Crea tu propio filtro Adems de toda la informacin que te he expuesto en esta seccin, en
este55 enlace tienes toda la informacin necesaria para crear tu filtro propio y hacerlo funcionar.
Personalmente, nunca he tenido la necesidad, pero si es tu caso, te agradezco que me hagas llegar el
motivo y el resultado final por Twitter para poder aprender contigo ;)
Multifiltro Necesitas 2 o ms filtros para tus imgenes? No pasa nada, ya que puedes aplicarlos
encadenados de la siguiente forma:

1 liip_imagine:
2 filter_sets:
3 carousel:
4 quality: 90
5 filters:
6 relative_resize: { widen: 1920, allow_upscale: true }
7 thumbnail: { size: [1920, 1040], mode: outbound }

Ms configuracin Adems de la configuracin relativa a los filtros, LiipImagineBundle permite


una gran variedad de modificaciones en su funcionamiento, como por ejemplo, la carpeta en la que
se guardarn las fotos tras ser tratadas. Aqu56 te dejo toda la informacin relativa a la configuracin.

Uso con Twig


Llegamos a la parte interesante: el uso del bundle. Lo primero que vamos a hacer es configurar un
filtro para realizar un thumbnail pequeo de una imagen, de tamao 60x60 por ejemplo. Edita el
archivo app/config/config.yml y aade el siguiente filtro al final del todo:
55
http://symfony.com/doc/current/bundles/LiipImagineBundle/filters.html#custom-filters
56
http://symfony.com/doc/master/bundles/LiipImagineBundle/configuration.html
Captulo 10: LiipImagineBundle 90

1 liip_imagine:
2 filter_sets:
3 aupa_thumbnail:
4 filters:
5 thumbnail: { size: [60,60], mode: outbound }

Siguiente paso: descrgate una imagen grande y colcala en la ruta web/images/prueba.jpg (o


el nombre que consideres oportuno). Una vez descargada la imagen, vamos a colocarla en una
de nuestras plantillas Twig. Por ejemplo, aprovecha la plantilla app/Resources/views/home/in-
dex.html.twig. Para usar el filtro, tan solo debes aadir el siguiente cdigo:

1 <img src="{{ '/images/prueba.jpg' | imagine_filter('aupa_thumbnail') }}" alt=""/>

Ahora, tras dirigirte a http://127.0.0.1:8000/home podrs ver la imagen a tamao 60x60. Es hora de
parar un tiempo tu lectura y probar el resto de los filtros. Cuando los hayas probado, contina con
la siguiente seccin: el uso de los filtros desde PHP.

Uso con PHP


Imagnate que tienes que devolver una respuesta en JSON con la imagen a la cual se le ha aplicado
un filtro o, por lo cualquier otra razn, que tienes la necesidad de utilizar alguno de tus filtros dentro
de un controlador. LiipImagineBundle te permite hacer esto a travs de un servicio al que podrs
acceder desde el container, como cualquier otro servicio. La funcin getBrowserPath te devolver la
ruta de la imagen a la que se le ha aplicado ya tu filtro para que trabajes con ella como te plazca. En
el siguiente cdigo te dejo el ejemplo anterior con PHP:

1 // Create Liip thumbnail


2 $imageSrc = $this
3 ->get('liip_imagine.cache.manager')
4 ->getBrowserPath(
5 '/images/prueba.jpg',
6 'aupa_thumbnail'
7 );

Qu puedes hacer con esa ruta? Pues lo que necesites, desde enviarla a un Twig hasta devolverla en
una respuesta de una API. Tambin tienes la opcin de devolver la imagen como respuesta a travs
de la funcin filterAction:
Captulo 10: LiipImagineBundle 91

1 // Devuelve un objeto RedirectResponse


2 $imagemanagerResponse = $this->container
3 ->get('liip_imagine.controller')
4 ->filterAction(
5 $this->request,
6 'images/prueba.jpg',
7 'aupa_thumbnail'
8 );

Truco 10
Sin duda, este es uno de los trucos que ms vas aplicar en tus proyectos. Como seguro que sabes, desde
la salida de los mviles, las pginas webs tienen un gran problema: los recursos que ms ocupan.
Estos recursos hacen que la navegacin en un mvil no sea satisfactoria debido a la velocidad de
carga y adems puede que enfade a nuestros visitantes por haber consumido su tarifa de datos.
En HTML5 tenemos la posibilidad de aliviar un poco la carga de imgenes gracias a la etiqueta
<picture>.

Esta etiqueta recibe una serie de imgenes que cargar segn la resolucin del viewport del usuario
visitante, por lo que, si le aadimos LiipImagineBundle con una serie de filtros para redimensionar
de forma automtica, tenemos la receta perfecta lista para ser utilizada.
El primer paso es declarar los filtros necesarios:

1 liip_imagine:
2 filter_sets:
3 widen640:
4 filters:
5 relative_resize: { widen: 640 }
6 widen1024:
7 filters:
8 relative_resize: { widen: 1024 }

Ahora, modifica la plantilla app/Resources/views/home/index.html.twig para utilizar tanto la


etiqueta <picture> como los nuevos filtros:
Captulo 10: LiipImagineBundle 92

1 <picture>
2 <source media="(max-width: 640px)" srcset="{{ '/images/prueba.jpg' | imagine\
3 _filter('widen640') }}">
4 <source media="(max-width: 1024px)" srcset="{{ '/images/prueba.jpg' | imagin\
5 e_filter('widen1024') }}">
6 <source media="(min-width: 1024px)" srcset="{{ '/images/prueba.jpg' }}">
7 <img src="{{ asset('images/prueba.jpg') }}" alt="">
8 </picture>

Entiendes lo que hace el cdigo anterior? Dentro de <picture> se van a ir leyendo todos los
<source> hasta que uno se cumpla, y entonces ese ser el que se muestre en pantalla. En el caso
de que el navegador no lo soporte, se renderizar el elemento de fallback <img>, qu pasada!
Aunque todava no est soportado por todos los navegadores, existe un polyfill que aadir su
funcionalidad a muchos de ellos. Te dejo aqu57 el enlace al mismo.

Resumen
En este captulo hemos utilizado un bundle dedicado al tratamiento de imgenes a travs de filtros.
Con unas pequeas configuraciones muy sencillas, la potencia que otorga es gigante, y aadida al
truco 10 de este captulo hace que este sea uno de los bundles indispensables para cualquiera de tus
proyectos.
En el siguiente captulo veremos otros posibles bundles que te pueden interesar, pero no nos
meteremos tan al detalle con ellos como lo hemos hecho en este captulo.
57
https://github.com/scottjehl/picturefill
Captulo 11: otros bundles
En este captulo me gustara ensearte un poco por encima algunos de los bundles que yo he utilizado
durante mi trayectoria profesional y que me han sido de gran ayuda en ciertos momentos. No es
mi intencin llenar este captulo de documentacin y casos de uso como te he expuesto en captulos
anteriores, sino de darte una pequeas pinceladas sobre lo que hacen cada uno de ellos. S alguno
de los aqu expuestos te interesa, algo de lo que estoy seguro, no tienes ms que probarlo y empezar
a trabajar con l.

EWZRecaptchaBundle
Empezamos el captulo con un bundle bastante sencillo, pero a su vez bastante potente. Seguro que en
ms de uno de tus proyectos has tenido que utilizar alguna de las infinitas libreras que existen para
elementos captcha. Con este bundle podrs incorporar el famoso ReCaptcha de Google pero adems
de una forma muy sencilla, ya que se integra con los formularios de Symfony. En su repositorio
GitHub58 puedes ver cmo se instala, cmo se configura y lo ms importante: cmo se usa. Aunque
te he dicho que no copiara nada de documentacin, djame que me salte la norma por esta vez:

1 use EWZ\Bundle\RecaptchaBundle\Validator\Constraints\IsTrue as RecaptchaTrue;


2
3 public function buildForm(FormBuilder $builder, array $options)
4 {
5 // ...
6 $builder->add('recaptcha', 'ewz_recaptcha', array(
7 'mapped' => false,
8 'constraints' => array(
9 new RecaptchaTrue()
10 )
11 ));
12 }

Has visto lo sencillo que es aadir un ReCaptcha? No necesitas ni mapearlo en la entidad y l solo se
encargar de lanzar una excepcin si el valor introducido no es el correcto. Pero, aqu tengo que darte
una mala noticia, y lo pongo entre comillas ya que es posible que no te encuentres esta casustica
en tus proyectos. Al renderizar el ReCaptcha de esta forma, segn estoy escribiendo este captulo, el
bundle no tiene la opcin de que en una misma pgina existan dos formularios con ReCaptcha. Te
atreves con un pull request?
58
https://github.com/excelwebzone/EWZRecaptchaBundle
Captulo 11: otros bundles 94

AcceleratorCacheBundle
Las cosas estn cambiando mucho en el tema de aceleradores en PHP, pero an as es interesante
conocer cmo funciona APC siempre y cuando tengas un proyecto en PHP con una versin inferior
a PHP 5.5 (lo cual espero que no). Por qu antes de esta versin? Puesto que en PHP 5.5 tenemos
OPcache por defecto, cuando antes tenamos la necesidad de instalar algn otro byte code cache,
como el mencionado APC.
Dentro de app/config/config_prod.yml puedes ver diferentes configuraciones a activar segn las
necesidades de tu proyecto. Todos los elementos que actives pasarn a estar en la cach de APC
y qu problema hay? Pues que si realizas algn cambio dentro de algunos elementos que estn
cacheados, no vas a ver dichos cambios hasta que no limpies la cach de APC. Por eso existe este59
bundle, el que aadir un nuevo comando de Symfony para limpiar los elementos cacheados que
necesites de forma sencilla, tanto de APC como de OPcode cache.

FOSRestBundle
Te voy a dar una buena noticia, y es que este bundle ya lo tienes instalado puesto que es una
dependencia de SonataUserBundle. Y te preguntars, qu puedo hacer con este bundle? Con
FOSRestBundle60 podrs realizar una API en cuestin de minutos y con mucha funcionalidad
aadida. Es un bundle un poco complejo por la cantidad de magia que realiza por detrs, as que te
recomiendo que te leas bien la documentacin que te he dejado en el enlace anterior sin tener alguna
prisa. Prueba cada uno de los puntos y, muy importante, mira cdigo de otros para saber cmo lo
usan. Este bundle es muy conocido y usado, por eso tiene su propia documentacin introducida en
symfony.com.

NelmioApiDocBundle
He odo que has hecho una API en tu proyecto, la has documentado? A los programadores no
nos gusta mucho documentar pero tenemos que hacer el esfuerzo de dejar cada cosa que hagamos
lo mejor comentada posible. Con este bundle61 podrs documentar tanto tu API como el resto de
cdigo que desarrolles de una forma muy sencilla, y es que gracias a una serie de anotaciones
especficamente creadas para tal objetivo, el bundle es capaz de crear un website navegable con todo
aquello que has desarrollado, y por supuesto, documentado. En la documentacin62 del bundle en
GitHub puedes ver cmo realizar todo esto que te cuento adems de unas imgenes en las que podrs
previsualizar cmo quedar tu documentacin en modo web. Por cierto: este bundle tambin lo
tienes instalado gracias a SonataUserBundle, as que ya no tienes excusa para evitar utilizarlo.
59
https://github.com/Smart-Core/AcceleratorCacheBundle
60
http://symfony.com/doc/master/bundles/FOSRestBundle/index.html
61
https://github.com/nelmio/NelmioApiDocBundle
62
https://github.com/nelmio/NelmioApiDocBundle/blob/master/Resources/doc/index.md
Captulo 11: otros bundles 95

DoctrineFixturesBundle
Los fixtures sirven para realizar una carga de datos controlada a la base de datos. Estos datos
introducidos se pueden usar para tests o para una carga inicial que requiera la aplicacin que estamos
desarrollando. Por defecto, Symfony no dispone de una forma sencilla de cargar datos como traen
otros frameworks o incluso CMS, pero este bundle te ayudar a hacerlo tanto con Doctrine ORM
como ODM (MongoDB).
En la documentacin63 del bundle tienes la instalacin (interesante en qu punto de AppKernel.php
se instancia) y la forma de crear las clases de carga que implementan la interfaz FixtureInterface.
Gracias al mtodo load el que recibe como parmetro un manager (ORM u ODM) y a los coman-
dos app/console doctrine:fixtures:load y app/console doctrine:mongodb:fixtures:load en
apenas unos minutos tendremos una base de datos repleta de informacin. Como vers en la
documentacin anterior, todo esto se puede complicar a gran escala, tanto como quieras. Aqu me
gustara parar un poco y ofrecerte un desafo: nuestra base de datos no tiene muchas entidades as
que te propongo crear una carga inicial de usuarios. La complicacin aqu ser que tendrs que
averiguar como se crean los usuarios gracias a Sonata User y FOSUserBundle.

DoctrineMigrationsBundle
Este bundle es una extensin de la capa de abstraccin de base de datos y te ofrece la posibilidad
de desplegar nuevas versiones de tu base de datos programticamente de forma segura, fcil y
estandarizada.
En la documentacin64 puedes leer cmo se instala y cmo funciona, pero todo se resume en una
serie de archivos de migracin que contienen los mtodos up() y down(). El mtodo up() contiene
lo nuevo a realizar cuando se despliegue (por ejemplo, crea la tabla acme_hello con los campos
a, b, c, ) mientras que el mtodo down() solo se ejecutar en caso de deshacer algn despliegue
(por ejemplo, borra la tabla acme_hello). Las clases las puedes generar a mano o simplemente
ejecutar el comando app/console doctrine:migrations:diff el cual te generer la clase con todo
lo necesario automgicamente. Por supuesto, si quieres tener el control o quizs slo subir una parte
de esas modificaciones, puedes crear y/o editar el fichero generado manualmente.
Este bundle se puede unir al sistema de despliegue que veremos en el captulo 15, y que sin duda
marcar un antes y un despus en tus despliegues (o al menos, as lo hizo conmigo).

FOSJSRoutingBundle
Este bundle te permite generar rutas de forma Symfony pero desde tu cdigo JavaScript. Mucha
gente me dice que la solucin es meter ese pedazo de JavaScript en una plantilla Twig y as tener
63
http://symfony.com/doc/current/bundles/DoctrineFixturesBundle/index.html
64
http://symfony.com/doc/master/bundles/DoctrineMigrationsBundle/index.html
Captulo 11: otros bundles 96

acceso al sistema de rutas, pero, adems de no ser una buena prctica sino un parche, esto no te
soluciona todos los casos. Si necesitas pasar variables de JavaScript a la ruta de Symfony, no te
queda ms remedio que generar la ruta a mano o utilizar este bundle y trabajar prcticamente igual
que como lo has hecho hasta ahora.
En la documentacin65 tienes todo lo necesario para empezar a probarlo. Quizs sea un poco raro
comenzar si ya ests acostumbrado a los path y url de Twig y al $this->generateUrl de los
controllers de Symfony, pero si realmente es necesario en tu proyecto, te acostumbrars muy rpido
a su utilizacin.

HWIOAuthBundle
Cuntas veces has ido a un sitio web y en vez de tener que rellenar un aburrido y largo formulario
te has conectado con tu cuenta de Facebook? Cada vez es ms comn que los sitios web te permitan
iniciar sesin o incluso crearte una cuenta desde una red social o incluso desde otro sitio web
normal y corriente. Para que esto funcione, el sitio del que sacar la informacin debe implementar
el protocolo OAuth. De esta forma, otros desarrollos pueden comunicarse con l y obtener la
informacin necesaria.
Es posible que incluso hayas tenido que realizar todo esto de forma manual alguna vez y, aunque
es una forma muy didctica de aprender cmo funciona OAuth, a veces es mejor no perder el
tiempo. HWIOAuthBundle66 te aade soporte para autenticar a tus usuarios a travs de OAuth1.0a
o OAuth2 con ms de 40 proveedores diferentes, entre los que estn Amazon, Dropbox, Facebook,
GitHub, Google, LinkedIn, Twitter, WordPress, Youtube y un largusimo etc. A menos que tengas
una necesidad muy concreta de desarrollar esta seccin a mano, para que vas a hacerlo? Como
siempre en Symfony: dedica tu tiempo a lo que realmente necesita tu aplicacin.

KnpSnappyBundle
Tu aplicacin es genial, y llega el punto en el que hay que generar informes para que ciertos usuarios
puedan descargarse informacin. Gracias a este bundle podrs generar tanto documentos pdf como
imgenes desde tus documentos HTML, y es que Snappy es un wrapper de la utilidad de conversin
wkhtmltopdf que se integra de forma trivial en tu proyecto Symfony.
Dentro de la documentacin67 vers que debes indicar la localizacin de los binarios de wkhtmltopdf
y wkhtmltoimage, por lo que antes de instalar el bundle dirgete a esta68 direccin web y descrgate
los archivos necesarios. Jams pensaras que crear y devolver documentos pdf fuese tan sencillo, a
que no?
65
https://github.com/FriendsOfSymfony/FOSJsRoutingBundle/blob/master/Resources/doc/index.md
66
http://knpbundles.com/hwi/HWIOAuthBundle
67
https://github.com/KnpLabs/KnpSnappyBundle
68
http://wkhtmltopdf.org/
Captulo 11: otros bundles 97

JMSTranslationBundle
Espero que para este punto del libro ya te hayas enamorado de Symfony y te hayas ledo toda la
documentacin, pero para este punto es deseable que te hayas ledo el apartado de traducciones69 .
Como vers, Symfony se basa en unos archivos de traducciones que se pueden generar gracias al
comando translations:update pero todo este proceso se queda muy corto en cuanto a usabilidad.
Gracias a este bundle podrs vitaminar todo este proceso de una forma impresionante. Como puedes
leer en la documentacin70 , primero hay que extraer las cadenas a traducir con el comando trans-
lations:extract y, gracias a una serie de rutas importadas en nuestro routing.yml, dispondremos
de un interfaz de traduccin un tanto sencillo pero increblemente eficaz.
Basta de traducir los archivos a mano o con software externo como Pootle que dificultan el proceso
demasiado. Ahora incluso podemos generar un permiso especfico como ROLE_TRANSLATOR y
dejarle el acceso a nuestro traductor que ver lo que est cambiando y dnde lo est cambiando,
aunque para que se reflejen los cambios en la web debers limpiar la cach. Si tu web es multi
idioma, sin duda es un bundle que debe estar en tu proyecto.

Truco 11
En este truco me gustara recordarte una cuestin ya comentada con anterioridad y ofrecerte dos
sitios web que espero que te sirvan de cara a tus futuros proyectos. La primera cosa que quiero
recordarte es que visites knpbundles.com71 con asiduidad y revises los bundles que ms se estn
utilizando y los que estn siendo actualizados. Es importante que tengas en cuenta el mantenimiento
de los bundles por parte de la gente que los haya generado o de la comunidad, sino es posible que te
encuentres con un problema a corto plazo.
El segundo apartado a compartir en este truco es una cheatsheet de Symfony. En http://www.symfony2cheatsheet.co
encontrars muchos de los elementos que se utilizan da a da en Symfony. Bien es cierto que le
falta alguna actualizacin a esta pgina, como por ejemplo la funcin $this->redirectToRoute()
que yo utilizo bastante, pero siempre est bien tener esta pgina para echar un vistazo rpido y la
documentacin oficial para un vistazo un poco ms detallado.
Por ltimo, si vas a subir tu proyecto a produccin, quizs te interesara leer los consejos que
se plantan en este checklist 73 de Symfony2, entre los que encontrars elementos de rendimiento,
seguridad, etc.
Y como siempre me gusta aportar un poquito ms, te comparto el canal de Youtube del evento espaol
deSymfony74 en el que muchos profesionales del sector exponen lo que han realizado con Symfony
69
http://symfony.com/doc/current/book/translation.html
70
http://jmsyst.com/bundles/JMSTranslationBundle
71
http://knpbundles.com/
72
http://www.symfony2cheatsheet.com/
73
http://www.symfony2-checklist.com/
74
https://www.youtube.com/user/desymfony
Captulo 11: otros bundles 98

y cmo lo han hecho, o simplemente como funciona algn componente de Symfony de forma ms
detallada. Sin duda, aprenders muchsimo de lo que otros profesionales aportan al mundo. Ah! Y
si sabes ingls, existen muchos eventos internacionales de Symfony con informacin buensima, te
van a faltar horas!

Conclusin
En este captulo hemos visto una gran variedad de bundles que muy posiblemente encajarn en
alguno de tus proyectos. Sin entrar en detalle, hemos explicado lo que hace cada uno de ellos y es
tu trabajo el profundizar en los que realmente te parezcan interestantes.
En el siguiente captulo, avanzamos un poco ms con nuestro sistema Gulp para darle an ms
potencia.
Captulo 12: Gulp (2)
Durante este captulo vamos a mejorar nuestro gulpfile.js para otorgarle mucha ms potencia de
la que tena. Todo lo que contiene hasta ahora hace referencia a los estilos de la pgina web, pero es
hora de empezar a trabajar los archivos JavaScript y mantener nuestro cdigo siguiendo una gua
de estilos. Tener un buen cdigo es fundamental.

Sass Lint
Seguro que te has puesto a modificar la maquetacin que ha hecho otra persona y has pensado por
qu lo hace diferente a mi?. Cada uno tenemos una forma de trabajar diferente y es algo realmente
malo y a que ralentiza a futuros desarrolladores y/o maquetadores. Por eso existen unas guas de
estilo que cada vez se afinan ms y que todos deberamos seguir. Solo de esta forma se consigue
aumentar la productividad de una forma casi inmediata.
Un claro ejemplo es el orden de nuestros estilos para cada elemento de la pgina web. Unos ponen
arriba los elementos de espaciado y posteriormente los colores; otros ponen la disposicin del bloque
y posteriormente los tamaos cul es mejor? Realmente ninguna, as que la solucin que se
escogi fue por orden alfabtico.
Vaya, esto te ha asustado, tanto que te estars preguntando si vas a tener que volver a leerte todas
tus hojas de estilo para ver si est todo en su sitio (aunque ya sabes que no lo est). De nuevo, Gulp
al rescate con una herramienta llamada Sass Lint. Esta herramienta es capaz de analizarte los SCSS
que le indiques y sacarte por pantalla los fallos de estilo que haya.
Para instalarla, ejecuta el siguiente comando:

1 npm install gulp-sass-lint --save-dev

Siguiente paso: crea la tarea Gulp para que te chive los fallos existentes.
Captulo 12: Gulp (2) 100

1 var gulp = require('gulp'),


2 //...
3 sasslint = require('gulp-sass-lint')
4 ;
5
6 gulp.task('sasslint', function () {
7 return gulp.src('./app/Resources/assets/scss/**/*.scss')
8 .pipe(sasslint())
9 .pipe(sasslint.format())
10 .pipe(sasslint.failOnError());
11 });

Lo mejor en este punto es que configures las reglas que debe seguir tu Sass lint. Para ello, crea
un archivo .sass-lint.yml en la raz del proyecto y aade las reglas que quieras. Aqu te dejo un
archivo de configuracin mo:

1 files:
2 include: '**/*.scss'
3 options:
4 formatter: stylish
5 merge-default-rules: false
6 rules:
7 bem-depth:
8 - 0
9 - max-depth: 1
10 border-zero:
11 - 1
12 - convention: zero
13 brace-style:
14 - 1
15 - allow-single-line: true
16 class-name-format:
17 - 1
18 - convention: hyphenatedbem
19 clean-import-paths:
20 - 1
21 - filename-extension: false
22 leading-underscore: false
23 empty-line-between-blocks:
24 - 1
25 - ignore-single-line-rulesets: true
26 extends-before-declarations: 1
Captulo 12: Gulp (2) 101

27 extends-before-mixins: 1
28 final-newline:
29 - 1
30 - include: true
31 force-attribute-nesting: 1
32 force-element-nesting: 0
33 force-pseudo-nesting: 0
34 function-name-format:
35 - 1
36 - allow-leading-underscore: true
37 convention: hyphenatedlowercase
38 hex-length:
39 - 1
40 - style: short
41 hex-notation:
42 - 1
43 - style: lowercase
44 id-name-format:
45 - 1
46 - convention: hyphenatedbem
47 indentation:
48 - 1
49 - size: 2
50 leading-zero:
51 - 1
52 - include: false
53 mixin-name-format:
54 - 1
55 - allow-leading-underscore: true
56 convention: >-
57 ^([\.\%]?[a-z]*[-]?[a-z0-9\-]*)(\.[a-z0-9\-]*)?(__[a-z0-9]*[-]?[a-z0-9\-\
58 ]*)?(--[a-z0-9]*[-]?[a-z0-9\-]*)?(\:[a-z]*)*$
59 convention-explanation: BEM pattern
60 mixins-before-declarations: 1
61 nesting-depth:
62 - 1
63 - max-depth: 3
64 no-color-keywords: 1
65 no-color-literals:
66 - 1
67 - allow-rgba: true
68 no-css-comments: 1
Captulo 12: Gulp (2) 102

69 no-debug: 1
70 no-duplicate-properties: 1
71 no-empty-rulesets: 1
72 no-extends: 0
73 no-ids: 1
74 no-important: 1
75 no-invalid-hex: 1
76 no-mergeable-selectors: 0
77 no-misspelled-properties:
78 - 1
79 - extra-properties: []
80 no-qualifying-elements:
81 - 1
82 - allow-element-with-attribute: false
83 allow-element-with-class: false
84 allow-element-with-id: false
85 no-trailing-whitespace: true
86 no-trailing-zero: 1
87 no-url-protocols: 1
88 no-vendor-prefixes:
89 - 1
90 - additional-identifiers: []
91 excluded-identifiers: []
92 ignore-non-standard: false
93 placeholder-in-extend: 1
94 placeholder-name-format:
95 - 1
96 - convention: hyphenatedbem
97 property-sort-order:
98 - 1
99 - ignore-custom-properties: false
100 quotes:
101 - 1
102 - style: single
103 shorthand-values:
104 - 1
105 - allowed-shorthands:
106 - 1
107 - 2
108 - 3
109 - 4
110 single-line-per-selector: 1
Captulo 12: Gulp (2) 103

111 space-after-bang:
112 - 1
113 - include: false
114 space-after-colon:
115 - 1
116 - include: true
117 space-after-comma: 1
118 space-around-operator: true
119 space-before-bang:
120 - 1
121 - include: true
122 space-before-brace:
123 - 1
124 - include: true
125 space-before-colon: 1
126 space-between-parens:
127 - 1
128 - include: false
129 trailing-semicolon: 1
130 url-quotes: 1
131 variable-for-property:
132 - 0
133 - properties: []
134 variable-name-format:
135 - 1
136 - allow-leading-underscore: true
137 convention: hyphenatedlowercase
138 zero-unit: 1

Por ltimo, ejecuta gulp sass-lint y visualiza el resultado. Si no has modificado nada de lo que
hemos hecho en este libro, debera salirte algo como esto:

1 app/Resources/assets/scss/components/_claim.scss
2 warning Color literals such as '#f22' should only be used in variable decla\
3 rations no-color-literals

Bsicamente nos est diciendo que tenemos un fallo:

Un color que no est declarado como una variable. Solucin: declralo como variable y utiliza
la variable siempre que la necesites (_colors.scss).

Esta vez ha sido sencillo, no? Procura mantener as de limpio siempre todos los proyectos que hagas
a partir de ahora. Un pequeo consejo: modifica la tarea de watch para que te vaya indicando cuando
tengas errores de estilos.
Captulo 12: Gulp (2) 104

JSCS
Ya tienes tus archivos SCSS de una forma inmejorable, pero qu hay de nuestros archivos
JavaScript? Existen varias formas de comprobar las guas de estilos, algunas de ellas son:

JSLint
JSHint
ESLint
JSCS

Personalmente me gusta mucho ESLint pero, como siempre, te recomiendo que leas qu hace cada
una de ellas y cules son los pros y contras en comparacin con el resto.
Para nuestro proyecto base, vamos a configurar JSCS. Lo primero es descargar la dependencia:

1 npm install gulp-jscs --save-dev

El siguiente punto es configurar la tarea de Gulp que te alertar de los errores en tus ficheros
JavaScript:

1 var gulp = require('gulp'),


2 //...
3 jscs = require('gulp-jscs')
4 ;
5
6 gulp.task('jscs', function () {
7 return gulp.src('./app/Resources/assets/js/**/*.js')
8 .pipe(jscs({
9 configPath: './vendor/sonata-project/core-bundle/Resources/public/vendor/b\
10 ootstrap/js/.jscsrc'
11 }))
12 .pipe(jscs.reporter());
13 });

JSCS necesita de una archivo .jscsrc en la raz de tu proyecto que configure lo que tiene que hacer
el proceso. Si no lo tienes en la raz, puedes poner el parmetro configPath y decirle donde tienes
dicho archivo. Aprovecho que viene uno de ejemplo dentro del vendor SonataCoreBundle, pero lo
de siempre: investiga y crea el que se adapte a tu proyecto. Adems, en la documentacin75 de gulp-
jscs tienes ms posibilidades para aplicar, como un parmetro fix que adems de alertarte de los
fallos, los corregir por ti, a que te gusta?
Para probar que todo funciona correctamente, es hora de crear tu primer archivo JavaScript. Para
esta demostracin, yo he creado el archivo app/Resources/assets/js/test.js con el siguiente
contenido:
75
https://github.com/jscs-dev/gulp-jscs
Captulo 12: Gulp (2) 105

1 (function ($) {
2 'use strict';
3
4 var string = "this is a string";
5 var body = $('body');
6 })(jQuery);

Si quieres que este archivo se incluya en tu proyecto Symfony, recuerda modificar tu archivo
app/Resources/views/base.html.twig y aadirlo en la seccin correspondiente. Para este ejemplo,
no nos hace falta.
Ahora ejecuta la tarea gulp jscs, qu es lo que pasa?

1 Invalid quote mark found at /home/jon/Projects/aupa_bilbao/app/Resources/assets/\


2 js/test.js :
3 2 | 'use strict';
4 3 |
5 4 | var string = "this is a string";
6 -----------------------^
7 5 | var body = $('body');
8 6 |})(jQuery);
9
10 jQuery identifiers must start with a $ at /home/jon/Projects/aupa_bilbao/app/Res\
11 ources/assets/js/test.js :
12 3 |
13 4 | var string = "this is a string";
14 5 | var body = $('body');
15 --------------^
16 6 |})(jQuery);
17 7 |

Tenemos dos errores en nuestro JavaScript:

Un signo de comillas mal. Solucin: cambiar las dobles comillas por comillas simples.
Una variable jQuery que no tiene bien su nombre. Solucin: cambiar la variable body por
$body.

El ejemplo es bastante trivial, pero dejar de serlo en cuanto tu aplicacin crezca como la espuma.
Captulo 12: Gulp (2) 106

PHP CS
Hasta ahora hemos puesto bonito nuestro SCSS y nuestro JavaScript, pero qu hay de nuestro
PHP? Es tan importante como el resto, y ms teniendo en cuenta que estamos trabajando con un
framework PHP, por lo que dejar esta pata coja no es una opcin en este punto.
Para esta seccin, vamos a utilizar PHP Code Sniffer, en su versin para gulp, que puedes encontrar
aqu76 . Lo primero: instalacin.

1 npm install gulp-phpcs --save-dev

Como siempre, crea la tarea de gulp que ejecutar este mdulo recin instalado:

1 var gulp = require('gulp'),


2 //...
3 phpcs = require('gulp-phpcs')
4 ;
5
6 gulp.task('phpcs', function () {
7 return gulp.src(['./src/AppBundle/**/*.php'])
8 .pipe(phpcs({
9 bin: './bin/phpcs',
10 standard: 'PSR2',
11 warningSeverity: 0
12 }))
13 .pipe(phpcs.reporter('log'));
14 });

Si lees detenidamente esta tarea, vers que se ejecuta un binario bin/phpcs. Tienes que instalarlo
tambin, pero para ello, dispones de la herramienta necesaria para hacerlo de forma transparente:
Composer. Aade la dependencia necesaria a tu archivo composer.json ejecutando el siguiente
comando:

1 composer require squizlabs/php_codesniffer

Tras esto ya tendrs el binario necesario en la carpeta bin automticamente. Es hora de probar si
nuestro cdigo PHP es bonito o no lo es. Lanzamos el comando gulp phpcs y sorpresa! Seguro que
no te esperabas tantos errores, verdad? No te asustes, es algo normal que incluso muchos entornos
de desarrollo (IDEs) o editores no cumplen.
76
https://github.com/JustBlackBird/gulp-phpcs
Captulo 12: Gulp (2) 107

Este binario, phpcs, no puede corregir nuestro cdigo de forma automtica as que te va a tocar leer
los errores e ir poco a poco corrigiendo Bueno, no voy a ser tan malo y te ayudar con esto. Si ves
los binarios que se ha descargado la nueva dependencia de Composer recin aadida dentro de bin/,
adems de nuestro ya conocido phpcs tienes tambin el phpcbf. Este har su magia y te corregir
automticamente el cdigo siempre que pueda, claro!

NOTA: queda como ejercicio cambiar el standard de este ejemplo de PSR2 al standard
de Symfony. Puedes leer ms aqu77

PHP CBF
Es hora de aplicar algo de magia y que nuestros fallos se corrijan solos. En el futuro, el siguiente paso
ser que nuestra web se desarrolle sola con algunos comandos de voz o similar, pero de momento
tendremos que conformarnos con auto-corregir los fallos.
Paso nmero uno: descargar lo necesario para gulp.

1 npm install gulp-phpcbf --save-dev


2 npm install gulp-util --save-dev

Como te imaginars, necesitas un elemento ms en esta receta. Paso nmero dos: crear la tarea
gulp utilizando los nuevos ingredientes.

1 var gulp = require('gulp'),


2 //...
3 phpcbf = require('gulp-phpcbf'),
4 gutil = require('gulp-util')
5 ;
6
7 gulp.task('phpcbf', function () {
8 return gulp.src(['./src/AppBundle/**/*.php'])
9 .pipe(phpcbf({
10 bin: './bin/phpcbf',
11 standard: 'PSR2',
12 warningSeverity: 0
13 }))
14 .on('error', gutil.log)
15 .pipe(gulp.dest('src/AppBundle'));
16 });
77
https://github.com/leaphub/phpcs-symfony2-standard
Captulo 12: Gulp (2) 108

Ahora, agitemos nuestra varita con el comando gulp phpcbf y et voil, cdigo limpio y bonito.
Hay algo que no ha podido corregir phpcbf? Bueno, tampoco es mucho problema abrir los pocos
archivos con errores que haya dejado y corregirlos a mano despus del trabajo que nos ha quitado,
no?

Ejecutar comandos PHP


Esto es algo que a mucha gente no le gusta: juntar comandos de PHP, que en este caso sern de la
consola de Symfony, con cosas que no son PHP. Sin duda, a mi es algo que normalmente no me gusta
hacerlo pero cuando trabajaba con MopaBootstrapBundle o incluso sin l, tener todo centralizado
en comandos de gulp puede ayudar bastante. Para que seas capaz de decidir si te vale o no, vamos
a realizar el ejemplo pertinente.
Para este ejemplo necesitas una nica dependencia nueva:

1 npm install gulp-copy --save-dev

Tras instalarla, genera las tareas necesarias para tu proyecto Symfony:

1 var gulp = require('gulp'),


2 //...
3 copy = require('gulp-copy'),
4 cp = require('child_process')
5 ;
6
7 // Without this function exec() will not show any output
8 var logStdOutAndErr = function (err, stdout, stderr) {
9 console.log(stdout + stderr);
10 };
11
12 // Symfony & Mopa Stuff
13 gulp.task('fonts', function () {
14 return gulp.src('./web/bundles/mopabootstrap/fonts/bootstrap/*')
15 .pipe(copy('./web/fonts', {prefix: 7}));
16 });
17
18 gulp.task('installAssets', function () {
19 cp.execFile('bin/console assets:install', function (err, stdout, stderr) {
20 console.log(stdout);
21 console.log(stderr);
22 });
Captulo 12: Gulp (2) 109

23 });
24
25 gulp.task('installMopa', function () {
26 cp.execFile('bin/console mopa:bootstrap:symlink:sass', function (err, stdout, \
27 stderr) {
28 console.log(stdout);
29 console.log(stderr);
30 });
31 });
32
33 gulp.task('clearCache', function () {
34 cp.execFile('bin/console cache:clear', function (err, stdout, stderr) {
35 console.log(stdout);
36 console.log(stderr);
37 });
38 });

Con este cdigo podrs lanzar los comandos de Symfony necesarios para poder empezar a trabajar
de forma correcta. Adems de estos previamente expuestos, yo me suelo crear un comando que lance
todos los los anteriores y otro para empezar a trabajar:

1 gulp.task('symfony', ['fonts', 'installAssets', 'installMopa', 'clearCache']);


2
3 gulp.task('dev', ['sass', 'watch']);

Con estos comandos, una vez desplegado el Symfony en el ordenador junto a sus dependencias,
basta con lanzar gulp symfony y despus gulp dev y ponernos a trabajar.
Ahora, es tu decisin si quieres aplicar esto o no dentro de tus proyectos.

Truco 12
Cuando trabajas con este tipo de tecnologas, tienes que tener una buena documentacin que les
explique a tus usuarios qu necesitan instalar. En nuestro caso, yo te he ido diciendo que tienes que
instalar Ruby (antes era necesario para scss_lint, y ser necesario para el deploy con Capistrano),
que tienes que instalar npm, y todo el resto de elementos que forman parte de nuestro proyecto
base. Pero, si te das cuenta, todo est paquetizado. Es decir: las dependencias de PHP se declaran
en un archivo composer.json. T instalas PHP y con composer install tienes todo lo necesario. Lo
mismo con Node: instalas Node + npm y gracias al archivo package.json y el comando npm install
ya tienes todo lo necesario.
El truco de este captulo tiene que ver con esto mismo pero a nivel de Ruby. Estara genial decirle
a los desarrolladores que se instalen Ruby + Rubygems y que con un solo comando tengan todo
Captulo 12: Gulp (2) 110

lo necesario. Esta funcionalidad no est por defecto pero gracias a una gema se puede conseguir.
Adems de estos dos ingredientes, tienes que avisar al resto de desarrolladores que instalen la
siguiente gema:

1 gem install bundler

Esta gema es capaz de leer el archivo Gemfile desde donde ests ejecutando el comando de instalacin
de dependencias bundle install. Por lo que, creamos un archivo Gemfile en la raz de nuestro
proyecto e introducimos lo siguiente:

1 # Pull gems from RubyGems


2 source 'https://rubygems.org'
3 gem 'scss_lint', '~> 0.48.0'

Lo correcto es que visites la web https://rubygems.org/78 para ver lo que tiene que ir en este archivo
y que tengas cuidado con posibles breaks que puedan suceder, pero si necesitas una versin concreta
para uno de tus proyectos, gracias a este Gemfile logrars que todo tu equipo trabaje con las mismas
versiones.

Conclusin
En este captulo hemos aprendido a potenciar Gulp en gran medida y darle un sentido an mayor
en nuestro proyecto. Gracias a la cantidad de posibilidades que ofrece, hemos conseguido mejorar
nuestra calidad de cdigo y, lo mejor de todo, prcticamente sin esfuerzo alguno. Existen muchsimas
ms posibilidades, y algunas de ellas te las mencionar en el eplogo del libro.
78
https://rubygems.org/
Captulo 13: configuracin adicional
de Symfony
En este captulo descansamos un poco de lo que es programacin como tal y nos paramos a mirar
ciertas configuraciones que te permitirn o bien disponer de algn elemento adicional o bien recortar
algo de tiempo en tu desarrollo. Cada uno de estos trucos, algunos de captulos anteriores, te servirn
tarde o temprano as que asegrate de que los entiendes bien.

ParamConverter
En el captulo 9 veamos lo que para mi fue sin duda el gran descubrimiento tras llevar un tiempo
en Symfony: la anotacin ParamConverter. Es tan potente que a veces no hace falta ni ponerla, y
as te la presentaba el truco de dicho captulo. Suena raro, pero vamos a ver su funcionamiento
extendiendo el ejemplo que te di y aadiendo nuevos ejemplos.
Supongamos que tenemos una ruta que carga una entrada de un blog. El controller con su ruta en
forma de anotacin sera algo as:

1 /**
2 * @Route("/blog/{slug}", name="blog_show")
3 */
4 public function showAction($slug)
5 {
6 $em = $this->getDoctrine()->getManager();
7 $post = $em->getRepository("AppBundle:Post")->findBySlug($slug);
8 if (!$post) {
9 throw $this->createNotFoundException();
10 }
11 //...
12 }

Cada una de tus rutas que reciba un id o un slug tendr que repetir ms o menos este mismo pedazo
de cdigo o no. Se puede hacer una conversin directamente a la entidad de esta forma:
Captulo 13: configuracin adicional de Symfony 112

1 use AppBundle\Entity\Post;
2
3 /**
4 * @Route("/blog/{slug}", name="blog_show")
5 */
6 public function showAction(Post $post)
7 {
8 //...
9 }

Como ves, ahora por parmetro le he forzado recibir directamente un Post. Cmo funciona esta
clase de magia? En la ruta existe un placeholder que se llama slug. En nuestra funcin recibimos
una entidad de tipo Post. Lo que va a hacer internamente es comprobar que la clase Post contiene un
campo llamado slug y buscar por ese campo. En el caso de que no lo encuentre, automticamente
lanzar una excepcin 404.
El problema viene cuando los parmetros no se llaman igual, cuando tenemos que realizar algn tipo
de modificacin previa (como una fecha) o cuando tenemos parmetros que pertenecen a entidades
diferentes. En estos casos, necesitamos la anotacin @ParamConverter y, en algunos casos, un
poquito de cdigo que ir escrito en alguno de nuestros repositorios.
Complicamos un poco el ejemplo, y ahora queremos tanto la entrada de blog como un comentario
de dicha entrada:

1 /**
2 * @Route("/blog/{id}/comments/{comment_id}")
3 * @ParamConverter("comment", class="AppBundle:Comment", options={"id" = "commen\
4 t_id"})
5 */
6 public function showAction(Post $post, Comment $comment)
7 {
8 //...
9 }

Obviamente, no podemos poner dos {id} en la ruta, por lo que uno de ellos se tiene que llamar
diferente. Es por esto que tenemos que mapear el parmetro comment_id al id dentro de la entidad
Comment. An as, esto es muy sencillo, pero comprueba este ejemplo:
Captulo 13: configuracin adicional de Symfony 113

1 /**
2 * @Route("/user/{first_name}/{last_name}")
3 * @ParamConverter("user", class="ApplicationSonataUserBundle:User", options={
4 * "repository_method" = "findByFullName",
5 * "mapping": {"first_name": "firstName", "last_name": "lastName"},
6 * "map_method_signature" = true
7 * })
8 */
9 public function showAction(User $user)
10 {
11 //...
12 }

Wow! Qu ha sido eso? Vayamos por partes. Lo primero que leemos es la ruta, en la cual recibimos
tanto el nombre como el apellido de, supuestamente, un usuario de nuestra base de datos. En el
ParamConverter le estamos indicando que en el repositorio de usuarios UserRepository.php ejecute
la funcin findByFullName a la que le pasamos los parmetros recibidos en la ruta. La creacin de
esta funcin es cosa nuestra, as que manos a la obra:

1 class UserRepository
2 {
3 public function findByFullName($firstName, $lastName)
4 {
5 //...
6 }
7 }

Esta funcin devolver un User y, adems de ser utilizada en nuestra ruta y ahorrarnos un pedacito
de cdigo, es una funcin perfectamente reutilizable en toda la plataforma, algo que sin duda sigue
las buenas prcticas que hemos estado hablando varias veces durante el libro.
Estos y muchos ms ejemplos, adems de la creacin de tus propios converters, los tienes en la
documentacin79 oficial de Symfony de la anotacin @ParamConverter.

Plantilla sin controller


Estas desarrollando tu web y te toca poner la tpica pgina de poltica de privacidad o trminos y
condiciones. Son pginas con un texto escrito y que no suele cambiar, por lo que no existe ningn
inters en sacarlo de la base de datos. Nuestro controller para una de estas pginas sera algo como
esto:
79
http://symfony.com/doc/current/bundles/SensioFrameworkExtraBundle/annotations/converters.html
Captulo 13: configuracin adicional de Symfony 114

1 public function privacyAction()


2 {
3 return $this->render('static/privacy.html.twig');
4 }

Qu sentido tiene pasar por un controlador que nicamente redirige a una plantilla? Esta es una
de las cosas que con anotaciones no se puede hacer, pero como veamos en el truco del captulo 8
y que repito en esta seccin, tenemos un archivo de rutas en YAML llamado routing.yml que nos
ayudar a evitar este pequeo cdigo inservible. Es tan fcil como aadir una ruta como est a tu
archivo de rutas:

1 app_privacy:
2 path: /privacy
3 defaults:
4 _controller: FrameworkBundle:Template:template
5 template: static/privacy.html.twig

Si has ledo el apartado de HTTP Cach del libro de Symfony, me dirs que con este sistema no se
puede cachear la pgina. No te preocupes, te dejo aqu la solucin para este problema tambin:

1 app_privacy:
2 path: /privacy
3 defaults:
4 _controller: FrameworkBundle:Template:template
5 template: 'static/privacy.html.twig'
6 maxAge: 86400
7 sharedAge: 86400

Redireccionamientos sin controller


Estas trabajando el SEO de tu web y quieres que una URL antigua redirija a una nueva. Al igual que
en el caso anterior, si pasas por un Controller te quedar un tanto escueto, tal que as:

1 public function redirectAction()


2 {
3 return $this->redirectToRoute('new_route');
4 }

Y, tambin como en el caso anterior, la solucin est en el archivo routing.yml de la siguiente forma:
Captulo 13: configuracin adicional de Symfony 115

1 redirect_route:
2 path: /old-page
3 defaults:
4 _controller: FrameworkBundle:Redirect:urlRedirect
5 route: new_route
6 permanent: true

Este truco tambin lo veamos en el captulo 8, pero no te dije que admite varias configuraciones.
Yo te he explicado el ejemplo tpico para que sepas de su existencia, y tus deberes en este apartado
son leerte esta documentacin80 para saber todas las posibilidades que existen.

Dump autoload
Esto deberas haberlo ledo ya en el checklist que te compart previamente, pero es tan sencillo a la
par de importante que le dedico una pequea seccin en este captulo.
Cuando lanzas composer para que obtenga las dependencias, se genera un autoload que no est del
todo optimizado. Dicen que incluso puede ralentizarse bastante si dispones de muchas clases en tu
proyecto. Por eso se cre un comando especfico para que mejorase el autoload generado, y como
no cuesta nada, te aconsejo que lo utilices al menos en produccin:

1 composer dump-autoload --optimize

Hide logs
Si alguna vez te pones a mirar los logs que se generan en la carpeta var/logs vers que existe
mucha informacin que realmente no es necesaria, al menos en el log de desarrollo. Principalmente
contienen unas serie lneas que no aportan nada y son los eventos. Symfony utiliza Monolog para
la gestin de estos archivos mediante una configuracin por defecto que a mi me gusta cambiar
nada ms descargar un nuevo proyecto Symfony. El objetivo es que no aparezcan estas lneas para
que nuestros logs contengan informacin ms relevante. Edita app/config/config_dev.yml para
eliminar los eventos:

80
http://symfony.com/doc/current/cookbook/routing/redirect_in_config.html
Captulo 13: configuracin adicional de Symfony 116

1 monolog:
2 handlers:
3 type: stream
4 path: %kernel.logs_dir%/%kernel.environment%.log
5 level: debug
6 channels: "!event"

Se acabaron las lneas sobrantes!

Enviar errores 500 por email


Y ya que estamos con los logs, me gustara darte otro consejo ms. Siempre que tengas una web en
produccin, lo mejor sera que si existe un error te enterases antes que tus usuarios. Como de tests no
hablaremos hasta el final de este libro, intentemos reducir el tiempo que est el fallo en produccin
al mnimo.
Un primer acercamiento sera leer los errores 500 de los logs de Apache, o nuestro servidor web
cualquiera, todas las maanas y corregirlos. Esto puede llegar a ser tedioso, puede que se nos escape
alguno y muy importante: como somos tan buenos/as programadores/as, muchas veces perderemos
el tiempo leyendo logs que no contienen fallos.
Un segundo acercamiento sera utilizar algn software o realizarlo nosotros/as para que lea cada x
minutos el archivo anterior y nos mande un aviso cuando encuentre una anomala. Sin duda esto es
mucho ms ptimo pero, y si te digo que hay algo todava ms sencillo y que no tienes que tocar
prcticamente nada?
Como dice el ttulo de esta seccin, se puede configurar Monolog para que cada vez que detecte un
error 500 en tu proyecto web se te notifique por correo electrnico. Tan solo tienes que realizar una
pequea configuracin, esta vez en el archivo app/config/config_prod.yml:

1 monolog:
2 handlers:
3 mail:
4 type: fingers_crossed
5 action_level: critical
6 handler: buffered
7 buffered:
8 type: buffer
9 handler: swift
10 swift:
11 type: swift_mailer
12 from_email: error@website.com
13 to_email: webmaster@website.com
Captulo 13: configuracin adicional de Symfony 117

14 subject: Error crtico detectado


15 level: debug

Asegrate de tener el correo bien configurado ;)

Configuracin para PhpStorm


Cuando ests desarrollando con app_dev.php (por defecto con bin/console server:run), las
pantallas de error son bastante descriptivas, pero an puedes dar un pasito ms. Si tu IDE de
desarrollo es PhpStorm, se puede habilitar una pequea configuracin que te permitir pulsar con
el ratn sobre el error que se muestra en estas pantallas para que se abra el archivo concreto en la
lnea del error, listo para corregir!
Para esto, debes editar el archivo app/config/config.yml y aadir lo siguiente:

1 framework:
2 ide: "phpstorm://open?file=%%f&line=%%l"

Si antes de esto no utilizabas PhpStorm este es un buen momento de que, al menos, le des una
oportunidad.

Sacando las sesiones a otra carpeta


Por defecto hasta la versin 3.0, Symfony almacenaba los archivos de sesin dentro de la carpeta ap-
p/cache/<entorno>. Si realizas una modificacin en cualquier nivel, supongamos una modificacin
visual en alguna plantilla, y quieres que se vea, no te quedar ms remedio que limpiar la cache. El
efecto colateral es que deslogueabas a todos tus usuarios, puesto que les ests eliminando la sesin.
Como te he dicho, ya han corregido esta configuracin moviendo la carepta de sesiones fuera de la
cache. Tienes la solucin en el nuevo archivo app/config/config.yml que viene con las versiones
3.x the Symfony (aunque tambin tenas la solucin en la documentacin de Symfony). La carpeta
final ser app/sessions/<environment> para Symfony 2.x o, con la configuracin por defecto,
var/sessions/<environment> para Symfony 3.x. Si ests usando Symfony 2.x, necesitas este bloque
de configuracin:

1 framework:
2 session:
3 handler_id: session.handler.native_file
4 save_path: "%kernel.root_dir%/sessions/%kernel.environment%"
Captulo 13: configuracin adicional de Symfony 118

Truco 13
Es posible que no dispongas de un servidor de envo de correos y no quieras utilizar tu cuenta de
Gmail para este menester, as que cul es la solucin? Si buscas un poco en Google vers que existen
varias posibilidades gratuitas, pero a mi la que mejor resultado me ha dado es SparkPost. Hoy en
da, se pueden enviar hasta 100.000 emails al mes con la cuenta gratuita. Si necesitas caractersticas
especiales o una mayor cantidad mensual, debers pasar por caja. Pero 100.000 es un nmero que
viene bastante bien para hacer alguna que otra prueba.
Para configurar Sparkpost en Symfony, hay que realizar unos pequeos cambios en la configuracin
de Swiftmailer. Lo primero es modificar el archivo app/config/config.yml con lo siguiente:

1 swiftmailer:
2 transport: "%mailer_transport%"
3 host: "%mailer_host%"
4 username: "%mailer_user%"
5 password: "%mailer_password%"
6 spool: { type: memory }
7 port: "%mailer_port%"
8 encryption: "%mailer_encryption%"

Si lo comparas con la configuracin por defecto, vers que he aadido el parmetro port y el
parmetro encryption. Ambos cogern sus valores del archivo app/config/parameters.yml para
que, dependiendo de donde est situado el proyecto, se comporte de una forma u otra. Debes ayudar
a nuestros usuarios a desplegar, y para ello tienes que modificar el archivo app/config/parame-
ters.yml.dist para que cuando realicen el primer composer install se les pida por consola el
valor para estos parmetros recin aadidos. Editas este archivo e introduce los parmetros por
defecto:

1 mailer_transport: smtp
2 mailer_host: 127.0.0.1
3 mailer_user: ~
4 mailer_password: ~
5 mailer_port: 25
6 mailer_encryption: null

En tu caso, como ya lo tienes Symfony funcionando en tu ordenador, deberas rellenar el archivo


parameters.yml con la informacin necesaria. Para obtener esta informacin, tienes que registrarte
en https://www.sparkpost.com/81 , y tras seguir los primeros pasos, se te dar la configuracin
necesaria. Cuidado! No volvers a ver la contrasea de nuevo por lo que si la pierdas, tendrs
81
https://www.sparkpost.com/
Captulo 13: configuracin adicional de Symfony 119

que generar una nueva. Adems, deberas validar el dominio desde el que se van a enviar los correos
electrnicos. Esto ltimo lo puedes leer en su documentacin. Con los parmetros que te han dado,
edita tu app/config/parameters.yml:

1 mailer_transport: smtp
2 mailer_host: smtp.sparkpostmail.com
3 mailer_user: SMTP_Injection
4 mailer_password: PASSWORD
5 mailer_port: 587
6 mailer_encryption: tls

El nico parmetro que tienes que modificar de este cdigo es el mailer_password, que es la
contrasea que te ha generado la web. Con esto, ya tendrs tu Symfony enviando emails desde
SparkPost.

Conclusin
En este captulo hemos visto ciertas configuraciones interesantes de Symfony. Existen muchas ms
pero estas son las que yo utilizo con mayor frecuencia en mis proyectos. En este82 enlace te dejo la
parte de configuracin del Cookbook de Symfony, un libro en el que encontrars las mejores recetas
adicionales para tu Symfony y que debes revisar con cierta asiduidad, ya que aaden bastantes cosas
interestantes con el paso del tiempo.
82
https://symfony.com/doc/current/cookbook/configuration/index.html
Captulo 14: modificando
SonataUserBundle
Hasta este punto, hemos dejado de lado el tema de los usuarios porque ya nos vena gratis. Pero es
posible que esa funcionalidad no se ajuste del todo a las necesidades de tu proyecto. En este captulo
veremos algunas de las configuraciones que puedes modificar de este bundle realmente interesantes
y algunas modificaciones que puedes hacer mediante cdigo.

Modificando FOSUserBundle
Aunque hayas instalado SonataUserBundle, como ya deberas saber, este bundle depende directa-
mente de FOSUserBundle, por lo que nos fijaremos en este ltimo por ser la base de los usuarios.
Todo lo que vas a hacer lo tienes en este apartado83 de la documentacin, solo que aqu lo
aplicars directamente mediante ejemplos prcticos. Ten cuidado ya que, aunque la documentacin
sea de FOSUserBundle, nosotros los que estamos modificando es el bundle que hace uso de l:
SonataUserBundle. Comencemos!

Sobrescribiendo las plantillas


Al instalar tu bundle para usuarios, tuviste que cargar un montn de rutas en el archivo routing.yml:
la pgina de login, la pgina de perfil, el registro Pero todas tienen como base una plantilla blanca
que no se adapta a la nuestra.
Al igual que con el resto de los bundles, puedes sobrescribir este funcionamiento y hacer que extienda
de tu plantilla base. Adems, como la gente de FOSUserBundle (y Sonata) ya saben que esto ocurrir
en prcticamente todos los proyectos que lo utilicen, dividieron casi todas las respuestas de sus
rutas en dos plantillas: una que dibuja el layout de la pgina y, dentro de esta, una plantilla con el
contenido. Para qu sirve esto? Pues para que los desarrolladores puedan sobrescribir la parte que
les interese modificar de cada una de ellas. Por ltimo, proporcionan una plantilla layout.html.twig
de la que extienden todas sus plantillas y que nosotros arrasaremos en este ejemplo.
Para sobrescribir una plantilla en Symfony, se puede hacer de dos formas:

1. Mediante un bundle que sea hijo del bundle que queremos modificar.
2. Mediante la carpeta app/.
83
https://symfony.com/doc/master/bundles/FOSUserBundle/index.html#next-steps
Captulo 14: modificando SonataUserBundle 121

En ambos casos, la ruta a las vistas debe coincidir de forma exacta, tanto nombres de carpetas como
de archivos. Adems, tienes que tener en cuenta que Symfony buscar la plantilla primero en app/,
luego en un bundle hijo y por ltimo en el bundle concreto, por lo que una vez encontrada en este
orden, no seguir buscando.
Lo que vamos a hacer en el ejemplo es modificar la plantilla base de FOSUserBundle. Para ello, debes
tener un poco de conocimiento sobre cmo estn estructuradas las vistas en este bundle. Dirgete a
vendor/friendsofsymfony/user-bundle/Resources/views y prate un momento a ver lo que hay
dentro de esa carpeta.
Lo tienes? Como habrs visto, en la raz de sus vistas est la plantilla layout.html.twig, justo la que
queremos modificar. Como todas tus plantillas estn en app/, lo correcto es que contines creando
ah el resto, incluso las de sobrescritura. Aqu viene el truco: debes crear la misma estructura de car-
petas que lo que vas a modificar, por lo que crea la carpeta app/Resources/FOSUserBundle/views/
y dentro de ella copiamos el archivo layout.html.twig que est en el vendor de Sonata. Una vez
hayas hecho esto, hay que limpiar la cach para que Symfony se entere de la nueva plantilla:

1 bin/console cache:clear

A partir de aqu tienes el control de la plantilla base. Antes de realizar modificaciones a lo loco,
mira bien lo que contiene la plantilla y visualiza cmo puedes encajarla con la plantilla base de tu
proyecto. Intenta hacerlo t, aunque te dejo aqu mi solucin, ya que seguro que hay varias:

1 {% extends 'base.html.twig' %}
2
3 {% block content %}
4 <div>
5 {% block fos_user_content %}
6 {% endblock fos_user_content %}
7 </div>
8 {% endblock content %}

Si ahora abres a la pgina de login http://127.0.0.1:8000/login vers ya tienes tu plantilla base


funcionando pero con un pequeo error. La parte de la derecha de la web, el registro, es un
embebido completo, por lo que se vuelve a ver el headline y el footer. Por qu pasa esto?
Es importante que entiendas lo que ocurre. Para ello, abre la plantilla de login y busca la parte que
muestra el formulario de registro embebido. Exactamente es esta:
Captulo 14: modificando SonataUserBundle 122

1 <form action="{{ path("fos_user_security_check") }}" method="post">


2 {% if csrf_token %}
3 <input type="hidden" name="_csrf_token" value="{{ csrf_token }}" />
4 {% endif %}
5
6 <label for="username">{{ 'security.login.username'|trans }}</label>
7 <input type="text" id="username" name="_username" value="{{ last_username }}\
8 " required="required" />
9
10 <label for="password">{{ 'security.login.password'|trans }}</label>
11 <input type="password" id="password" name="_password" required="required" />
12
13 <input type="checkbox" id="remember_me" name="_remember_me" value="on" />
14 <label for="remember_me">{{ 'security.login.remember_me'|trans }}</label>
15
16 <input type="submit" id="_submit" name="_submit" value="{{ 'security.login.s\
17 ubmit'|trans }}" />
18 </form>

La plantilla register.html.twig contiene lo siguiente:

1 {% extends "@FOSUser/layout.html.twig" %}
2
3 {% block fos_user_content %}
4 {% include "FOSUserBundle:Registration:register_content.html.twig" %}
5 {% endblock fos_user_content %}

Hasta la versin anterior, para Symfony 2.*, el extends de la parte superior haca que tuvieras dos
layouts en la misma pgina. No podas sobrescribir esta plantilla y quitar el extends, porque de esta
forma, si te dirigas a http://127.0.0.1:8000/register/, te encontrabas con un formulario de registro sin
layout alguno. La solucin era fcil: en la plantilla de login.html.twig modificar ese bloque que
renderiza el formulario de registro por un cdigo HTML que explique los beneficios de registrarse
junto a un enlace a la pgina de registro. De hecho, esto lo hacen muchas webs, pero como en este
libro nos va la marcha, lo que haremos ser explicar cmo sobrescribir el controller.

Sobrescribiendo los controladores


Al igual que las plantillas, podemos sobrescribir controladores para editar o modificar completa-
mente la lgica ejecutada. Esto no significa que tengas que sobrescribir el controlador completo, tal
y como hacamos con las plantillas, sino que podemos sobrescribir nicamente una funcin que nos
interesa.
Captulo 14: modificando SonataUserBundle 123

Lo primero que debemos hacer es indicarle a nuestro bundle de usuarios que su padre es FOSUser-
Bundle. Abre el archivo ApplicationSonataUserBundle.php y aade lo siguiente:

1 public function getParent()


2 {
3 return 'FOSUserBundle';
4 }

En la seccin anterior nos interesaba modificar la funcin registerAction dentro del Registra-
tionController de FOSuserBundle. En la carpeta app no hay controllers por lo que esta vez debes
realizar el cdigo dentro de tu UserBundle, en la carpeta src.
En la carpeta Controller de este bundle, crea el mismo archivo que est creado en el vendor, es
decir, RegistrationController.php junto a la funcin que quieres modificar:

1 <?php
2
3 namespace Application\Sonata\UserBundle\Controller;
4
5 use FOS\UserBundle\Controller\RegistrationController as BaseController;
6
7 class RegistrationController extends BaseController
8 {
9 public function registerAction()
10 {
11 //...
12 }
13 }

Ves el truco? Hacemos un use del controlador original y lo renombramos como BaseController
para que los nombres no colisionen. Despus, hacemos que el controlador extienda del original y
ya est. Es hora de meter la lgica que queremos. Realmente no queremos cambiar mucho as que
copia y pega el contenido original y, nicamente, modifica la respuesta por la plantilla que quieres
renderizar.
Con este mtodo debes tener cuidado ya que es posible que modifiquen el controlador base y tu
no adaptes el tuyo. De vez en cuando revsalo y traspasa sus cambios a tu controller para estar a la
ltima.

Modificando el login y el registro


Actualmente, tu base de datos contiene usuarios cuyo login es el nombre de usuario. En muchas
plataformas se ha puesto de moda que se pueda realizar el login mediante el email del usuario.
Captulo 14: modificando SonataUserBundle 124

Conseguir esto en FOSUserBundle es tan sencillo como aadir esta configuracin a tu app/confi-
g/security.yml en la seccin correspondiente:

1 security:
2 providers:
3 fos_userbundle:
4 id: fos_user.user_provider.username_email

Pero esto solo te acerca un poco al funcionamiento actual y es que, an realizando esta configuracin,
en el registro se le solicitar al usuario tanto el username como el email, cuando realmente ambas
cosas deberan ser lo mismo. Aqu te dejo unos deberes: deberas modificar los formularios de registro
para que solo pida el email. Adems, cuando se establezca el email, ste debera copiarse al usuario
para que no de error a la hora de crear el usuario.
Si quieres intentarlo solo, adelante; si quieres un poco de ayuda para la ltima parte, aqu te dejo lo
que necesitas aadir a tu User.php:

1 /**
2 * Overrides default setEmail function
3 * @param String $email
4 */
5 public function setEmail($email)
6 {
7 $email = is_null($email) ? '' : $email;
8 parent::setEmail($email);
9 $this->setUsername($email);
10 }

Adems de esto, recuerda sobrescribir el RegistrationFormType.php. En este84 enlace te dejo un


poco de ayuda ;)

Modificando el registro (2) y los emails


Cuntas veces te has registrado y has recibido un email para confirmar tu cuenta de correo?
Nosotros no vamos a ser menos, as que vamos a desarrollar esta funcionalidad en nuestro proyecto
base. He dicho desarrollar? Perdona, quera decir configurar:

84
https://symfony.com/doc/master/bundles/FOSUserBundle/overriding_forms.html
Captulo 14: modificando SonataUserBundle 125

1 fos_user:
2 # ...
3 registration:
4 confirmation:
5 enabled: true

Ya has terminado. Con esta configuracin en app/config/config.yml tus usuarios recibirn un


correo de notificacin cuando se registren y no podrn realizar login hasta que confirmen su cuenta
de correo. Asegrate de tener bien configurado el servidor de envo de correos.
Por otro lado, existen un montn de emails que se mandan desde FOSUserBundle y que puedes
modificar como cualquier otra plantilla. Pero lo que no puedes modificar desde las plantillas es quin
los manda, entre otras cosas. Esta configuracin y muchas otras ms las tienes en la documentacin
que te dejo aqu85 . No considero importante copiar y pegar esa parte de configuraciones en el libro
as que, una vez que leas esa pgina, seguimos con el siguiente apartado.

Otras modificaciones
En las secciones anteriores he querido compartir contigo las modificaciones que yo suelo utilizar en
muchos de mis proyectos. He compartido tambin el enlace a la parte de la documentacin en la
que se explican todas estas modificaciones y muchas otras que se pueden hacer, y considero muy
interesante su lectura, tanto que te vuelvo a compartir dicho enlace para que digieras la informacin
con calma. No hace falta que sea ahora, pero s que tengas presente dnde est para cuando llegue
el momento.

Documentacin: https://symfony.com/doc/master/bundles/FOSUserBundle/index.html#next-
steps86

Truco 14
La clase base de User que hemos creado solo dispone del $id y de todo lo que hereda de SonataUser-
Bundle. Obviamente, es posible que necesites ms campos para tu proyecto. Estos campos los aadi-
rs en la entidad que creaste en captulos anteriores src\Application\Sonata\UserBundle\Entity\User.php.
Pero tienes un problema con esto: todo lo que aadas no se ver en tu admin correspondiente.
Para que se vean, tienes que modificar este admin. El primer paso es indicarle al bundle de Sonata que
coja tu clase admin en vez de la suya prefabricada. Edita app/config/config.ymlcon este cambio:

85
https://symfony.com/doc/master/bundles/FOSUserBundle/emails.html
86
https://symfony.com/doc/master/bundles/FOSUserBundle/index.html#next-steps
Captulo 14: modificando SonataUserBundle 126

1 sonata_user:
2 admin:
3 user:
4 class: Application\Sonata\UserBundle\Admin\UserAdmin

Hecho esto, tienes que crear la clase que has indicado en la configuracin previa, as que crea tu
UserAdmin.php con el siguiente contenido:

1 <?php
2
3 namespace Application\Sonata\UserBundle\Admin;
4
5 use Sonata\AdminBundle\Datagrid\ListMapper;
6 use Sonata\AdminBundle\Form\FormMapper;
7 use Sonata\UserBundle\Admin\Entity\UserAdmin as BaseUserAdmin;
8
9 class UserAdmin extends BaseUserAdmin
10 {
11 protected function configureFormFields(FormMapper $formMapper)
12 {
13 parent::configureFormFields($formMapper);
14 }
15
16 protected function configureListFields(ListMapper $listMapper)
17 {
18 parent::configureListFields($listMapper);
19 }
20 }

Tal y como te lo he dejado en el cdigo anterior, todo estar igual que antes. Es hora de
que modifiques el listado, el formulario o cualquier otra seccin que desees modificar con tus
necesidades. Puedes incluso borrar el parent:: y reescribirlo por completo, tienes el poder!

Conclusin
En este captulo hemos aprendido a gestionar un poco mejor los usuarios de nuestra plataforma
modificando o aadiendo funcionalidades que dotan a nuestro proyecto de ms potencia y flexi-
bilidad. Con esto terminamos de desarrollar una base slida para nuestro proyecto, y el siguiente
paso es pasarlo a produccin. En el siguiente captulo aprenders a realizar un despliegue con una
herramienta creada para ello.
Captulo 15: despliegue
Seguramente has sufrido este ttulo ms de una vez. Los primeros despliegues se hacan mediante FTP
subiendo todos los archivos o, si sabas con certeza los que se haban modificado, nicamente dichos
archivos modificados. Despus, los desarrolladores empezaron a utilizar los sistemas de control
de versiones como Git para hacer despliegues mucho ms simples, ya que traan nicamente lo
necesario y de una forma mucho ms controlada y segura, pudiendo volver atrs en caso de fallo
salvo casos puntuales.
Hoy en da tenemos los sistemas de despliegue. Existen una gran variedad y en diferentes lenguajes
de programacin, as que en este captulo veremos por encima las diferentes posibilidades que hay
en este campo y realizaremos un ejemplo de despliegue con uno de ellos.

Magallanes
Magallenes es una herramienta de despliegue realizada en PHP y que sirve para desplegar otras
herramientas de PHP principalmente. Es muy fcil de usar y gestionar, pero sobre todo, es muy
fcil de extender aadiendo nuestras propias tareas. Permite desplegar nuestra aplicacin a todos
los servidores que queramos tanto por rsync como por ssh, y tras esto, ejecutar las tareas necesarias
para que el despliegue llegue a buen puerto, como por ejemplo, la instalacin de dependencias de
Composer.
En Symfony es muy sencillo utilizar esta herramienta ya que se instala mediante Composer y el
creador es un fan de este framework, por lo que hay tareas por defecto que nos sirven para nuestro
despliegue. Sin embargo, tras probarla en varios proyectos, es una herramienta que se queda un poco
corta de funcionalidades, como por ejemplo, la gestin de carpetas y archivos compartidos que no
cambian de despliegue en despliegue: parameters.yml, var/logs,
De todas formas, aqu87 te dejo un post mo sobre cmo desplegar tu aplicacin Symfony con
Magallanes.

Ansible
Esta herramienta es enorme y permite realizar una gran variedad de acciones. Solo con ver las
empresas que hacen uso de ella en su propia home ya te da una idea de la potencia que tiene este
software. Est escrita en Python, un lenguaje que est bastante de moda ahora, pero que esto no te
quite las ganas de probar semejante herramienta.
87
https://medium.com/@jontorrado/deploying-a-symfony2-project-with-magallanes-28abe452c54c
Captulo 15: despliegue 128

He odo a mucha gente decir que es la herramienta definitiva pero yo soy un gran defensor de
utilizar la herramienta que se adapta ms a nuestras necesidades, incluyendo la necesidad de estar
cmodo con ella. Personalmente, no me ha sido sencillo adentrarme en esta herramienta, aunque
reconozco su potencia. Por otro lado, si buscas en GitHub encontrars a gente que ha hecho posible el
despliegue de aplicaciones Symfony con Ansible. Tan solo realiza una bsqueda Symfony ansible
y vers los resultados.
No vamos a usar esta herramienta en el libro pero aqu te dejo un post mo sobre cmo usar Ansible
para desplegar aplicaciones Symfony: https://medium.com/@jontorrado/deploying-a-symfony-ap-
plication-with-ansible-c517c26ccbfc88 .

Capifony
Es posiblemente la herramienta ms utilizada para despliegues de Symfony o lo era. Esto es
debido a que utiliza Capistrano 2 por detrs, y ya hace bastante tiempo que sali Capistrano
3 con muchsimas mejoras. An as, te aconsejo que le eches un vistazo en su pgina web
http://capifony.org/89 .
La herramienta est escrita en Ruby pero todo lo que tendrs que saber de Ruby es copiar y pegar,
puesto que la gran mayora de configuraciones ya estn realizadas y solo tendrs que cambiar IPs,
carpetas y parmetros de este estilo. Si vas a su repositorio de GitHub vers que ms de 100 personas
han contribuido en el avance de esta herramienta con ms de 700 aportaciones totales, algo a tener
en cuenta si lo que buscas es tambin una comunidad activa que mantenga el software en el tiempo
(Capifony no est mantenido por la salida de Capistrano 3).
Despus de leer esto, puede que te haya quedado ese mal sabor de boca por lo de Capistrano 3, pero
no te preocupes porque te traigo la solucin en el siguiente apartado.
Despus de leer esto, puede que sientas la necesidad de probar Capistrano 3 as que, no te preocupes,
sigue con la siguiente seccin y comencemos la accin.

Capistrano
Capistrano es una herramienta de automatizacin de servidores remotos realizada en Ruby. Soporta
el desarrollo y ejecucin de tareas e incluye una gran variedad de ellas por defecto para tareas de
despliegue. Como te ests imaginando, sirve para muchas otras cosas adems de para despliegues y,
no ser esta herramienta demasiado para lo que queremos?
Siempre he odo la frase ir a por el pan en un Ferrari, y muchas veces es as. Nos complicamos
nuestros desarrollos con softwares complicados y gigantes para realizar algo diminuto y que
no explotar prcticamente nada lo que hemos utilizado como base. Sin embargo, hay algunas
88
https://medium.com/@jontorrado/deploying-a-symfony-application-with-ansible-c517c26ccbfc
89
http://capifony.org/
Captulo 15: despliegue 129

excepciones que merecen la pena puesto que no penalizan nuestro tiempo de desarrollo. Es ms,
si en un futuro queremos ampliar funcionalidades y automatizar algo remoto, con Magallanes no
podramos hacerlo, y tendramos que cambiar todo a un sistema como Ansible o Capistrano, con la
prdida de tiempo que ello conlleva.
Por esto, mi consejo es que utilices un sistema de este tipo que ya tienen configuraciones generadas
por la comunidad y que te ser tremendamente fcil poner en marcha con solo copiar y pegar algunos
trozos de cdigo. En la mayora de mis proyectos utilizo Capistrano como sistema de despliegue, as
que en el siguiente apartado voy a explicarte como desplegar una aplicacin Symfony con un plugin
de Capistrano 3 realizado para tal accin.

Desplegando con capistrano-symfony


No existe Capifony con Capistrano 3? O no est planificado su desarrollo? Pues la verdad es que
no, y realmente esto es porque no es necesario. Existe un plugin de Capistrano 3 llamado capistrano-
symfony que est muy basado en Capifony y ya tiene todo lo que necesitamos. Si a esto le sumamos
que su instalacin es tan sencilla como aadir una lnea a nuestro Gemfile, todo indica que vamos
por buen camino.
Vamos a ir paso a paso generando todo lo necesario para que despliegues tu proyecto en un servidor
remoto. Si no dispones de uno, puedes realizarlo contra tu propio ordenador para realizar pruebas o
contra una mquina virtual.
Lo primero que vamos a hacer es instalar lo necesario. Abre tu archivo Gemfile y aade lo siguiente:

1 source 'https://rubygems.org'
2 #...
3 gem 'capistrano-symfony', '~> 1.0.0.rc1'

Tras lanzar el comando de instalacin bundle install, es posible que veas un WARNING en la
instalacin, no te preocupes puesto que la gema ya est cambiada de repositorio y tienes la correcta,
solo que no han quitado este mensaje.
Lo siguiente es configurar nuestro Capistrano para que cargue todo lo necesario para el despliegue.
Crea un archivo Capfile en la raz de tu proyecto con el siguiente contenido:

1 # Capfile
2 require 'capistrano/setup'
3 require 'capistrano/deploy'
4 require 'capistrano/symfony'
5 require 'capistrano/scm/git'
6 install_plugin Capistrano::SCM::Git
Captulo 15: despliegue 130

El require de setup es necesario para los stages de Capistrano::DSL:Module; el require de deploy


para las tareas que vamos a definir en la parte inferior de deploy:***; el require de symfony ya tiene
el resto de dependencias necesarias, como capistrano/composer que obviamente necesitaremos
para la instalacin de nuestras dependencias.
Hecho esto, crea una carpeta config en la raz de tu proyecto y dentro de esta crea el archivo
config/deploy.rb. Aqu es donde configurars las tareas que tiene que realizar en cada servidor,
sea donde sea el despliegue. Antes de nada he de decirte que lo ms correcto es que tu proyecto se
encuentre en un repositorio Git, as que voy a presuponer que es as. Si no lo tienes, puedes utilizar
Bitbucket o GitHub para continuar con el ejemplo:

1 ############################################
2 # Setup project
3 ############################################
4
5 set :application, "aupa-bilbao"
6 set :repo_url, "https://github.com/grupo/repositorio.git"
7
8 ############################################
9 # Setup Capistrano
10 ############################################
11
12 set :log_level, :info
13 set :use_sudo, false
14
15 set :ssh_options, {
16 forward_agent: true
17 }
18
19 set :keep_releases, 3
20
21 ############################################
22 # Linked files and directories (symlinks)
23 ############################################
24
25 set :linked_files, ["app/config/parameters.yml"]
26 set :linked_dirs, [fetch(:log_path), fetch(:web_path) + "/uploads"]
27 set :file_permissions_paths, [fetch(:log_path), fetch(:cache_path)]
28
29 set :composer_install_flags, '--no-interaction --optimize-autoloader'
30
31 namespace :compile_and_upload do
32
Captulo 15: despliegue 131

33 desc 'Compile and upload'


34
35 task :gulp do
36 if fetch(:env) == "prod"
37 run_locally do
38 execute "gulp prod"
39 end
40 else
41 on roles(:all) do |host|
42 execute "cd #{release_path}; npm install && gulp prod"
43 end
44 end
45 end
46
47 task :upload do
48 if fetch(:env) == "prod"
49 on roles(:all) do |host|
50 upload! "#{fetch(:web_path)}/css", "#{release_path}/#{fetch(:web_name)}/\
51 css", recursive: true
52 upload! "#{fetch(:web_path)}/js", "#{release_path}/#{fetch(:web_name)}/j\
53 s", recursive: true
54 end
55 end
56 end
57 end
58
59 namespace :deploy do
60 after :updated, 'composer:install_executable'
61 after :updated, 'compile_and_upload:gulp'
62 after :updated, 'compile_and_upload:upload'
63 end

Qu demonios es todo esto? Lelo otra vez, despacio, ya que es Ruby y es posible que no te hayas
dado cuenta pero realmente entiendes todo lo que hay en ese pedazo de cdigo. Solo has de saber
un par de cosas:

El ciclo de Capistrano, explicado aqu90


Las variables expuestas de capistrano-symfony, explicadas aqu91
90
http://capistranorb.com/documentation/getting-started/flow/
91
https://github.com/capistrano/symfony/#settings
Captulo 15: despliegue 132

La primera parte del cdigo es nicamente configuracin del despliegue en cuanto a variables. Si
no entiendes alguna, puedes leer qu hacen en la documentacin de Capistrano o de capistrano-
symfony, dependiendo de la variable. En la segunda parte, lo que hago es crearme dos tareas: una
para el Gulp y otra para la subida de ficheros necesarios, por qu?
Vamos a diferenciar los servidores de prueba con los servidores de produccin. Es posible que en los
servidores de prueba nos interese tener npm instalado junto a todo lo dems, tal y como trabajamos
en nuestro ordenador de desarrollo. Sin embargo, en produccin no necesitamos tener todo esto, con
disponer nicamente de los archivos CSS y JS finales nos vale, y todo el resto de software y archivos
son innecesarios.
Pero te estars preguntando dnde est la configuracin de pruebas y produccin. Para eso, tenemos
que crear una carpeta deploy dentro de la recin creada config y ah crear un archivo por cada
sistema de despliegue que queramos. Para este ejemplo, crea el archivo config/deploy/pre.rb que
hace referencia al entorno de pruebas:

1 ######################################################################
2 # Setup Server
3 ######################################################################
4 server "dev.company.com", user: "sshuser", roles: %w{web}
5 set :deploy_to, "/path/to/your/deployment/directory"
6 set :env, "dev"
7
8 ######################################################################
9 # Capistrano Symfony - https://github.com/capistrano/symfony/#settings
10 ######################################################################
11 set :file_permissions_users, ['www-data']
12 set :webserver_user, "www-data"
13
14 ######################################################################
15 # Setup Git
16 ######################################################################
17 set :branch, "master"

Este archivo es mucho ms sencillo y en l nicamente tienes que declarar variables que modifican el
funcionamiento del deploy.rb. En la primera lnea se indica el servidor al que hacer SSH y desplegar.
Aqu puedes poner la IP de tu propio PC si tienes funcionando el servidor SSH.
En la variable deploy_to se introduce la ruta en la que se va a desplegar el proyecto Symfony. En el
siguiente apartado hablaremos sobre el resultado final de carpetas en el destino.
La variable env hace referencia al entorno en el que lo se va a desplegar y, si te acuerdas de nuestro
deploy.rb esta variable har que nuestros assets se compilen en local y se suban o que este proceso
se haga en el servidor. Si la estableces como dev, el sistema va a suponer que dispones de Gulp
instalado en el ordenador destino, as que asegrate de que es as.
Captulo 15: despliegue 133

Las variables de permisos de archivos y del servidor web sirven para que las carpetas de cach, logs
y web/uploads tengan permiso de escritura por parte del servidor web. Debers saber de antemano
cul es el usuario de este servidor, sino te encontrars con un error importante que impedir que se
visualice tu web.
Por ltimo, le se le indica al Git cul es la rama de la que debe obtener la informacin. Lgicamente,
en pruebas y en produccin es muy posible que sean ramas diferentes, por lo que este es el punto
idneo para configurarlo.
A lo mejor te he vuelto un poco loco/a con este apartado, as que leelo con calma, prubalo en tu
ordenador y vers que es muy sencillo. Y adems, te aadir otra cosa: en el resto de tus proyectos
puedes copiar y pegar cada lnea que hemos hecho aqu y, con tan solo modificar algunas lneas, ya
tendrs tu sistema de despliegue montado.
Pero ahora, cmo lanzas el proceso? Debes recordar el nombre de los ficheros que creas dentro de
la carpeta config/depoy. Estos son los que te sirven para el siguiente comando:

1 cap pre deploy

Ya est! Todo ha funcionado correctamente (espero), pero te acabas de dar cuenta de que tu cdigo
tiene un bug importante, puedes volver atrs? Siempre que hayas realizado antes algn despliegue,
puedes volver a un despliegue anterior con lo explicado en este92 enlace. Adems, debes saber que
el sistema mantiene un nmero de despliegues y va borrando los ms antiguos. Esto lo puedes
configurar en la variable keep_releases. Adems, quizs tengas que modificar tambin el deploy.rb
para que se realicen algunas tareas de deshacer lo que hemos hecho al desplegar, por ejemplo,
modificaciones en la base de datos.
Te acuerdas del DoctrineMigrationsBundle? Es hora de que lo pongas en funcionamiento y
aproveches el flujo de despliegue de Capistrano para lanzar los diferentes comandos que te ofrece
el bundle. Aqu93 te dejo la informacin necesaria para que lo pruebes.
Por ltimo, unos pequeos deberes: no hemos tenido en cuenta la carpeta de sesiones en ningn
momento, por lo que cada vez que se realice un despliegue tendremos un problema. Modifica este
funcionamiento editando el archivo deploy.rb para que la carpeta app/sessions se mantenga entre
despliegues.

Capistrano-symfony: resultado final


Si nunca has utilizado un sistema de despligue, es conveniente que leas esto detenidamente para que
entiendas como funcionan.
El resultado final se compone de varias carpetas. La primera de ellas es la carpeta repo, donde
encontrars todo lo que tiene que ver con el repositorio de control de versiones. La siguiente carpeta
92
http://capistranorb.com/documentation/getting-started/flow/#rollback-flow
93
http://symfony.com/doc/current/bundles/DoctrineMigrationsBundle/index.html#running-migrations-during-deployment
Captulo 15: despliegue 134

es releases, donde podrs encontrar dentro una carpeta por cada despliegue realizado no borrado.
El nombre de la carpeta es la fecha concreta en la que se realiz el despliegue. Aqu ya te estars
preguntando: voy a tener que cambiar el DocumentRoot de mi servidor Apache cada vez que realice
un despliegue? La respuesta es, obviamente, que no, ya que el sistema de despliegue realiza un enlace
simblico llamado current que apunta a la ltima carpeta desplegada dentro de releases. Por eso,
cuando se realiza un rollback, se lanzan las tareas definidas y se cambia el enlace de current
a la carpeta anterior. Conectando esto con la configuracin de tu Apache, el DocumentRoot lo
tienes que establecer a /path/to/your/deployment/directory/current/web, y no lo tienes que
tocar nunca ms. Por ltimo tenemos la carpeta shared en donde estarn todos los archivos y
carpetas que se comparten entre despliegues y que estn definidos en el archivo deploy.rb, como
por ejemplo, el archivo parameters.yml que contiene la informacin de la base de datos entre otras
configuraciones.
En resumen:

Una carpeta repo que tiene que ver con el sistema de control de versiones
Una carpeta releases con una carpeta por cada despliegue
Un enlace simblico current que apunta a una carpeta dentro de releases, normalmente el
ltimo despliegue correcto
Una carpeta shared que contiene los archivos y carpetas que nunca se modifican entre
despliegues, como configuracin y logs (y los deberes).

Espero que esto te ayude significativamente a disminuir el tiempo de despliegue y los problemas
que puedas tener tanto si lo haces a mano como si lo haces con un sistema de control de versiones.

Truco 15
El truco de este captulo tiene que ver con sistemas, algo que muchos desarrolladores no estn
muy acostumbrados a controlar pero que a mi es un tema que me apasiona. Cuando vas a realizar
un despliegue, bien sea con la herramienta comentada en este captulo o conectndote mediante
sFTP o SSH, vas a necesitar la contrasea del servidor para poder hacerlo. Esto es un problema de
seguridad importante, pero que a su vez es fcilmente solventable.
Lo que debemos hacer es solicitarle al usuario una identificacin de su ordenador y darle permiso
a ese ordenador para conectarse con un usuario concreto. Dicho ordenador tendr permiso para
conectarse como ese usuario sin necesidad de contrasea, lo que acota bastante los problemas de
seguridad, ya que los usuarios no sabrn la contrasea y tendremos controlados los ordenadores
que pueden entrar. Para conseguir esto, hay que seguir los siguientes pasos.
El usuario que va a conectarse debe proporcionar al administrador de sistemas su clave pblica.
Puede que ya disponga una. Para saberlo, hay que ejecutar el siguiente comando:
Captulo 15: despliegue 135

1 ls -al ~/.ssh

Dentro de esa carpeta debera existir un archivo id_rsa.pub cuyo contenido es el interesante. Si no
dispone de ese archivo o se quiere volver a generar, hay que ejecutar el siguiente comando:

1 ssh-keygen -t rsa -C "email@dominio.com"

Con esto, ya debera tener el archivo generado. El contenido de este archivo se proporcionar al
administrador de sistemas.
Dicho administrador de sistemas deber conectarse al servidor destino. Dentro de ste, hay que
dirigirse al archivo authorized_keys que se encuentra dentro de la carpeta .ssh en la home de cada
usuario. Es decir, hay que editar $HOME/.ssh/authorized_keys y aadir el contenido del archivo
id_rsa.pub recibido.

Hecho esto, el usuario podr realizar ssh usuario@servidor.dominio.com y no se le solicitar


contrasea, por lo que tampoco se le solicitar contrasea al hacer cap pre deploy, fantstico!

Nota: algunas distribuciones tienen desactivada la conexin mediante el archivo


authorized_keys en la configuracin SSH. Si esto no te funciona, comprueba esta
configuracin. Tambin comprueba los permisos de los archivos: 600 para .ssh y 700
para authorized_keys.

Conclusin
En este captulo hemos aprendido a desplegar nuestro proyecto Symfony a un servidor remoto con
diferentes posibilidades. El ejemplo realizado contiene una base slida para tus futuros proyectos
pero que a su vez es fcilmente extensible a necesidades futuras. Con esto hemos completado el ciclo
completo del desarrollo de nuestra aplicacin, por lo que solo queda comentar cmo se realizan los
tests, explicado en el siguiente captulo escrito por Beat Espia.
Captulo 16: testing
Como nosotros somos desarrolladores que queremos hacer bien las cosas, este captulo debera
tenerse muy en cuenta ya que trata de un tema que hoy en da esta cogiendo bastante fuerza, pero
quizs no toda la que debera: el testing.
La intencin es hacer una introduccin interesante sobre todo lo que puede abarcar el mundo del
testing sobre todo en PHP y que obviamente implica que tambin para nuestro querido framework
Symfony.
PHP es un lenguaje ya con cierta edad que como todo lo antiguo en esto del desarrollo viene de
una poca oscura, ignorando totalmente cualquier patrn de diseo y las buenas prcticas sobre
arquitectura software. Pero todo esto no es ms que cosa del pasado ya que desde hace unos aos
gente como el creador de Symfony, Fabien Potencier junto con muchos otros, han forzado a PHP a
redirigir su rumbo hacia un lenguaje serio, nutrindose para ello de caractersticas propias de otros
lenguajes como pueden ser JAVA o Python. La versin 5 fue slo la primera piedra para lo que estara
por venir y en pocos aos se ha pasado del functions.php de WordPress de 5000 lneas, mezclando
variables superglobales con funciones y HTML concatenado por doquier, a todo un ecosistema muy
completo y robusto que por citar algunas herramientas como pueden ser el propio framework sobre el
que va este libro, Composer como gestor de dependencias, Doctrine como ORM, clases, namespaces,
herramientas de QA a tutipln y muchas ms cosas. Todo esto ha traido otra forma de programar
en PHP hacindose un hueco en el mundo de las anteriormente mencionadas arquitecturas software,
de ah la fuerte corriente del DDD y los microservicios, y lo habitual que es la aplicacin de patrones
de diseo como la herencia, el polimorfismo o la inyeccin de dependencias. El crecimiento de PHP
como lenguaje es imparable y agarrmonos fuerte que todava PHP7 est aqu con un montn de
novedades interesantes :).

Qu es eso del testing? Me sirve eso a m?


Para comenzar este apartado empiezo con la segunda pregunta y la respuesta rotunda y corta debera
ser S, pero como hay que buscar el punto pragmtico en el mundo real de la programacin dira
que es un s con matices.
Para aquellos lectores de este libro que no hayan tenido ningn contacto directo con esto del testing,
me gusta definirlo como uno de los pilares fundamentales que hace que un cdigo sea solido, robusto
y mantenible, es decir, lo que viene a denominarse en la jerga de la programacin como Clean code.
Al igual que los patrones de diseo, el testing no es algo de antes de ayer, tiene su historia, pero hay
que decir que es un hbito que se ha ido implantando de forma muy novedosa en la construccin
de software estos ltimos aos, de hecho, la teora es ms o menos conocida por la mayora de las
empresas pero luego la realidad no se invierte tiempo en este aspecto.
Captulo 16: testing 137

El testing consiste en generar cdigo que obliga a cumplir una serie de casos de uso a tu software
devolviendo un resultado positivo o negativo. Esos tipos de casos de uso se pueden definir a muchos
niveles dentro de una arquitectura software por eso tenemos dos grandes familias en cuanto al nivel
de concrecin: los teses unitarios y los teses funcionales. Los primeros son los de ms abajo en la
jerarqua y se encargan de definir casos de uso muy concretos haciendo que su complejidad, y por
ende la del cdigo de la aplicacin que cubre, sea sencilla y bsica. No debera conocer nada que
exista fuera de su mbito ni comunicarse con servicios de terceros ni bases de datos. Sin embargo,
los teses funcionales abarcan una caracterstica concreta de la aplicacin y para probar su correcto
funcionamiento requieren de conexin de bases de datos, sistemas de mailing, etc. Los teses unitarios
son muy rpidos de probar y aunque hay tcnicas para conseguir que se ejecuten en menos tiempo
si cabe, lo normal es que unos mil teses unitarios pasen entre los primeros 20-30 segundos. Los
funcionales, como ya hemos repetido anteriormente, al requerir de conexiones a otras partes del
sistema y tener ms complejidad la ejecucin es ms pesada, por lo que los tiempos de respuesta en
los que nos movemos son mucho ms elevados.
Segn va pasando el tiempo y el testing va volvindose ms robusto y slido, existen quienes dicen
que en muchas ocasiones los teses unitarios no tienen sentido y que slo se debera analizar la parte
funcional; por otro lado tambin estn los que dicen que testear al 100% una aplicacin es una prdida
de tiempo. Yo discrepo de esas dos afirmaciones, la primera de ellas nunca la he llegado a compartir
pero la segunda, por el contrario, hubo una poca en la que me la llegu a creer; an as, la prctica
y el uso en el da a da me han hecho ver que por muy absurdo que pueda parecer un test, maana
puede salvarte de un fallo enorme. La realidad es que muchas veces, invertir tiempo en testear un
software al 100% en el mundo empresarial es inviable por los plazos que se manejan, pero lo que es
innegable es que mientras se pueda se debera testear absolutamente todo (recalco que esto es una
opinin personal). Por otro lado, me he encontrado casos en los que que si hubiese omitido los teses
unitarios, detalles que al fin de cuentas son imprescindibles como pueden ser la suma correcta de
un saldo, podran haber provocado un absoluto caos en la aplicacin.
Para concluir esta introduccin, y respondiendo nuevamente a la pregunta del principio, el s con
matices va muy ligado a si se tiene una aplicacin construida y hay que mantener sobretodo un
cdigo que escribi otra persona probablemente testearlo no nos salga a cuenta y nos requiera ms
esfuerzo del que nos pueda evitar. En estos casos nos podra interesar despus de un anlisis, mirar
qu partes son las crticas y centrarnos en ellas, pero ya digo que esto puede variar dependiendo del
caso concreto. Otra solucin interesante podra ser, segn se va modificando el software ir testeando
el cdigo que uno mismo va generando asegurando por lo menos que el cdigo construido a partir
de esa fecha es un cdigo limpio. De todas formas, el testing debera existir como una premisa bsica
de nuestros futuros desarrollos y no simplemente como algo opcional o algo de gurs.

TDD vs BDD
Son siglas que cada vez ms se empiezan a ver por posts, documentaciones, buenas prcticas e incluso
por ofertas de trabajo. Pese a poner un versus en el ttulo no son tcnicas antagnicas, realmente
BDD es una interpretacin concreta del TDD centrndose en el diseo.
Captulo 16: testing 138

TDD son las siglas de Test Driven Development o desarrollo guiado por teses. Esta tcnica se basa
en desarrollar tests unitarios, despus construir el cdigo que cubre ese test unitario haciendo que
ese test pase y finalmente refactorizarlo.
BDD, Behaviour Driven Development o desarrollo guiado por comportamiento. Realmente son
tcnicas que se parecen en esencia, la diferencia ms importante se encuentra en el concepto en
s. Mientras que en TDD lo que se escriben son teses para despus crear cdigo que los cumplan, en
BDD los teses pasan a ser requerimientos o especificaciones de diseo. El BDDismo tiene un campo
ms centrado en la lgica de negocio de un software y para cubrir los diferentes niveles existen dos
subprcticas: storyBDD centrada ms en la parte funcional y specBDD en la parte ms unitaria.

Herramientas de testing en PHP


Adentrndonos ya en el desarrollo PHP, existen innumerables herramientas que se centran en el
testing ya sea como libreras, frameworks metodologas etc. Sabiendo que se dejan muchas puntos
sin tocar, los siguientes puntos son una breve descripcin de tres de las herramientas ms famosas
dentro de este entorno.

PHPUnit
El indiscutible padre del testing en PHP. Practiques o no, si eres programador PHP habrs odo hablar
de l. Es un framework de testeo en PHP orientado al programador. La gran mayora de las libreras
y frameworks open-source utilizan PHPUnit como herramienta de testeo, entre ellos Symfony. Es
un framework de la familia xUnit y para comprobar si un cdigo cumple con lo esperado utiliza
aserciones.
Para el uso de cualquier herramienta de testeo es indispensable una librera de mockeo. En el mundo
de PHP existen dos alternativas que destacan por encima del resto que son Mockery y Prophecy.
Desde la versin 4.5, PHPUnit trae Prophecy por defecto.
La instalacin de PHPUnit se realiza de forma sencilla mediante Composer:

1 composer require --dev phpunit/phpunit

A continuacin, un ejemplo bsico extrado la propia web oficial que puede servir como primera
toma de contacto con la herramienta:
El cdigo de produccin:
Captulo 16: testing 139

1 <?php
2
3 namespace AprendeSymfony;
4
5 class Money
6 {
7 private $amount;
8
9 public function __construct($amount)
10 {
11 $this->amount = $amount;
12 }
13
14 public function getAmount()
15 {
16 return $this->amount;
17 }
18
19 public function negate()
20 {
21 return new self(-1 * $this->amount);
22 }
23 }

El test:

1 <?php
2
3 namespace AprendeSymfony;
4
5 class MoneyTest extends \PHPUnit_Framework_TestCase
6 {
7 public function testCanBeNegated()
8 {
9 $a = new Money(1);
10 $b = $a->negate();
11 $this->assertEquals(-1, $b->getAmount());
12 }
13 }

Una vez tenemos tanto el cdigo como el test basta con ejecutar vendor/bin/phpunit para obtener
un resultado como el siguiente.
Captulo 16: testing 140

1 $ vendor/bin/phpunit tests/
2 PHPUnit 4.8.6 by Sebastian Bergmann and contributors.
3 .
4 Time: 655 ms, Memory: 4.25Mb
5 OK (1 test, 1 assertion)

PhpSpec
Es una herramienta mucho ms joven, creada para un enfoque ms bdddiano. A diferencia de
PHPUnit que es una herramienta de xUnit, PhpSpec es un framework de testeo del tipo xSpec, lo que
implica que se centra ms en el comportamiento que en la estructura de cdigo. Anteriormente ya se
ha hablado de Prophecy, pues bien, cabe destacar que este framework de mockeo est desarrollado
por el mismo grupo de personas que estn detrs del core del propio PhpSpec siendo ste una
dependencia directa de esta herramienta.
Volviendo al apartado algo ms terico donde se explicaba a grandes rasgos las caractersticas del
TDD y el BDD, PhpSpec es una herramienta enfocada muy claramente para practicar la tcnica del
BDD, pero adems, dentro del BDD, ms concretamente specBDD.
El gran aporte que hace esta herramienta aparte de servir como framework de testeo, es que cumple
muy bien su funcin de herramienta de diseo ya que tiene un potente generador de cdigo que
con cada nueva versin van mejorando. La idea es que se genere el test o la especificacin, y que
mediante cuestionarios de consola se vaya generando el cdigo base.
Al igual que PHPUnit, PhpSpec se instala de la misma forma con Composer:

1 composer install --dev phpspec/phpspec

De la misma manera que se ha hecho el ejemplo prctico de PHPUnit aqu tambin se muestra el
ejemplo base obtenido de la documentacin oficial.

1 vendor/bin/phpspec desc Markdown


2 Specification for Markdown created in spec.

Este fichero es la estructura base de una clase xSpec de PhpSpec que se puede autogenerar mediante
el anterior comando.
Captulo 16: testing 141

1 <?php
2
3 namespace spec;
4
5 use PhpSpec\ObjectBehavior;
6 use Prophecy\Argument;
7
8 class MarkdownSpec extends ObjectBehavior
9 {
10 function it_is_initializable()
11 {
12 $this->shouldHaveType('Markdown');
13 }
14 }

Aadimos una nueva especificacin a nuestra clase xSpec del ejemplo:

1 function it_converts_plain_text_to_html_paragraphs()
2 {
3 $this->toHtml("Hi, there")->shouldReturn("<p>Hi, there</p>");
4 }

Y ahora s pasamos el comando de ejecucin de los teses:

1 vendor/bin/phpspec run
2 > spec\Markdown
3 it converts plain text to html paragraphs
4 Class Markdown does not exist.
5 Do you want me to create it for you? [Y/n]

PhpSpec observa que la clase Markdown no existe por lo que nos invita a decirle que el mismo nos
la puede generar. Volvemos a ejecutar el comando y esta vez nos dice que el mtodo toHtml no
existe y nuevamente nos invita a que nos lo cree por nosotros.

1 vendor/bin/phpspec run
2 > spec\Markdown
3 it converts plain text to html paragraphs
4 Method Markdown::toHtml() not found.
5 Do you want me to create it for you? [Y/n]

Todo esto hace que nuestro cdigo de produccin, sin que nosotros hayamos picado nada, sea el
siguiente:
Captulo 16: testing 142

1 <?php
2
3 class Markdown
4 {
5 public function toHtml($argument1)
6 {
7 // TODO: write logic here
8 }
9 }

Si ahora ejecutamos vendor/bin/phpspec run nos mostrar un error como el de abajo que obvia-
mente viene ha decir que debemos implementar el mtodo toHtml para que cumpla los requisitos
que en el spec se ha definido.

1 > spec\Markdown
2 it converts plain text to html paragraphs
3 Expected "<p>Hi, there</p>", but got null.
4 1 examples (1 failed)
5
6 <?php
7
8 class Markdown
9 {
10 public function toHtml()
11 {
12 return "<p>Hi, there</p>";
13 }
14 }

Un vez implementado el mtodo de forma adecuada el resultado final es el siguiente:

1 vendor/bin/phpspec run
2 > spec\Markdown
3 it converts plain text to html paragraphs
4 1 examples (1 passed)

Behat
Si PhpSpec es la herramienta ms propicia para hacer specBDD, entonces Behat es la ms idnea
para la prctica del storyBDD. Como ya se menciona en apartados anteriores, el storyBDD es un
enfoque mucho ms a alto nivel centrndose en un mbito de negocio/funcional.
Captulo 16: testing 143

Una de las limitaciones ms importantes que en ningn sitio se menciona a la hora de generar teses
funcionales y que a m me gusta dejarla clara es que para poder ejecutar esta clase de teses necesitas
una aplicacin como tal, con su punto de entrada, entry point, endpoint, front controller o como
queramos llamarlo, porque sin ello no se puede ejecutar Behat ni nada del estilo. Por ejemplo, si
seguimos nuestro ejemplo que se est construyendo durante todo el libro no habra ningn problema
ya que al ser una aplicacin Symfony ya es una aplicacin web con su front controller que en este
caso sera el app.php o el app_dev.php. Sin embargo, si nosotros creamos un bundle open-source
para utilizar en nuestras futuras aplicaciones Symfony y quisiramos testear funcionalmente ese
bundle deberamos generar nuestro propio bootstrap.php front controller y todo el stack bsico para
que una aplicacin Symfony pudiese funcionar.
Behat tiene dos partes diferenciadas como son la parte del cdigo como tal, que no son ms que
clases PHP con ciertas anotaciones concretas que Behat sabe interpretar en forma de conceptos
propios denominados steps. Por otro lado, existe otra parte que mediante Gherkin, (un lenguaje
muy prximo al humano) se generan los scenarios que contienen steps que a su vez conectan con
el mencionado cdigo PHP.
Exactamente igual que las anteriores herramientas Behat se instala con Composer pero como hay que
instalar varias dependencias aparte del propio Behat recomendamos copiar esas cuatro dependencias
directamente en el apartado de require-dev del composer.json y ejecutar composer update.

1 "require-dev": {
2 (...)
3
4 "behat/behat" : "~3.0",
5 "behat/symfony2-extension" : "~2.0",
6 "behat/mink-extension" : "~2.0",
7 "behat/mink-browserkit-driver": "~1.2"
8 }

A continuacin se muestra un ejemplo muy sencillo de cmo funcionara esta herramienta de


storyBDD. No es tan fcilmente demostrable como las anteriores dos as que debemos mirarlo como
algo ms educativo que real. Si se quiere profundizar ms en el tema aconsejo mirar el siguiente
enlace de la documentacin oficial, aqu94 .
El mtodo PHP perteneciente a la clase CommandContext que como se puede comprobar tiene una
anotacin con @When que es una de las palabras reservadas que el parser de Behat interpreta y conecta
con el cdigo escrito en Gherkin.

94
http://docs.behat.org/en/latest/quick_intro_pt1.html
Captulo 16: testing 144

1 /**
2 * @When /^I run "([^"]*)" interactive command$/
3 */
4 public function iRunInteractiveCommand($command)
5 {
6 $this->loadCommands();
7 $command = $this->application->find($command);
8 $this->tester = new CommandTester($command);
9 $helper = $command->getHelper('dialog');
10 $helper->setInputStream($this->getInputStream('Test\\n'));
11 $this->inputs['command'] = $command->getName();
12 $this->tester->execute($this->inputs, $this->options);
13 $this->initialize();
14 }

El siguiente cdigo Gherkin no tiene demasiado que comentar, personas no tcnicas pueden entender
de forma sencilla lo que cada uno de los scenarios resuelve.

1 @user
2 Feature: Manage users via cli
3 In order to manage users cli
4 As a console
5 I want to be able create a new user
6
7 Scenario: Executing command to create a new user
8 Given the following command inputs:
9 | email | me@aprendesymfony2.com |
10 | username | aprendeSymfony2 |
11 | firstName | Aprende |
12 | lastName | Symfony2 |
13 | password | 123456 |
14 When I run "aprendeSymfony2:user:create" interactive command
15 Then I should see the following output:
16 """
17 A new aprendeSymfony2 user has been created
18 """

El siguiente cdigo YAML corresponde al fichero de configuracin behat.yml donde se definen una
serie de opciones sobre Behat y los directorios y path donde se encuentran los contextos que al
fin de cuentas son las antes mencionadas clases PHP. Importante recalcar el detalle de la existencia
de Behat\Symfony2Extension una extensin para Behat aportando algunos detalles importantes
cuando se utiliza en conjuncin con Symfony2.
Captulo 16: testing 145

1 default:
2 formatters:
3 pretty:
4 verbose: true
5 paths: true
6 snippets: false
7 suites:
8 user:
9 paths:
10 features: src/AppBundle
11 contexts:
12 AppBundle\Behat\Context\CommandContext
13 filters:
14 tags: "@user"
15 extensions:
16 Behat\MinkExtension:
17 sessions:
18 default:
19 symfony2: ~
20 browser_name: firefox
21 show_auto: false
22 Behat\Symfony2Extension: ~

Por ltimo, decir que de forma muy parecida tanto a PHPUnit como a PhpSpec, para ejecutar Behat
es tan simple como:

1 vendor/bin/behat

Integracin continua
Continuous Integration is a software development practice where members of a team
integrate their work frequently, usually each person integrates at least daily - leading
to multiple integrations per day. Each integration is verified by an automated build
(including test) to detect integration errors as quickly as possible.

La anterior cita es la forma que tiene Martin Fowler de definir el concepto de integracin continua. Es
una forma de desarrollo que implica despliegues automticos continuados como forma de encontrar
posibles errores en una fase ms temprana del desarrollo.
A continuacin, se muestran dos de las alternativas ms consolidadas dentro de las herramientas de
integracin continua como son Jenkins y Travis.
Captulo 16: testing 146

Jenkins
Si hay algn software que se pueda asociar directamente al concepto de integracin continua ese es
Jenkins. Escrito en JAVA y corriendo en un servidor que es un contenedor de servlets, como Apache
Tomcat. Se empez a desarrollar sobre el nombre de Hudson y sus dos grandes secretos para el xito
han sido ser la primeros (y encima open-source), y su arquitectura basada en plugins que ha hecho
que su extensin sea sencilla y que a da de hoy Jenkins cuente con plugins prcticamente para todo.

Travis
Otra alternativa, mucho ms actual y conceptualmente muy diferente es Travis, el rey de la
integracin continua para proyectos, libreras, frameworks, etc. open-source. Gran parte del cdigo
albergado en GitHub como abierto y que usan integracin continua utilizan Travis. Su simplicidad
ha sido su clave del xito, ya que mientras Jekins es un paquete que hay que descargar e instalar en
tu sistema configurarlo a tu gusto y dems Travis simplemente con cuatro configuraciones sencillas
en un fichero YAML hace que todo empiece a funcionar como por arte de magia haciendo muy
viable y rpida la integracin continua en este tipo de repositorios. No me voy a extender ms sobre
la configuracin de Travis ya que en el apartado del truco de este captulo se habla largo y tendido
sobre ello.

Truco 16
En el anterior apartado hemos tratado el tema de la integracin continua explicando de forma rpida
y sencilla lo qu es y dos de las herramientas ms utilizadas no solo por la comunidad de PHP
sino por la comunidad de desarrolladores en general. Pues bien, en el decimosexto truco del libro
explicaremos de forma simple como configurar un fichero .travis.yml para que nuestro servidor
de integracin continua favorito nos de un reporte de los tests de Behat y PhpSpec que tenemos en
verde y cuales en rojo de la build en nuestra aplicacin de Symfony2, dndonos va libre (o no) al
deploy.

1 language: php
2 # sirve para decirle a Travis sobre qu lenguaje se est trabajando.
3
4 php:
5 - 5.4
6 - 5.5
7 - 5.6
8 - 7.0
9 - hhvm
10 # indica los diferentes entornos de php en los que se debe ejecutar la build, en\
11 este caso de la versin de PHP 5.4 a la 7.0 y tambin en HipHop Virtual Machine\
Captulo 16: testing 147

12 .
13
14 env:
15 - SYMFONY_VERSION=2.3.*
16 - SYMFONY_VERSION=2.7.*
17 - SYMFONY_VERSION=dev-master
18 # indica los diferentes versiones de Symfony en los que se debe ejecutar
19 # la build de la aplicacin, Symfony 2.3, 2.7 y la dev-master que ahora
20 # mismo se encuentra en la 3.0.
21
22 before_install:
23 - sudo apt-get update
24 - sudo apt-get -qq install ruby-sass
25 # bloque que Travis utiliza para ejecutar comandos antes del proceso de
26 # instalacin del entorno. En este caso se utiliza para actualizar los
27 # paquetes de Ubuntu y para instalar Sass a nivel de sistema operativo.
28
29 install:
30 - sudo apt-get install apache2 libapache2-mod-fastcgi
31 - sudo a2enmod rewrite actions fastcgi alias
32 - echo "cgi.fix_pathinfo = 1" >> ~/.phpenv/versions/$(phpenv version-name)/e\
33 tc/php.ini
34 - sudo cp -f scripts/travis/vhost /etc/apache2/sites-available/default
35 - sudo sed -e "s?%TRAVIS_BUILD_DIR%?$(pwd)?g" --in-place /etc/apache2/sites-\
36 available/default
37 - sudo service apache2 restart
38 - cat /etc/apache2/sites-available/default
39 # quizs, el bloque ms importante de Travis, es donde se prepara el
40 # grueso del entorno para poder hacer ejecutable la build. Al ser un
41 # proyecto PHP requerimos de un servidor web (en este caso Apache).
42
43 - sudo apt-get install node
44 - npm install -g bower
45 - npm install -g gulp
46 - gem install scss-lint
47 - cd src/AppBundle
48 - npm install
49 - bower install
50 - cd -
51 # seguimos en el bloque de install de Travis ejecutaando los comandos
52 # referentes a la instalacin de las herrmientas de front-end.
53
Captulo 16: testing 148

54 before_script:
55 - export MINK_EXTENSION_PARAMS='base_url=http://localhost'
56 - composer self-update
57 - composer require symfony/symfony:${SYMFONY_VERSION}
58 - app/console doctrine:database:create --env=test
59 - app/console doctrine:schema:create --env=test
60 - app/console cache:warmup --env=test
61 - app/console assets:install --symlink
62 - cd src/AppBundle
63 - gulp
64 - cd -
65 # configuraciones propias de la aplicacin. Setear la variable global de
66 # MINK, necesaria para el buen funcionamiento de Behat. Actualizar el
67 # propio composer antes de ejecutar el composer install. Ejecutar los
68 # comandos tipicos de la generacin de la db con Doctrine. Limpiar la
69 # cache con el comando de Symfony y por ltimo generar el enlace simblico
70 # de los assets para despus compilarlos con Gulp.
71
72 script:
73 - bin/phpspec run -fpretty
74 - bin/behat --stop-on-failure --suite=main
75 # el bloque que realmente ejecuta nuestros teses. Tan simple como decirle
76 # que ejecute el comando tpico de PhpSpec como el de Behat. (A nivel
77 # personal soy bastante fan del BDD as que en mi da a da suelo utilizar
78 # el stack PhpSpec + Behat para cubrir los teses de mis apps, sin embargo
79 # si te has decantado por PHPUnit como suite de testing tu solucin
80 # debera ser tan sencilla como cambiar los anteriores comandos por el que
81 # comando que ejecuta los teses en PHPUnit).
82
83 matrix:
84 allow_failures:
85 - php: hhvm
86 - php: 7.0
87 - env: SYMFONY_VERSION=dev-master
88 # bloque matrix que echando un ojo a la documentacin de Travis contiene
89 # una serie de subapartados pero que por el momento y como ms interesante
90 # est el "allow failures" que como se puede intuir permite que tanto los
91 # entornos de PHP como los de Symfony bajo este apartado admitan fallos en
92 # sus ejecuciones manteniendo pese al error la build en verde. Esto suele
93 # ser muy util para probar versiones venideras de PHP como es el caso
94 # actual de PHP7 o la rama master de Symfony.
Captulo 16: testing 149

Conclusin
En este captulo hemos podido apreciar una breve pero clara introduccin a todo el mundo que rodea
al testing desde la visin de una aplicacin Symfony. Hemos analizado las diferentes corrientes del
testing y sus principales herramientas en PHP; hemos dado un par de pinceladas conceptuales sobre
la integracin continua nombrando las dos alternativas ms famosas como son Jenkins y Travis y
por ltimo, el truco, aprovechando el enfoque open source de este libro e intentando englobar en un
ejemplo prctico varios aspectos de este capitulo se ha mostrado un fichero .travis.yml completo de
una aplicacin Symfony.
El objetivo no ha sido ms que mostrar un punto de entrada por el que poder seguir aprendiendo so-
bre algo tan importante como es el testing. Como no era la misin del libro centrarse exclusivamente
en ello para los ms inquietos sobre este tema recomiendo un muy buen libro escrito por Fernando
Arconada titulado Testing para Aplicaciones Symfony295 .
95
https://leanpub.com/testingsymfony2
Eplogo
Nuestro viaje llega a su fin tras ms de 150 intensas pginas llenas de cdigo y, sobre todo, ganas
de aprender. Esto es solo el comienzo avanzado de algo muchsimo ms grande, pero iremos por
pasos ya que lo importante es afianzar estos conocimientos y dominarlos al mximo. Es posible que
en futuros libros escriba ms tecnologas y otras formas de trabajar en las que los profesionales de
hoy en da nos basamos, pero me gustara despedirme con algunas cosas que conozco y algunas que
he estado leyendo ltimamente y que considero interesantes para que les eches un vistazo.

Bower
En ingls se suele decir un must have hasta hace un tiempo. Es un gestor de paquetes para
la web. Y dirs otro? Efectivamente, otro ms, pero ste debes usarlo para tus libreras de
front: jQuery, Foundation, carruseles, etc. Mediante el archivo bower.json podrs establecer las
dependencias de front que quieras en la versin que necesites, mientras que gracias al archivo
.bowerrc podras situar estas libreras descargadas en la ruta correcta (ms te vale que las dejes en
app/Resources/assets/vendor/ o me enfadar). Se acab el tener que estar bajando manualmente
libreras e ir buscando versiones en pginas de descargas poco amigables. Eso s: puedes usar npm
para front tambin.

Gassetic
Assetic es esa herramienta que te he dicho que no uses pero que hemos usado para ver didcticamente
como funciona. Mucha gente te dir lo mismo que yo: usa una herramienta de tareas para realizar
esta accin. Sin embargo, hay gente que le tiene un cario especial a Assetic y por eso han creado
Gassetic96 . No he tenido tiempo para utilizarla as que, si la incorporas en tu proyecto, hazmelo llegar
para incluir una pequea seccin en el libro.

Gulp con JavaScript


Sin duda, es algo que te dejo como deberes bueno no del todo, ya que te doy la solucin un poco ms
adelante. Quitar Assetic para mejorar nuestro Gulp es algo bsico en un proyecto web profesional.
Espero que consigas hacerlo sin mirar la solucin ya que eso demostrara que has entendido bien
gran parte de este libro.
96
https://github.com/romanschejbal/gassetic
Eplogo 151

PostCSS
PostCSS es una API con una gran cantidad the plugins que utilizan dicha API para realizar un
montn de cosas diferentes. No te fijes en el nombre de la tecnologa porque esta repleta de
habilidades impresionantes, incluso de preprocesado. Escribiendo testas lneas, mucha gente esta
creando ms y ms mdulos de PostCSS como AUtoprefixer, cssnext, grid, Casi cualquier cosa que
puedas imaginar para el procesamiento de CSS. Tambin es la solucin ms rpida de las actuales,
y por eso mucha gente se est cambiando de Sass a PostCSS. Puedes navegar entre varios de sus
plugins aqu97

Desarrollos hechos con Symfony


Si ests cansado del tpico WordPress para tus desarrollos pero tampoco quieres empezar de cero
con un Symfony, aqu te dejo un post98 en el que puedes leer sobre varios CMS que estn realizados
sobre Symfony. Yo he probado varios de ellos y, a pesar de que no tienen una madurez como la tiene
WordPress, alguno de ellos tiene una trayectoria sorprendente.
Me gustara aadir un sistema de comercio electrnico genial hecho con Symfony. Sylius99 es una
plataforma preparada para construir y escalar webs de ecommerce de una forma fcil y sencilla,
gracias a su estructura basada en componentes.

DDD
Hemos hablado de TDD y de BDD, pero no puedes terminar este libro sin investigar sobre DDD. Sin
duda, si quieres llegar a ser un gran programador, todo lo que rodea a DDD y patrones de diseo
debe ser familiar para ti. Esto te va a costar algo ms de lo esperado, pero el resultado no te va a
dejar indiferente.

Si te ha gustado
Con esta seccin termino este libro. He estado muchas semanas escribiendo cada lnea de l con
toda la pasin con la que voy a dar cada una de mis clases. Considero que no debo poner un precio
a lo que yo te enseo y que debes ser t el que decida lo que vale este libro que has ledo. Si te ha
gustado y crees que debes agradecer mi trabajo, ya sabes que puedes volver a la pgina de leanpub
y pagar por l, sabiendo que el 50% de lo que pagues ir destinado a Watchi.
97
http://postcss.parts/
98
http://symfony.es/noticias/2014/08/15/un-repaso-a-los-cms-basados-en-symfony/
99
http://sylius.org
Eplogo 152

Agradecimientos al lector
La parte ms importante del libro eres tu, as que te agradezo que hayas ledo el libro, y te lo
agradecer an ms si recibo feedback sobre l, tanto bueno como malo. Gracias lector o lectora
por haber pasado un rato de tu preciado tiempo conmigo tras estas lneas. Espero que gracias a ellas
te hayas convertido en mejor desarrollador o desarrolladora.

Vous aimerez peut-être aussi