Académique Documents
Professionnel Documents
Culture Documents
Jon Torrado
Este libro est a la venta en http://leanpub.com/aprendesymfony2
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.
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 5: MopaBootstrap . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29
Instalando MopaBootstrapBundle . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30
Preparando la plantilla base . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 31
Truco 5 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 36
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 38
Filtro slugabble . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 79
Filtro softdeleteable . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81
Otros filtros . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83
Truco 9 . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
Resumen . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 84
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
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
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:
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:
Genial! Ahora que ya dispones del instalador de forma global, ya puedes crear tu primer proyecto
Symfony. Para ello, ejecuta el siguiente comando:
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:
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 "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
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.
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:
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!
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!
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:
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
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:
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:
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
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:
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 }
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:
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!
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 ],
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
1 bin/console server:run
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.
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
Cuando hayas terminado de celebrarlo, ejecuta los cambios en tu base de datos para que se cree la
estructura:
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.
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.
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(),
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 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:
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>© <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
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 }
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:
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
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.
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:
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:
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:
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.
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:
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:
Por supuesto, tenemos que modificar la tarea de Gulp. Editamos gulpfile.js con la nueva funciona-
lidad:
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:
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:
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:
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(),
1 assetic:
2 debug: '%kernel.debug%'
3 use_controller: '%kernel.debug%'
4 filters:
5 cssrewrite: ~
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.
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 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:
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 }
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;
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.
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
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 <?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 }
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
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:
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 }
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
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:
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-).
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 }
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 %}
tenerlo todo en componentes nos premitir reutilizar el cdigo sin repetirlo. La plantilla contact_-
form.html.twig contiene lo siguiente:
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:
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:
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
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 }
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 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
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:
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:
Y, por supuesto, como hemos aadido nuevas columnas, tenemos que actualizar nuestro esquema
de base de datos:
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:
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.
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
Vamos a verlo en funcionamiento. De nuevo, editamos nuestro ProductAdmin para aadir el nuevo
campo en el listado:
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:
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();
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:
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
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: ~
1 liip_imagine:
2 filter_sets:
3 my_thumb:
4 filters:
5 rotate: { angle: 90 }
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 }
1 liip_imagine:
2 filter_sets:
3 aupa_thumbnail:
4 filters:
5 thumbnail: { size: [60,60], mode: outbound }
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.
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
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 }
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:
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:
Siguiente paso: crea la tarea Gulp para que te chive los fallos existentes.
Captulo 12: Gulp (2) 100
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
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:
El siguiente punto es configurar la tarea de Gulp que te alertar de los errores en tus ficheros
JavaScript:
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?
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.
Como siempre, crea la tarea de gulp que ejecutar este mdulo recin instalado:
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:
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.
Como te imaginars, necesitas un elemento ms en esta receta. Paso nmero dos: crear la tarea
gulp utilizando los nuevos ingredientes.
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?
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:
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:
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:
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.
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
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:
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"
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
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.
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
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!
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 %}
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.
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:
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.
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 }
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
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.
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
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
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:
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:
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.
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:
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.
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 :).
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.
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:
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:
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.
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 }
1 function it_converts_plain_text_to_html_paragraphs()
2 {
3 $this->toHtml("Hi, there")->shouldReturn("<p>Hi, there</p>");
4 }
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 }
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 }
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.
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
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.