Vous êtes sur la page 1sur 262

Desarrollo de aplicaciones web con

Symfony2
version 1.0
Juan David Rodrguez Garca
16 de Julio de 2012
Contenido
Curso: Desarrollo de Aplicaciones Web con Symfony2 1
Licencia 2
Unidad 1. Inmersin 3
Aplicaciones web 3
Desarrollo rpido y de calidad de aplicaciones web 4
Presentacin del curso 5
A quin va dirigido 5
Objetivos del curso 6
Plan del curso 7
Sobre Symfony2 8
La documentacin de Symfony2 8
Comentarios 9
Unidad 2: Desarrollo de una aplicacin web siguiendo el patrn MVC 10
El patrn MVC en el desarrollo de aplicaciones web 10
Descripcin de la aplicacin 11
Diseo de la aplicacin (I). Organizacin de los archivos 12
Diseo de la aplicacin (II). El controlador frontal 12
Construccin de la aplicacin. Vamos al lo. 13
Creacin de la estructura de directorios 13
El controlador frontal y el mapeo de rutas 14
Las acciones del Controlador. La clase C o n t r o l l e r . 16
La implementacin de la Vista. 19
Las plantillas PHP 19
El layout y el proceso de decoracin de plantillas 20
El Modelo. Accediendo a la base de datos 24
La configuracin de la aplicacin 26
Incorporar las CSS's 27
La base de datos 27
Comentarios 28
Unidad 3: Symfony2 a vista de pjaro 29
Qu es Symfony2? 29
Instalacin y configuracin de Symfony2 30
El directorio web 32
El directorio a p p 33
El directorio v e n d o r 34
El directorio s r c 35
El directorio b i n 35
Los Bundles: Plugins de primera clase 35
La aplicacin gestin de alimentos en Symfony2 36
Generacin de un Bundle 36
Anatoma de un Bundle 38
Flujo bsico de creacin de pginas en Symfony2 40
Definicin de las rutas del bundle 41
Creacin de la accin en el controlador 43
Creacin de la plantilla 45
Decoracin de la plantilla con un layout 46
Instalacin de los assets de un bundle 50
Implementamos el resto de la aplicacin 51
La unidad en chuletas 61
Generar un bundle 61
Registrar un bundle 61
Enlazar el routing de un bundle con el routing general de la
aplicacin
62
Pasos para acoplar un bundle al framework 62
Flujo para la creacin de pginas en Symfony2 62
Nombres lgicos de acciones 62
Sintaxis bsica de twig 62
Herencia en plantilla twig 62
Funcin p a t h de twig 63
Iterar una coleccin (array) de datoso en twig 63
Cdigo condicional en twig 63
Inclusin de plantillas en twig 63
Estructura bsica de una ruta 63
Comentarios 63
Unidad 4: Inyeccin de Dependencias 64
Primer paso: inyeccin de dependencias 64
Segundo paso: el contenedor de dependencias 65
Integracin de la clase M o d e l como un servicio de Symfony2 67
El Contenedor de Servicios de Symfony2 68
Y qu hemos ganado con todo esto? 71
Servicios al poder! 74
Ms servicios an 77
La unidad en chuletas 78
Comentarios 79
Unidad 5: Desarrollo de la aplicacin MentorNotas (I). Anlisis 80
Descripcin de la aplicacin 80
Descripcin general 81
Catlogo de requisitos 81
Requisitos del frontend 81
Requisitos del backend 82
Gestin de usuarios 82
Gestin de publicidad 82
Gestin de planes de pagos 82
Modelo de datos 83
Descripcin de los procesos 83
Registro de nuevos usuarios 83
Creacin de una nota 83
Contratacin de una cuenta premium 84
Presentacin de la publicidad 84
Interfaz de usuario 84
Pantalla de login 84
Pantalla de registro 84
Pantalla principal (inicio) 85
Pantalla de creacin de notas 85
Pantalla de modificacin de notas 85
Pantalla de planes de pago 86
Men de la aplicacin de administracin 86
Gestin de entidades 86
Listado de usuarios 86
Creacin de usuarios 87
Edicin de usuarios 87
Recursos para la construccin de la aplicacin 87
Conclusin 88
Comentarios 88
Unidad 6: Desarrollo de la aplicacin MentorNotas (II). Rutas y
Controladores
89
Lo primero: el bundle 89
Definimos las rutas y sus acciones asociadas 91
Diseo de la lgica de control para las acciones del controlador
N o t a s C o n t r o l l e r
93
Diseo de la lgica de control para las acciones del controlador
L o g i n C o n t r o l l e r
94
Diseo de la lgica de control para las acciones del controlador
C o n t r a t o s C o n t r o l l e r
94
Implemetacin de la lgica de control del controlador
N o t a s C o n t r o l l e r
94
La accin i n d e x A c t i o n ( ) 100
La accin n u e v a A c t i o n 100
La accin e d i t a r A c t i o n 101
La accin b o r r a r A c t i o n ( ) 102
Las acciones m i E s p a c i o A c t i o n ( ) y r s s A c t i o n 102
Implemetacin de las plantillas del controlador N o t a s C o n t r o l l e r 102
La unidad en chuletas 105
Requerimientos en el Routing 105
Generando rutas 106
Servicio Request 106
Servicio Session 106
Ampliando el cdigo de un bloque heredado en una plantilla twig 107
Generando redirecciones desde un controlador 107
Haciendo forwarding a otra accin 107
Comentarios 107
Unidad 7: Desarrollo de la aplicacin MentorNotas (III). El modelo y la
persistencia de datos.
108
El Object Relational Mapping (ORM) 108
Las entidades 109
Construccin de las entidades. El generador de entidades de
Symfony2
111
Creacin de la base de datos 118
El servicio de persistencia Doctrine2 119
Refinamos el modelo. Las asociaciones entre objetos 125
La relacin One to Many 126
La relacin Many to One 127
La relacin Many to Many 127
Ms all de los mtodos f i n d B y . El lenguaje DQL 132
Organizamos las consultas en repositorios 135
Incorporamos el modelo a la aplicacin 138
La unidad en chuletas 139
Construir entidades 139
Configuracin de la base de datos 139
Crear la base de datos y las tablas 139
Persistir un objeto en la base de datos 140
Recuperar objetos con mtodos f i n d 140
Borrar un objeto 140
Especificacin de las relaciones entre entidades (forma simple y
prctica)
141
Implementacin de un mtodo de repositorio 142
Comentarios 142
Unidad 8: Desarrollo de la aplicacin MentorNotas (IV). Validacin y
Formularios
143
El servicio de validacin 143
El servicio de formularios 149
Formularios definidos en la accin 150
Definicin de tipos para crear formularios 154
Validacin de formularios 156
La unidad en chuletas 160
El servicio de validacin 160
El servicio de formularios 161
Mtodo para la validacin de formularios. 163
Comentarios 164
Unidad 9: Desarrollo de la aplicacin MentorNotas (V). Seguridad -
Autentificacin y Autorizacin
165
Componentes y funcionamiento del servicio de seguridad de
Symfony2.
165
Los proveedores de usuarios (user providers) 166
Los cortafuegos (firewalls) 166
El control de acceso (access control) y la jerarqua de roles (role
hierarchy)
167
Los codificadores (encoders) 168
La seguridad en accin 168
El proveedor de usuarios in memory 169
Autentificacin 170
Autentificar con HTTP Basic 171
Autentificar con un formulario de Login tradicional 173
Saliendo de la sesin (logout) 176
Autorizacin 177
Protegiendo los recursos de la aplicacin 177
Protegiendo los controladores y plantillas de la aplicacin 178
Exigiendo canal seguro 179
La base de datos como proveedor de usuarios 179
El servicio de codificacin. Los e n c o d e r s 184
Recuperando el objeto Usuario 185
La unidad en chuletas 186
Users providers 186
Cortafuegos 187
Autorizacin 188
Encoders 188
Comentarios 189
Unidad 10: Desarrollo de la aplicacin MentorNotas (VI). Esamblando todo
el frontend
190
La eclosin de la crislida 190
Primero los activos (assets) 191
Despus las plantillas 191
Adaptacin de las pantallas del panel de notas 193
El layout del panel de notas 193
Las acciones del panel de notas 196
El proceso de registro 207
El resto de la aplicacin. 214
Unidad 11: Desarrollo de la aplicacin MentorNotas (VII). Desarrollo del
backend
215
Estrategias de desarrollo para la parte de administracin 215
El generador de mdulos CRUD de Symfony2 216
El generador de mdulos CRUD SonataAdminBundle 217
Instalacin del SonataAdminBundle 217
Inyeccin de los mdulos de administracin 221
Entidad Contrato 223
Entidad Grupo 225
Entidad Publicidad 226
Entidad U s u a r i o 230
Ejercicios del Curso: Desarrollo de Aplicaciones Web con Symfony2 235
Ejercicios de la unidad 2 235
Ejercicio 1 235
Ejercicio 2 235
Ejercicio 3 235
Ejercicio 4 235
Ejercicio 5 235
Ejercicio 6 235
Ejercicios de la unidad 3 235
Ejercicio 1 236
Ejercicio 2 236
Ejercicio 3 236
Ejercicio 4 237
Ejercicio 5 237
Ejercicios de la unidad 4 237
Ejercicio 1 237
Ejercicio 2 237
Ejercicios de la unidad 5 238
Ejercicio 1 238
Ejercicio 2 238
Ejercicio 3 238
Ejercicio 4 238
Ejercicios de la unidad 6 239
Ejercicio 1 239
Ejercicio 2 239
Ejercicio 3 241
Ejercicios de la unidad 7 242
Ejercicio 1. Fixtures 242
Instalacin del bundle DoctrineFixturesBundle 242
Creacin de los fixtures 244
Instalacin manual del bundle (sin utilizar b i n / v e n d o r ) 248
Ejercicio 2 248
Ejercicio 3 248
Ejercicios de la unidad 9 248
Ejercicio 1 249
Ejercicio 2 249
Ejercicio 3 249
Ejercicio 4 249
Ejercicios de la unidad 10 249
Ejercicio 1 250
Ejercicio 2 250
Ejercicio 3 250
Indices y tablas 251
Indices y tablas 252
Curso: Desarrollo de Aplicaciones Web con
Symfony2
Contenidos:
Curso: Desarrollo de Aplicaciones Web con Symfony2
1
Licencia
Este trabajo:
Desarrollo de Aplicaciones web con Symfony2
ha sido elaborado por Juan David Rodrguez Garca (juandavid.rodriguez@ite.educacion.es)
y se encuentra bajo una Licencia:
Creative Commons Reconocimiento-NoComercial-CompartirIgual 3.0 Unported.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Licencia
2
Unidad 1. Inmersin
Aplicaciones web
La World Wide Web es un sistema de documentos de hipertexto o hipermedios enlazados y
distribuidos a travs de Internet. En sus comienzos, la interaccin entre los usuarios de la
WWW y sus servidores era muy reducida: a travs de un software cliente denominado
navegador web, el usuario se limitaba a solicitar documentos a los servidores y estos
respondan a aquellos con el envo del documento solicitado. A estos documentos que
circulaban por el espacio web se les denomin pginas web. Cada recurso, conocido como
pgina web, se localiza en este espacio mediante una direccin nica que describe tanto al
servidor como al recurso: la URL. Cada pgina web puede incorporar las URL's de otras
pginas como parte de su contenido. De esta manera se enlazan unas con otras.
Han pasado casi 20 aos desde la aparicin de la Word Wide Web y, aunque en esencia su
funcionamiento, basado en el protocolo HTTP, sigue siendo el mismo, la capacidad de
interaccin entre usuarios y servidores se ha enriquecido sustancialmente. De la pgina web
hemos pasado a la aplicacin web; un tipo de aplicacin informtica que adopta, de manera
natural, la arquitectura cliente-servidor de la web. De manera que en las peticiones al
servidor, el usuario no slo solicita un recurso, si no que adems puede enviar datos. El
servidor los procesa y elabora la respuesta que corresponda en funcin de ellos. Es decir, el
servidor construye dinmicamente la pgina web que le enva al cliente.
Todo el peso de la aplicacin reside en el servidor, mientras que el cliente, esto es, el
navegador web, se limita a presentar el contenido que recibe mostrndolo al usuario.
Esta evolucin comenz con la aparicin de los CGI's, que son aplicaciones escritas en
cualquier lenguaje de programacin y que pueden ser accedidas por el servidor web a
peticin del cliente, y ha madurado gracias a la aparicin de los lenguajes de programacin
del lado del servidor, como PHP, Java o Python, gracias a los cuales los servidores web
(apache como ejemplo ms conocido y usado) han ampliado su funcionalidades; ya no slo
son capaces de buscar, encontrar y enviar documentos a peticin del cliente, si no que
tambin pueden procesar peticiones (acceder a base de datos, realizar peticiones a otros
servidores, ejecutar algoritmos, ) y construir los documentos que finalmente sern
enviados al cliente en funcin de los datos que este les ha proporcionado.
Tambin es relevante en esta evolucin de la web la incorporacin de procesamiento en los
navegadores web mediante lenguajes de scripting como javascript, que permiten la
ejecucin de ciertos procesos (casi todos relacionados con la manipulacin de la interfaz
grfica) en el lado del cliente. De hecho, en la actualidad existen aplicaciones que delegan
gran parte de sus procesos al lado del cliente, aunque de todas formas, todo el cdigo es
proporcionado desde la parte servidora la primera vez que se solicita el recurso.
Todo esto ha sido bautizado con el omnipresente y manido trmino de Web 2.0, que en
realidad es una manera de referirse a este aumento de la capacidad de interaccin con el
usuario, y que ha permitido el desarrollo y explosin de las redes sociales y la blogosfera
entre otros muchos fenmenos de la reducida pero incesante historia de la World Wide Web.
El panorama actual se resume en un inters creciente por las aplicaciones web, hasta el
punto de que, en muchos casos, han desplazado a la madura aplicacin de escritorio. Son
varias las razones que justifican este hecho y, aunque se trata de un tema que por su
amplitud no abordaremos en detalle, si que sealaremos algunas:
Se mejora la mantenibilidad de las aplicaciones gracias a su centralizacin. Al residir la
aplicacin en el servidor, desaparece el problema de la distribucin de las mismas. Por
ejemplo, los cambios en la interfaz de usuario son realizado una sola vez en el servidor
y tienen efecto inmediatamente en todos los clientes.
Unidad 1. Inmersin
3
Se aumenta la capacidad de interaccin y comunicacin entre los usuarios de la misma,
as como de su gestin.
Al ser HTTP un protocolo de comunicacin ligero y sin conexin (conectionless) se
evita mantener conexiones abiertas con todos y cada uno de sus clientes, mejorando la
eficiencia de los servidores.
Para utilizar la aplicacin, los usuarios tan solo necesitan tener instalado un software
denominado navegador web (browser). Esto reduce drsticamente los problemas de
portabilidad y distribucin. Tambin permite que terminales ligeras, con poca capacidad
de proceso, puedan utilizar grandes aplicaciones ya que su funcin se limita a mostrar
mediante el navegador los datos que le han sido enviado.
El desarrollo de dispositivos mviles con conectividad a redes expande el dominio de
uso de las aplicaciones web y abre nuevos mercados.
Se puede acceder a la aplicacin desde cualquier punto con acceso a la red donde
preste servicio la aplicacin. Si se trata de Internet, desde cualquier parte del mundo, si
se trata de una intranet desde cualquier parte del mundo con acceso a la misma. Todo
ello sin necesidad de instalar nada ms que el navegador en la computadora cliente
(punto anterior).
Los lenguajes utilizados para construir las aplicaciones web son relativamente fciles de
aprender. Adems algunos de los ms utilizados ,como PHP y Python, se distribuyen con
licencias libres y existe una gran cantidad de documentacin de calidad disponible en la
propia red Internet.
Recientemente han aparecido en escena varias plataformas y frameworks de desarrollo
(por ejemplo Zend Framework, CakePHP, symfony, Symfony2) que facilitan la
construccin de las aplicaciones web, reduciendo el tiempo de desarrollo y mejorando la
calidad.
Obviamente no todo son ventajas; incluso algunas de las ventajas que hemos sealado
pueden convertirse en desventajas desde otros puntos de vista. Por ejemplo:
el hecho de que los cambios realizados en una aplicacin web sean efectivos
inmediatamente en todos los clientes que la usan, puede dejar sin servicio a un gran
nmero de usuarios si este cambio provoca un fallo (intencionado si se trata de un
ataque, o no intencionado si se trata de una modificacin que no ha sido debidamente
probada). Esto repercute en la necesidad de aumentar las precauciones y la seguridad
por parte de los responsables tcnicos que mantienen la aplicacin.
La disponibilidad de la aplicacin es completamente dependiente de la disponibilidad de
la red. As la aplicacin web ser til en entornos donde se garantice la estabilidad de la
red. Los programadores necesitan dominar las distintas tecnologas y conceptos que, en
estrecha colaboracin, conforman la aplicacin (HTTP, HTML, XML, CSS, javascript,
lenguajes de scripting del lado del servidor como PHP, Java o Python, )
La triste realidad de las incompatibilidades entre navegadores.
No obstante, la realidad demuestra que el inters por las aplicaciones web es un hecho
consumado, lo cual seduce a los programadores de todo el mundo a formarse en las
tecnologas y estrategias que permiten desarrollarlas. Este curso tiene como objetivo
presentar una de las ms exitosas: el desarrollo de aplicaciones web mediante el uso del
framework Symfony2.
Desarrollo rpido y de calidad de aplicaciones web
La experiencia adquirida tras muchos aos de construccin de aplicaciones informticas de
escritorio, dio lugar a la aparicin de entornos y frameworks de desarrollo que no solo hacan
posible construir rpidamente las aplicaciones, si no que adems cuidaban la calidad de las
Desarrollo rpido y de calidad de aplicaciones web
4
mismas. Es lo que tcnicamente se conoce como Desarrollo Rpido de Aplicaciones.
Sin embargo, el desarrollo de aplicaciones web es muy reciente, por lo que estas
herramientas de desarrollo rpido y de calidad no han aparecido en el mundo de la web
hasta hace bien poco. De hecho la construccin de una aplicacin web de calidad no ha
estado exenta de dificultades debido a esta carencia. Afortunadamente contamos desde
hace unos pocos aos con frameworks de desarrollo de aplicaciones web que facilitan el
desarrollo de las mismas y estn haciendo que el concepto de Desarrollo Rpido de
Aplicaciones en este campo sea una realidad. Symfony2 representa una de las herramientas
de ms xito para la construccin de aplicaciones web de calidad con PHP.
Desarrollar con Symfony2 hace ms sencillo la construccin de aplicaciones web que
satisfagan las siguientes caractersticas, deseables en cualquier tipo de aplicacin
informtica profesional al margen de sus requisitos especficos.
Fiabilidad. La aplicacin debe responder de forma que sus resultados sean correctos y
podamos fiarnos de ellos. Tambin implica que los datos que introducimos como
entrada sean debidamente validados para asegurar un comportamiento correcto.
Seguridad. La aplicacin debe garantizar la confidencialidad y el acceso a la misma a
usuarios debidamente autentificados y autorizados. En el caso de las aplicaciones web
esto es especialmente importante puesto que residen en computadores que, al
pertenecer a una red, son accesibles a una gran cantidad de personas. Lo que significa
que inevitablemente estn expuestas a ser atacadas con fines maliciosos. Por ello
deben incorporar mecanismos de proteccin ante conocidas tcnicas de ataque web
como puede ser el Cross Site Scripting (XSS).
Disponibilidad. La aplicacin debe prestar servicio cuando se le solicite. Es importante,
por tanto, que los cambios requeridos por operaciones relacionadas con el
mantenimiento (actualizaciones, migraciones de datos, migraciones de la aplicacin a
otros servidores, etctera) sean sencillos de controlar. De esa manera se evitarn largas
temporadas de inactividad. La disponibilidad es una de las caractersticas ms valoradas
en las aplicaciones web, ya que el funcionamiento de la misma no depende, por lo
general, de sus usuarios si no de los responsables tcnicos del sistema donde se
encuentre alojada. Hay que pensar en ellos y ponrselo fcil cuando necesiten realizar
este tipo de tarea. Tambin es importante que los errores de funcionamiento debidos a
errores de programacin (bugs) sean rpidamente diagnosticados y resueltos para
mejorar tanto la disponibilidad como la fiabilidad de la aplicacin.
Mantenibilidad. A medida que se usa una aplicacin, aparecen nuevos requisitos y
funcionalidades que se desean ofrecer. Un sistema mantenible permite ser extendido
sin que ello suponga un coste muy alto, minimizando la probabilidad de introducir
errores en los aspectos que ya estaban funcionando antes de emprender la
implementacin de nuevas caracterstcas.
Escalabilidad, es decir, que la aplicacin pueda ampliarse sin perder calidad en los
servicios ofrecidos, lo cual se consigue disendola de manera que sea flexible y
modular.
Presentacin del curso
A quin va dirigido
Este curso va dirigido a personas que ya cuenten con cierta experiencia en la programacin
de aplicaciones web. A pesar de que Symfony2 est construido sobre PHP, no es tan
importante conocer dicho lenguaje como estar familiarizado con las tecnologas de la web y
con el paradigma de la programacin orientada a objetos.
Presentacin del curso
5
En la confeccin del curso hemos supuesto que el estudiante comprende los fundamentos de
las tecnologas que componen las aplicaciones web y las relaciones que existen entre ellas:
El protocolo HTTP y los servidores web,
Los lenguajes de marcado HTML y XML,
Las hojas de estilo CSS's,
Javascript como lenguaje de script del lado del cliente,
Los lenguajes de script del lado del servidor (PHP fundamentalmente),
Los fundamentos de la programacin orientada a objetos (mejor con PHP),
Los fundamentos de las bases de datos relacionales y los sistemas gestores de base de
datos.
Obviamente, para seguir el curso, no hay que ser un experto en cada uno de estas
tecnologas, pero s es importante conocerlas hasta el punto de saber cual es el papel que
desempea cada una y como se relacionan entre s. Cualquier persona que haya
desarrollado alguna aplicacin web mediante el archiconocido entorno LAMP o WAMP
(Linux/Windows Apache MySQL PHP), debera tener los conocimientos necesarios para
seguir con provecho este curso.
Objetivos del curso
Cuando finalices el curso habrs adquirido suficiente conocimiento para desarrollar
aplicaciones web mediante el empleo del framework de desarrollo en PHP Symfony2. Ello
significa a grandes rasgos que sers capaz de construir aplicaciones web que:
Son altamente modulares, extensibles y escalables.
Separan claramente la lgica de negocio de la presentacin, permitiendo que el trabajo
de programacin y de diseo puedan realizarse independientemente.
Incorporaran un sistema sencillo, flexible y robusto garantizar la seguridad a los niveles
de autentificacin y autorizacin.
Acceden a las bases de datos a travs de una capa de abstraccin que permite cambiar
de sistema gestor de base de datos sin ms que cambiar un parmetro de
configuracin. No es necesario tocar ni una sola lnea de cdigo para ello.
Cuentan con un flexible sistema de configuracin mediante el que se puede cambiar
gran parte del comportamiento de la aplicacin sin tocar nada de cdigo. Esto permite,
entre otras cosas, que se puedan ejecutar en distintos entornos: de produccin, de
desarrollo y de pruebas, segn la fase en la que se encuentre la aplicacin.
Pueden ofrecer el resultado final en varios formatos distintos (HTML, XML, JSON, RSS,
txt, ) gracias al avanzado sistema de generacin de vistas,
Cuentan con un potente sistema de gestin de errores y excepciones, especialmente
til en el entorno de desarrollo.
Implementan un sistema de cach que disminuye los tiempos de ejecucin de los
scripts.
Incorpora por defecto mecanismos de seguridad contra ataques XSS y CSRF.
Pueden ser internacionalizadas con facilidad, aunque la aplicacin no se haya
desarrollado con la internacionalizacin como requisito.
Incorporan un sistema de enrutamiento que proporciona URL's limpias, compuestas
exclusivamente por rutas que ocultan detalles sobre la estructura de la aplicacin.
Objetivos del curso
6
Cuentan con un avanzado sistema de autentificacin y autorizacin.
El curso cubre una porcin suficientemente completa sobre las mltiples posibilidades que
ofrece Symfony2 para desarrollar aplicaciones web, incidiendo en sus caractersticas y
herramientas ms fundamentales. El objetivo es que, al final del curso, te sientas cmodo
usando Symfony2 y te resulte sencillo y estimulante continuar profundizando en el
framework a medida que tu trabajo lo sugiera.
Cuando emprendas el estudio de este curso, debes tener en cuenta que el aprendizaje de
cualquier framework de desarrollo de aplicaciones, y Symfony2 no es una excepcin, es
bastante duro en los inicios. Sin embargo merece la pena, pues a medida que se van
asimilando los conceptos y procedimientos propios del framework, la productividad aumenta
muchsimo.
Plan del curso
Para conseguir los objetivos que nos hemos propuesto hemos optado por un planteamiento
completamente prctico en el que se est picando cdigo funcional desde el principio del
curso.
En la unidad 2, sin utilizar Symfony2 para nada, desarrollamos una sencilla aplicacin web
en PHP. El objetivo de esta unidad es mostrar como se puede organizar el cdigo para que
siga los planteamientos del patrn de diseo Modelo Vista Controlador (MVC), gracias al
cual separamos completamente la lgica de negocio de la presentacin de la informacin. Es
importante comprender los fundamentos de esta organizacin ya que las aplicaciones
desarrolladas con Symfony2 suelen seguir este patrn. Adems en esta unidad se introducen
los conceptos de controlador frontal, accin, plantilla y layout, ampliamente usados en el
resto del curso.
En la unidad 3 hacemos una presentacin panormica de Symfony2, exponiendo los
conceptos fundamentales. En esta unidad volveremos a escribir, esta vez utilizando
Symfony2, la aplicacin de la unidad 2. Dicho ejercicio nos ayudar a realizar la presentacin
del framework a la vez que servir como referencia para los conceptos bsicos. Avisamos:
esta unidad es bastante densa.
La unidad 4 la hemos dedicado completamente al estudio de un importante patrn de
diseo en torno al cual se ha construido Symfony2: La inyeccin de dependencias. Es muy
importante comprender este concepto para sentirse cmodo con Symfony2 y para poder
extender el framework con soltura.
En la unidad 5 planteamos el anlisis de una aplicacin, que an siendo concebida con
criterios pedaggicos, es suficientemente amplia como para ser considerada una aplicacin
profesional. Se trata de de una aplicacin para la gestin de notas (al estilo de los post-it)
inspirada en EverNote una herramienta que ltimamente est teniendo mucho xito entre
los usuarios de plataformas mviles (smartphones y tabletas). Su desarrollo nos servir
como vehculo para penetrar al interior de Symfony2 durante el resto del curso.
En las siguientes unidades se construyen progresivamente las distintas funcionalidades de la
aplicacin analizada en la unidad 5. Cada unidad incide sobre algn aspecto fundamental de
Symfony2.
En la unidad 6 profundizamos en el concepto de routing y los controladores, conceptos
fundamentales sobre los que descansa la lgica de las aplicaciones construidas con
Symfony2.
En la unidad 7 se realiza un estudio bastante detallado del ORM Doctrine2 para el
tratamiento de los datos persistentes, es decir, para el acceso a bases de datos.
Adelantamos aqu que, a pesar de cubrir los aspectos fundamentales para el desarrollo casi
cualquier tipo de aplicacin, Doctrine2 va mucho ms all. Un tratamiento completo de este
magnifico ORM requiere un curso dedicado al mismo.
Plan del curso
7
La unidad 8 est dedicada a los servicios de validacin de datos y de creacin de
formularios HTML, herramienta fundamental en cualquier aplicacin web.
En la unidad 9 estudiamos el novedoso sistema de seguridad de Symfony2, mediante el
cual podemos proteger nuestras aplicaciones web en los niveles de autentificacin y
autorizacin sin necesidad de picar demasiado cdigo.
La unidad 10 integra todos los conocimientos acumulados a lo largo del curso para dar
forma definitiva a la aplicacin de gestin de notas. Aqu incorporamos las plantillas, estilos
enriquecidos con javascript (jQuery) y perfilamos los flecos que se nos han ido quedando en
las unidades anteriores.
Por ltimo en la unidad 11 explicaremos las distintas estrategias que podemos seguir para
desarrollar el backend, o parte de administracin, de las aplicaciones web. Utilizaremos un
conocido y potente bundle (los plugins de Symfony2) con el que se pueden construir
elegantes y prcticas aplicaciones de backend sin necesidad de picar mucho cdigo.
Sobre Symfony2
La primera versin estable de Symfony2 ha sido desarrollada en un tiempo record de 448
das. El primer commit tiene fecha de 3/6/2010, mientras que el ltimo, correspondiente a la
versin 2.0.1, es del 25/08/2011. Al extraordinario talento del equipo liderado por Fabien
Potencier, lider del proyecto, se une los ms de 6 aos de experiencia acumulada durante el
desarrollo de la primera versin de este producto. El resultado ha sido doble; por una parte
han construido un conjunto de componentes que actan como piezas de un Lego para la
solucin de problemas relacionados con la web y, por otro, han elaborado un framework para
el desarrollo de aplicaciones web utilizando como pilares tales componentes. Por eso cuando
se habla de Symfony2 debemos interpretar, en funcin del contexto como con toda palabra
polismica, si se hace referencia a los componentes o al framework. Este curso va de lo
segundo.
Symfony 1, el antecesor de Symfony2, ha sido y sigue siendo un framework lder en el
mundo PHP. Prueba de ello es la amplia y activa comunidad que lo desarrolla y que lo utiliza.
El nmero de aplicaciones registradas en el trac5 del proyecto tambin da una idea de la
confianza que muchos desarrolladores han depositado en symfony 1. Pues bien, un anlisis
de la situacin actual parece indicar que Symfony2 va camino de destronar a su antecesor
(si no lo ha hecho ya). La actividad del proyecto, que puede seguirse pblicamente en su
repositorio de github (https://github.com/symfony/symfony) y la proliferacin de sitios sobre
Symfony2 soportan esta observacin. Otro indicativo del xito que est teniendo esta nueva
versin, es el hecho de que los desarrolladores de Drupal, han decidido utilizar varios de los
componentes de Symfony2 para el desarrollo de la versin 8 de su producto.
La documentacin de Symfony2
Posiblemente una de las caractersticas ms apreciadas de symfony 1 fue la cantidad y la
calidad de documentacin oficial que existe. Ello proporciona la tranquilidad de saber que
prcticamente cualquier problema que se le presente al programador estar resuelto, o al
menos estar resuelto algo parecido, en alguno de los muchos documentos que sobre
symfony 1 se han escrito.
Tambin en Symfony2, no poda ser de otra forma, se ha prestado mucha atencin a la
calidad de la documentacin, aunque por el momento nicamente se encuentra en ingls e
italiano. El documento imprescindible es el libro oficial
(http://symfony.com/doc/current/book/index.html), que est complementado por el libro de
recetas (http://symfony.com/doc/current/cookbook/index.html), una gua de referencia con
las principales opciones de configuracin
(http://symfony.com/doc/current/reference/index.html), algunos videos demostrativos, y la
documentacin de la API (http://api.symfony.com/2.0/).
Sobre Symfony2
8
Todo ello lo encontrars en el sitio oficial de Symfony2 (http://symfony.com/). Si finalmente
terminas atrapado en las redes de Symfony2 no te quepa duda de que se convertir en una
de tus herramientas imprescindibles. Esperamos que este curso tambin se encuentre entre
ellas.
Como ya hicimos con el curso "Desarrollo de aplicaciones web con symfony", hemos
desarrollado el texto desde una perspectiva ms pedaggica que tcnica, ya que es en aquel
aspecto donde la documentacin oficial de Symfony2 es ms endeble. Este curso supone un
apoyo pedaggico para aprender a desarrollar aplicaciones web con Symfony2, y no
pretende ni sustituir ni desdear la documentacin oficial. Muy al contrario creemos, como
ya hemos sealado, que dicha documentacin es muy valiosa y debe formar parte del
equipo de recursos necesarios para desarrollar con Symfony2.
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll interna para
recorrer todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Comentarios
9
Unidad 2: Desarrollo de una aplicacin web siguiendo
el patrn MVC
El patrn MVC en el desarrollo de aplicaciones web
Muchos de los problemas que aparecen en la ingeniera del software son similares en su
estructura. Y, por tanto, se resuelven de manera parecida. A lo largo de la historia de esta
disciplina se han elaborado un buen nmero de esquemas resolutivos que son conocidos con
el nombre des patrones de diseo
1
y cuyo conocimiento y aplicacin son de una inestimble
ayuda a la hora de disear y construir una aplicacin informtica.
Posiblemente uno de los ms conocidos y utilizados sea el patrn "Modelo, Vista,
Controlador" (MVC), que propone organizar una aplicacin en tres partes bien diferenciadas
y dbilmente acopladas entre s, de manera que los cambios que se produzcan en una no
afecten demasiado a las otras (idealmente nada). El nombre del patrn enumera cada una
de las partes:
El Controlador. En este artefacto se incluye todo lo referente a la lgica de control de
la aplicacin, que no tiene nada que ver con las caractersticas propias del negocio para
el que se est construyendo la aplicacin. En el caso de una aplicacin web, un ejemplo
sera la manipulacin de la request HTTP.
El Modelo. Donde se implementa todo lo relativo a la lgica de negocio, es decir, los
aspectos particulares del problema que la aplicacin resuelve. Si, por ejemplo estamos
desarrollando un blog, un ejemplo sera una librera de funciones para la gestin de los
comentarios.
La Vista. Aqu se ubica el cdigo encargado de "pintar" el resultado de los procesos de
la aplicacin. En una aplicacin web la vista se encarga de producir documentos HTML,
XML, JSON, etctera, con los datos que se hayan calculado previamente en la aplicacin.
Para que el conjunto funcione, las partes deben interaccionar entre s. Y en este punto
encontramos en la literatura distintas soluciones. La que proponemos en este curso es la
mostrada en la siguiente figura:
Diagrama del modelo MVC
Unidad 2: Desarrollo de una aplicacin web siguiendo el patrn MVC
10
El controlador recibe la orden de entrada y se encarga de procesarla utilizando, si es preciso,
los servicios del modelo para ello. Una vez que ha realizado el clculo entrega los datos
"crudos" a la vista y esta se encarga de decorarlos adecuadamente. La caracterstica ms
importante de esta solucin es que la vista nunca interacciona con el modelo.
Las aplicaciones web ms tpicas pueden plantearse segn este patrn: el controlador recibe
una peticin HTTP y la procesa, haciendo uso del modelo calcula los datos de salida y los
entrega a la vista, la cual se encarga de construir una respuesta HTTP con las cabeceras
adecuadas y un payload o cuerpo de la respuesta que suele ser un contenido HTML, XML o
JSON.
Aunque no todas las aplicaciones web se pueden ajustar a este modelo, si es cierto que la
idea de separar responsabilidades o las distintas areas de un problema en sistemas
dbilmente acoplados, es una estrategia comn en las metodologa que utilizan el
paradigma de programacin orientado a objetos. Es lo que se conoce en la terminologa
anglosajona como Separation Of Concerns
2
.
En esta unidad vamos a plantear y desarrollar una sencilla aplicacin web utilizando como
gua de diseo este patrn. De esta manera ilustraremos sus ventajas y nos servir como
material introductorio a la arquitectura de Symfony2. Llegados a este punto hemos de
indicar que Fabien Potencier, lider del proyecto, declara que Symfony2, o mejor dicho, la
edicin standard del framework construido con los componentes de Symfony2, no es un
framework MVC, ya que no trata para nada del modelo y deja al programador absoluta
libertad para incoporar las libreras que ms le convenga. En nuestra humilde opinin,
creemos que esta apreciacin es el resultado de "hilar muy fino", y que, en trminos
prcticos, Symfony2 se ajusta al patrn MVC. De hecho, en su edicin standard, incorpora
Doctrine como ORM. Y aunque es cierto que Doctrine es un proyecto autnomo y externo a
Symfony2, no deja de ser un servicio prestado por el framework para la construccin del
Modelo. Realmente este tipo de discusiones son interesante y nos ayudan a ejercitar nuestra
mente, pero no creemos que sean vitales para aprender a utilizar el framework.
Nota
En http://fabien.potencier.org/article/49/what-is-symfony2, puedes leer un interesante
artculo de Fabien Potencier en el que describe lo que es Symfony2 y trata su punto de
vista acerca de estos detalles.
Descripcin de la aplicacin
Vamos a construir una aplicacin web para elaborar y consultar un repositorio de alimentos
con datos acerca de sus propiedades dietticas. Utilizaremos una base de datos para
almacenar dichos datos que consistir en una sola tabla con la siguiente informacin sobre
alimentos:
El nombre del alimento,
la energa en kilocaloras ,
la cantidad de protenas,
la cantidad hidratos de carbono en gramos
la cantidad de fibra en gramos y
la cantidad de grasa en gramos,
todo ello por cada 100 gramos de alimento.
Descripcin de la aplicacin
11
Aunque se trata de una aplicacin muy sencilla, cuenta con los elementos suficientes para
trabajar el aspecto que realmente pretendemos estudiar en esta unidad: la organizacin del
cdigo siguiendo las directrices del patrn MVC. Comprobaremos como esta estrategia nos
ayuda a mejorar las posibilidades de crecimiento (escalabilidad) y el mantenimiento de las
aplicaciones que desarrollamos.
Diseo de la aplicacin (I). Organizacin de los archivos
La "anatoma" de una aplicacin web tpica consiste en:
1. El cdigo que ser procesado en el servidor (PHP, Java, Python, etctera) para construir
dinmicamente la respuesta.
2. Los Assets, que podemos traducir como "activos" de la aplicacin, y que lo constituyen
todos aquellos archivos que se sirven directamente sin ningn tipo de proceso. Suelen
ser imgenes, CSS's y cdigo Javascript.
El servidor web nicamente puede acceder a una parte del sistema de ficheros que se
denomina Document Root. Es ah donde se buscan los recursos cuando se realiza una
peticin a la raz del servidor a travs de la URL h t t p : / / e l . s e r v i d o r . q u e . s e a / . Sin
embargo, el cdigo ejecutado para construir dinmicamente la respuesta puede "vivir" en
cualquier otra parte, fuera del Document root
3
.
Tode esto sugiere una manera de organizar el cdigo de la aplicacin para que no se pueda
acceder desde el navegador ms que al cdigo estrictamente imprescindible para que esta
funcione. Se trata, simplemente, de colocar en el Document root slo los activos y los
scripts PHP de entrada a la aplicacin. El resto de archivos, fundamentalmente libreras
PHP's y ficheros de configuracin (XML, YAML, JSON, etctera), se ubicarn fuera del
Document Root y sern incluidos por los scripts de inicio segn lo requieran.
Siguiendo estas conclusiones, nuestra aplicacin presentar la siguiente estructura de
directorio:
.
a p p
w e b
c s s
i m a g e s
j s
Configuraremos nuestro servidor web para que el directorio w e b sea su Document root, y en
a p p colocaremos el cdigo PHP y la configuracin de la aplicacin.
Diseo de la aplicacin (II). El controlador frontal
La manera ms directa y naf de construir una aplicacin en PHP consiste en escribir un
script PHP para cada pgina de la aplicacin. Sin embargo esta prctica presenta algunos
problemas, especialmente cuando la aplicacin que desarrollamos adquiere cierto tamao y
pretendemos que siga creciendo. Veamos algunos de los problemas ms significativos de
este planteamiento.
Por lo general, todos los scripts de una aplicacin realizan una serie de tareas que son
comunes. Por ejemplo: interpretar y manipular la request, comprobar las credenciales de
seguridad y cargar la configuracin. Esto significa que una buena parte del cdigo puede ser
compartido entre los scripts. Para ello podemos utilizar el mecanismo de inclusin de
ficheros de PHP y fin de la historia. Pero, qu ocurre si en un momento dado, cuando ya
tengamos escrito mucho cdigo, queremos aadir a todas las pginas de la aplicacin una
nueva caracterstica que requiere, por ejemplo, el uso de una nueva librera?. Tenemos,
Diseo de la aplicacin (I). Organizacin de los archivos
12
entonces, que aadir dicha modificacin a todos los scripts PHP de la aplicacin. Lo cual
supone una degradacin en el mantenimiento y un motivo que aumenta la probabilidad de
fallos una vez que el cambio se haya realizado.
Otro problema que ocurre con esta estrategia es que si se solicita una pgina que no tiene
ningn script PHP asociado, el servidor arrojar un error (404 Not Found) cuyo aspecto no
podemos controlar dentro de la propia aplicacin (es decir, sin tocar la configuracin del
servidor web).
Como se suele decir, a grandes males grandes remedios!; si el problema lo genera el hecho
de tener muchos scripts, que adems comparten bastante cdigo, utilicemos uno solo que
se encargue de procesar todas las peticiones. A este nico script de entrada se le conoce
como controlador frontal.
Entonces, cmo puedo crear muchas pginas distintas con un solo script?. La clave est en
utilizar la query string de la URL como parte de la ruta que define la pgina que se solicita. El
controlador frontal, en funcin de los parmetro que lleguen en la query string determinar
que acciones debe realizar para construir la pgina solicidada.
Nota
La query string es la parte de la URL que contiene los datos que se pasarn a la
aplicacin web. Por ejemlo, en: h t t p : / / t u . s e r v i d o r / i n d e x . p h p ? a c c i o n = h o l a , la
query string es: ? a c c i o n = h o l a .
Construccin de la aplicacin. Vamos al lo.
Pues eso, vamos al lo aplicando todo lo que llevamos dicho hasta el momento:
El patrn de diseo MVC,
La estructura de directorios que expone nicamente los ficheros indispensables para el
servidor web y,
La idea de que todas la peticiones pasen por un solo script, el controlador frontal
Creacin de la estructura de directorios
Comenzamos creando la estructura de directorios propuesta anteriormente. Por lo pronto, en
nuestro entorno de desarrollo y por cuestiones de comodidad, crearemos la estructura en
alguna ubicacin dentro del Document root.
Nota
Si ests utilizando como sistema operativo Ubuntu, el Document root se encuentra
en / v a r / w w w , es ah donde debes crear un directorio denominado a l i m e n t o s que
alojar la estructura propuesta. Si ests utilizando XAMP en Windows, se encuentra en
C : / x a m p p / h t d o c s .
Es importante resaltar que esto no debera hacerse en un entorno de produccin, ya
que dejamos al servidor web acceder directamente al directorio a p p , y es algo que
deseamos evitar. Sin embargo, de esta manera podemos aadir todos los proyectos
que queramos sin tener que tocar la configuracin del servidor web. Lo cual es algo
muy agradecido cuando se est desarrollando. En un entorno de produccin debemos
Construccin de la aplicacin. Vamos al lo.
13
asegurarnos de que el directorio w e b es el Document root del servidor (o del
VirtualHost de nuestra aplicacin, si es que estamos alojando varias webs en un
mismo servidor).
Nuestra implementacin del patrn MVC ser muy sencilla; crearemos una clase para la
parte del controlador que denominaremos C o n t r o l l e r , otra para el modelo que
denominaremos M o d e l , y para los parmetros de configuracin de la aplicacin utilizaremos
una clase que llamaremos C o n f i g . Los archivos donde se definen estas clases los
ubicaremos en el directorio a p p . Por otro lado las Vistas sern implementadas como
plantillas PHP en el directorio a p p / t e m p l a t e s .
Los archivos CSS, Javascript , las imgenes y el controlador frontal los colocaremos en el
directorio web.
Cuando terminemos de codificar, la estructura de ficheros de la aplicacin presentar el
siguiente aspecto:
/ v a r / w w w / a l i m e n t o s
a p p
| t e m p l a t e s
| C o n t r o l l e r . p h p
| M o d e l . p h p
| C o n f i g . p h p
|
w e b
c s s
i m a g e s
j s
i n d e x . p h p
El controlador frontal y el mapeo de rutas
En cualquier aplicacin web se deben definir las URL's asociadas a cada una de sus pginas.
Para la nuestra definiremos las siguientes:
URL Accin
http://tu.servidor/alimentos/index.php?ctl=inicio mostrar pantalla inicio
http://tu.servidor/alimentos/index.php?ctl=listar listar alimentos
http://tu.servidor/alimentos/index.php?ctl=insertar insertar un alimento
http://tu.servidor/alimentos/index.php?ctl=buscar buscar alimentos
http://tu.servidor/alimentos/index.php?ctl=ver&id=x ver el alimento x
A cada una de estas URL's les vamos a asociar un mtodo pblico de la clase C o n t r o l l e r .
Estos mtodos se suelen denominar acciones. Cada accin se encarga de calcular
dinmicamente los datos requeridos para construir su pgina. Podr utilizar, si le hace falta,
lo servicios de la clase M o d e l . Una vez calculados los datos, se los pasar a una plantilla
donde se realizar, finalmente, la construccin del documento HTML que ser devuelto al
cliente.
Todos estos elementos sern "orquestados" por el controlador frontal, el cual lo
implementaremos en un script llamado i n d e x . p h p ubicado en el directorio w e b . En concreto,
El controlador frontal y el mapeo de rutas
14
la responsabilidad del controlador frontal ser:
cargar la configuracin del proyecto y las libreras donde implementaremos la parte del
Modelo, del Controlador y de la Vista.
Analizar los parmetros de la peticin HTTP (request) comprobando si la pgina
solicitada en ella tiene asignada alguna accin del Controlador. Si es as la ejecutar, si
no dar un error 404 (page not found).
Llegados a este punto es importante aclara que, el controlador frontal y la clase
C o n t r o l l e r , son distintas cosas y tienen distintas responsabilidades. El hecho de que ambos
se llamen controladores puede dar lugar a confusiones.
El controlador frontal tiene el siguiente aspecto. Crea el archivo w e b / i n d e x . p h p y copia el
siguiente cdigo.
1 < ? p h p
2 / / w e b / i n d e x . p h p
3
4 / / c a r g a d e l m o d e l o y l o s c o n t r o l a d o r e s
5 r e q u i r e _ o n c e _ _ D I R _ _ . ' / . . / a p p / C o n f i g . p h p ' ;
6 r e q u i r e _ o n c e _ _ D I R _ _ . ' / . . / a p p / M o d e l . p h p ' ;
7 r e q u i r e _ o n c e _ _ D I R _ _ . ' / . . / a p p / C o n t r o l l e r . p h p ' ;
8
9 / / e n r u t a m i e n t o
1 0 $ m a p = a r r a y (
1 1 ' i n i c i o ' = > a r r a y ( ' c o n t r o l l e r ' = > ' C o n t r o l l e r ' , ' a c t i o n ' = > ' i n i c i o ' ) ,
1 2 ' l i s t a r ' = > a r r a y ( ' c o n t r o l l e r ' = > ' C o n t r o l l e r ' , ' a c t i o n ' = > ' l i s t a r ' ) ,
1 3 ' i n s e r t a r ' = > a r r a y ( ' c o n t r o l l e r ' = > ' C o n t r o l l e r ' , ' a c t i o n ' = > ' i n s e r t a r ' ) ,
1 4 ' b u s c a r ' = > a r r a y ( ' c o n t r o l l e r ' = > ' C o n t r o l l e r ' , ' a c t i o n ' = > ' b u s c a r P o r N o m b r e ' ) ,
1 5 ' v e r ' = > a r r a y ( ' c o n t r o l l e r ' = > ' C o n t r o l l e r ' , ' a c t i o n ' = > ' v e r ' )
1 6 ) ;
1 7
1 8 / / P a r s e o d e l a r u t a
1 9 i f ( i s s e t ( $ _ G E T [ ' c t l ' ] ) ) {
2 0 i f ( i s s e t ( $ m a p [ $ _ G E T [ ' c t l ' ] ] ) ) {
2 1 $ r u t a = $ _ G E T [ ' c t l ' ] ;
2 2 } e l s e {
2 3 h e a d e r ( ' S t a t u s : 4 0 4 N o t F o u n d ' ) ;
2 4 e c h o ' < h t m l > < b o d y > < h 1 > E r r o r 4 0 4 : N o e x i s t e l a r u t a < i > ' .
2 5 $ _ G E T [ ' c t l ' ] .
2 6 ' < / p > < / b o d y > < / h t m l > ' ;
2 7 e x i t ;
2 8 }
2 9 } e l s e {
3 0 $ r u t a = ' i n i c i o ' ;
3 1 }
3 2
3 3 $ c o n t r o l a d o r = $ m a p [ $ r u t a ] ;
3 4 / / E j e c u c i n d e l c o n t r o l a d o r a s o c i a d o a l a r u t a
3 5
3 6 i f ( m e t h o d _ e x i s t s ( $ c o n t r o l a d o r [ ' c o n t r o l l e r ' ] , $ c o n t r o l a d o r [ ' a c t i o n ' ] ) ) {
3 7 c a l l _ u s e r _ f u n c ( a r r a y ( n e w $ c o n t r o l a d o r [ ' c o n t r o l l e r ' ] , $ c o n t r o l a d o r [ ' a c t i o n ' ] ) ) ;
3 8 } e l s e {
3 9
4 0 h e a d e r ( ' S t a t u s : 4 0 4 N o t F o u n d ' ) ;
4 1 e c h o ' < h t m l > < b o d y > < h 1 > E r r o r 4 0 4 : E l c o n t r o l a d o r < i > ' .
4 2 $ c o n t r o l a d o r [ ' c o n t r o l l e r ' ] .
4 3 ' - > ' .
El controlador frontal y el mapeo de rutas
15
4 4 $ c o n t r o l a d o r [ ' a c t i o n ' ] .
4 5 ' < / i > n o e x i s t e < / h 1 > < / b o d y > < / h t m l > ' ;
4 6 }
En las lneas 5-7 se realiza la carga de la configuracin del modelo y de los
controladores.
En las lneas 10-16 se declara un array asociativo cuya funcin es definir una tabla para
mapear (asociar), rutas en acciones de un controlador. Esta tabla ser utilizada a
continuacin para saber qu accin se debe disparar.
En las lneas 19-31 se lleva a cabo el parseo de la URL y la carga de la accin, si la ruta
est definida en la tabla de rutas. En caso contrario se devuelve una pgina de error.
Observa que hemos utilizado la funcin h e a d e r ( ) de PHP para indicar en la cabecera
HTTP el cdigo de error correcto. Adems enviamos un pequeo documento HTML que
informa del error. Tambin definimos a i n i c i o como una ruta por defecto, ya que si la
query string llega vaca, se opta por cargar esta accin.
Nota
En honor a la verdad tenemos que decir que lo que estamos llamando parseo de la
URL, no es tal. Simplemente estamos extrayendo el valor de la variable c t l que se ha
pasado a travs de la peticin HTTP. Sin embargo, hemos utilizado este termino
porque lo ideal sera que, en lugar de utilizar parmetros de la peticin HTTP para
resolver la ruta, pudisemos utilizar rutas limpias (es decir, sin caracteres ? ni & ) del
tipo:
h t t p : / / t u . s e r v i d o r / i n d e x . p h p / i n i c i o
h t t p : / / t u . s e r v i d o r / i n d e x . p h p / b u s c a r
h t t p : / / t u . s e r v i d o r / i n d e x . p h p / v e r / 5
En este caso s es necesario proceder a un parseo de la URL para buscar en la tabla de
rutas la accin que le corresponde. Esto, obviamente, es ms complejo. Pero es lo que
hace (y muchas cosas ms) el componente Routing de Symfony2. En la siguiente
unidad realizaremos un primer acercamiento a dicho componente que te dar una
idea de la potencia del mismo.
Las acciones del Controlador. La clase C o n t r o l l e r .
Ahora vamos a implementar las acciones asociadas a las URL's en la clase C o n t r o l l e r . Crea
el archivo a p p / C o n t r o l l e r . p h p y copia el siguiente cdigo:
1 < ? p h p
2
3 c l a s s C o n t r o l l e r
4 {
5
6 p u b l i c f u n c t i o n i n i c i o ( )
7 {
8 $ p a r a m s = a r r a y (
Las acciones del Controlador. La clase Controller.
16
9 ' m e n s a j e ' = > ' B i e n v e n i d o a l c u r s o d e S y m f o n y 2 ' ,
1 0 ' f e c h a ' = > d a t e ( ' d - m - y y y ' ) ,
1 1 ) ;
1 2 r e q u i r e _ _ D I R _ _ . ' / t e m p l a t e s / i n i c i o . p h p ' ;
1 3 }
1 4
1 5 p u b l i c f u n c t i o n l i s t a r ( )
1 6 {
1 7 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
1 8 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
1 9
2 0 $ p a r a m s = a r r a y (
2 1 ' a l i m e n t o s ' = > $ m - > d a m e A l i m e n t o s ( ) ,
2 2 ) ;
2 3
2 4 r e q u i r e _ _ D I R _ _ . ' / t e m p l a t e s / m o s t r a r A l i m e n t o s . p h p ' ;
2 5 }
2 6
2 7 p u b l i c f u n c t i o n i n s e r t a r ( )
2 8 {
2 9 $ p a r a m s = a r r a y (
3 0 ' n o m b r e ' = > ' ' ,
3 1 ' e n e r g i a ' = > ' ' ,
3 2 ' p r o t e i n a ' = > ' ' ,
3 3 ' h c ' = > ' ' ,
3 4 ' f i b r a ' = > ' ' ,
3 5 ' g r a s a ' = > ' ' ,
3 6 ) ;
3 7
3 8 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
3 9 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
4 0
4 1 i f ( $ _ S E R V E R [ ' R E Q U E S T _ M E T H O D ' ] = = ' P O S T ' ) {
4 2
4 3 / / c o m p r o b a r c a m p o s f o r m u l a r i o
4 4 i f ( $ m - > v a l i d a r D a t o s ( $ _ P O S T [ ' n o m b r e ' ] , $ _ P O S T [ ' e n e r g i a ' ] ,
4 5 $ _ P O S T [ ' p r o t e i n a ' ] , $ _ P O S T [ ' h c ' ] , $ _ P O S T [ ' f i b r a ' ] ,
4 6 $ _ P O S T [ ' g r a s a ' ] ) ) {
4 7 $ m - > i n s e r t a r A l i m e n t o ( $ _ P O S T [ ' n o m b r e ' ] , $ _ P O S T [ ' e n e r g i a ' ] ,
4 8 $ _ P O S T [ ' p r o t e i n a ' ] , $ _ P O S T [ ' h c ' ] , $ _ P O S T [ ' f i b r a ' ] ,
4 9 $ _ P O S T [ ' g r a s a ' ] ) ;
5 0 h e a d e r ( ' L o c a t i o n : i n d e x . p h p ? c t l = l i s t a r ' ) ;
5 1
5 2 } e l s e {
5 3 $ p a r a m s = a r r a y (
5 4 ' n o m b r e ' = > $ _ P O S T [ ' n o m b r e ' ] ,
5 5 ' e n e r g i a ' = > $ _ P O S T [ ' e n e r g i a ' ] ,
5 6 ' p r o t e i n a ' = > $ _ P O S T [ ' p r o t e i n a ' ] ,
5 7 ' h c ' = > $ _ P O S T [ ' h c ' ] ,
5 8 ' f i b r a ' = > $ _ P O S T [ ' f i b r a ' ] ,
5 9 ' g r a s a ' = > $ _ P O S T [ ' g r a s a ' ] ,
6 0 ) ;
6 1 $ p a r a m s [ ' m e n s a j e ' ] = ' N o s e h a p o d i d o i n s e r t a r e l a l i m e n t o . R e v i s a e l f o r m u l a r i o ' ;
6 2 }
6 3 }
6 4
6 5 r e q u i r e _ _ D I R _ _ . ' / t e m p l a t e s / f o r m I n s e r t a r . p h p ' ;
6 6 }
6 7
6 8 p u b l i c f u n c t i o n b u s c a r P o r N o m b r e ( )
6 9 {
7 0 $ p a r a m s = a r r a y (
7 1 ' n o m b r e ' = > ' ' ,
7 2 ' r e s u l t a d o ' = > a r r a y ( ) ,
7 3 ) ;
7 4
Las acciones del Controlador. La clase Controller.
17
7 5 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
7 6 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
7 7
7 8 i f ( $ _ S E R V E R [ ' R E Q U E S T _ M E T H O D ' ] = = ' P O S T ' ) {
7 9 $ p a r a m s [ ' n o m b r e ' ] = $ _ P O S T [ ' n o m b r e ' ] ;
8 0 $ p a r a m s [ ' r e s u l t a d o ' ] = $ m - > b u s c a r A l i m e n t o s P o r N o m b r e ( $ _ P O S T [ ' n o m b r e ' ] ) ;
8 1 }
8 2
8 3 r e q u i r e _ _ D I R _ _ . ' / t e m p l a t e s / b u s c a r P o r N o m b r e . p h p ' ;
8 4 }
8 5
8 6 p u b l i c f u n c t i o n v e r ( )
8 7 {
8 8 i f ( ! i s s e t ( $ _ G E T [ ' i d ' ] ) ) {
8 9 t h r o w n e w E x c e p t i o n ( ' P g i n a n o e n c o n t r a d a ' ) ;
9 0 }
9 1
9 2 $ i d = $ _ G E T [ ' i d ' ] ;
9 3
9 4 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
9 5 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
9 6
9 7 $ a l i m e n t o = $ m - > d a m e A l i m e n t o ( $ i d ) ;
9 8
9 9 $ p a r a m s = $ a l i m e n t o ;
1 0 0
1 0 1 r e q u i r e _ _ D I R _ _ . ' / t e m p l a t e s / v e r A l i m e n t o . p h p ' ;
1 0 2 }
1 0 3
1 0 4 }
Esta clase implementa una serie de mtodos pblicos, que hemos denominado acciones
para indicar que son mtodos asociados a URL's. Fjate como en cada una de las acciones se
declara un array asociativo ( p a r a m s ) con los datos que sern pintados en la plantilla. Pero
en ningn caso hay informacin acerca de como se pintarn dichos datos. Por otro lado, casi
todas las acciones utilizan un objeto de la clase M o d e l s para realizar operaciones relativas a
la lgica de negocio, en nuestro caso a todo lo relativo con la gestin de los alimentos.
Para comprender el funcionamiento de las acciones, comencemos por Analizar la funcin
l i s t a r ( ) . Comienza declarando un objeto del modelo (lnea 17) para pedirle
posteriormente el conjunto de alimentos almacenados en la base de datos. Los datos
recopilados son almacenados en el array asociativo p a r a m s (lneas 20-22). Por ltimo
incluye el archivo / t e m p l a t e s / m o s t r a r A l i m e n t o s . p h p (lnea 24). Tal archivo, que
denominamos plantilla, ser el encargado de construir el documento HTML con los datos
del array p a r a m s . Observa que todas las acciones tienen la misma estructura: realizan
operaciones, recojen datos y llaman a una plantilla para construir el documento HTML que
ser devuelto al cliente.
Observa tambin que en las acciones del controlador no hay ninguna operacin que tenga
que ver con la lgica de negocio, todo lo que se hace es lgica de control.
Analicemos ahora la accin i n s e r t a r ( ) , cuya lgica de control es algo ms compleja debido
a que tiene una doble funcionalidad:
1. Enviar al cliente un formulario HTML,
2. Validar los datos sobre un alimento que se reciben desde el cliente para insertarlos en la
base de datos.
La funcin comienza por declarar un array asociativo con campos vacos que coinciden con
los de la tabla alimento (lneas 29-36). A continuacin comprueba si la peticin se ha
Las acciones del Controlador. La clase Controller.
18
realizado mediante la operacin POST (lnea 41), si es as significa que se han pasado datos
a travs de un formulario, si no es as quiere decir que simplemente se ha solicitado la
pgina para ver el formulario de insercin. En este ltimo caso, la accin pasa directamente
a incluir la plantilla que pinta el formulario (lnea 65). Como el array de parmetros est
vaco, se enviar al cliente un formulario con los campos vacos (cuando veas el cdigo de la
plantilla lo vers en directo, por lo pronto basta con saber que es as).
Por otro lado, si la peticin a la accin i n s e r t a r ( ) se ha hecho mediante la operacin POST,
significa que se han enviado datos de un formulario desde el cliente (precisamente del
formulario vaco que hemos descrito un poco ms arriba). Entonces se extraen los datos de
la peticin, se comprueba si son vlidos (lnea 44) y en su caso se realiza la insercin (lnea
47) y una redireccin al listado de alimentos (lnea 50). Si los datos no son vlidos, entonces
se rellena el array de parmetros con los datos de la peticin (lneas 53-60) y se vuelve a
pintar el formulario, esta vez con los campos rellenos con los valores que se enviaron en la
peticin anterior y con un mensaje de error.
Todo el proceso que acabamos de contar no tiene nada que ver con la lgica de negocio;
esto es, no decide cmo deben validarse los datos, ni cmo deben insertarse en la base de
datos, esas tareas recaen en el modelo (el cual, obviamente debemos utilizar). Lo
importante aqu es que debe haber una operacin de validacin para tomar una decisin:
insertar los datos o reenviar el formulario relleno con los datos que envi el usuario y con un
mensaje de error. Es decir, nicamente hay cdigo que implementa la lgica de control.
Nota
El esquema de control que se acaba de presentar resulta muy prctico y ordenado
para implementar acciones que consisten en recopilar datos del usuario y realizar
algn proceso con ellos (almacenarlos en una base de datos, por ejemplo). A lo largo
del curso aparecer, con ms o menos variaciones, en varias ocasiones.
La implementacin de la Vista.
Las plantillas PHP
Ahora vamos a pasar a estudiar la parte de la Vista, representada en nuestra solucin por las
plantillas. Aunque en el anlisis que estamos haciendo ya hemos utilizado la palabra
"plantilla" en varias ocasiones, an no la hemos definido con precisin. As que comenzamos
por ah.
Una plantilla es un fichero de texto con la informacin necesaria para generar documentos
en cualquier formato de texto (HTML, XML, CSV, LaTeX, JSON, etctera). Cualquier tipo de
plantilla consiste en un documento con el formato que se quiere generar, y con variables
expresadas en el lenguaje propio de la plantilla y que representas a lo valores que son
calculados dinmicamente por la aplicacin.
Cuando desarrollamos aplicaciones web con PHP, la forma ms sencilla de implementar
plantillas es usando el propio PHP como lenguaje de plantillas. Qu significa esto? Acudimos
al refranero popular y decimos aquello de que una imagen vale ms que mil palabras. Con
todos vosotros un ejemplo de plantilla HTML que usa PHP como lenguaje de plantillas
(dedcale un ratito a observarla y analizarla, qu es lo que te llama la atencin en el
aspecto del cdigo PHP que aparece?)
La implementacin de la Vista.
19
1 < t a b l e >
2 < t r >
3 < t h > a l i m e n t o ( p o r 1 0 0 g ) < / t h >
4 < t h > e n e r g a ( K c a l ) < / t h >
5 < t h > g r a s a ( g ) < / t h >
6 < / t r >
7 < ? p h p f o r e a c h ( $ p a r a m s [ ' a l i m e n t o s ' ] a s $ a l i m e n t o ) : ? >
8 < t r >
9 < t d > < a h r e f = " i n d e x . p h p ? c t l = v e r & i d = < ? p h p e c h o $ a l i m e n t o [ ' i d ' ] ? > " >
1 0 < ? p h p e c h o $ a l i m e n t o [ ' n o m b r e ' ] ? >
1 1 < / a >
1 2 < / t d >
1 3 < t d > < ? p h p e c h o $ a l i m e n t o [ ' e n e r g i a ' ] ? > < / t d >
1 4 < t d > < ? p h p e c h o $ a l i m e n t o [ ' g r a s a t o t a l ' ] ? > < / t d >
1 5 < / t r >
1 6 < ? p h p e n d f o r e a c h ; ? >
1 7
1 8 < / t a b l e >
Esencialmente no es ms que un trozo de documento HTML donde la informacin dinmica
se obtiene procesando cdigo PHP. La caracterstica principal de este cdigo PHP es que
debe ser escueto y corto. De manera que no "contamine" la estructura del HTML. Por ello
cada instruccin PHP comienza y termina en la misma lnea. La mayor parte de estas
instrucciones son e c h o ' s de variables escalares. Pero tambin son muy usuales la
utilizacin de bucles f o r e a c h - e n d f o r e a c h para recorrer arrays de datos, as como los
bloques condicionales i f - e n d i f para pintar bloques segn determinadas condiciones.
En el ejemplo de ms arriba se genera el cdigo HTML de una tabla que puede tener un
nmero variable de filas. Se recoje en la plantilla el parmetro a l i m e n t o s , que es un array
con datos de alimentos, y se genera una fila por cada elemento del array con informacin de
la URL de una pgina sobre el alimento (lnea 9), y su nombre, energa y grasa total (lneas
10-14).
Observa tambin la forma de construir el bucle f o r e a c h , se abre en la lnea 7 y se cierra en
la 16. Lo particular de la sintaxis de este tipo de bucle para plantillas es que la instruccin
f o r e a c h que lo abre terminan con el caracter : . Y la necesidad de cerrarlo con un < ? p h p
e n d f o r e a c h ; ? > .
El layout y el proceso de decoracin de plantillas
En una aplicacin web, muchas de las pginas tienen elementos comunes. Por ejemplo, un
caso tpico es la cabecera donde se coloca el mensaje de bienvenida, el men y el pie de
pgina. Este hecho, y la aplicacin del conocido principio de buenas prcticas de
programacin DRY (Don't Repeat Yourself, No Te Repitas), lleva a que cualquier sistema de
plantillas que se utilice para implementar la vista utilice otro conocido patrn de diseo: El
Decorator, o Decorador
4
. Aplicado a la generacin de vistas la solucin que ofrece dicho
patrn es la de aadir funcionalidad adicional a las plantillas. Por ejemplo, aadir el men y
el pie de pgina a las plantillas que lo requieran, de manera que dichos elementos puedan
reutilizarse en distintas plantillas. Literalmente se trata de decorar las plantillas con
elementos adicionales reutilizables.
Nuestra implementacin del patrn Decorator es muy simple y, por tanto limitada, pero
suficiente para asimilar las bases del concepto y ayudarnos a comprender ms adelante la
filosofa del sistema de plantillas de Symfony2, denomindado twig
5
.
Nuestras plantillas sern ficheros PHP del tipo que acabamos de explicar, y las ubicaremos
en el directorio a p p / t e m p l a t e s . Como ya has visto en el cdigo del controlador, las acciones
finalizan incluyendo alguno de estos archivos. Comencemos por estudiar la plantilla
El layout y el proceso de decoracin de plantillas
20
a p p / t e m p l a t e s / m o s t r a r A l i m e n t o s . p h p , que es la que utiliza la accin l i s t a r ( ) para
pintar los alimentos que obtiene del modelo. Crea el archivo
a p p / t e m p l a t e s / m o s t r a r A l i m e n t o s . p h p con el siguiente cdigo:
a p p / t e m p l a t e s / m o s t r a r A l i m e n t o s . p h p
1 < ? p h p o b _ s t a r t ( ) ? >
2
3 < t a b l e >
4 < t r >
5 < t h > a l i m e n t o ( p o r 1 0 0 g ) < / t h >
6 < t h > e n e r g a ( K c a l ) < / t h >
7 < t h > g r a s a ( g ) < / t h >
8 < / t r >
9 < ? p h p f o r e a c h ( $ p a r a m s [ ' a l i m e n t o s ' ] a s $ a l i m e n t o ) : ? >
1 0 < t r >
1 1 < t d > < a h r e f = " i n d e x . p h p ? c t l = v e r & i d = < ? p h p e c h o $ a l i m e n t o [ ' i d ' ] ? > " >
1 2 < ? p h p e c h o $ a l i m e n t o [ ' n o m b r e ' ] ? > < / a > < / t d >
1 3 < t d > < ? p h p e c h o $ a l i m e n t o [ ' e n e r g i a ' ] ? > < / t d >
1 4 < t d > < ? p h p e c h o $ a l i m e n t o [ ' g r a s a t o t a l ' ] ? > < / t d >
1 5 < / t r >
1 6 < ? p h p e n d f o r e a c h ; ? >
1 7
1 8 < / t a b l e >
1 9
2 0
2 1 < ? p h p $ c o n t e n i d o = o b _ g e t _ c l e a n ( ) ? >
2 2
2 3 < ? p h p i n c l u d e ' l a y o u t . p h p ' ? >
Como ves, las lneas 3-18 son las que se han puesto como ejemplo de plantilla PHP hace un
momento. La novedad son las lneas 1 y 21-23. En ellas est la clave del nuestro proceso de
decoracin. Para comprenderlo del todo es importante echarle un vistazo al fichero
a p p / t e m p l a t e s / l a y o u t . p h p , incluido al final de la plantilla. Cralo y copia el siguiente
cdigo:
a p p / t e m p l a t e s / l a y o u t . p h p
1 < ! D O C T Y P E H T M L P U B L I C " - / / W 3 C / / D T D H T M L 4 . 0 1 T r a n s i t i o n a l / / E N " >
2 < h t m l >
3 < h e a d >
4 < t i t l e > I n f o r m a c i n A l i m e n t o s < / t i t l e >
5 < m e t a h t t p - e q u i v = " C o n t e n t - T y p e " c o n t e n t = " t e x t / h t m l ; c h a r s e t = U T F - 8 " >
6 < l i n k r e l = " s t y l e s h e e t " t y p e = " t e x t / c s s " h r e f = " < ? p h p e c h o ' c s s / ' . C o n f i g : : $ m v c _ v i s _ c s s ? > " / >
7
8 < / h e a d >
9 < b o d y >
1 0 < d i v i d = " c a b e c e r a " >
1 1 < h 1 > I n f o r m a c i n d e a l i m e n t o s < / h 1 >
1 2 < / d i v >
1 3
1 4 < d i v i d = " m e n u " >
1 5 < h r / >
1 6 < a h r e f = " i n d e x . p h p ? c t l = i n i c i o " > i n i c i o < / a > |
1 7 < a h r e f = " i n d e x . p h p ? c t l = l i s t a r " > v e r a l i m e n t o s < / a > |
1 8 < a h r e f = " i n d e x . p h p ? c t l = i n s e r t a r " > i n s e r t a r a l i m e n t o < / a > |
1 9 < a h r e f = " i n d e x . p h p ? c t l = b u s c a r " > b u s c a r p o r n o m b r e < / a > |
El layout y el proceso de decoracin de plantillas
21
2 0 < a h r e f = " i n d e x . p h p ? c t l = b u s c a r A l i m e n t o s P o r E n e r g i a " > b u s c a r p o r e n e r g i a < / a > |
2 1 < a h r e f = " i n d e x . p h p ? c t l = b u s c a r A l i m e n t o s C o m b i n a d a " > b s q u e d a c o m b i n a d a < / a >
2 2 < h r / >
2 3 < / d i v >
2 4
2 5 < d i v i d = " c o n t e n i d o " >
2 6 < ? p h p e c h o $ c o n t e n i d o ? >
2 7 < / d i v >
2 8
2 9 < d i v i d = " p i e " >
3 0 < h r / >
3 1 < d i v a l i g n = " c e n t e r " > - p i e d e p g i n a - < / d i v >
3 2 < / d i v >
3 3 < / b o d y >
3 4 < / h t m l >
El nombre del fichero es bastante ilustrativo, es un layout HTML, es decir, un diseo de un
documento HTML que incluye como elemento dinmico a la variable $ c o n t e n i d o (lnea 26),
la cual esta definida al final de la plantilla m o s t r a r A l i m e n t o s . p h p , y cuyo contenido es
precisamente el resultado de interpretar las lneas comprendidas entre el o b _ s t a r t ( ) y
$ c o n t e n i d o = o b _ g e t _ c l e a n ( ) . En la documentacin de estas funciones
(http://php.net/manual/es/function.ob-start.php) puedes ver que el efecto de o b _ s t a r t ( ) es
enviar todos los resultados del script desde la invocacin de la funcin a un buffer interno.
Dichos resultados se recojen a travs de la funcin o b _ g e t _ c l e a n ( ) . De esa manera
conseguimos decorar la plantilla con el layout. Esta tcnica es utilizada en todas las
plantillas, de manera que todos los elementos comunes a todas las pginas son escritos una
sla vez en l a y o u t . p h p y reutilizados con todas las plantillas generadas con los datos de
cada accin.
Observa que el layout que hemos propuesto incluye:
los estilos CSS (lnea 6),
el men de la aplicacin (lneas 14-23)
el pie de pgina (lneas 29-32)
A continuacin mostramos el cdigo del resto de las plantillas:
a p p / t e m p l a t e s / i n i c i o . p h p
1 < ? p h p o b _ s t a r t ( ) ? >
2 < h 1 > I n i c i o < / h 1 >
3 < h 3 > F e c h a : < ? p h p e c h o $ p a r a m s [ ' f e c h a ' ] ? > < / h 3 >
4 < ? p h p e c h o $ p a r a m s [ ' m e n s a j e ' ] ? >
5
6 < ? p h p $ c o n t e n i d o = o b _ g e t _ c l e a n ( ) ? >
7
8 < ? p h p i n c l u d e ' l a y o u t . p h p ' ? >
a p p / t e m p l a t e s / f o r m I n s e r t a r . p h p
1 < ? p h p o b _ s t a r t ( ) ? >
2
3 < ? p h p i f ( i s s e t ( $ p a r a m s [ ' m e n s a j e ' ] ) ) : ? >
4 < b > < s p a n s t y l e = " c o l o r : r e d ; " > < ? p h p e c h o $ p a r a m s [ ' m e n s a j e ' ] ? > < / s p a n > < / b >
5 < ? p h p e n d i f ; ? >
6 < b r / >
El layout y el proceso de decoracin de plantillas
22
7 < f o r m n a m e = " f o r m I n s e r t a r " a c t i o n = " i n d e x . p h p ? c t l = i n s e r t a r " m e t h o d = " P O S T " >
8 < t a b l e >
9 < t r >
1 0 < t h > N o m b r e < / t h >
1 1 < t h > E n e r g a ( K c a l ) < / t h >
1 2 < t h > P r o t e i n a ( g ) < / t h >
1 3 < t h > H . d e c a r b o n o ( g ) < / t h >
1 4 < t h > F i b r a ( g ) < / t h >
1 5 < t h > G r a s a t o t a l ( g ) < / t h >
1 6 < / t r >
1 7 < t r >
1 8 < t d > < i n p u t t y p e = " t e x t " n a m e = " n o m b r e " v a l u e = " < ? p h p e c h o $ p a r a m s [ ' n o m b r e ' ] ? > " / > < / t d >
1 9 < t d > < i n p u t t y p e = " t e x t " n a m e = " e n e r g i a " v a l u e = " < ? p h p e c h o $ p a r a m s [ ' e n e r g i a ' ] ? > " / > < / t d >
2 0 < t d > < i n p u t t y p e = " t e x t " n a m e = " p r o t e i n a " v a l u e = " < ? p h p e c h o $ p a r a m s [ ' p r o t e i n a ' ] ? > " / > < / t d >
2 1 < t d > < i n p u t t y p e = " t e x t " n a m e = " h c " v a l u e = " < ? p h p e c h o $ p a r a m s [ ' h c ' ] ? > " / > < / t d >
2 2 < t d > < i n p u t t y p e = " t e x t " n a m e = " f i b r a " v a l u e = " < ? p h p e c h o $ p a r a m s [ ' f i b r a ' ] ? > " / > < / t d >
2 3 < t d > < i n p u t t y p e = " t e x t " n a m e = " g r a s a " v a l u e = " < ? p h p e c h o $ p a r a m s [ ' g r a s a ' ] ? > " / > < / t d >
2 4 < / t r >
2 5
2 6 < / t a b l e >
2 7 < i n p u t t y p e = " s u b m i t " v a l u e = " i n s e r t a r " n a m e = " i n s e r t a r " / >
2 8 < / f o r m >
2 9 * L o s v a l o r e s d e b e n r e f e r i r s e a 1 0 0 g d e l a l i m e n t o
3 0
3 1 < ? p h p $ c o n t e n i d o = o b _ g e t _ c l e a n ( ) ? >
3 2
3 3 < ? p h p i n c l u d e ' l a y o u t . p h p ' ? >
a p p / t e m p l a t e s / b u s c a r P o r N o m b r e . p h p
1 < ? p h p o b _ s t a r t ( ) ? >
2
3 < f o r m n a m e = " f o r m B u s q u e d a " a c t i o n = " i n d e x . p h p ? c t l = b u s c a r " m e t h o d = " P O S T " >
4
5 < t a b l e >
6 < t r >
7 < t d > n o m b r e a l i m e n t o : < / t d >
8 < t d > < i n p u t t y p e = " t e x t " n a m e = " n o m b r e " v a l u e = " < ? p h p e c h o $ p a r a m s [ ' n o m b r e ' ] ? > " > ( p u e d e s u t i l i z a r ' % ' c o m o c o m o d n ) < / t d >
9
1 0 < t d > < i n p u t t y p e = " s u b m i t " v a l u e = " b u s c a r " > < / t d >
1 1 < / t r >
1 2 < / t a b l e >
1 3
1 4 < / t a b l e >
1 5
1 6 < / f o r m >
1 7
1 8 < ? p h p i f ( c o u n t ( $ p a r a m s [ ' r e s u l t a d o ' ] ) > 0 ) : ? >
1 9 < t a b l e >
2 0 < t r >
2 1 < t h > a l i m e n t o ( p o r 1 0 0 g ) < / t h >
2 2 < t h > e n e r g a ( K c a l ) < / t h >
2 3 < t h > g r a s a ( g ) < / t h >
2 4 < / t r >
2 5 < ? p h p f o r e a c h ( $ p a r a m s [ ' r e s u l t a d o ' ] a s $ a l i m e n t o ) : ? >
2 6 < t r >
2 7 < t d > < a h r e f = " i n d e x . p h p ? c t l = v e r & i d = < ? p h p e c h o $ a l i m e n t o [ ' i d ' ] ? > " >
2 8 < ? p h p e c h o $ a l i m e n t o [ ' n o m b r e ' ] ? > < / a > < / t d >
2 9 < t d > < ? p h p e c h o $ a l i m e n t o [ ' e n e r g i a ' ] ? > < / t d >
3 0 < t d > < ? p h p e c h o $ a l i m e n t o [ ' g r a s a t o t a l ' ] ? > < / t d >
3 1 < / t r >
3 2 < ? p h p e n d f o r e a c h ; ? >
3 3
3 4 < / t a b l e >
3 5 < ? p h p e n d i f ; ? >
3 6
3 7 < ? p h p $ c o n t e n i d o = o b _ g e t _ c l e a n ( ) ? >
3 8
3 9 < ? p h p i n c l u d e ' l a y o u t . p h p ' ? >
a p p / t e m p l a t e s / v e r A l i m e n t o . p h p
1 < ? p h p o b _ s t a r t ( ) ? >
2
3 < h 1 > < ? p h p e c h o $ p a r a m s [ ' n o m b r e ' ] ? > < / h 1 >
4 < t a b l e b o r d e r = " 1 " >
5
6 < t r >
El layout y el proceso de decoracin de plantillas
23
7 < t d > E n e r g a < / t d >
8 < t d > < ? p h p e c h o $ a l i m e n t o [ ' e n e r g i a ' ] ? > < / t d >
9
1 0 < / t r >
1 1 < t r >
1 2 < t d > P r o t e i n a < / t d >
1 3 < t d > < ? p h p e c h o $ a l i m e n t o [ ' p r o t e i n a ' ] ? > < / t d >
1 4
1 5 < / t r >
1 6 < t r >
1 7 < t d > H i d r a t o s d e C a r b o n o < / t d >
1 8 < t d > < ? p h p e c h o $ a l i m e n t o [ ' h i d r a t o c a r b o n o ' ] ? > < / t d >
1 9
2 0 < / t r >
2 1 < t r >
2 2 < t d > F i b r a < / t d >
2 3 < t d > < ? p h p e c h o $ a l i m e n t o [ ' f i b r a ' ] ? > < / t d >
2 4
2 5 < / t r >
2 6 < t r >
2 7 < t d > G r a s a t o t a l < / t d >
2 8 < t d > < ? p h p e c h o $ a l i m e n t o [ ' g r a s a t o t a l ' ] ? > < / t d >
2 9
3 0 < / t r >
3 1
3 2 < / t a b l e >
3 3
3 4
3 5 < ? p h p $ c o n t e n i d o = o b _ g e t _ c l e a n ( ) ? >
3 6
3 7 < ? p h p i n c l u d e ' l a y o u t . p h p ' ? >
Todas las plantillas recurren al uso de las funciones o b _ s t a r t ( ) y o b _ g e t _ c l e a n ( ) y a la
inclusin del layout para realizar el proceso de decoracin.
El Modelo. Accediendo a la base de datos
Ya slo nos queda presentar al Modelo. En nuestra aplicacin se ha implementado en la
clase M o d e l y esta compuesto por una serie de funciones para persistir datos en la base de
datos, recuperarlos y realizar su validacin.
Dependiendo de la complejidad del negocio con el que tratemos, el modelo puede ser ms o
menos complejo y, adems de tratar con la persistencia de los datos puede incluir funciones
para ofrecer otros servicios relacionados con el negocio en cuestin. Crea el archivo
a p p / M o d e l . p h p y copia el siguiente cdigo:
a p p / M o d e l . p h p
1 < ? p h p
2
3 c l a s s M o d e l
4 {
5 p r o t e c t e d $ c o n e x i o n ;
6
7 p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ d b n a m e , $ d b u s e r , $ d b p a s s , $ d b h o s t )
El Modelo. Accediendo a la base de datos
24
8 {
9 $ m v c _ b d _ c o n e x i o n = m y s q l _ c o n n e c t ( $ d b h o s t , $ d b u s e r , $ d b p a s s ) ;
1 0
1 1 i f ( ! $ m v c _ b d _ c o n e x i o n ) {
1 2 d i e ( ' N o h a s i d o p o s i b l e r e a l i z a r l a c o n e x i n c o n l a b a s e d e d a t o s : ' . m y s q l _ e r r o r ( ) ) ;
1 3 }
1 4 m y s q l _ s e l e c t _ d b ( $ d b n a m e , $ m v c _ b d _ c o n e x i o n ) ;
1 5
1 6 m y s q l _ s e t _ c h a r s e t ( ' u t f 8 ' ) ;
1 7
1 8 $ t h i s - > c o n e x i o n = $ m v c _ b d _ c o n e x i o n ;
1 9 }
2 0
2 1
2 2
2 3 p u b l i c f u n c t i o n b d _ c o n e x i o n ( )
2 4 {
2 5
2 6 }
2 7
2 8 p u b l i c f u n c t i o n d a m e A l i m e n t o s ( )
2 9 {
3 0 $ s q l = " s e l e c t * f r o m a l i m e n t o s o r d e r b y e n e r g i a d e s c " ;
3 1
3 2 $ r e s u l t = m y s q l _ q u e r y ( $ s q l , $ t h i s - > c o n e x i o n ) ;
3 3
3 4 $ a l i m e n t o s = a r r a y ( ) ;
3 5 w h i l e ( $ r o w = m y s q l _ f e t c h _ a s s o c ( $ r e s u l t ) )
3 6 {
3 7 $ a l i m e n t o s [ ] = $ r o w ;
3 8 }
3 9
4 0 r e t u r n $ a l i m e n t o s ;
4 1 }
4 2
4 3 p u b l i c f u n c t i o n b u s c a r A l i m e n t o s P o r N o m b r e ( $ n o m b r e )
4 4 {
4 5 $ n o m b r e = h t m l s p e c i a l c h a r s ( $ n o m b r e ) ;
4 6
4 7 $ s q l = " s e l e c t * f r o m a l i m e n t o s w h e r e n o m b r e l i k e ' " . $ n o m b r e . " ' o r d e r b y e n e r g i a d e s c " ;
4 8
4 9 $ r e s u l t = m y s q l _ q u e r y ( $ s q l , $ t h i s - > c o n e x i o n ) ;
5 0
5 1 $ a l i m e n t o s = a r r a y ( ) ;
5 2 w h i l e ( $ r o w = m y s q l _ f e t c h _ a s s o c ( $ r e s u l t ) )
5 3 {
5 4 $ a l i m e n t o s [ ] = $ r o w ;
5 5 }
5 6
5 7 r e t u r n $ a l i m e n t o s ;
5 8 }
5 9
6 0 p u b l i c f u n c t i o n d a m e A l i m e n t o ( $ i d )
6 1 {
6 2 $ i d = h t m l s p e c i a l c h a r s ( $ i d ) ;
6 3
6 4 $ s q l = " s e l e c t * f r o m a l i m e n t o s w h e r e i d = " . $ i d ;
6 5
6 6 $ r e s u l t = m y s q l _ q u e r y ( $ s q l , $ t h i s - > c o n e x i o n ) ;
6 7
6 8 $ a l i m e n t o s = a r r a y ( ) ;
6 9 $ r o w = m y s q l _ f e t c h _ a s s o c ( $ r e s u l t ) ;
7 0
7 1 r e t u r n $ r o w ;
7 2
7 3 }
7 4
El Modelo. Accediendo a la base de datos
25
7 5 p u b l i c f u n c t i o n i n s e r t a r A l i m e n t o ( $ n , $ e , $ p , $ h c , $ f , $ g )
7 6 {
7 7 $ n = h t m l s p e c i a l c h a r s ( $ n ) ;
7 8 $ e = h t m l s p e c i a l c h a r s ( $ e ) ;
7 9 $ p = h t m l s p e c i a l c h a r s ( $ p ) ;
8 0 $ h c = h t m l s p e c i a l c h a r s ( $ h c ) ;
8 1 $ f = h t m l s p e c i a l c h a r s ( $ f ) ;
8 2 $ g = h t m l s p e c i a l c h a r s ( $ g ) ;
8 3
8 4 $ s q l = " i n s e r t i n t o a l i m e n t o s ( n o m b r e , e n e r g i a , p r o t e i n a , h i d r a t o c a r b o n o , f i b r a , g r a s a t o t a l ) v a l u e s ( ' " .
8 5 $ n . " ' , " . $ e . " , " . $ p . " , " . $ h c . " , " . $ f . " , " . $ g . " ) " ;
8 6
8 7 $ r e s u l t = m y s q l _ q u e r y ( $ s q l , $ t h i s - > c o n e x i o n ) ;
8 8
8 9 r e t u r n $ r e s u l t ;
9 0 }
9 1
9 2 p u b l i c f u n c t i o n v a l i d a r D a t o s ( $ n , $ e , $ p , $ h c , $ f , $ g )
9 3 {
9 4 r e t u r n ( i s _ s t r i n g ( $ n ) &
9 5 i s _ n u m e r i c ( $ e ) &
9 6 i s _ n u m e r i c ( $ p ) &
9 7 i s _ n u m e r i c ( $ h c ) &
9 8 i s _ n u m e r i c ( $ f ) &
9 9 i s _ n u m e r i c ( $ g ) ) ;
1 0 0 }
1 0 1
1 0 2 }
Cuando el controlador requiere el uso del modelo, creamos un objeto de tipo $ m = n e w
M o d e l ( ) . El constructor de esta clase realiza una conexin con la base de datos y la pone
disponible a todos sus mtodos al aadir la conexin creada como atributo del objeto. Cada
funcin utiliza esta conexin para realizar su cometido contra la base de datos.
La ltima funcin de la clase, v a l i d a r D a t o s ( ) , es algo distinta, ya que no utiliza para nada
la conexin con la base de datos. Simplemente valida datos. Si la aplicacin fuera ms
compleja sera interesante crear una clase dedicada a la validacin. De manera que
atendamos al principio de la Separation of Concerns .
La configuracin de la aplicacin
A lo largo y ancho de todo el cdigo expuesto, aparece cada tanto una referencia a unos
atributos estticos de la clase C o n f i g . Por ejemplo, en la lnea 17 del archivo
a p p / C o n t r o l l e r . p h p , aparece C o n f i g : : $ m v c _ b d _ h o s t n a m e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
etctera. Se trata de parmetros de configuracin que hemos definido en una clase
denominada C o n f i g :
a p p / C o n f i g . p h p
1 < ? p h p
2
3 c l a s s C o n f i g
4 {
5 s t a t i c p u b l i c $ m v c _ b d _ h o s t n a m e = " l o c a l h o s t " ;
6 s t a t i c p u b l i c $ m v c _ b d _ n o m b r e = " a l i m e n t o s " ;
7 s t a t i c p u b l i c $ m v c _ b d _ u s u a r i o = " r o o t " ;
8 s t a t i c p u b l i c $ m v c _ b d _ c l a v e = " r o o t " ;
9 s t a t i c p u b l i c $ m v c _ v i s _ c s s = " e s t i l o . c s s " ;
1 0 }
Esta clase est disponible durante todo el script de manera que se pueden utilizar sus
valores a lo largo del cdigo, y cambiarlos sin ms que modificar este fichero.
Con esto ya tenemos todo el cdigo de la parte de servidor. Ya slo nos falta darle un toque
de estilo a los documentos HTML que enviamos al cliente y crear la base de datos que
almacenar los datos persistentes sobre los alimentos.
La configuracin de la aplicacin
26
Incorporar las CSS's
Crea el directorio w e b / c s s y en l coloca un archivo llamado e s t i l o . c s s con el siguiente
contenido:
w e b / c s s / e s t i l o . c s s
b o d y {
p a d d i n g - l e f t : 1 1 e m ;
f o n t - f a m i l y : G e o r g i a , " T i m e s N e w R o m a n " ,
T i m e s , s e r i f ;
c o l o r : p u r p l e ;
b a c k g r o u n d - c o l o r : # d 8 d a 3 d }
u l . n a v b a r {
l i s t - s t y l e - t y p e : n o n e ;
p a d d i n g : 0 ;
m a r g i n : 0 ;
p o s i t i o n : a b s o l u t e ;
t o p : 2 e m ;
l e f t : 1 e m ;
w i d t h : 9 e m }
h 1 {
f o n t - f a m i l y : H e l v e t i c a , G e n e v a , A r i a l ,
S u n S a n s - R e g u l a r , s a n s - s e r i f }
u l . n a v b a r l i {
b a c k g r o u n d : w h i t e ;
m a r g i n : 0 . 5 e m 0 ;
p a d d i n g : 0 . 3 e m ;
b o r d e r - r i g h t : 1 e m s o l i d b l a c k }
u l . n a v b a r a {
t e x t - d e c o r a t i o n : n o n e }
a : l i n k {
c o l o r : b l u e }
a : v i s i t e d {
c o l o r : p u r p l e }
a d d r e s s {
m a r g i n - t o p : 1 e m ;
p a d d i n g - t o p : 1 e m ;
b o r d e r - t o p : t h i n d o t t e d }
# c o n t e n i d o {
d i s p l a y : b l o c k ;
m a r g i n : a u t o ;
w i d t h : a u t o ;
m i n - h e i g h t : 4 0 0 p x ;
}
Fjate que en el archivo a p p / t e m p l a t e s / l a y o u t . p h p se incluye (lnea 6) este archivo CSS
que acabamos de crear. Como dicho layout decora a todas las plantillas, estos estilos
afectarn a todas las pginas.
La base de datos
En el Sistema Gestor de Base de Datos MySQL que vayas a utilizar, utilizando algn cliente
MySQL crea una base de datos para almacenar los alimentos. Introduce algunos registros
para probar la aplicacin.
Incorporar las CSS's
27
C R E A T E T A B L E ` a l i m e n t o s ` (
` i d ` i n t ( 1 1 ) N O T N U L L A U T O _ I N C R E M E N T ,
` n o m b r e ` v a r c h a r ( 2 5 5 ) N O T N U L L ,
` e n e r g i a ` d e c i m a l ( 1 0 , 0 ) N O T N U L L ,
` p r o t e i n a ` d e c i m a l ( 1 0 , 0 ) N O T N U L L ,
` h i d r a t o c a r b o n o ` d e c i m a l ( 1 0 , 0 ) N O T N U L L ,
` f i b r a ` d e c i m a l ( 1 0 , 0 ) N O T N U L L ,
` g r a s a t o t a l ` d e c i m a l ( 1 0 , 0 ) N O T N U L L ,
P R I M A R Y K E Y ( ` i d ` )
) E N G I N E = I n n o D B D E F A U L T C H A R S E T = u t f 8 ;
Lo importante para que la conexin funcione, es que los parmetros de conexin que se
establecen en el fichero a p p / C o n f i g . p h p coincidan con los de tu base de datos.
Sugerencia
Lo ms cmodo para desarrollar es tener todo en el mismo computador: tanto el
servidor web (apache) como el servidor de base de datos (MySQL). Adems es muy
prctico, aunque nada seguro, utilizar en el servidor MySQL el usuario r o o t
(superadministrador) como usuario de nuestras aplicaciones. As no hay que
preocuparse por temas de permisos. Repetimos: es lo ms cmodo pero, a la vez, lo
ms inseguro. Esta prctica est justificada NICAMENTE en un entorno de desarrollo
en local, donde la seguridad, en principio no es primordial.
Ya puedes juntar todas las piezas y probar la aplicacin introduciendo en tu navegador la
URL correspondiente:
h t t p : / / t u . s e r v i d o r / r u t a / a / a l i m e n t o s / w e b / i n d e x . p h p
Suerte!
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll para recorrer
todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Comentarios
28
Unidad 3: Symfony2 a vista de pjaro
El objetivo fundamental de este curso es que aprendas a desarrollar aplicaciones web de
calidad con Symfony2, un potente framework de desarrollo en PHP. Para conocerlo en
profundidad utilizaremos una estrategia de acercamiento en espiral a nuestro objeto de
estudio, es decir, lo rodearemos trazando crculos de radio cada vez ms pequeos, de
forma que cada vuelta que demos nos revelar un conocimiento ms profundo de esta
magnfica herramienta de desarrollo.
Esta estrategia nos proporcionar resultados visibles y prcticos desde el principio, desde la
primera vuelta, aunque ello ser a costa de tratar superficialmente algunos conceptos que,
progresivamente, irn definindose con ms precisin a medida que avanza el curso.
En esta unidad volveremos a desarrollar la aplicacin que construimos en la unidad 2. Pero
ahora utilizaremos Symfony2. Ser nuestra primera vuelta, la menos precisa pero la ms
panormica, que nos proporcionar una primera imagen del framework an difusa pero
suficiente para mostrar sus caractersticas fundamentales.
Qu es Symfony2?
La respuesta rpida, no por ello menos cierta, es que Symfony2 es un framework PHP para el
desarrollo de aplicaciones web. Sin embargo, los creadores de Symfony2 no la daran por
buena. Posiblemente tampoco aprobasen completamente lo que voy a decir a continuacin
(son unos programadores, adems de excelentes, muy "quisquillosos"!).
El objetivo principal de Symfony2 no es tanto desarrollar otro framework PHP, como
desarrollar un conjunto de componentes estables, independientes, fcilmente acoplables
(que no acoplados) para formar sistemas ms complejos, mediante los cuales se puedan
resolver problemas relacionados con el desarrollo de aplicaciones web en PHP. Obviamente
no es necesario utilizar todos los componentes de Symfony2; gracias a su absoluta
independencia se pueden utilizar nicamente aquellos que se requieran para resolver el
problema en cuestin. Symfony2 se presenta as como una especie de Lego en el mundo del
desarrollo en PHP.
Con estos componentes, uno de los problemas que se puede resolver es el de la creacin de
un framework de desarrollo web. De hecho, uno de los primeros productos que se han
construido con estos componentes ha sido el framework de desarrollo conocido como
Distribucin Standard de Symfony2, o ms brevemente Symfony2 sin ms. Y es
precisamente del estudio de esta distribucin de lo que trata este curso.
Por ello, el trmino Symfony2 puede referirse a:
1. Los componentes de Symfony2
2. El framework de Symfony2, o distribucin standard de Symfony2.
En el momento en que se escribe este texto, lo ms probable es que se trate de lo segundo.
Los componentes de Symfony2, se estn utilizando para otros proyectos
6
, y su uso
aumentar cuando dispongan de una documentacin tan exhaustiva y bien elaborada como
la que cuenta en estos momentos el framework Symfony2. Es muy probable (ojal) que
cuando este curso vea la luz esta situacin haya cambiado
7
. Entonces, a lo mejor sera
interesante elaborar otro curso sobre los componentes de Symfony2, pero sera eso: otro
curso. Los componentes son los elementos de bajo nivel del framework Symfony2, y para
utilizar dicho framework no es necesario conocerlos.
De todas formas vamos a aprovechar este apartado para enumerar los componentes de
Symfony2, dando una breve descripcin de su funcionalidad. Pero, repetimos: no es
necesario conocerlos para utilizar el framework, que es de lo que trata este curso. Damos
esta informacin con el fin de calmar al curioso y de aclarar las posibles confusiones que se
Unidad 3: Symfony2 a vista de pjaro
29
dan al hablar de Symfony2 entre los componentes y el framework. Obviamente, el contexto
aclarar de que se habla en cada ocasin.
Componente Descripcin
DependencyInjection Automaticacin de la Inyeccin de dependencias
EventDispatcher Implementacin del patrn observer
HttpFoundationProporciona una capa orientada a objetos para el tratamiento del HTTP
DomCrawler Para navegar el DOM de un documento HTML
ClassLoader Autoloader que implementa PSR-0 standard (forma standard de autocargar
classes con espacios de nombre)
CssSelector Para convertir un CSS selector en un XPath
HttpKernel Proporciona la parte dinmica de la especificacin HTTP
BrowserKit Simula el comportamiento de un Browser
Templating Proporciona elementos para construir sistemas de plantillas
Translation Soporte para las traducciones
Serializer Para crear arrays a partir de estructuras complejas como grficos
Validator Validaciones basadas en JSR-303 Bean Validation specification
Security Infraestructura para el tratamiento de la autentificacin y la autorizacin
Routing Potente sistema para asociar rutas a acciones
Console Para desarrollar herramientas CLI
Process Para ejecutar procesos del sistema
Config Herramientas para cargar configuraciones de distintas fuentes
Finder Para encontrar archivos en el sistema de archivos
Locale Para tratar la localizacin cuando no est disponible la extensin i n t l
Yaml Para la manipulacin de archivos de configuracin en formato YAML
Form Herramientas para definir formularios, pintarlos y asociarle datos
Y estas son las "tripas" del monstruo Symfony2. No hablaremos mucho ms acerca de estos
componentes a lo largo del cursos. Pero que sepas que existen. Puede que te ayuden a
resolver tu prximo proyecto, y muy probablemente sean los ladrillos fundamentales con los
que se construyan muchas de las aplicaciones PHP en un futuro no muy lejano.
Instalacin y configuracin de Symfony2
A partir de este momento, y mientras no lo especifiquemos explicitamente, cuando
hablemos de Symfony2 nos estamos refiriendo al framework, concretamente a la edicin
estndard.
En este apartado vamos a instalar y configurar Symfony2, y lo dejaremos listo para construir
la aplicacin de gestin de alimentos sobre l.
Bjate de http://symfony.com/download la ltima versin de la rama 2.0 de Symfony2. Vers
que hay una modalidad normal y otra without vendors. Utiliza la primera.
Instalacin y configuracin de Symfony2
30
Nota
La modalidad normal contiene todas las libreras de terceros (vendors) necesarias
para comenzar a trabajar con el framework, mientras que la modalidad without
vendors, como su nombre indica, viene sin estas libreras, razn por lo que hay que
instalarlas posteriormente mediante una herramienta incluida con Symfony2
(b i n / v e n d o r s ) que utiliza el sistema de control de versiones g i t para bajar las
ltimas versiones desde el repositorio de github
8
, donde se encuentra todo el cdigo
de Symfony2.
Descomprme el archivo descargado en algn directorio accesible al servidor web, esto es,
dentro de su Document root. Para que la aplicacin funcione, el servidor web debe poder
escribir en los directorios a p p / c a c h e y a p p / l o g s . Si ests utilizando un sistema operativo
tipo UNIX (Ubuntu, MacOSX, etctera), la forma ms fcil de dar dichos permisos es:
c h m o d - R 7 7 7 a p p / c a c h e a p p / l o g s
Nota
Durante toda la unidad suponemos que has hecho esta operacin directamente en el
Document root del servidor web, de manera que tendr la siguiente estructura de
directorios:
/ v a r / w w w / ( o d o n d e t e n g a s m a p e a d o t u D o c u m e n t r o o t )
|
S y m f o n y
|
L I C E N S E
R E A D M E . m d
a p p /
b i n /
d e p s
d e p s . l o c k
s r c /
v e n d o r /
w e b /
Y que tanto el servidor web como el servidor de MySQL estn instalado en la mquina
local.
A continuacin comprobamos que nuestro sistema cumple los requisitos mnimos ejecutando
por la interfaz de comandos la siguiente orden:
p h p a p p / c h e c k . p h p
Instalacin y configuracin de Symfony2
31
Si el resultado nos seala algn error, debemos resolverlo antes de continuar. Una vez que
pasemos al menos los requisitos obligatorios (mandatory requirements), podemos ejecutar
la demo que viene incorporada en la distribucin standard de Symfony2. Para ello apunta
con tu navegador a la siguiente URL:
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p
Y juega un poquito!, Por ejemplo, pica en Run the demo y navega por los distintos enlaces.
Fjate en la pinta que tienen las URL's. La demo muestra el cdigo que genera las pginas de
la propia demo. Fjate en l detenidamente. Vers que muestra dos partes: el del controlador
y el de la plantilla (template). Te suena?. Si no es as: G O T O u n i d a d 2 .
Ya has visto en accin la primera aplicacin construida con Symfony2. Ahora vamos a
describir la manera en que Symfony2 organiza el cdigo.
Como en el mini-framework que hemos construido en la unidad 2, Symfony2 tambin
organiza los archivos en dos grupos: los que deben estar directamente accesibles al servidor
web (CSS's, Javascript, imgenes y el controlador frontal) y los que pueden ser incluidos
desde el controlador frontal (libreras PHP y ficheros de configuracin). Los primeros viven en
el directorio w e b , y los segundos, segn su funcionalidad, estn repartidos entre los
directorios a p p , s r c y v e n d o r . Si has asimilado bien todo lo que se ha dicho en la unidad 2,
estars pensando que en una instalacin en un entorno de produccin, el Document root
del servidor web (o del Virtual host dedicado para la aplicacin), debe coincidir con el
directorio w e b , y que el resto de directorios deben ubicarse fuera del Document root. Si es
as, ests en lo cierto, y si no, te sugerimos que antes de continuar con esta unidad, repases
la anterior.
Nota
Para paliar el efecto de posibles despistes o malas prcticas por desconocimiento,
pereza y otras fatales causas, los directorios s r c y a p p , contienen un fichero
. h t a c c e s s que indica al servidor web que no debe mostrar su contenido.
Veamos ahora para que se utiliza cada uno de estos directorios.
El directorio web
Poco hay que decir ya de este directorio, aqu encontraremos el controlador frontal y todos
los assets de la aplicacin: CSS's, Javascipts, imgenes, etctera.
Esta es la estructura del directorio:
w e b
a p p _ d e v . p h p
a p p l e - t o u c h - i c o n . p n g
a p p . p h p
b u n d l e s
a c m e d e m o
f r a m e w o r k
s e n s i o d i s t r i b u t i o n
w e b p r o f i l e r
c o n f i g . p h p
f a v i c o n . i c o
El directorio web
32
r o b o t s . t x t
Podemos ver 3 scripts PHP:
c o n f i g . p h p es un script que asiste en la configuracin del framework. No es
imprescindible. De hecho cuando uno se siente confortable con Symfony2, es ms
sencillo realizar la configuracin directamente sobre el cdigo fuente. Pero para
empezar puede servir de ayuda. Si lo utilizas ten en cuenta los permisos de los ficheros
del directorio a p p / c o n f i g , pues este script debe poder escribir all.
a p p . p h p es el controlador frontal de la aplicacin. No vamos a repetir aqu que este
concepto. Si no sabes de que hablamos, T H E N G O T O U n i d a d 2 .
a p p _ d e v . p h p tambin es el controlador frontal de la aplicacin. Cmo? dos
controladores frontales? eso no encaja con lo que hemos aprendido!. Bueno tranquilos,
tiene su explicacin. Se trata de lo que se denomina en Symfony2 el controlador frontal
de desarrollo. En principio pinta lo mismo que a p p . p h p , pero le aade una barra de
depuracin que ofrece muchsima informacin sobre todo lo relacionado con la
ejecucin del script. Puedes ver la barra de depuracin en la demo que has ejecutado
hace un momento. Se encuentra abajo de la pgina. Explrala un poco, te asombrars
de la cantidad de informacin que te proporciona. Cuando desarrollamos es muy
conveniente utilizar este controlador frontal, pero en produccin NUNCA debe utilizarse,
pues daramos a los usuario de la web informacin que podra comprometer nuestro
sistema.
Por otro lado los assets se ubicarn en el directorio b u n d l e s / n o m b r e _ b u n d l e , donde
n o m b r e _ b u n d l e es el nombre del bundle al que pertenece el asset en cuestin. Vale, y que
es un bundle?, pues por lo pronto quedate con que "es la unidad funcional de cdigo que
utiliza Symfony2". Algo as como una de las piezas del Lego Symfony2. En la seccin Los
Bundles: Plugins de primera clase hablaremos de estos "personajes" con ms detalle.
El directorio a p p
La finalidad de este directorio es alojar a a los scripts PHP encargados de los procesos de
carga del framework (lo que se conoce como bootstraping) y a todo lo que tenga que ver
con la configuracin general de la aplicacin. Los archivos de este directorio son los
encargados de unir y dar cohesin a los distintos componentes del framework.
Son especialmente importantes los ficheros a u t o l o a d . p h p y A p p K e r n e l . p h p , ya que hay
que tocarlos cada vez que extendemos el framework con nuevas funcionalidades, es decir
cada vez que incorporamos nuevos bundles (vamos poniendo en circulacin a esta palabreja
que usaremos hasta la saciedad).
En a u t o l o a d . p h p se mapean los espacios de nombres contra los directorios en los que
residirn las clases pertenecientes a dichos espacios de nombre. De esa manera el proceso
de autocarga de clases sabr donde tiene que buscar las clases cuando se usen dichos
espacios, sin necesidad de incluir explicitamente (esto es, usando i n c l u d e o r e q u i r e ) los
archivos donde se definen las clases.
En A p p K e r n e l . p h p , se declaran los bundles que se utilizarn en la aplicacin.
En el directorio c o n f i g se encuentran los archivos de configuracin de la aplicacin:
c o n f i g . y m l , r o u t i n g . y m l y s e c u r i t y . y m l .
El sistema de configuracin de Symfony2 permite trabajar con distintos entornos de
ejecucin. Los ms tpicos son p r o d , para produccin y d e v , para desarrollo. Pero se pueden
definir tantos entornos como deseemos. En el controlador frontal se indica qu entorno
deseamos utilizar en la ejecucin del script. Fjate en la lnea 22 de w e b / a p p _ d e v . p h p , o en
la lnea 9 del w e b / a p p . p h p :
El directorio app
33
. . .
$ k e r n e l = n e w A p p K e r n e l ( ' p r o d ' , f a l s e ) ;
. . .
El primer argumento decide el entorno de ejecucin que se utilizar, y el segundo sirve para
habilitar los mensajes de depuracin
9
. Y para que sirve esto del entorno de ejecucin?.
Symfony2 utiliza este dato para saber qu ficheros de configuracin debe cargar.
Supongamos, por ejemplo, que se especifica d e v como entorno de ejecucin. Entonces, si
existe el fichero c o n f i g _ d e v . y m l lo cargar, y si no es as cargar c o n f i g . y m l . Lo mismo
ocurre con los ficheros r o u t i n g . y m l , s e c u r i t y . y m l y s e r v i c e s . y m l . Ms adelante
estudiaremos para que sirven cada uno de ellos. Por lo pronto nos conformaremos con saber
la dinmica de funcionamiento.
Los entornos proporcionan mucha flexibilidad a la hora de desarrollar una aplicacin. Vamos
a ilustrar con un ejemplo esta flexibilidad. Un caso que nos encontramos habitualmente es
que la aplicacin que estamos construyendo debe enviar e-mails. Es bastante molesto tener
que disponer de cuentas reales y gestionarlas para que podamos probar la aplicacin
mientras desarrollamos. Podemos utilizar este sistema de configuracin para indicar al
framework que en el entorno de desarrollo se enven todos los e-mails a una sola cuenta, o
incluso que no se enven. Otro ejemplo tpico podra ser el definir unos parmetros de
conexin a la base de datos para el entorno de produccin y otro para el de desarrollo.
Una estratega muy adecuada para tratar con los ficheros de configuracin cuando
queremos que haya partes comunes y partes diferentes en cada entorno, es definir todos los
parmetros comunes en el fichero f i c h c o n f i g . y m l (donde f i c h c o n f i g es c o n f i g ,
s e c u r i t y , r o u t i n g o s e r v i c e s ), y los particulares de cada entorno en el fichero
f i c h c o n f i g _ e n v . y m l (donde e n v es d e v , p r o d o cualquier otro nombre de entorno que
usemos). Por ltimo importamos los primeros (comunes) desde los ltimos (particulares) de
la siguiente manera:
Inicio del fichero f i c h c o n f i g _ e n v . y m l
i m p o r t s :
- { r e s o u r c e : f i c h c o n f i g . y m l }
. . .
Puedes comprobar que esta es la estrategia utilizada por la distribucin standard de
Symfony2 con los ficheros c o n f i g . y m l , c o n f i g _ d e v . y m l y c o n f i g _ p r o d . y m l .
Para acelerar la ejecucin de los scripts, la configuracin, el enrutamiento y las plantillas de
twig son compiladas y almacenadas en el directorio c a c h e . Por otro lado, los errores y otra
informacin de inters acerca de eventos que ocurren cuando se ejecuta el framework, son
registrados en archivos que se almacenan en el directorio l o g s . Por eso estos dos
directorios deben tener permisos de escritura para el servidor web.
Por ltimo, en este directorio tan "denso", encontramos la navaja suiza de Symfony2, la
aplicacin a p p / c o n s o l e . Prueba a ejecutarla sin pasarle ningn argumento. Vers una lista
con todas las tareas que se pueden lanzar por lnea de comandos.
p h p a p p / c o n s o l e
El directorio v e n d o r
Aqu se aloja todo el cdigo funcional que no es tuyo. Es lo que tradicionalmente se conoce
como libreras de terceros. Entre otras cosas, el directorio contiene los componentes de
El directorio vendor
34
Symfony2, el ORM Doctrine2 y el sistema de plantillas twig. Cuando amplies tu aplicacin
con nuevos bundles de terceros instalados automticamente con la aplicacin b i n / v e n d o r s ,
ser aqu donde se ubique el cdigo.
El directorio s r c
Es el directorio donde colocars tu cdigo. Ms concretamente: tus bundles. A base de
utilizar este palabro acabars por asimilarlo antes de que te lo expliquemos :-).
El directorio b i n
El nombre de este directorio es un clsico en el mundo UNIX. En l se colocan archivos
ejecutables. La distribucin standard solo trae el ejecutable v e n d o r s que se utiliza, en
combinacin con el fichero d e p s (dependencias), para instalar componentes de terceros
(vendors).
Y con esto acabamos la descripcin de los directorios de Symfony2. Ha llegado el momento
de hablar de los bundles, esos grandes desconocidos (por ahora!).
Los Bundles: Plugins de primera clase
Si los creadores de Symfony2 hubieran elegido la palabra plugin en lugar de bundle, es
probable que te hubieses hecho una idea ms concreta de lo que es un bundle. Pues bien,
por lo pronto, piensa que un bundle es un plugin, por que no es ni ms ni menos que eso.
Cualquier framework que se precie debe ofrecer un mecanismo de extensin que permita
ampliar la aplicacin sin compromenter la escalabilidad. Para ello las piezas que se aaden
al sistema deben ser bloques prcticamente autnomos y con una interfaz sencilla para
engancharlos (to plug, en ingls) al sistema. A estos bloques se les conoce a lo largo y ancho
de la galaxia con el nombre de plugin (o complemento, en castellano). Por qu los
creadores de Symfony2 han decidido llamarles bundles en su lugar? Lo mismo hay alguna
razn terica que se me escapa. Pero de lo que si estoy seguro es de que hay una razn
histrica:
El antecesor de Symfony2, el fantstico symfony 1.x organiza el cdigo en aplicaciones, que
a su vez estn formadas por mdulos con la implementacin de las acciones. Adems ofrece
un mecanismo de extensin basado en plugins, los cuales tambin organizan el cdigo en
mdulos con sus acciones. Pero a pesar de este paralelismo las aplicaciones son "ms
importantes" que los plugins. De hecho, las aplicaciones pueden usar mdulos de los
plugins, pero lo contrario no tiene sentido tal y como est concebido symfony 1.x. Con el
tiempo los desarrolladores se dieron cuenta de que era ms fcil de mantener y organizar
los plugins, ya que son bloques de cdigo autnomos y fcilmente acoplables a la aplicacin.
Este hecho llev de forma natural a reorganizar la aplicacin colocando todo el cdigo
funcional en los plugins. Las aplicaciones se quedaban prcticamente vacas de cdigo y tan
solo contenan ficheros de configuracin.
As pues, en Symfony2 decidieron olvidarse del concepto de aplicacin (en el sentido de
symfony 1.x), y obligar a que todo el cdigo funcional se organizase en plugins. Es como
hacer a los plugins ciudadanos de primera clase del framework. Finalmente, para evitar
cualquier confusin y dirimir la diferencia entre plugin y aplicacin, decidieron usar la
palabra bundle. Y eso es todo. Si no conoces symfony 1.x, seguro que hubieras preferido
llamarle plugin. Y si lo conoces es probable que tambin.
En fin, lo que realmente debes saber:
El directorio src
35
Importante
Un bundle no es ms que un directorio que aloja todo aquello relativo a una
funcionalidad determinada. Puede incluir clases PHP, plantillas, configuraciones, CSS's
y Javascript.
La aplicacin gestin de alimentos en Symfony2
Sin ms prembulo vamos a comenzar a reimplementar con Symfony2 la misma aplicacin
que hemos desarrollado en la unidad 2 . Este ejercicio nos va a permitir conocer los
conceptos ms bsicos del framework, muchos de los cuales sern profundizados conforme
avancemos en el curso.
Generacin de un Bundle
La primera idea que debe quedar clara, expresada de manera simplista, es que "todo es un
bundle" en Symfony2. Por tanto, si queremos desarrollar una aplicacin necesitaremos, por
lo menos, tener un bundle para alojar el cdigo de la misma. Comencemos por ah. El
siguiente comando de Symfony2 nos ayuda a generar el esqueleto de un bundle de manera
interactiva:
p h p a p p / c o n s o l e g e n e r a t e : b u n d l e
A cada pregunta que nos hace le acompaa una pequea ayuda. En primer lugar nos
pregunta por el espacio de nombre que compartiran las clases del bundle. La
recomendacin, como se dice en el texto de ayuda del comando, es que comience por el
nombre del fabricante del bundle, el nombre del proyecto o del cliente, seguido,
opcionalemente, por una o ms categoras, y finalizar con el nombre del bundle seguido del
sufijo Bundle. Es decir el nombre completo del espacio de nombres del bundle debe seguir el
siguiente patrn:
F a b r i c a n t e / c a t e g o r i a 1 / c a t e g o r i a 2 / . . / c a t e g o r i a N / n o m b r e b u n d l e B u n d l e
Ilustremos esto con varios ejemplos de nombres de bundles vlidos:
A u l a s M e n t o r / A l i m e n t o s B u n d l e
A u l a s M e n t o r / U n i d a d 3 / A l i m e n t o s B u n d l e
A u l a s M e n t o r / C u r s o S f 2 / U n i d a d 3 / A l i m e n t o s B u n d l e
J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e
Nos quedaremos con el ltimo de los nombres para el bundle que vamos a construir. Con
este nombre se quiere expresar algo as como que el bundle A l i m e n t o s B u n d l e ha sido
creado por J a z z y w e b (una empresa ficticia) para el cliente A u l a s M e n t o r . Como ves,
cualquier nombre vale siempre que contenga un nombre de fabricante (vendor name) y un
nombre de bundle. En medio podemos poner lo que queramos para organizar nuestro
trabajo.
Introduce J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e como espacio de nombres del bundle.
A continuacin nos pregunta por el nombre del bundle. Y nos ofrece una recomendacin que
es el mismo nombre del espacio de nombres anterior pero sin los separadores / . El nombre
del bundle es importante pues, en ocasiones, hay que referirse al bundle por este nombre.
La aplicacin gestin de alimentos en Symfony2
36
Presiona e n t e r para aceptar la sugerencia.
El prximo paso es asignarle una ubicacin en la estructura de directorios del proyecto. La
flexibilidad de Symfony2 permite que lo coloques donde quieras. Pero es muy recomendable
que lo coloques en el directorio s r c , ya que est pensado para alojar nuestro cdigo. Si lo
haces as, te ahorrars tener que incluir una lnea de cdigo en el fichero a p p / a u t o l o a d . p h p
para registrar el espacio de nombre en el sistema de autocarga de clases. Esto ltimo es as
porque en dicho fichero ya se ha contemplado que todas las clases que se aloje en s r c sean
autocargadas asignndole como espacio de nombre raz el mismo nombre que la estructura
de directorios computada desde s r c .
Presiona e n t e r para aceptar la sugerencia. Cuando termines de generar el bundle vers
como se ha creado en s r c el directorio J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e , es decir
un directorio que tiene la misma estructura que el espacio de nombres que hemos asignado
al bundle. Esto es lo que se quiere decir de manera genrica en el prrafo anterior.
Los bundles llevarn asociados algo de configuracin. Como mnimo ser necesario
configurar las rutas que mapean las URL's en acciones del bundle. Symfony2 admite 4
formas de representar las configuraciones: con ficheros XML, YML o PHP, y mediante
anotaciones, que es una manera de expresar parmetros de configuracin en el propio
cdigo funcional aprovechando para ello los comentarios de PHP.
Ms adelante tendremos ocasin de utilizar las anotaciones y las entenders mejor. Llegados
a este punto hemos de decir que la eleccin es una cuestin de gusto; discutir con alguien
acerca de cual es la mejor opcin sera una prdida de tiempo. Para el caso de la
configuracin de los bundles (prcticamente para definir rutas como veremos despus)
hemos elegido los fichero YAML como formato para la configuracin.
Selecciona (escribe) y m l como formato de configuracin.
Por ltimo contesta y e s a la pregunta de si quieres generar la estructura completa. Esta
opcin generar algunos directorios y archivos extra que siguen las recomendaciones de
Symfony2 para alojar cdigo. Es posible que no los utilices, pero no hacen "dao" y sugieren
como debe organizarse el cdigo. No obstante el programador tiene bastante libertad a la
hora de organizar los archivos del bundle como quiera.
Confirma la generacin del cdigo. Una vez generado, el asistente te realizar dos preguntas
ms. Primera pregunta: quieres actualizar automticamente el Kernel? y segunda pregunta
quieres actualizar directamente el routing? Contesta a las dos afirmativamente. Vamos a
ver con ms detalle las consecuencias de estas actualizaciones automticas.
Por una parte el bundle, como ya hemos explicado, es un bloque desacoplado y reutilizable
de cdigo que agrupa a una serie de funcionalidades. Si queremos utilizarlo en nuestro
proyecto debemos "notificarlo" al framework. Es decir, hemos de "engancharlo". Esto se
hace registrndolo en el archivo a p p / A p p K e r n e l . p h p . La primera actualizacin automtica
ha realizado dicho registro. Abre ese archivo y fjate como al final del mtodo
r e g i s t e r B u n d l e s ( ) aparece la siguiente lnea:
. . .
n e w J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e ( ) ,
. . .
Dicha lnea ha sido insertada automticamente como consecuencia de haber respondido
afirmativamente a la primera pregunta. El cometido de la lnea es registrar el bundle recien
creado en el framework para poder hacer uso del mismo.
La segunda actualizacin automtica "enlaza" la tabla enrutamiento general de la aplicacin
con la tabla de enrutamiento particular del bundle. La tabla de enrutamiento es la
La aplicacin gestin de alimentos en Symfony2
37
responsable de indicar al framework como deben mapearse las URL's en acciones PHP. Para
ver como se ha realizado este enlace mira el fichero a p p / c o n f i g / r o u t i n g . y m l :
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e :
r e s o u r c e : " @ J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l "
p r e f i x : /
Estas lneas han sido introducidas automticamente como consecuencia de contestar
afirmativamente a la segunda pregunta. Observa que el apartado resource es la direccin en
el sistema de ficheros de la tabla de enrutamiento propia del bundle que acabamos de crear.
Symfony2 sabe convertir @ J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e en la ubicacin del
bundle pues est debidamente registrado.
Es importante que conozcas como se acopla un bundle a la aplicacin, pues si falla la
actualizacin automtica del A p p K e r n e l . p h p y/o del r o u t i n g . y m l , debes realizarlas
manualmente.
Ahora puedes echarle un vistazo al fichero r o u t i n g . y m l del bundle
(s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l ). Vers
que existe una ruta mapeada contra una accin. Despus explicaremos los detalles de la
ruta. Esta ltima ruta sirve para probar el bundle. As que accede desde tu navegador web a
la siguiente URL (que es la que se corresponde con esta ruta de prueba)
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / h e l l o / a l b e r t o
Si todo va bien, obtendrs como respuesta un saludo. Puedes cambiar el nombre del final de
la ruta.
Resumiendo: Para desarrollar nuestra aplicacin hemos de contar al menos con un bundle
para escribir el cdigo. Segn la complejidad de la aplicacin ser ms o menos adecuado
organizar el cdigo en varios bundles. El criterio a seguir es el de agrupar en cada bundle
funcionalidades similares o del mismo tipo. Los bundles son bloques desacoplados y tienen
asociado un espacio de nombre. Para acoplar un bundle al framework hay que :
1. Registrar el espacio de nombre en el sistema de autocarga (fichero a p p / a u t o l o a d . p h p .
Este paso no es necesario si ubicamos al bundle en el directorio s r c .
2. Registrar al bundle en el fichero a p p / A p p K e r n e l . p h p . Esta operacin se puede hacer
automticamente a travs del generador interactivo de bundles, pero si fallase por
alguna razn (por ejemplo que los permisos de dicho archivo no estn bien definidos).
Habra que hacerlo a mano.
3. Importar las tablas de enrutamiento del bundle en la tabla de enrutamiento de la
aplicacin.
Anatoma de un Bundle
Si has seguido las indicaciones que hemos dado en esta unidad, debes tener en tu directorio
s r c dos directorios: J a z z y w e b y A c m e . El primero se corresponde con el bundle que
acabamos de crear, y el segundo es un ejemplo que viene de serie con la distribucin
standard de Symfony2 y que contiene el cdigo de la demo con la que has jugado hace un
rato. Vamos a utilizar este ltimo para realizar la diseccin de un bundle, ya que est ms
rellenito de cdigo que nuestro recien horneado y esqueltico bundle.
A c m e /
D e m o B u n d l e
A c m e D e m o B u n d l e . p h p
Anatoma de un Bundle
38
C o n t r o l l e r
D e m o C o n t r o l l e r . p h p
S e c u r e d C o n t r o l l e r . p h p
W e l c o m e C o n t r o l l e r . p h p
C o n t r o l l e r L i s t e n e r . p h p
D e p e n d e n c y I n j e c t i o n
A c m e D e m o E x t e n s i o n . p h p
F o r m
C o n t a c t T y p e . p h p
R e s o u r c e s
c o n f i g
s e r v i c e s . x m l
p u b l i c
c s s
d e m o . c s s
i m a g e s
b l u e - a r r o w . p n g
f i e l d - b a c k g r o u n d . g i f
l o g o . g i f
s e a r c h . p n g
w e l c o m e - c o n f i g u r e . g i f
w e l c o m e - d e m o . g i f
w e l c o m e - q u i c k - t o u r . g i f
v i e w s
D e m o
c o n t a c t . h t m l . t w i g
h e l l o . h t m l . t w i g
i n d e x . h t m l . t w i g
l a y o u t . h t m l . t w i g
S e c u r e d
h e l l o a d m i n . h t m l . t w i g
h e l l o . h t m l . t w i g
l a y o u t . h t m l . t w i g
l o g i n . h t m l . t w i g
W e l c o m e
i n d e x . h t m l . t w i g
T e s t s
C o n t r o l l e r
D e m o C o n t r o l l e r T e s t . p h p
T w i g
E x t e n s i o n
D e m o E x t e n s i o n . p h p
A c m e D e m o B u n d l e . p h p es una clase que extiende a
S y m f o n y \ C o m p o n e n t \ H t t p K e r n e l \ B u n d l e \ B u n d l e y que define al bundle. Se utiliza en
el proceso de registro del mismo (recuerda, en el fichero a p p / A p p K e r n e l . p h p ). Todos
los bundles deben incorporar esta clase (bueno, el nombre cambiar segn el nombre
del bundle)
C o n t r o l l e r es el directorio donde se deben colocar los controladores con las distintas
acciones del bundle. Recuerda el concepto de controlador y accin que se estudi en la
unidad 2. Lo lgico y recomendado, es crear una clase C o n t r o l l e r por cada grupo de
funcionalidades. Pero no es una exigencia, si quieres puedes colocar todas tus acciones
en el mismo controlador. Cuando se genera un bundle se crea el controlador
Anatoma de un Bundle
39
DefaultController.
D e p e n d e n c y I n j e c t i o n . Una de las caractersticas ms sobresaliente de Symfony2 es el
uso intensivo que hace de la Inyeccin de Dependencias, un potente patrn de diseo
mediante el que se facilita la creacin y configuracin de objetos que prestan servicios
en una aplicacin gracias a la gestin automtica de sus dependencias. Contribuye a
crear un cdigo ms desacoplado y coherente. La unidad 4 se ha dedicado
exclusivamente a presentar este concepto. Aunque no es un patrn complicado, es
dificil de explicar con precisin y claridad.
Symfony2 nos ofrece dos maneras de "cargar" la configuracin de las dependencias y
los servicios creados. Una ms sencilla y directa, y otra ms elaborada y apropiada para
el caso en que nuestro bundle vaya a ser distribuido con la intencin de que se utilice
en otros proyectos Symfony2. En este directorio se ubican las clases relacionadas con
este segundo mtodo de gestionar las dependencias.
R e s o u r c e s , es decir, recursos. Entendemos por recursos: los ficheros de configuracin
del bundle (directorio c o n f i g ), los assets que requiere el bundle para enviar en sus
respuestas (directorio p u b l i c ) y las plantillas con las que se renderizan (pintan) el
resultado de las acciones de los controladores (directorio v i e w s ). Fjate como en este
bundle, las plantillas estn organizadas en tres directorios (D e m o , S e c u r e d y W e l c o m e )
cuyos nombres coinciden con los de los controladores.
T e s t , es el directorio donde viven las pruebas unitarias y funcionales del bundle.
Estos son los directorios ms tpicos de cualquier bundle, de hecho son los que se generan
automticamente con el comando a p p / c o n s o l e g e n e r a t e : b u n d l e . Sin embargo un bundle
puede tener muchos ms directorios y ficheros, organizados como su creador crea
conveniente. En el caso del bundle A c m e D e m o B u n d l e , puedes ver los siguientes "extras":
F o r m es el directorio donde se colocarn las clases que definen los formularios de la
aplicacin.
C o n t r o l l e r L i s t e n e r . p h p describe un event listener que es un mecanismo muy
adecuado de extender y alterar el flujo del framework sin tener que tocar el cdigo
original del componente del framework que utiliza dicho sistema. Se trata de una
caracterstica avanzada de Symfony2 raramente utilizada cuando uno se esta iniciando.
T w i g es un directorio propio de este bundle, en el que se ha implementado una
extensin del sistema de plantillas.
Ahora ya nos encontramos con un mnimo bagaje para emprender el desarrollo del bundle
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e y, por tanto de la aplicacin.
Flujo bsico de creacin de pginas en Symfony2
La creacin de pginas web con Symfony2 involucra tres pasos:
1. Creacin de la ruta que mapea la URL de la pgina en una accin de algn controlador.
Dicha ruta se registra en el archivo c o n f i g / R e s o u r c e s / r o u t i n g . y m l del bundle, que a
su vez debe estar correctamente importado en el archivo de rutas general de la
aplicacin a p p / c o n f i g / r o u t i n g .
2. Creacin de dicha accin en el controlador correspondiente. La accin, haciendo uso del
modelo, realizar las operaciones necesarias y obtendr los datos crudos (raw), es decir
sin ningn tipo de formato, que facilitar a una plantilla para ser pintados
(renderizados). El cdigo de los controladores debe ubicarse en el directorio
C o n t r o l l e r s del bundle.
3. Creacin de dicha plantilla. Esto se hace en el directorio R e s o u r c e s / v i e w s . Con el fin
de organizar bien las plantillas, es recomendable crear un directorio con el nombre de
Flujo bsico de creacin de pginas en Symfony2
40
cada controlador. Tambin es muy recomendable utilizar Twig como sistema de
plantillas, aunque tambin se puede utilizar PHP.
Como puedes comprobar el procedimiento no es muy diferente al que hemos estudiado en la
unidad anterior.
Estos pasos son, por supuesto, una gua general y mnima que debemos seguir en la
creacin de las pginas de nuestra aplicacin. No obstante, en muchos casos tendremos que
realizar otras operaciones que se salen de este flujo y que tienen que ver ms con la
construccin del modelo de la aplicacin.
Definicin de las rutas del bundle
En la unidad anterior propusimos las siguientes rutas para acceder a las distintas partes de
la aplicacin:
URL Accin
http://tu.servidor/alimentos/index.php?ctl=inicio mostrar pantalla inicio
http://tu.servidor/alimentos/index.php?ctl=listar listar alimentos
http://tu.servidor/alimentos/index.php?ctl=insertar insertar un alimento
http://tu.servidor/alimentos/index.php?ctl=buscar buscar alimentos
http://tu.servidor/alimentos/index.php?ctl=ver&id=x ver el alimento x
Ahora vamos a volver a definirlas usando el potente sistema de rutas de Symfony2.
Comencemos con una observacin. El usuario que utiliza el navegador "siente" que la URL
que aparece en la barra de direcciones forma parte de la aplicacin que est utilizando. Por
tanto, cualquier URL llena de carcteres extraos y demasiado larga redunda en una
degradacin esttica. Ms all de estos problemas estticos, cuando utilizamos query strings
clsicas, es decir, del tipo: ? p a r a m 1 = v a l 1 & p a r a m 2 = v a l 2 & . . . & p a r a m N = v a l N , estamos dando
informacin innecesaria al usuario, ya que el nombre de los parmetros (p a r a m X ) es algo que
tiene sentido nicamente para la aplicacin en el servidor. Esta informacin extra, adems
de dar lugar a URL's horribles, supone un problema de seguridad, ya que el usuario podra
utilizarlas para sacar conclusiones acerca de la aplicacin que est en el servidor.
El sistema de Routing de Symfony2 nos ofrece la posibilidad de utilizar autnticas rutas para
las URL's de nuestra aplicacin, es decir, rutas que slo utilizan el carcter / como
separador. Adems los nombre de los parmetros que reciben los datos en el servidor, no
aparecen en las rutas. nicamente aparece el valor de dichos parmetros. Las URL's as
construidas identifican ms elegantemente los recursos del servidor, adems de no dar ms
informacin de la estrictamente necesaria.
Adems, si utilizamos el mdulo R e w r i t e del servidor web, podemos eliminar de las URL's el
nombre del controlador frontal (a p p . p h p es el nombre que le da la distribucin standard de
Symfony2 por defecto). En cuyo caso, adems de mejorar el estilo de la URL, ocultamos al
usuario informacin acerca del lenguaje de programacin que estamos utilizando en el
servidor. Nos quedaran URL's de este tipo:
h t t p : / / t u . s e r v i d o r /
h t t p : / / t u . s e r v i d o r / l i s t a r
h t t p : / / t u . s e r v i d o r / v e r / 4
Mucho ms legibles y elegantes!
Definicin de las rutas del bundle
41
Nota
En el directorio w e b existe un fichero . h t a c c e s s con el siguiente contenido:
< I f M o d u l e m o d _ r e w r i t e . c >
R e w r i t e E n g i n e O n
R e w r i t e C o n d % { R E Q U E S T _ F I L E N A M E } ! - f
R e w r i t e R u l e ^ ( . * ) $ a p p . p h p [ Q S A , L ]
< / I f M o d u l e >
La funcin de dicho fichero es, precisamente, reescribir las rutas anteponiendo
a p p . p h p , de manera que no sea necesario especificar el controlador frontal en la URL.
Para que esto funcione es necesario que el servidor web tenga instalado el mdulo
R e w r i t e , y permita el cambio de directivas a travs de ficheros . h t a c c e s s .
Por otro lado, para definir la ruta no es necesario especificar la URL completa. De hecho, el
sentido de ruta para Symfony2 es la parte del URL a partir del nombre del controlador
frontal. O si se ha eliminado este gracias al uso del mdulo R e w r i t e , la parte de la URL
detrs del dominio.
La tabla anterior, con las rutas de Symfony2 quedara as:
URL Accin
/ mostrar pantalla inicio
/listar listar alimentos
/insertar insertar un alimento
/buscar buscar alimentos
/ver/x ver el alimento x
En Symfony2 las rutas se definen en el archivo a p p / c o n f i g / r o u t i n g . y m l . Para que los
bundles no pierdan la autonoma que debe caracterizarlos, las rutas que se mapean en un
controlador de un determinado bundle deberan definirse dentro del propio bundle.
Concretamente en el archivo R e s o u r c e s / c o n f i g / r o u t i n g . y m l del bundle. Y para hacerlas
disponibles a la aplicacin, se importa este ltimo fichero en a p p / c o n f i g / r o u t i n g . y m l .
Nota
Aunque el sitio recomendado para ubicar el fichero r o u t i n g . y m l de un bundle es
R e s o u r c e s / c o n f i g , Symfony2 no lo exige, ya que en el archivo
a p p / c o n f i g / r o u t i n g . y m l , que es el que realmente define las rutas, puedes indicar la
ruta concreta de los archivos que se quieren importar.
Abre el archivo
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l y borra las
siguientes lneas:
Definicin de las rutas del bundle
42
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e _ h o m e p a g e :
p a t t e r n : / h e l l o / { n a m e }
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x }
Las lneas que acabas de borrar definan la ruta de la accin de ejemplo que se crea
automticamente al generar el bundle. Fjate en la estructura de la definicin de una ruta;
consisten en un identificador de la ruta (J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e _ h o m e p a g e ),
que puede ser cualquiera siempre que sea nico en todo el framework, el patrn de la ruta
(p a t t e r n : / h e l l o / { n a m e } ), que describe la estructura de la ruta, y la declaracin del
controlador sobre el que se mapea la ruta (d e f a u l t s : { _ c o n t r o l l e r :
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x } ).
Creamos nuestra primera ruta aadiendo al archivo anterior lo siguiente:
J A M A B _ h o m e p a g e :
p a t t e r n : /
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x }
Como el nombre de la ruta debe ser nico en toda la aplicacin, es una buena prctica
nombrarlas anteponiendo un prefijo con el nombre del bundle, o con algo que lo identifique.
Como el nombre de nuestro bundle es muy largo, hemos optado por usar como prefijo las
siglas J A M A B .
Una vez definida la ruta debemos implementar la accin del controlador especificada en la
misma, es decir J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x .
Nota
Fjate en el patrn que se utiliza para especificar la accin del controlador:
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . A esto se le llama en
Symfony2 un nombre lgico. Est compuesto por el nombre del bundle, el nombre del
controlador, y el nombre de la accin separados por el caracter : . En este caso, el
nombre lgico hace referencia a el mtodo i n d e x A c t i o n ( ) de una clase PHP llamada
J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n t r o l l e r \ D e f a u l t C o n t r o l l e r . Es
decir, hay que aadir el sufijo C o n t r o l l e r al nombre del controlador, y el sufijo
A c t i o n al nombre de la accin.
Creacin de la accin en el controlador
Editamos el fichero
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / C o n t r o l l e r / D e f a u l t C o n t r o l l e r . p h p , y
reescribimos el mtodo i n d e x A c t i o n ( ) :
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n t r o l l e r ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6
7 c l a s s D e f a u l t C o n t r o l l e r e x t e n d s C o n t r o l l e r
8 {
Creacin de la accin en el controlador
43
9
1 0 p u b l i c f u n c t i o n i n d e x A c t i o n ( )
1 1 {
1 2 $ p a r a m s = a r r a y (
1 3 ' m e n s a j e ' = > ' B i e n v e n i d o a l c u r s o d e S y m f o n y 2 ' ,
1 4 ' f e c h a ' = > d a t e ( ' d - m - y y ' ) ,
1 5 ) ;
1 6
1 7 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g ' ,
1 8 $ p a r a m s ) ;
1 9 }
2 0 }
Analicemos el cdigo anterior. La clase D e f a u l t C o n t r o l l e r "vive" en el espacio de nombres
J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n t r o l l e r , por lo que su nombre completo es
J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n t r o l l e r \ D e f a u l t C o n t r o l l e r . La clase
extiende de S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r , la cual forma
parte de Symfony2 y, aunque no es necesario que nuestros controladores deriven de dicha
clase, si lo hacemos nos facilitar mucho la vida, ya que esta clase base cuenta con potentes
herramientas para trabajar con Symfony2. Posiblemente la ms til sea el Contenedor de
Dependencias tambien conocido como Contenedor de Servicios, con el que podemos obtener
fcilmente instancias bien configuradas de cualquier servicio del framework, tanto de los
incluidos en la distribucin estndard, como de los que nosotros creemos o de los que se
aadan en las extensiones de terceros (vendors) que podamos instalar. Qudate tranquilo
con esto de los servicios pues ser un tema que abordaremos ms adelante. Por lo pronto es
suficiente con que sepas que los servicios son objetos ofrecidos por el framework para
realizar determinadas tareas (como por ejemplo enviar emails o manipular una base de
datos).
Nota
Sobre los espacios de nombre de PHP 5.3 Si en la lnea 7 se utiliza nicamente el
nombre C o n t r o l l e r en lugar del nombre completo
S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r , es por que
previamente, en la lnea 5, se ha indicado en el archivo que se va a utilizar la clase
C o n t r o l l e r de dicho espacio de nombre.
El mtodo i n d e x A c t i o n ( ) es una accin en el mismo sentido que ya explicamos en la
unidad 2, es decir, es un mtodo que est mapeado en una URL a travs de una ruta (o tabla
de rutas), es decir de un fichero r o u t i n g . y m l . En esencia es el mismo que su homlogo de
la unidad 2: define un array asociativo con los datos "crudos" (raw) m e n s a j e y f e c h a , y se
los pasa a una plantilla para que los pinte. Esto ltimo se hace en la lnea 17 utilizando el
mtodo r e n d e r de la clase padre
S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r . Este mtodo recibe dos
argumentos, el primero es el nombre lgico de la plantilla que se desea utilizar, y el segundo
es un array asociativo con los datos. Las acciones terminan con la devolucin de un objeto
R e s p o n s e . Precisamente, el mtodo r e n d e r convierte una plantilla en un objeto de este tipo.
El mtodo r e n d e r es uno de los servicios disponibles en el framework y accesible desde
cualquier clase que derive de S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r .
Es un servicio que usaremos hasta la saciedad. El nombre lgico de una plantilla, es similar
al nombre lgico de un controlador; esta compuesto por el nombre del bundle, el nombre del
directorio que aloja a la plantilla en el directorio R e s o u r c e s / v i e w s (que suele coincidir con
el nombre del controlador, en este caso D e f a u l t ), y el nombre del archivo que implementa
Creacin de la accin en el controlador
44
la plantilla (en este caso i n d e x . h t m l . t w i g ). Es decir que el nombre lgico:
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g , hace referencia al
archivo
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / i n d e x . h t m l . t w i g .
Creacin de la plantilla
Siguiendo los pasos para la creacin de una pgina en Symfony2, lo siguiente que tenemos
que hacer es crear la plantilla. Edita el fichero
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / i n d e x . h t m l . t w i g
con el siguiente contenido:
1 < h 1 > I n i c i o < / h 1 >
2 < h 3 > F e c h a : { { f e c h a } } < / h 3 >
3 { { m e n s a j e } }
Aunque Symfony2 permite el uso de PHP como sistema de plantillas, en este curso
utilizaremos Twig, que es lo recomendado oficialmente. El cdigo anterior es una plantilla
twig. Fjate que es muy parecida a su homloga de la unidad 2 (i n i c i o . p h p ). La informacin
que se pinta y el formato HTML son identicos. Pero hay que reconocer que la plantilla twig es
mucho ms limpia.
En twig, el contenido dinmico, es decir, los datos "crudos" que le son pasados desde el
controlador (segundo argumento del mtodo r e n d e r en la accin i n d e x A c t i o n ( ) ), se
referencian con dobles llaves ({ { d a t o } } ). En el ejemplo anterior { { f e c h a } } hace
referencia al elemento f e c h a del array construido en el controlador, y { { m e n s a j e } } , como
ya has deducido, al elemento m e n s a j e de dicho array.
Pues con esto hemos terminado. Vamos a probar lo que acabamos de hacer. Introduce en la
barra de direcciones de tu navegador la URL correspondiente a la ruta que acabamos de
crear. Utiliza el controlador de desarrollo:
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p /
Vaya! parece que nada de lo que hemos hecho ha funcionado. Vuelve a aparecer la
aplicacin demo de Symfony2.
Ahora prueba con el controlador de produccin:
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p . p h p /
Ahora si! Vemos la pantalla de inicio de nuestro bundle. Pero entonces, qu est
pansando? las rutas tienen distinto sentido segn el controlador frontal que usemos. Por
qu?. La respuesta a este comportamiento se encuentra en las distintas configuraciones que
se cargan en funcin del entorno de ejecucin. Cuando utilizamos el controlador frontal de
desarrollo a p p _ d e v . p h p , se carga el fichero de routing a p p / c o n f i g / r o u t i n g _ d e v . p h p . Si le
echas un vistazo al fichero vers que comienza con la siguiente ruta:
_ w e l c o m e :
p a t t e r n : /
d e f a u l t s : { _ c o n t r o l l e r : A c m e D e m o B u n d l e : W e l c o m e : i n d e x }
La cual colisiona con la que nosotros hemos creado, ya que el patrn de la ruta es el mismo:
/ . El sistema de enrutamiento de Symfony2 va leyendo todas las rutas y cuando encuentra
una que coincide con la URL que se ha pedido, ejecuta la accin asociada. No sigue leyendo
Creacin de la plantilla
45
ms rutas. Por eso, si en un mismo proyecto hay dos rutas, o ms precisamente, dos
patrones de rutas que coincidan, se ejecutar la primera que se encuentre. Atencin por que
no se producir ningn error. Esto hay que tenerlo muy en cuenta cuando se desarrolla con
Symfony2 para evitarnos algn que otro dolor de cabeza.
En el caso del controlador frontal de produccin, el framework lee el fichero r o u t i n g . y m l , ya
que no existe r o u t i n g _ p r o d . y m l . Mira el fichero y podrs comprobar que no hay ninguna
ruta que colisione con la que nosotros hemos definido. Por tanto todo est bien y se ejecuta
la accin correcta.
Una vez que sabemos las causas del problema, si queremos que el controlador de desarrollo
cargue la ruta de nuestro bundle, cualquier solucin que propongamos pasa por evitar la
colisin entre rutas. Y para ello podemos hacer varias cosas:
1. Deshabilitar el plugin AcmeDemoBundle y sus rutas.
2. Cambiar el patrn de las rutas del plugin AcmeDemoBundle, anteponiendole a todas
ellas un prefijo (a c m e , por ejemplo)
3. Cambiar el patrn de las rutas del Jazzyweb/AulasMentorAlimentosBundle,
anteponiendole a todas ellas un prefijo (a l i m e n t o s , por ejemplo)
Con el fin de ilustrar una carcteristica del sistema de routing, hemos optado por la 3
solucin. Podemos aadir un prefijo a todas las rutas del bundle sin ms que cambiar el
parmetro p r e f i x en la ruta importada en el archivo a p p / c o n f i g / r o u t i n g . y m l :
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e :
r e s o u r c e : " @ J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l "
p r e f i x : / a l i m e n t o s
Ahora, para ver la pgina de inicio de nuestro bundle, apuntamos nuestro navegador a:
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / a l i m e n t o s
Y ya est! A partir de ahora todas las rutas de nuestro bundle llevarn el prefijo a l i m e n t o s
delante.
Atencin!
Como hemos cambiado un fichero de configuracin, para que el cambio se haga
efectivo en el entorno de produccin hay que borrar la cach con el siguiente
comando:
# a p p / c o n s o l e c a c h e : c l e a r - - e n v = p r o d
Decoracin de la plantilla con un layout
Te habrs dado cuenta que hemos pintado un bloque HTML incompleto. Si no te has
percatado de ello mira el cdigo fuente HTML que llega al navegador. Nos falta someter a la
plantilla al proceso de decoracin que hemos estudiado en la unidad 2, mediante el cual se
le aade funcionalidad. En el caso de la aplicacin de gestin de alimentos hay que aadir la
cabecera con el men, el pie de pgina y los estilos.
Decoracin de la plantilla con un layout
46
El sistema de plantillas twig, est provisto de un mecanismo de herencia gracias al cual la
decoracin de plantillas resulta de una flexibilidad y versatilidad total. Podemos hacer
cualquier cosa que nos imaginemos, como por ejemplo fragmentar la vista en distintas
plantillas organizadas por criterios funcionales, y combinarlas para producir la vista
completa. Podemos colocar en una un men, en otra un pie de pgina, en otra la estructura
bsica del documento HTML, otra puede pintar un listado de twits, etctera.
La herencia es un mecanismo tpico de la programacin orientada a objetos mediante el que
un componente software hereda todas las funcionalidades de otro y puede extenderlas y/o
cambiarlas. Es exactamente esto lo que ocurre cuando una plantilla twig hereda de otra.
En twig la herencia se implementa mediante el concepto de bloque. En las plantillas
podemos delimitar bloques que comienzan con un { % b l o c k n o m b r e _ b l o q u e % } y finalizan
con { % e n d b l o c k % } . Las plantillas heredan todas las funcionalidades de las plantillas que
extienden y pueden cambiar el cdigo de los bloques heredadados. Como siempre un
ejemplo vale ms que mil palabras.
Fjate en el fichero a p p / R e s o u r c e s / v i e w s / b a s e . h t m l . t w i g que viene de serie en la
distribucin standard de Symfony2:
a p p / R e s o u r c e s / v i e w s / b a s e . h t m l . t w i g
1 < ! D O C T Y P E h t m l >
2 < h t m l >
3 < h e a d >
4 < m e t a h t t p - e q u i v = " C o n t e n t - T y p e " c o n t e n t = " t e x t / h t m l ; c h a r s e t = u t f - 8 " / >
5 < t i t l e > { % b l o c k t i t l e % } W e l c o m e ! { % e n d b l o c k % } < / t i t l e >
6 { % b l o c k s t y l e s h e e t s % } { % e n d b l o c k % }
7 < l i n k r e l = " s h o r t c u t i c o n " h r e f = " { { a s s e t ( ' f a v i c o n . i c o ' ) } } " / >
8 < / h e a d >
9 < b o d y >
1 0 { % b l o c k b o d y % } { % e n d b l o c k % }
1 1 { % b l o c k j a v a s c r i p t s % } { % e n d b l o c k % }
1 2 < / b o d y >
1 3 < / h t m l >
Representa la estructura bsica de un documento HTML. Y presenta varios bloques: t i t l e ,
s t y l e s h e e t s , b o d y y j a v a s c r i p t s . Esta plantilla es ofrecida por Symfony2 para que sirva
de ejemplo. Pero puede utilizarse como plantilla bsica de casi cualquier aplicacin web.
Vamos a modificar nuestra plantilla i n d e x . h t m l . t w i g para que la herede (o para que la
extienda, son dos maneras de decir lo mismo):
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / i n d e x . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 < h 1 > I n i c i o < / h 1 >
6 < h 3 > F e c h a : { { f e c h a } } < / h 3 >
7 { { m e n s a j e } }
8
9 { % e n d b l o c k % }
En la lnea 1 se indica la herencia de la plantilla base. Esto significa que la plantilla
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g asume todo el
contenido de la plantilla : : b a s e . h t m l . t w i g . Pero adems se modifica el contenido del
Decoracin de la plantilla con un layout
47
bloque b o d y con las lneas 5-7.
Si adems queremos modificar el bloque t i t l e , no tenemos ms que aadirlo en nuestra
plantilla i n d e x . h t m l . t w i g :
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / i n d e x . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k t i t l e % }
4 B i e n v e n i d o a l a a p l i c a c i n a l i m e n t o s
5 { % e n d b l o c k % }
6
7 { % b l o c k b o d y % }
8
9 < h 1 > I n i c i o < / h 1 >
1 0 < h 3 > F e c h a : { { f e c h a } } < / h 3 >
1 1 { { m e n s a j e } }
1 2
1 3 { % e n d b l o c k % }
Ahora, en la seccin < t i t l e > del documento se pintar: B i e n v e n i d o a l a a p l i c a c i n
a l i m e n t o s en lugar de W e l c o m e .
Puedes probar a recargar la pgina a travs de la URL:
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / a l i m e n t o s /
Aunque el aspecto de la pgina es el mismo que antes, si ves el cdigo fuente HTML en el
navegador, comprobars que el documento est completo, es decir, con todas sus etiquetas
HTML. Tambin puedes comprobar que, al utilizar el controlador frontal de desarrollo,
aparece en la parte de abajo de la pgina la barra de depuracin de Symfony2.
Nota
Recuerda el concepto de nombre lgico de una plantilla. Y fjate en el nombre lgico
de la plantilla : : b a s e . h t m l . t w i g . Como no pertenece a ningn bundle (es comn a la
aplicacin), y est bicada directamente en el directorio v i e w s , no lleva nada ni antes
del primer : ni del segundo.
La herencia de plantillas puede llevarse a cabo a varios niveles, esto es, una plantilla puede
heredar de otra plantilla que a su vez hereda de otra plantilla, etctera. No obstante no se
recomienda llevar a cabo muchos niveles de herencia, ya que puede llegar a ser bastante
confuso e incontrolable. La estrategia que recomiendan los creadores de Symfony2 es usar
tres niveles de herencia:
en el primer nivel se colocan la estructura bsica del documento HTML, se corresponde
con lo que hace la plantilla : : b a s e . h t m l . t w i g ,
en el segundo se colocan los elementos especficos de cada seccin del sitio, por
ejemplo el men de la seccin,
y en el tercero se reserva para los elementos propios de la accin, se corresponde con
nuestra plantilla J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g
Decoracin de la plantilla con un layout
48
Tan slo nos falta incluir los mens que sern comunes a todas las pginas de la aplicacin.
Seguiremos la estrategia de tres niveles de herencia que acabamos de exponer. Creamos la
plantilla genral J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g . Segn la
lgica de los nombres lgicos, esta se debe ubicar en:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / l a y o u t . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4 < d i v i d = " c a b e c e r a " >
5 < h 1 > I n f o r m a c i n d e a l i m e n t o s < / h 1 >
6 < / d i v >
7
8 < d i v i d = " m e n u " >
9 < h r / >
1 0 < a h r e f = " { { p a t h ( ' J A M A B _ h o m e p a g e ' ) } } " > i n i c i o < / a > |
1 1 < a h r e f = " # " > v e r a l i m e n t o s < / a > |
1 2 < a h r e f = " # " > i n s e r t a r a l i m e n t o < / a > |
1 3 < a h r e f = " # " > b u s c a r p o r n o m b r e < / a > |
1 4 < a h r e f = " " > b u s c a r p o r e n e r g i a < / a > |
1 5 < a h r e f = " " > b s q u e d a c o m b i n a d a < / a >
1 6 < h r / >
1 7 < / d i v >
1 8
1 9 < d i v i d = " c o n t e n i d o " >
2 0 { % b l o c k c o n t e n i d o % }
2 1
2 2 { % e n d b l o c k % }
2 3 < / d i v >
2 4
2 5 < d i v i d = " p i e " >
2 6 < h r / >
2 7 < d i v a l i g n = " c e n t e r " > - p i e d e p g i n a - < / d i v >
2 8 < / d i v >
2 9
3 0 { % e n d b l o c k % }
Nota
En la lnea 10 hemos usado la funcin p a t h de twig para construir la URL's del men.
Est funcin recibe como argumento el nombre de la ruta cuya URL se desea calcular.
nicamente la hemos usado en el primer enlace del men, pus, por ahora, es la
nica ruta que hemos definido.
Ahora es esta plantilla la que extiende a la plantilla base, por tanto, habr que cambiar la
plantilla J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g para que
extienda de J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g , y para que
redefina el bloque c o n t e n i d o de esta ltima. Quedara as:
Decoracin de la plantilla con un layout
49
1 { % e x t e n d s ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g ' % }
2
3 { % b l o c k c o n t e n i d o % }
4
5 < h 1 > I n i c i o < / h 1 >
6 < h 3 > F e c h a : { { f e c h a } } < / h 3 >
7 { { m e n s a j e } }
8
9 { % e n d b l o c k % }
Vuelve a probar la pgina. Ya slo nos falta incorporarle estilos CSS's para que tenga la
misma pinta que en la unidad 2.
Instalacin de los assets de un bundle
Ya hemos dicho que un bundle es un directorio que aloja todo aquello relativo a una
funcionalidad determinada. Puede incluir clases PHP, plantillas, configuraciones, CSSs y
javascripts.
Cuando los bundles incluyen assets, es decir archivos que no son procesados por PHP y son
servidos directamente por el servidor web (CSS's, javascripts e imgenes son los assets ms
habituales), estos deben ser copiados dentro del directorio w e b del proyecto o enlazados
desde dicho directorio, ya que es ah nicamente donde el servidor web puede acceder en
busca de archivos (suponiendo que lo hemos configurado correctamente para un entorno de
produccin).
Por otro lado en un bundle los assets deben ser ubicados en el directorio R e s o u r c e s / p u b l i c .
Si lo examinas vers que tiene la siguiente estructura:
R e s o u r c e s
p u b l i c
c s s
i m a g e s
j s
Se ha reservado un directorio para cada tipo de asset. Copia el archivo e s t i l o s . c s s de la
unidad 2 en el directorio que le corresponde; R e s o u r c e s / p u b l i c / c s s . Para que el servidor
web la pueda cargar, se utiliza el siguiente comando de consola:
p h p a p p / c o n s o l e a s s e t s : i n s t a l l w e b - - s y m l i n k
La funcin de este comando es realizar una copia o un enlace simblico (si se especifica la
opin - - s y m l i n k , aunque en la plataforma Windows esto ltimo no es posible) del contenido
de los directorios R e s o u c e s / p u b l i c de todos los bundles que se encuentren registrados en
el framework. El comando requiere un argumento (w e b en nuestro caso), que especifica el
directorio donde se realizar la copia o el enlace simblico.
Dicha copia o enlazado se organiza de la siguiente manera:
w e b
n o m b r e _ b u n d l e _ 1
| c s s
| i m a g e s
| j s
Instalacin de los assets de un bundle
50
n o m b r e _ b u n d l e _ 2
| c s s
| i m a g e s
| j s
. . .
n o m b r e _ b u n d l e _ N
c s s
i m a g e s
j s
Ya slo falta incluir una referencia en el cdigo HTML a la CSS que acabamos de incorporar.
Aunque es posible incluir el enlace a la CSS directamente en la plantilla : : b a s e . h t m l . t w i g ,
el lugar correcto es en la plantilla
J a z z y w e b A u l a s M e n t o s A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g . Teniendo en cuenta lo que
hemos explicado acerca del mecanismo de herencia, habra que aadir un bloque
s t y l e s h e e t s (heredado de la plantilla padre : : b a s e . h t m l . t w i g ), en el que se haga
referencia al archivo CSS.
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / l a y o u t . h t m l . t w i g
. . .
{ % b l o c k s t y l e s h e e t s % }
< l i n k h r e f = " { { a s s e t ( ' b u n d l e s / j a z z y w e b a u l a s m e n t o r a l i m e n t o s / c s s / e s t i l o . c s s ' ) } } " t y p e = " t e x t / c s s " r e l = " s t y l e s h e e t " / >
{ % e n d b l o c k % }
. . .
En este cdigo hemos utilizado la funcin de twig a s s e t , la cual crea la URL correcta que
apunta al asset en cuestion. La ruta que toma como argumento la funcin asset se
especifica tomando como raz el directorio w e b .
Nota
Puedes colocar el bloque s t y l e s h e e t s delante o detrs del bloque b o d y .
Recarga la pgina y la vers con los estilos aplicados.
Implementamos el resto de la aplicacin
Siguiendo estos tres pasos: enrutar, crear cdigo de la accin (controlador) y crear la
plantilla, podemos completar lo que nos falta de la aplicacin. No obstante, en las acciones
que faltan, se necesita acceder a la base de datos para recuperar, modificar y crear
alimentos. Recuerda que esto se haca en la unidad 2 utilizando una clase denominada
M o d e l . La distribucin standard de Symfony2 proporciona un potente servicio para el acceso
a los datos persistentes, es decir, los que se almacenan en algn tipo de base de datos. Pero
no obliga a utilizarlo. No solo eso, tampoco forma parte del ncleo de Symfony2, es decir, no
es un componente. Por ello es una decisin del desarrollador utilizarlo o no. Es en ese
sentido que Fabien Potencier, lider del proyecto Symfony2, proclama que este ltimo no es
un framework MVC, ya que no dice nada sobre como debes construir tu modelo.
Aunque lo recomendable es utilizar Doctrine2 (que es el servicio de persistencia que viene
incorporado en la distribucin standard), o Propel, en esta unidad no los vamos a utilizar por
las siguientes razones:
1. Ya llevamos muchos conceptos introducidos y no queremos sobrecargar la unidad. Ms
adelante estudiaremos y usaremos Doctrine2.
Implementamos el resto de la aplicacin
51
2. Queremos ilustrar como podemos construir el modelo a nuestro antojo, y como
podemos reutilizar cdigo fcilmente cuando lo tenemos bien organizado bajo las
pautas del patrn MVC.
As pues, antes de implentar el resto de las acciones que componen la aplicacin, vamos a
elaborar el modelo.
Crea un directorio denominado M o d e l (el nombre puede ser cualquiera), y copia ah el
fichero M o d e l . p h p de la unidad 2. Incorpora la clase M o d e l al espacio de nombres del
bundle aadiendo al principio del fichero: n a m e s p a c e
J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l . Nos quedara as:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / M o d e l / M o d e l . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l ;
4
5 c l a s s M o d e l
6 {
7 p r o t e c t e d $ c o n e x i o n ;
8
9 p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ d b n a m e , $ d b u s e r , $ d b p a s s , $ d b h o s t )
1 0 {
1 1 $ m v c _ b d _ c o n e x i o n = m y s q l _ c o n n e c t ( $ d b h o s t , $ d b u s e r , $ d b p a s s ) ;
1 2
1 3 i f ( ! $ m v c _ b d _ c o n e x i o n ) {
1 4 d i e ( ' N o h a s i d o p o s i b l e r e a l i z a r l a c o n e x i n c o n l a b a s e d e d a t o s : '
1 5 . m y s q l _ e r r o r ( ) ) ;
1 6 }
1 7 m y s q l _ s e l e c t _ d b ( $ d b n a m e , $ m v c _ b d _ c o n e x i o n ) ;
1 8
1 9 m y s q l _ s e t _ c h a r s e t ( ' u t f 8 ' ) ;
2 0
2 1 $ t h i s - > c o n e x i o n = $ m v c _ b d _ c o n e x i o n ;
2 2 }
2 3
2 4
2 5 p u b l i c f u n c t i o n b d _ c o n e x i o n ( )
2 6 {
2 7
2 8 }
2 9
3 0 p u b l i c f u n c t i o n d a m e A l i m e n t o s ( )
3 1 {
3 2 $ s q l = " s e l e c t * f r o m a l i m e n t o s o r d e r b y e n e r g i a d e s c " ;
3 3
3 4 $ r e s u l t = m y s q l _ q u e r y ( $ s q l , $ t h i s - > c o n e x i o n ) ;
3 5
3 6 $ a l i m e n t o s = a r r a y ( ) ;
3 7 w h i l e ( $ r o w = m y s q l _ f e t c h _ a s s o c ( $ r e s u l t ) )
3 8 {
3 9 $ a l i m e n t o s [ ] = $ r o w ;
4 0 }
4 1
4 2 r e t u r n $ a l i m e n t o s ;
Implementamos el resto de la aplicacin
52
4 3 }
4 4
4 5 p u b l i c f u n c t i o n b u s c a r A l i m e n t o s P o r N o m b r e ( $ n o m b r e )
4 6 {
4 7 $ n o m b r e = h t m l s p e c i a l c h a r s ( $ n o m b r e ) ;
4 8
4 9 $ s q l = " s e l e c t * f r o m a l i m e n t o s w h e r e n o m b r e l i k e ' " . $ n o m b r e . " ' o r d e r
5 0 b y e n e r g i a d e s c " ;
5 1
5 2 $ r e s u l t = m y s q l _ q u e r y ( $ s q l , $ t h i s - > c o n e x i o n ) ;
5 3
5 4 $ a l i m e n t o s = a r r a y ( ) ;
5 5 w h i l e ( $ r o w = m y s q l _ f e t c h _ a s s o c ( $ r e s u l t ) )
5 6 {
5 7 $ a l i m e n t o s [ ] = $ r o w ;
5 8 }
5 9
6 0 r e t u r n $ a l i m e n t o s ;
6 1 }
6 2
6 3 p u b l i c f u n c t i o n d a m e A l i m e n t o ( $ i d )
6 4 {
6 5 $ i d = h t m l s p e c i a l c h a r s ( $ i d ) ;
6 6
6 7 $ s q l = " s e l e c t * f r o m a l i m e n t o s w h e r e i d = " . $ i d ;
6 8
6 9 $ r e s u l t = m y s q l _ q u e r y ( $ s q l , $ t h i s - > c o n e x i o n ) ;
7 0
7 1 $ a l i m e n t o s = a r r a y ( ) ;
7 2 $ r o w = m y s q l _ f e t c h _ a s s o c ( $ r e s u l t ) ;
7 3
7 4 r e t u r n $ r o w ;
7 5
7 6 }
7 7
7 8 p u b l i c f u n c t i o n i n s e r t a r A l i m e n t o ( $ n , $ e , $ p , $ h c , $ f , $ g )
7 9 {
8 0 $ n = h t m l s p e c i a l c h a r s ( $ n ) ;
8 1 $ e = h t m l s p e c i a l c h a r s ( $ e ) ;
8 2 $ p = h t m l s p e c i a l c h a r s ( $ p ) ;
8 3 $ h c = h t m l s p e c i a l c h a r s ( $ h c ) ;
8 4 $ f = h t m l s p e c i a l c h a r s ( $ f ) ;
8 5 $ g = h t m l s p e c i a l c h a r s ( $ g ) ;
8 6
8 7 $ s q l = " i n s e r t i n t o a l i m e n t o s ( n o m b r e , e n e r g i a , p r o t e i n a , h i d r a t o c a r b o n o ,
8 8 f i b r a , g r a s a t o t a l ) v a l u e s ( ' " .
8 9 $ n . " ' , " . $ e . " , " . $ p . " , " . $ h c . " , " . $ f . " , " . $ g . " ) " ;
9 0
9 1 $ r e s u l t = m y s q l _ q u e r y ( $ s q l , $ t h i s - > c o n e x i o n ) ;
9 2
9 3 r e t u r n $ r e s u l t ;
9 4 }
9 5
9 6 }
El prximo paso es adaptar el cdigo del controlador de la unidad 2. Se trata de aadir el
sufijo A c t i o n a todas las acciones, y en utilizar el servicio de renderizado de Symfony2 para
invocar a las plantillas. El cdigo del controlador D e f a u l t C o n t r o l l e r quedara as:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / C o n t r o l l e r / D e f a u l t C o n t r o l l e r . p h p
Implementamos el resto de la aplicacin
53
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n t r o l l e r ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6 u s e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l \ M o d e l ;
7 u s e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n f i g \ C o n f i g ;
8
9 c l a s s D e f a u l t C o n t r o l l e r e x t e n d s C o n t r o l l e r
1 0 {
1 1
1 2 p u b l i c f u n c t i o n i n d e x A c t i o n ( )
1 3 {
1 4 $ p a r a m s = a r r a y (
1 5 ' m e n s a j e ' = > ' B i e n v e n i d o a l c u r s o d e S y m f o n y 2 ' ,
1 6 ' f e c h a ' = > d a t e ( ' d - m - y y ' ) ,
1 7 ) ;
1 8
1 9 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g ' ,
2 0 $ p a r a m s ) ;
2 1 }
2 2
2 3 p u b l i c f u n c t i o n l i s t a r A c t i o n ( )
2 4 {
2 5 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
2 6 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
2 7
2 8 $ p a r a m s = a r r a y (
2 9 ' a l i m e n t o s ' = > $ m - > d a m e A l i m e n t o s ( ) ,
3 0 ) ;
3 1
3 2 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : m o s t r a r A l i m e n t o s . h t m l . t w i g ' ,
3 3 $ p a r a m s ) ;
3 4
3 5 }
3 6
3 7 p u b l i c f u n c t i o n i n s e r t a r A c t i o n ( )
3 8 {
3 9 $ p a r a m s = a r r a y (
4 0 ' n o m b r e ' = > ' ' ,
4 1 ' e n e r g i a ' = > ' ' ,
4 2 ' p r o t e i n a ' = > ' ' ,
4 3 ' h c ' = > ' ' ,
4 4 ' f i b r a ' = > ' ' ,
4 5 ' g r a s a ' = > ' ' ,
4 6 ) ;
4 7
4 8 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
4 9 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
5 0
5 1 i f ( $ _ S E R V E R [ ' R E Q U E S T _ M E T H O D ' ] = = ' P O S T ' ) {
5 2
5 3 / / c o m p r o b a r c a m p o s f o r m u l a r i o
5 4 i f ( $ m - > i n s e r t a r A l i m e n t o ( $ _ P O S T [ ' n o m b r e ' ] , $ _ P O S T [ ' e n e r g i a ' ] ,
5 5 $ _ P O S T [ ' p r o t e i n a ' ] , $ _ P O S T [ ' h c ' ] , $ _ P O S T [ ' f i b r a ' ] , $ _ P O S T [ ' g r a s a ' ] ) ) {
5 6 $ p a r a m s [ ' m e n s a j e ' ] = ' A l i m e n t o i n s e r t a d o c o r r e c t a m e n t e ' ;
5 7 } e l s e {
5 8 $ p a r a m s = a r r a y (
5 9 ' n o m b r e ' = > $ _ P O S T [ ' n o m b r e ' ] ,
6 0 ' e n e r g i a ' = > $ _ P O S T [ ' e n e r g i a ' ] ,
6 1 ' p r o t e i n a ' = > $ _ P O S T [ ' p r o t e i n a ' ] ,
6 2 ' h c ' = > $ _ P O S T [ ' h c ' ] ,
6 3 ' f i b r a ' = > $ _ P O S T [ ' f i b r a ' ] ,
6 4 ' g r a s a ' = > $ _ P O S T [ ' g r a s a ' ] ,
6 5 ) ;
6 6 $ p a r a m s [ ' m e n s a j e ' ] = ' N o s e h a p o d i d o i n s e r t a r e l a l i m e n t o . R e v i s a e l f o r m u l a r i o ' ;
6 7 }
6 8 }
6 9
Implementamos el resto de la aplicacin
54
7 0 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : f o r m I n s e r t a r . h t m l . t w i g ' ,
7 1 $ p a r a m s ) ;
7 2
7 3 }
7 4
7 5 p u b l i c f u n c t i o n b u s c a r P o r N o m b r e A c t i o n ( )
7 6 {
7 7 $ p a r a m s = a r r a y (
7 8 ' n o m b r e ' = > ' ' ,
7 9 ' r e s u l t a d o ' = > a r r a y ( ) ,
8 0 ) ;
8 1
8 2 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
8 3 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
8 4
8 5 i f ( $ _ S E R V E R [ ' R E Q U E S T _ M E T H O D ' ] = = ' P O S T ' ) {
8 6 $ p a r a m s [ ' n o m b r e ' ] = $ _ P O S T [ ' n o m b r e ' ] ;
8 7 $ p a r a m s [ ' r e s u l t a d o ' ] = $ m - > b u s c a r A l i m e n t o s P o r N o m b r e ( $ _ P O S T [ ' n o m b r e ' ] ) ;
8 8 }
8 9
9 0 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : b u s c a r P o r N o m b r e . h t m l . t w i g ' ,
9 1 $ p a r a m s ) ;
9 2
9 3 }
9 4
9 5 p u b l i c f u n c t i o n v e r A c t i o n ( )
9 6 {
9 7 i f ( ! i s s e t ( $ _ G E T [ ' i d ' ] ) ) {
9 8 t h r o w n e w E x c e p t i o n ( ' P g i n a n o e n c o n t r a d a ' ) ;
9 9 }
1 0 0
1 0 1 $ i d = $ _ G E T [ ' i d ' ] ;
1 0 2
1 0 3 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
1 0 4 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
1 0 5
1 0 6 $ a l i m e n t o = $ m - > d a m e A l i m e n t o ( $ i d ) ;
1 0 7
1 0 8 $ p a r a m s = $ a l i m e n t o ;
1 0 9
1 1 0 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : v e r A l i m e n t o . h t m l . t w i g ' ,
1 1 1 $ p a r a m s ) ;
1 1 2
1 1 3 }
1 1 4
1 1 5 }
Para que podamos utilizar la clase M o d e l en el controlador sin necesidad de referirnos a ella
por su nombre completo, J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l \ M o d e l , hemos
utilizado (lnea 6) la directiva u s e de PHP 5.3 en dicho fichero. As podemos utilizar la clase
M o d e l directamente en el controlador.
Crea el directorio s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / C o n f i g , y coloca ah el
fichero C o n f i g . p h p de la unidad 2. Adelo al espacio de nombres
J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n f i g . Este fichero es usado por el controlador
anterior para inicializar el modelo (el objeto M o d e l ). En la lnea7 puedes ver que hemos
aadido el espacio de nombre de esta clase.
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / C o n f i g / C o n f i g . p h p
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n f i g ;
c l a s s C o n f i g
{
s t a t i c p u b l i c $ m v c _ b d _ h o s t n a m e = " l o c a l h o s t " ;
Implementamos el resto de la aplicacin
55
s t a t i c p u b l i c $ m v c _ b d _ n o m b r e = " a l i m e n t o s " ;
s t a t i c p u b l i c $ m v c _ b d _ u s u a r i o = " r o o t " ;
s t a t i c p u b l i c $ m v c _ b d _ c l a v e = " r o o t " ;
s t a t i c p u b l i c $ m v c _ v i s _ c s s = " e s t i l o . c s s " ;
}
Como puedes ver hemos comenzado por el 2 paso del flujo bsico de desarrollo de pginas
con Symfony2 es decir, escribir el controlador. En realidad el orden no importa mucho; al
final hay que tener los tres pasos resueltos antes de que funcione. As que vamos a por el
primer paso: definir las rutas. Esto lo hacemos editando el fichero
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l y
plasmando ah la tabla de rutas. Recuerda:
URL Accin
/ mostrar pantalla inicio
/listar listar alimentos
/insertar insertar un alimento
/buscar buscar alimentos
/ver/x ver el alimento x
El archivo
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l queda
as:
J A M A B _ h o m e p a g e :
p a t t e r n : /
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x }
J A M A B _ l i s t a r :
p a t t e r n : / l i s t a r
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : l i s t a r }
J A M A B _ i n s e r t a r :
p a t t e r n : / i n s e r t a r
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n s e r t a r }
J A M A B _ b u s c a r :
p a t t e r n : / b u s c a r
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : b u s c a r P o r N o m b r e }
J A M A B _ v e r :
p a t t e r n : / v e r / { i d }
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : v e r }
La ltima ruta (J A M A B _ v e r ) utiliza una funcionalidad muy interesante del sistema de Routing
de Symfony2 que se utiliza continuamente. Se trata de introducir en la propia ruta los
parmetros que se pasarn por GET al servidor web. Los valores encerrados entre llaves, en
nuestro caso { i d } , se denominan placeholders. El sistema de Routing parsea las URL's que
coincidan con la ruta y asigna el valor que venga en la posicin de cada placeholder a una
variable denominada con el nombre especificado entre las llaves. Vemoslo con un ejemplo.
La siguiente ruta:
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / a l i m e n t o s / v e r / 5
Implementamos el resto de la aplicacin
56
Coincide con la ruta J A M A B _ v e r (recuerda que a todas las rutas del bundle les hemos
colocado el prefijo a l i m e n t o s ). El sistema de Routing, al parsearla, asignar al objeto
Request de Symfony2 una variable denominada i d , con un valor 5 . Adems, esta variable se
pasar como argumento al controlador especificado en la ruta, en nuestro caso a
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : v e r . Se consigue, adems de usar URL's
elegantes en la que slo se utiliza el caracter / , eliminar el nombre de las variables de la
query string, ocultando informacin que no es necesaria para el cliente.
Symfony2 mapea esta ruta en una accin llamada v e r A c t i o n ( $ i d ) a la que se le pasa el
argumento i d . Vamos a cambiar la accin v e r A c t i o n ( ) para que su cdigo sea ms
correcto y symfnico:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / C o n t r o l l e r / D e f a u l t C o n t r o l l e r . p h p
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n v e r A c t i o n ( $ i d )
4 {
5 $ m = n e w M o d e l ( C o n f i g : : $ m v c _ b d _ n o m b r e , C o n f i g : : $ m v c _ b d _ u s u a r i o ,
6 C o n f i g : : $ m v c _ b d _ c l a v e , C o n f i g : : $ m v c _ b d _ h o s t n a m e ) ;
7
8 $ a l i m e n t o = $ m - > d a m e A l i m e n t o ( $ i d ) ;
9
1 0 i f ( ! $ a l i m e n t o )
1 1 {
1 2 t h r o w n e w \ S y m f o n y \ C o m p o n e n t \ H t t p K e r n e l \ E x c e p t i o n \ A c c e s s D e n i e d H t t p E x c e p t i o n ( ) ;
1 3 }
1 4
1 5 $ p a r a m s = $ a l i m e n t o ;
1 6
1 7 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : v e r A l i m e n t o . h t m l . t w i g ' , $ p a r a m s ) ;
1 8
1 9 }
2 0 . . .
En la lnea 3 hemos introducido un argumento para recoger la variable creada por el sistema
de Routing, y en las lneas 9-12 hemos utilizado las excepciones de Symfony2 para tratar el
caso de que el registro no exista. Fjate que de esta manera no necesitamos utilizar la
variable superglobal $ _ G E T de PHP.
Nota
En lugar del nombre completo
\ S y m f o n y \ C o m p o n e n t \ H t t p K e r n e l \ E x c e p t i o n \ A c c e s s D e n i e d H t t p E x c e p t i o n ( ) ,
puedes utilizar A c c e s s D e n i e d H t t p E x c e p t i o n ( ) , si referencias el espacio de nombre al
principio del fichero mediante la directiva u s e .
Nota
Las acciones b u s c a r P o r N o m b r e A c t i o n y i n s e r t a r A c t i o n , hacen uso de la variable
global de PHP $ _ P O S T . Esto es una mala prctica en Symfony2, ya que en su lugar se
debe utilizar el objeto R e q u e s t del framework, que es una abstraccin de la peticin
(request) HTTP en la que se han "limpiado" los valores de sus atributos de posibles
cadenas potencialmente peligrosas (cdigo malicioso). Ser la primera y ultima vez
que haremos esto. Sirva como ejemplo de que el hecho de utilizar un framework
ayuda pero no es suficiente para generar un cdigo de calidad. Es el programador
Implementamos el resto de la aplicacin
57
quien, conociendo y aplicando las buenas prcticas de programacin, produce un
buen cdigo.
Y ahora a por las plantillas. Igual que hemos hecho con las acciones del controlador,
adaptaremos las plantillas de la unidad 2 al sistema twig.
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / v e r A l i m e n t o . h t m l . t w i g
1 { % e x t e n d s ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g ' % }
2
3 { % b l o c k c o n t e n i d o % }
4
5 < h 1 > { { n o m b r e } } < / h 1 >
6 < t a b l e b o r d e r = " 1 " >
7
8 < t r >
9 < t d > E n e r g a < / t d >
1 0 < t d > { { e n e r g i a } } < / t d >
1 1 < / t r >
1 2 < t r >
1 3 < t d > P r o t e i n a < / t d >
1 4 < t d > { { p r o t e i n a } } < / t d >
1 5 < / t r >
1 6 < t r >
1 7 < t d > H i d r a t o s d e C a r b o n o < / t d >
1 8 < t d > { { h i d r a t o c a r b o n o } } < / t d >
1 9 < / t r >
2 0 < t r >
2 1 < t d > F i b r a < / t d >
2 2 < t d > { { f i b r a } } < / t d >
2 3 < / t r >
2 4 < t r >
2 5 < t d > G r a s a t o t a l < / t d >
2 6 < t d > { { g r a s a t o t a l } } < / t d >
2 7 < / t r >
2 8 < / t a b l e >
2 9
3 0 { % e n d b l o c k % }
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / m o s t r a r A l i m e n t o s . h t m l . t w i g
1 { % e x t e n d s ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g ' % }
2
3 { % b l o c k c o n t e n i d o % }
4
5 < t a b l e >
6 < t r >
7 < t h > a l i m e n t o ( p o r 1 0 0 g ) < / t h >
8 < t h > e n e r g a ( K c a l ) < / t h >
9 < t h > g r a s a ( g ) < / t h >
1 0 < / t r >
Implementamos el resto de la aplicacin
58
1 1 { % f o r a l i m e n t o i n a l i m e n t o s % }
1 2 < t r >
1 3 < t d > < a h r e f = " { { p a t h ( ' J A M A B _ v e r ' , { ' i d ' : a l i m e n t o . i d } ) } } " > { { a l i m e n t o . n o m b r e } } < / a > < / t d >
1 4 < t d > { { a l i m e n t o . e n e r g i a } } < / t d >
1 5 < t d > { { a l i m e n t o . g r a s a t o t a l } } < / t d >
1 6 < / t r >
1 7 { % e n d f o r % }
1 8
1 9 < / t a b l e >
2 0
2 1 { % e n d b l o c k % }
En esta ltima plantilla hemos introducido tres elementos nuevos del sistema twig:
La navegacin por un array. Fjate que la accin que utiliza esta plantilla,
l i s t a r A c t i o n ( ) , le pasa como parmetros una coleccin (array) de alimentos
devueltos por el mtodo d a m e A l i m e n t o s del modelo. Las colecciones, es decir los
arrays indexados (no asociativos), pueden ser iterados en una plantilla twig mediante la
construccin { % f o r d a t o i n d a t o s % } - { % e n d f o r % } , donde d a t o s es el array que
llega a la plantilla.
Por otro lado, cada elemento del array a l i m e n t o s es un array asociativo. Sus
elementos pueden ser accedido mediante la notacin d a t o . p r o p i e d a d . Una
caracterstica interesante de esta notacin es que se puede utilizar no solo con arrays
asociativos, sino con objetos provistos de getters sobre sus propiedades. Este hecho se
utiliza intensa y extensamente en Symfony2.
Por ltimo se utiliza la funcin p a t h ( ) de twig, que sirve para calcular la URL correcta a
partir del nombre de la ruta. As cuando cambiemos la aplicacin de servidor o de
ubicacin, la ruta ser calculada correctamente. Los argumentos de la ruta se pasan a
la funcin p a t h usando la sintaxis de un objeto JSON, es decir: { ' p a r a m 1 ' : v a l 1 ,
. . . , ` p a r a m N ' : v a l N } . Esta funcin ser otro de los elementos omnipresentes en
cualquier aplicacin web construida con Symfony2 y twig.
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / f o r m I n s e r t a r . h t m l . t w i g
{ % e x t e n d s ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g ' % }
{ % b l o c k c o n t e n i d o % }
{ % i f m e n s a j e i s d e f i n e d % }
< b > < s p a n s t y l e = " c o l o r : r e d ; " > { { m e n s a j e } } < / s p a n > < / b >
{ % e n d i f % }
< b r / >
< f o r m n a m e = " f o r m I n s e r t a r " a c t i o n = " { { p a t h ( ' J A M A B _ i n s e r t a r ' ) } } " m e t h o d = " P O S T " >
< t a b l e >
< t r >
< t h > N o m b r e < / t h >
< t h > E n e r g a ( K c a l ) < / t h >
< t h > P r o t e i n a ( g ) < / t h >
< t h > H . d e c a r b o n o ( g ) < / t h >
< t h > F i b r a ( g ) < / t h >
< t h > G r a s a t o t a l ( g ) < / t h >
< / t r >
< t r >
< t d > < i n p u t t y p e = " t e x t " n a m e = " n o m b r e " v a l u e = " { { n o m b r e } } " / > < / t d >
< t d > < i n p u t t y p e = " t e x t " n a m e = " e n e r g i a " v a l u e = " { { e n e r g i a } } " / > < / t d >
< t d > < i n p u t t y p e = " t e x t " n a m e = " p r o t e i n a " v a l u e = " { { p r o t e i n a } } " / > < / t d >
Implementamos el resto de la aplicacin
59
< t d > < i n p u t t y p e = " t e x t " n a m e = " h c " v a l u e = " { { h c } } " / > < / t d >
< t d > < i n p u t t y p e = " t e x t " n a m e = " f i b r a " v a l u e = " { { f i b r a } } " / > < / t d >
< t d > < i n p u t t y p e = " t e x t " n a m e = " g r a s a " v a l u e = " { { g r a s a } } " / > < / t d >
< / t r >
< / t a b l e >
< i n p u t t y p e = " s u b m i t " v a l u e = " i n s e r t a r " n a m e = " i n s e r t a r " / >
< / f o r m >
* L o s v a l o r e s d e b e n r e f e r i r s e a 1 0 0 g d e l a l i m e n t o
{ % e n d b l o c k % }
En esta plantilla hemos introducido otro elemento nuevo; la construccin { % i f d a t a i s
d e f i n e d % } - { % e n d i f % } , que como puedes deducir, comprueba si la variable d a t a ha
sido definida. A lo largo del curso veremos ms expresiones lgicas utilizadas en los bloques
i f - e n d i f .
Tambin hemos vuelto a utilizar la funcin p a t h para escribir el parmetro a c t i o n del
formulario HTML.
Llegados a este punto hemos de aclarar que Symfony2 proporciona un potente servicio para
la construccin de formularios que estudiaremos en su momento. Por lo pronto nos
quedamos con esta manera sencilla y directa de crear formularios.
Vamos a por la siguiente plantilla:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / b u s c a r P o r N o m b r e . h t m l . t w i g
1 { % e x t e n d s ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g ' % }
2
3 { % b l o c k c o n t e n i d o % }
4
5 < f o r m n a m e = " f o r m B u s q u e d a " a c t i o n = " { { p a t h ( ' J A M A B _ b u s c a r ' ) } } " m e t h o d = " P O S T " >
6
7 < t a b l e >
8 < t r >
9 < t d > n o m b r e a l i m e n t o : < / t d >
1 0 < t d > < i n p u t t y p e = " t e x t " n a m e = " n o m b r e " v a l u e = " { { n o m b r e } } " > ( p u e d e s u t i l i z a r ' % ' c o m o c o m o d n ) < / t d >
1 1
1 2 < t d > < i n p u t t y p e = " s u b m i t " v a l u e = " b u s c a r " > < / t d >
1 3 < / t r >
1 4 < / t a b l e >
1 5
1 6 < / t a b l e >
1 7
1 8 < / f o r m >
1 9
2 0 { % i f r e s u l t a d o % }
2 1 { % i n c l u d e ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : _ t a b l a A l i m e n t o s . h t m l . t w i g ' w i t h { ' a l i m e n t o s ' : r e s u l t a d o } % }
2 2 { % e n d i f % }
2 3
2 4 { % e n d b l o c k % }
Otro elemento nuevo; la inclusin de plantillas en otras plantillas. Esto lo hacemos en la lnea
21 mediante la funcin i n c l u d e de twig, la cual requiere como argumento el nombre lgico
de la plantilla que se desea incluir. Los parmetros que necesita la plantilla incluida se pasan
en un array con sintaxis JSON despues del token w i t h .
Este mecanismo de inclusin combinado con la herencia proporciona una gran flexibilidad al
programador, otorgndole las herramientas necesarias para elaborar un cdigo bien
organizado y reusable.
La plantilla incluida es la siguiente:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / _ t a b l a A l i m e n t o s . h t m l . t w i g
Implementamos el resto de la aplicacin
60
1 < t a b l e >
2 < t r >
3 < t h > a l i m e n t o ( p o r 1 0 0 g ) < / t h >
4 < t h > e n e r g a ( K c a l ) < / t h >
5 < t h > g r a s a ( g ) < / t h >
6 < / t r >
7 { % f o r a l i m e n t o i n a l i m e n t o s % }
8 < t r >
9 < t d > { { a l i m e n t o . n o m b r e } } < / t d >
1 0 < t d > { { a l i m e n t o . e n e r g i a } } < / t d >
1 1 < t d > { { a l i m e n t o . g r a s a t o t a l } } < / t d >
1 2 < / t r >
1 3 { % e n d f o r % }
1 4
1 5 < / t a b l e >
Y, por ltimo, utilizando esta ltima plantilla que pinta un listado de alimentos, podemos
simplificar la plantilla m o s t r a r A l i m e n t o s . h t m l . t w i g evitando la repeticin de cdigo
innecesariamente.
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / v i e w s / D e f a u l t / m o s t r a r A l i m e n t o s . h t m l . t w i g
1 { % e x t e n d s ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g ' % }
2
3 { % b l o c k c o n t e n i d o % }
4
5 { % i n c l u d e ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : _ t a b l a A l i m e n t o s . h t m l . t w i g ' w i t h { ' a l i m e n t o s ' : a l i m e n t o s } % }
6
7 { % e n d b l o c k % }
Y con esto ya tenemos la aplicacin de gestin de alimentos terminada y construida en
Symfony2. An puede hacerse de un modo ms symfnico, utilizando los servicios de
persistencia de datos (Doctrine), de creacin de formularios y de validacin de datos. Pero la
intencin de esta unidad es mostrar los elementos bsicos para la creacin de pginas en
Symfony2, y por tanto vamos a dar por buena la aplicacin tal y como est. En las prximas
unidades tendremos ocasin de estudiar estos servicios y de ilustrarlos con el desarrollo de
una aplicacin ms completa y compleja.
nicamente faltara usar la funcin p a t h ( ) de twig para completar los enlaces de los
mens. Pero eso vamos a dejar que lo hagas t.
La unidad en chuletas
Generar un bundle
p h p a p p / c o n s o l e g e n e r a t e : b u n d l e
Registrar un bundle
Se hace en el archivo a p p / A p p K e r n e l . p h p , de la siguiente manera:
. . .
n e w J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e ( ) ,
. . .
La unidad en chuletas
61
Enlazar el routing de un bundle con el routing general de la aplicacin
Se hace aadiendo al archivo a p p / c o n f i g / r o u t i n g . y m l (o r o u t i n g _ { e n v } . y m l ):
J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e :
r e s o u r c e : " @ J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l "
p r e f i x : /
Pasos para acoplar un bundle al framework
1. Registrar el espacio de nombre en el sistema de autocarga. Este paso no es necesario si
ubicamos al bundle en el directorio s r c .
2. Registrar al bundle en el fichero a p p / A p p K e r n e l . p h p . Esta operacin se puede hacer
automticamente a travs del generador interactivo de bundles, pero si fallase por
alguna razn (por ejemplo que los permisos de dicho archivo no estn bien definidos).
Habra que hacerlo a mano.
3. Importar las tablas de enrutamiento del bundle en la tabla de enrutamiento de la
aplicacin.
Flujo para la creacin de pginas en Symfony2
1. Creacin de la ruta en c o n f i g / R e s o u r c e s / r o u t i n g . y m l del bundle, o directamente en
a p p / c o n f i g / r o u t i n g . y m l .
2. Creacin de la accin en el controlador correspondiente en una clase que debe ubicarse
en un fichero del directorio C o n t r o l l e r s del bundle.
3. Creacin de una plantilla en el directorio R e s o u r c e s / v i e w s .
Nombres lgicos de acciones
N o m b r e B u n d l e : N o m b r e C o n t r o l a d o r : N o m b r e A c c i n .
Ejemplo:
A c m e D e m o B u n d l e : S e c u r e d : l o g i n se mapea en la accin l o g i n A c t i o n ( ) de la clase
A c m e \ D e m o B u n d l e \ C o n t r o l l e r \ S e c u r e d C o n t r o l l e r definida (normalmente) en
s r c / A c m e / D e m o B u n d l e / C o n t r o l l e r / S e c u r e d C o n t r o l l e r . p h p .
Sintaxis bsica de twig
{ { p a r a m e t r o } } -> pinta el valor de la variable p a r a m e t r o .
{ % c o m a n d o % } . . . { % e n d c o m a n d o % } -> ejecuta la accin expresada por c o m a n d o en el
bloque definido desde su declaracin hasta { % e n d c o m a n d o % } .
Herencia en plantilla twig
Esta plantilla hereda de J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g , y
modifica el bloque c o n t e n i d o que all se declara.
1 { % e x t e n d s ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : : l a y o u t . h t m l . t w i g ' % }
2
3 { % b l o c k c o n t e n i d o % }
4
5 < h 1 > I n i c i o < / h 1 >
6 < h 3 > F e c h a : { { f e c h a } } < / h 3 >
Enlazar el routing de un bundle con el routing general de la aplicacin
62
7 { { m e n s a j e } }
8
9 { % e n d b l o c k % }
Funcin p a t h de twig
{ { p a t h ( ' J A M A B _ l i s t a r ' , { ' i d ' : a l i m e n t o . i d } ) } }
Iterar una coleccin (array) de datoso en twig
{ % f o r a l i m e n t o i n a l i m e n t o s % }
< t r >
< t d > < a h r e f = " { { p a t h ( ' J A M A B _ l i s t a r ' , { ' i d ' : a l i m e n t o . i d } ) } } " > { { a l i m e n t o . n o m b r e } } < / a > < / t d >
< t d > { { a l i m e n t o . e n e r g i a } } < / t d >
< t d > { { a l i m e n t o . g r a s a t o t a l } } < / t d >
< / t r >
{ % e n d f o r % }
Cdigo condicional en twig
{ % i f d a t a i s d e f i n e d % }
. . .
{ % e n d i f % }
Inclusin de plantillas en twig
{ % i n c l u d e ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : _ t a b l a A l i m e n t o s . h t m l . t w i g ' w i t h { ' r e s u l t a d o ' : r e s u l t a d o } % }
Estructura bsica de una ruta
n o m b r e _ u n i c o _ d e _ l a _ r u t a :
p a t t e r n : / e l / p a t r o n / d e / l a / r u t a / { p a r a m 1 } / { p a r a m 2 } / . . . / { p a r a m N }
d e f a u l t s : { _ c o n t r o l l e r : N o m b r e L o g i c o : d e l : C o n t r o l a d o r }
E j e m p l o :
J A M A B _ v e r :
p a t t e r n : / v e r / { i d }
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : v e r }
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll para recorrer
todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Funcin path de twig
63
Unidad 4: Inyeccin de Dependencias
Inyeccin de Dependencia (Dependency Injection), Inversin de Control (Control Inversion),
Contenedor de Servicios (Service Container) y Contenedor de Dependencias (Dependency
Container) son palabros que te vas a encontrar continuamente cuando te sumerges en el
mundo de Symfony2. Todas hacen referencia a una misma cosa: un patrn de diseo, en el
"que se suministran objetos a una clase en lugar de ser la propia clase quien cree los
objetos" (definicin encontrada en la Wikipedia y bastante acertada para lo cortita que es).
Este patrn es muy popular en el mundo de Java y ha sido muy bien explicado por un famoso
gur del desarrollo de software: Martin Fowler
10
. Los creadores de Symfony2 han
comprendido su eficacia para organizar sistemas compuestos por muchos objetos que
presentan dependencias entre ellos y han decidido disear la arquitectura del framework
Symfony2 alrededor de este patrn.
Y ya est bien de palabritas de presentacin. Vamos a contarlo. Lo haremos directamente
con ejemplos, aqu se aplica al cien por cien aquello de que una imagen vale ms que mil
palabras.
En un sistema software orientado a objetos, como puede ser un framework de desarrollo
web, se llevan a cabo tareas de distinta ndole: interpretar la peticin HTTP, construir una
respuesta HTTP, manipular la sesin, controlar la autentificacin y autorizacin, persistir los
datos en una base de datos, contruir formularios, validar datos, crear documentos HTML,
JSON, XML y de otros tipos, enviar e-mails, mapear las URL's en acciones, y otras tareas que
pueden ser incorporadas por el desarrollador de la aplicacin. Cada una de estas tareas se
delega en distintos objetos atendiendo al principio de separacin de mbitos (Separation Of
Concern). Este tipo de objetos que realizan algn tipo de tarea "global" en el sistema se
denominan Servicios, para diferenciarlos de los objetos que tienen utilidad en partes
concretas de la aplicacin, esto es que se crean y se destruyen en el momento en que han
realizado su labor. Estos ltimos objetos son ms propios de la lgica de negocio de la
aplicacin (por ejemplo un objeto Documento en un aplicacin de gestin documental).
Es muy usual, prcticamente lo normal, que los servicios dependan de parmetros de
configuracin e incluso de otros servicios que a su vez dependen de parmetros de
configuracin y, posiblemente, de otros servicios. Por ejemplo, un servicio de persistencia de
datos va a depender de los parmetros de conexin a la base de datos, un servicio de
mailing depender de los parmetros de conexin al servidor SMTP, un servicio diseado
para registrar usuarios puede depender del servicio de persistencia y del servicio de mailing.
Y as hasta que construyamos nuestro sistema completo.
Primer paso: inyeccin de dependencias
El primer paso en la aplicacin del patrn consiste en disear los servicios de manera que
NO construyan ellos mismos los servicios de los cuales dependen, sino que los servicios
dependientes se pasen debidamente construidos a travs del constructor. Es decir si un
servicio S 2 depende de otro servicio S 1 , lo siguiente no cumple el patrn:
1 < ? p h p
2
3 c l a s s S 1 {
4
5 p u b l i c _ _ c o n s t r u c t ( $ p 1 1 , $ p 1 2 , . . . , $ p 1 n )
6 { . . . }
7
8 . . .
9 }
Unidad 4: Inyeccin de Dependencias
64
1 0
1 1 c l a s s S 2 {
1 2
1 3 p u b l i c _ _ c o n s t r u c t ( $ p 2 1 , $ p 2 2 , . . . , $ p 2 m )
1 4 {
1 5 . . .
1 6 $ s 2 = n e w S 1 ( $ p 1 1 , $ p 1 2 , . . . , $ p 1 n ) ;
1 7 . . .
1 8 }
En su lugar las clases anteriores se escribiran as:
1 < ? p h p
2
3 c l a s s S 1 {
4
5 p u b l i c _ _ c o n s t r u c t ( $ p 1 1 , $ p 1 2 , . . . , $ p 1 n )
6 { . . . }
7
8 . . .
9 }
1 0
1 1 c l a s s S 2 {
1 2
1 3 p u b l i c _ _ c o n s t r u c t ( S 1 $ s 1 $ p 2 1 , $ p 2 2 , . . . , $ p 2 m )
1 4 { . . . }
Esto es, para instanciar un objeto S 2 , hay que crear previamente un objeto S 1 y, entonces
pasarselo como argumento en la construccin del objeto S 2 . Esta manera de tratar las
dependencias, mediante inyeccin en los objetos, redunda en un sistema ms
desacoplado. Si en un momento dado tenemos que cambiar, la clase S 1 por S 1 b i s , en el
primer plantemiento tenemos que tocar el cdigo de la clase S 2 , mientras que en el
segundo, si la clases S 1 b i s mantiene la misma interfaz que S 1 todo seguira funcionando
sin ms que pasarle al constructor de S 2 un objeto de S 1 b i s en lugar de uno de S 1 .
Esta es la parte fcil de patrn. De hecho, es muy probable que hayas usado ms de una vez
esta manera de construir los objetos sin haber odo nunca hablar de la "inyeccin de
dependencias"
Segundo paso: el contenedor de dependencias
No obstante, cuando el sistema contenga un nmero considerable de servicios, las
relaciones de dependencia pueden llegar a ser bastante complejas. Entonces, lo que hemos
ganado en flexibilidad al separar las tareas en distintos objetos o servicios, lo perdemos en
complejidad a la hora de instanciarlos, pues hay que tener en cuenta todas las dependencias
para instanciar correctamente un servicio.
La siguiente figura ilustra una imagen grfica de lo que acabamos de decir. Los servicios
(objetos) se han representado con un crculo, y los parmetros de configuracin con un
cuadrado. Observese que un mismo parmetro de configuracin puede ser utilizado por
varios servicios, y un mismo servicio puede ser utilizado por varios servicios.
Segundo paso: el contenedor de dependencias
65
Sistema de servicios/objetos dependientes
Si en alguna parte de la aplicacin necesitamos utilizar el servicio S 4 en PHP haramos algo
as:
1 < ? p h p
2 . . .
3
4 $ p a r a m s = a r r a y (
5 ' p 1 ' = > ' v 1 ' ,
6 ' p 2 ' = > ' v 2 ' ,
7 ' p 3 ' = > ' v 3 ' ,
8 ' p 4 ' = > ' v 4 ' ,
9 ' p 5 ' = > ' v 5 ' ,
1 0 ' p 6 ' = > ' v 6 ' ,
1 1 ) ;
1 2
1 3 $ s 1 = n e w S 1 ( $ p a r a m s [ ' p 1 ' ] , $ p a r a m s [ ' p 2 ' ] ) ;
1 4
1 5 $ s 2 = n e w S 2 ( $ p a r a m s [ ' p 3 ' ] ) ;
1 6
1 7 $ s 4 = n e w S 4 ( $ s 1 , $ s 2 , $ p a r a m s [ ' p 6 ' ] ) ;
1 8
1 9 / / y y a p o d e m o s u s a r $ s 4
Como puedes comprobar resulta un poco engorroso; tenemos que conocer las relaciones de
dependencias entre los servicios para realizar una instancia correcta. Qu podemos hacer
para seguir gozando de la flexibilidad ofrecida por un conjunto de objetos desacoplados y
evitar, a la vez, tener que instanciar todas las dependencias de un objeto cada vez que lo
necesitamos?
La respuesta a esta pregunta es la segunda parte del patrn, y es lo que, en nuestra opinin,
lo hace realmente til. Se trata de elaborar un objeto "inteligente" que conozca las
dependencias de los servicios y sea capaz de construir cualquier servicio que le pidamos y
entregarnslo bien configurado, listo para su uso, sin que nos tengamos que preocupar de
instanciar e "inyectar"sus dependencias. Este objeto tan fantstico y listo se denomina
Segundo paso: el contenedor de dependencias
66
"Injector de Dependencias", "Contenedor de Dependencias", "Contenedor de Servicios" o,
simplemente "Contenedor". Nosotros utilizaremos preferentemente el trmino: "Contenedor
de Servicios". Si contsemos con un " Contenedor de Servicios" implementado, por ejemplo,
en una clase llamada C o n t a i n e r , el cdigo anterior quedara as.
1 < ? p h p
2 . . .
3
4 $ c o n t a i n e r = n e w C o n t a i n e r ( ' / r u t a / a / f i c h e r o / d e / c o n f i g u r a c i o n ' ) ;
5
6 $ s 4 = $ c o n t a i n e r - > g e t ( ' S 4 ' ) ;
7
8 / / y y a p o d e m o s u s a r $ s 4
Es decir, creamos un "Contenedor de Servicios" y le pedimos una instancia del servicio S 4
que es el que vamos a usar en ese momento. Y ya est!. El contenedor se encarga de
construir los objetos necesarios con los parmetros de configuracin correctos.
Importante
Con la aplicacin de este patrn, se ha separado la configuracin de los
servicios de su uso. Reflexiona acerca de esta ltima frase; resume bastante bien la
finalidad del patrn Inyeccin de Dependencias
Bueno, en realidad esto suena muy bien pero no hemos hablado del principal problema:
cmo diseamos y elaboramos un objeto tan magnfico e inteligente?. El problema no es
nada sencillo. Lo que est claro es que ese objeto debe conocer todas las dependencias de
los servicios de la aplicacin. Por eso hemos pasado como argumento a nuestro hipottico
contenedor un, tambin hipottico, fichero de configuracin donde estaran definidas dichas
dependencias.
No vamos a invertir tiempo en contar el diseo y construccin de un "Contenedor de
Servicios". Aunque es un ejercicio realmente interesante e instructivo, el curso va de
Symfony2, as que contaremos, directamente, como funciona el que trae incorporado este
framework a travs de su componente DependencyInjection. Mostraremos su uso integrando
la clase M o d e l de la aplicacin de gestin de alimentos como un servicio.
Integracin de la clase M o d e l como un servicio de Symfony2
La clase M o d e l de nuestra aplicacin realiza un tipo de tarea "global" en el framework;
conectarse a la base de datos MySQL para realizar operaciones sobre sus datos. Por tanto se
trata de un servicio en el sentido que hemos explicado anteriormente. Para convertirla en un
servicio de Symfony2 no hay que tocar nada de su cdigo, ya que todas sus dependencias
son pasadas (inyectadas) a travs del constructor, tal y como se ha explicado en el punto
anterior. Tan slo debemos "decirle" al Contenedor de Servicios Symfony2 que la aada al
conjunto de servicios que es capaz de manipular. Y es lo que haremos.
Integracin de la clase Model como un servicio de Symfony2
67
Nota
Para que el Contenedor de Dependencias pueda crear instancias de la clase que
vamos a integrar como servicio, dicha clase debe construirse como se ha indicado en
el apartado Primer paso: inyeccin de dependencias, es decir, los objetos que requiera
la clase no deben ser instanciados por ella, sino pasados ya construidos a travs de su
constructor.
Todos los bundles construidos con el generador de bundles de Symfony2, proporcionan el
fichero R e s o u r c e s / c o n f i g / s e r v i c e s . y m l . chale un vistazo. Trae algunas lneas
comentadas para que sirvan como ejemplo. En este fichero se describen los servicios del
bundle y sus dependencias. El fichero tiene dos secciones; p a r a m e t e r s y s e r v i c e s . La
primera sirve para declarar parmetros de configuracin, y la segunda para declarar los
servicios (los nombres lo dicen todo). Los parmetros declarados en la seccin p a r a m e t e r s ,
pueden ser referenciados en cualquier fichero de configuracin del framework rodeando su
nombre con el caracter % . Para incorporar la clase M o d e l como servicio de Symfony2
debemos aadir al fichero s e r v i c e s . y m l el siguiente cdigo.
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
p a r a m e t e r s :
j a m a b . d a t a b a s e _ n a m e : a l i m e n t o s
j a m a b . d a t a b a s e _ u s e r : r o o t
j a m a b . d a t a b a s e _ p a s s w o r d : r o o t
j a m a b . d a t a b a s e _ h o s t : l o c a l h o s t
j a m a b . m o d e l . c l a s s : J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l \ M o d e l
s e r v i c e s :
j a m a b . m o d e l :
c l a s s : % j a m a b . m o d e l . c l a s s %
a r g u m e n t s : [ % j a m a b . d a t a b a s e _ n a m e % , % j a m a b . d a t a b a s e _ u s e r % , % j a m a b . d a t a b a s e _ p a s s w o r d % , % j a m a b . d a t a b a s e _ h o s t % ]
La seccin p a r a m e t e r s es autodescriptiva. El nombre de los parmetros de configuracin
puede ser cualquiera. Hemos antepuesto el prefijo j a m a b para evitar posibles colisiones con
otros bundles. Observa que hemos parametrizado incluso el nombre de la clase. La idea es
que la definicin de todo lo que sea parametrizable, es decir, susceptible de ser cambiado,
se encuentre juntito y fcilmente localizable.
En la seccin s e r v i c e s , cada servicio es declarado con un nombre nico. Podemos elegir el
que queramos siempre que no coincida con otro que ya exista en el sistema. Por eso es
siempre una buena prctica usar un prefijo que haga referencia al bundle. En nuestro caso
hemos llamado al servicio: j a m a b . m o d e l .
A continuacin hay que indicar qu clase implementa el servicio y qu argumentos necesita
dicha clase para crear objetos de su tipo. Las directivas c l a s s y a r g u m e n t s recogen dicha
informacin. Fjate la forma de referenciar los parmetros de configuracin con el carcter % .
Esta informacin es todo lo que necesita Symfony2 para incorporar la clase M o d e l como
servicio del framework. Ahora podrs obtener instancias de la misma a travs del
"Contenedor de Servicios" de Symfony2.
El Contenedor de Servicios de Symfony2
La clase S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r del framework,
proporciona en un atributo pblico llamado c o n t a i n e r , una instancia del contenedor de
dependecias. Por tanto, desde cualquier clase derivada de
S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r se puede obtener una
instancia del Contenedor de Dependencias as:
El Contenedor de Servicios de Symfony2
68
$ c = $ t h i s - > c o n t a i n e r ;
Y una vez que lo tengamos podemos instanciar cualquier servicio existente a travs del
mtodo g e t ( ) del Contenedor de Servicios. A este mtodo se le pasa como argumento una
string con el nombre que se le ha dado a dicho servicio en la declaracin del mismo. Por
ejemplo para obtener una instancia del servicio que acabamos de crear con la clase M o d e l ,
haramos lo siguiente:
$ c = $ t h i s - > c o n t a i n e r - > g e t ( ' j a m a b . m o d e l ' ) ;
O ms sencillo an:
$ c = $ t h i s - > g e t ( ' j a m a b . m o d e l ' ) ;
Ya que el mtodo g e t ( ) de la clase
S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r , es un w r a p p e r , es decir llama
directamente al mtodo g e t ( ) del Contenedor de Dependencia de Symfony2.
Como nuestro controlador D e f a u l t C o n t r o l l e r extiende a la clase
S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r , podemos obtener instancias
de la clase M o d e l a travs del Contenedor de Servicios de Symfony2. El cdigo de la clase
D e f a u l t C o n t r o l l e r quedara as:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / C o n t r o l l e r / D e f a u l t C o n t r o l l e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n t r o l l e r ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6
7 c l a s s D e f a u l t C o n t r o l l e r e x t e n d s C o n t r o l l e r
8 {
9
1 0 p u b l i c f u n c t i o n i n d e x A c t i o n ( )
1 1 {
1 2 $ p a r a m s = a r r a y (
1 3 ' m e n s a j e ' = > ' B i e n v e n i d o a l c u r s o d e S y m f o n y 2 ' ,
1 4 ' f e c h a ' = > d a t e ( ' d - m - y y y ' ) ,
1 5 ) ;
1 6
1 7 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g ' , $ p a r a m s ) ;
1 8 }
1 9
2 0 p u b l i c f u n c t i o n l i s t a r A c t i o n ( )
2 1 {
2 2 $ m = $ t h i s - > g e t ( ' j a m a b . m o d e l ' ) ;
2 3
2 4 $ p a r a m s = a r r a y (
2 5 ' a l i m e n t o s ' = > $ m - > d a m e A l i m e n t o s ( ) ,
2 6 ) ;
2 7
2 8 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : m o s t r a r A l i m e n t o s . h t m l . t w i g ' ,
2 9 $ p a r a m s ) ;
3 0 }
3 1
3 2 p u b l i c f u n c t i o n i n s e r t a r A c t i o n ( )
3 3 {
3 4 $ p a r a m s = a r r a y (
3 5 ' n o m b r e ' = > ' ' ,
El Contenedor de Servicios de Symfony2
69
3 6 ' e n e r g i a ' = > ' ' ,
3 7 ' p r o t e i n a ' = > ' ' ,
3 8 ' h c ' = > ' ' ,
3 9 ' f i b r a ' = > ' ' ,
4 0 ' g r a s a ' = > ' ' ,
4 1 ) ;
4 2
4 3 $ m = $ t h i s - > g e t ( ' j a m a b . m o d e l ' ) ;
4 4
4 5 i f ( $ _ S E R V E R [ ' R E Q U E S T _ M E T H O D ' ] = = ' P O S T ' ) {
4 6
4 7 / / c o m p r o b a r c a m p o s f o r m u l a r i o
4 8 i f ( $ m - > i n s e r t a r A l i m e n t o ( $ _ P O S T [ ' n o m b r e ' ] , $ _ P O S T [ ' e n e r g i a ' ] ,
4 9 $ _ P O S T [ ' p r o t e i n a ' ] , $ _ P O S T [ ' h c ' ] , $ _ P O S T [ ' f i b r a ' ] , $ _ P O S T [ ' g r a s a ' ] ) ) {
5 0 $ p a r a m s [ ' m e n s a j e ' ] = ' A l i m e n t o i n s e r t a d o c o r r e c t a m e n t e ' ;
5 1 } e l s e {
5 2 $ p a r a m s = a r r a y (
5 3 ' n o m b r e ' = > $ _ P O S T [ ' n o m b r e ' ] ,
5 4 ' e n e r g i a ' = > $ _ P O S T [ ' e n e r g i a ' ] ,
5 5 ' p r o t e i n a ' = > $ _ P O S T [ ' p r o t e i n a ' ] ,
5 6 ' h c ' = > $ _ P O S T [ ' h c ' ] ,
5 7 ' f i b r a ' = > $ _ P O S T [ ' f i b r a ' ] ,
5 8 ' g r a s a ' = > $ _ P O S T [ ' g r a s a ' ] ,
5 9 ) ;
6 0 $ p a r a m s [ ' m e n s a j e ' ] = ' N o s e h a p o d i d o i n s e r t a r e l a l i m e n t o . R e v i s a e l f o r m u l a r i o ' ;
6 1 }
6 2 }
6 3
6 4 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : f o r m I n s e r t a r . h t m l . t w i g ' ,
6 5 $ p a r a m s ) ;
6 6 }
6 7
6 8 p u b l i c f u n c t i o n b u s c a r P o r N o m b r e A c t i o n ( )
6 9 {
7 0 $ p a r a m s = a r r a y (
7 1 ' n o m b r e ' = > ' ' ,
7 2 ' r e s u l t a d o ' = > a r r a y ( ) ,
7 3 ) ;
7 4
7 5 $ m = $ t h i s - > g e t ( ' j a m a b . m o d e l ' ) ;
7 6
7 7 i f ( $ _ S E R V E R [ ' R E Q U E S T _ M E T H O D ' ] = = ' P O S T ' ) {
7 8 $ p a r a m s [ ' n o m b r e ' ] = $ _ P O S T [ ' n o m b r e ' ] ;
7 9 $ p a r a m s [ ' r e s u l t a d o ' ] = $ m - > b u s c a r A l i m e n t o s P o r N o m b r e ( $ _ P O S T [ ' n o m b r e ' ] ) ;
8 0 }
8 1
8 2 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : b u s c a r P o r N o m b r e . h t m l . t w i g ' ,
8 3 $ p a r a m s ) ;
8 4 }
8 5
8 6 p u b l i c f u n c t i o n v e r A c t i o n ( $ i d )
8 7 {
8 8 $ m = $ t h i s - > g e t ( ' j a m a b . m o d e l ' ) ;
8 9
9 0 $ a l i m e n t o = $ m - > d a m e A l i m e n t o ( $ i d ) ;
9 1
9 2 i f ( ! $ a l i m e n t o ) {
9 3 t h r o w n e w \ S y m f o n y \ C o m p o n e n t \ H t t p K e r n e l \ E x c e p t i o n \ A c c e s s D e n i e d H t t p E x c e p t i o n ( ) ;
9 4 }
9 5
9 6 $ p a r a m s = $ a l i m e n t o ;
9 7
9 8 r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : v e r A l i m e n t o . h t m l . t w i g ' ,
9 9 $ p a r a m s ) ;
1 0 0 }
1 0 1
1 0 2 }
Observa (lneas 22, 43, 75 y 88) que ya no tenemos que pasar parmetros de configuracin
para obtener una instancia de la clase M o d e l , puesto que el Contenedor de Servicios sabe
hacerlo y lo hace por nosotros. Como consecuencia, ya no es necesario la clase
J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ C o n f i g \ C o n f i g , puesto que los parmetros de
configuracin han sido definidos en el archivo
El Contenedor de Servicios de Symfony2
70
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l . As que
puedes borrarla (menos mal! era una clase que "chirriaba" demasiado).
Y qu hemos ganado con todo esto?
Es muy probable que estes preguntndote qu hemos ganado con todo esto de la inyeccin
de dependencias. Y es normal, por que el concepto del Contenedor de Servicios es una
nueva forma de entender la programacin orientada a objetos.
En esta seccin vamos a indicar algunas de las cosas que hemos ganado al convertir en
servicio de Symfony2 nuestra clase M o d e l . Despus seguiremos mostrando las ventajas de
Contenedor de Servicios implementando nuevos servicios para la aplicacin de gestin de
alimentos.
La ventaja ms inmediata es el hecho de poder instanciar un objeto M o d e l sin tener que
pasarle nada ni conocer nada sobre sus dependencias, tan solo hay que pedrselo al
contenedor.
Otra cosa buena es que los parmetros de configuracin son declarados en un fichero de
configuracin y son accesibles desde cualquier otro fichero de configuracin usando el
carcter % alrededor del nombre del parmetro. Tambin se puede obtener el valor de un
determinado parmetro de configuracin desde un controlador utilizando el mtodo
g e t P a r a m e t e r ( ) .
. . .
$ d a t a b a s e = $ t h i s - > g e t P a r a m e t e r ( ' j a m a b . d a t a b a s e _ n a m e ' ) ;
. . .
En ocasiones resulta muy til cambiar la clase que implementa un servicio. Por ejemplo, si
queremos realizar pruebas aisladas sobre la clase D e f a u l t C o n t r o l l e r para comprobar que
la lgica de control funciona como se espera, es decir, pruebas en las que no interfiera la
base de datos, podemos utilizar en lugar de la clase M o d e l , una clase "tonta" con la misma
interfaz pero independiente de la conexin a la base de datos. Por ejemplo la siguiente:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / M o d e l / M o d e l M o c k . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l ;
4
5 c l a s s M o d e l M o c k
6 {
7 p r o t e c t e d $ c o n e x i o n ;
8
9 p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ d b n a m e , $ d b u s e r , $ d b p a s s , $ d b h o s t )
1 0 {
1 1
1 2 }
1 3
1 4 p u b l i c f u n c t i o n d a m e A l i m e n t o s ( )
1 5 {
1 6 $ a l i m e n t o s = a r r a y (
1 7 a r r a y (
1 8 ' i d ' = > 1 ,
1 9 ' n o m b r e ' = > ' p e r a ' ,
2 0 ' e n e r g i a ' = > ' 9 0 ' ,
Y qu hemos ganado con todo esto?
71
2 1 ' p r o t e i n a ' = > ' 8 0 ' ,
2 2 ' h i d r a t o c a r b o n o ' = > ' 7 8 ' ,
2 3 ' f i b r a ' = > ' 8 9 ' ,
2 4 ' g r a s a t o t a l ' = > ' 9 8 ' ,
2 5 ) ,
2 6 a r r a y (
2 7 ' i d ' = > 2 ,
2 8 ' n o m b r e ' = > ' m a n z a n a ' ,
2 9 ' e n e r g i a ' = > ' 9 4 ' ,
3 0 ' p r o t e i n a ' = > ' 6 0 ' ,
3 1 ' h i d r a t o c a r b o n o ' = > ' 3 8 ' ,
3 2 ' f i b r a ' = > ' 8 3 ' ,
3 3 ' g r a s a t o t a l ' = > ' 4 8 ' ,
3 4 ) ,
3 5 ) ;
3 6
3 7
3 8 r e t u r n $ a l i m e n t o s ;
3 9 }
4 0
4 1 p u b l i c f u n c t i o n b u s c a r A l i m e n t o s P o r N o m b r e ( $ n o m b r e )
4 2 {
4 3 r e t u r n $ t h i s - > d a m e A l i m e n t o s ( ) ;
4 4 }
4 5
4 6 p u b l i c f u n c t i o n d a m e A l i m e n t o ( $ i d )
4 7 {
4 8 $ a l i m e n t o = a r r a y (
4 9 ' i d ' = > 1 ,
5 0 ' n o m b r e ' = > ' m a n z a n a ' ,
5 1 ' e n e r g i a ' = > ' 9 4 ' ,
5 2 ' p r o t e i n a ' = > ' 6 0 ' ,
5 3 ' h i d r a t o c a r b o n o ' = > ' 3 8 ' ,
5 4 ' f i b r a ' = > ' 8 3 ' ,
5 5 ' g r a s a t o t a l ' = > ' 4 8 ' ,
5 6 ) ;
5 7
5 8 r e t u r n $ a l i m e n t o ;
5 9
6 0 }
6 1
6 2 p u b l i c f u n c t i o n i n s e r t a r A l i m e n t o ( $ n , $ e , $ p , $ h c , $ f , $ g )
6 3 {
6 4 }
6 5
6 6 }
Fjate que la clase M o d e l M o c k clase no requiere una conexin a la base de datos, pero
mantiene la misma interfaz que M o d e l . Puedes probar a cambiar en el archivo de
configuracin de los servicios del bundle, el nombre de la clase que implementa el servicio
j a m a b . m o d e l :
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
Y qu hemos ganado con todo esto?
72
1 p a r a m e t e r s :
2 j a m a b . d a t a b a s e _ n a m e : a l i m e n t o s
3 j a m a b . d a t a b a s e _ u s e r : r o o t
4 j a m a b . d a t a b a s e _ p a s s w o r d : r o o t
5 j a m a b . d a t a b a s e _ h o s t : l o c a l h o s t
6 j a m a b . m o d e l . c l a s s : J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l \ M o d e l M o c k
7
8 s e r v i c e s :
9 j a m a b . m o d e l :
1 0 c l a s s : % j a m a b . m o d e l . c l a s s %
1 1 a r g u m e n t s : [ % j a m a b . d a t a b a s e _ n a m e % , % j a m a b . d a t a b a s e _ u s e r % , % j a m a b . d a t a b a s e _ p a s s w o r d % , % j a m a b . d a t a b a s e _ h o s t % ]
Si ejecutas la aplicacin ahora puedes comprobar que todo sigue funcionando, aunque los
alimentos que devuelve son los especificados a "capn" en los arrays de la clase M o d e l M o c k .
Esto nos puede servir, como ya hemos dicho antes, para construir tests con PHPUnit que
comprueben la funcionalidad del controlador D e f a u l t C o n t r o l l e r sin necesidad de
"contaminar" las pruebas con posibles fallos con la conexin a la base de datos. Todo esto es
posible gracias al uso de objetos (servicios) absolutamente desacoplados y que son
"juntados" entre s mediante la inyeccin de dependencias.
Imginemos ahora que, despus de que nuestra aplicacin de gestin de alimentos ha
crecido un montn, por "exigencias del guon" nos han migrado los datos a un sistema
gestor de base de datos Postgresql. O que nos exigen hacerla compatible tambin con este
sistema. En tal caso podramos crear una clase M o d e l P o s t g r e s q l , con la misma interfaz que
M o d e l , pero con los mtodos adaptados para atacar una base de datos Postgresql. Hecho
esto, bastara con cambiar en la configuracin la clase que implementa el servicio
j a m a b . m o d e l .
Terminamos la seccin "symfonizando" un poco ms nuestro bundle. La distribucin
standard de Symfony2 proporciona el archivo a p p / c o n f i g / p a r a m e t e r s . i n i , con el objetivo
de que se declaren ah los parmetros globales de la aplicacin. Si lo abres vers que
propone como tales a los parmetros de conexin a una base de datos y a un servidor de
correo. Por tanto deberamos utilizar estos parmetros como argumentos de nuestro servicio
j a m a b . m o d l e . Para ello basta con realizar el cambio en el fichero s e r v i c e s . y m l del bundle:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
1 p a r a m e t e r s :
2 j a m a b . m o d e l . c l a s s : J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l \ M o d e l M o c k
3
4 s e r v i c e s :
5 j a m a b . m o d e l :
6 c l a s s : % j a m a b . m o d e l . c l a s s %
7 a r g u m e n t s : [ % d a t a b a s e _ n a m e % , % d a t a b a s e _ u s e r % , % d a t a b a s e _ p a s s w o r d % , % d a t a b a s e _ h o s t % ]
Ya no necesitamos los parmetros j a m a b . d a t a b a s e _ n a m e , etctera. Obviamente debes
colocar los valores correctos para la conexin a la base de datos en el fichero de
configuracin global p a r a m e t e r s . i n i :
a p p / c o n f i g / p a r a m e t e r s . i n i
1 [ p a r a m e t e r s ]
2 d a t a b a s e _ d r i v e r = p d o _ m y s q l
3 d a t a b a s e _ h o s t = l o c a l h o s t
4 d a t a b a s e _ p o r t =
5 d a t a b a s e _ n a m e = a l i m e n t o s
6 d a t a b a s e _ u s e r = r o o t
7 d a t a b a s e _ p a s s w o r d = r o o t
8
9 m a i l e r _ t r a n s p o r t = s m t p
Y qu hemos ganado con todo esto?
73
1 0 m a i l e r _ h o s t = l o c a l h o s t
1 1 m a i l e r _ u s e r =
1 2 m a i l e r _ p a s s w o r d =
1 3
1 4 l o c a l e = e n
1 5
1 6 s e c r e t = T h i s T o k e n I s N o t S o S e c r e t C h a n g e I t
Servicios al poder!
Continuamos mostrando la potencia de los servicios. Ahora vamos a construir un servicio
que combinar el que acabamos de crear j a m a b . m o d e l , y el servicio de mailing que viene
incluido en Symfony2. Denominaremos al nuevo servicio j a m a b . i n f o s e n d e r , y su cometido
ser enviar por correo electrnico la informacin que tenemos en nuestro sistema sobre un
determinado alimento.
En primer lugar vamos a crear la clase que implementar el servicio. La llamaremos
I n f o S e n d e r y la colocaremos en el espacio de nombres
J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l , se alojar por tanto en el directorio
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / M o d e l .
Dicha clase, para que cumpla el patrn y pueda integrarse como servicio, debe recibir en su
constructor un objeto j a m a b . m o d e l y otro m a i l e r . Con el primero buscar informacin
sobre alimentos en el sistema, y con el segundo enviar el e-mail con dicha informacin. La
clase que nos hemos inventado para este menester es la siguiente:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / M o d e l / I n f o S e n d e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l ;
4
5 c l a s s I n f o S e n d e r
6 {
7
8 p r o t e c t e d $ m o d e l ;
9 p r o t e c t e d $ m a i l e r ;
1 0
1 1 p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ m o d e l , $ m a i l e r )
1 2 {
1 3 $ t h i s - > m o d e l = $ m o d e l ;
1 4 $ t h i s - > m a i l e r = $ m a i l e r ;
1 5 }
1 6
1 7 p u b l i c f u n c t i o n s e n d ( $ t e r m i n o B u s q u e d a , $ d i r e c c i o n E m a i l )
1 8 {
1 9 $ a l i m e n t o s = $ t h i s - > m o d e l - > b u s c a r A l i m e n t o s P o r N o m b r e ( $ t e r m i n o B u s q u e d a ) ;
2 0
2 1 $ t e x t o = ' ' ;
2 2 f o r e a c h ( $ a l i m e n t o s a s $ a l i m e n t o )
2 3 {
2 4 $ t e x t o = i m p l o d e ( ' , ' , $ a l i m e n t o ) ;
2 5 $ t e x t o . = P H P _ E O L ;
2 6 }
2 7
Servicios al poder!
74
2 8 $ m e s s a g e = \ S w i f t _ M e s s a g e : : n e w I n s t a n c e ( )
2 9 - > s e t S u b j e c t ( ' I n f o r m a c i n s o b r e a l i m e n t o s ' )
3 0 - > s e t F r o m ( ' n o r e p l a y @ a u l a s m e n t o r . c o m ' )
3 1 - > s e t T o ( $ d i r e c c i o n E m a i l )
3 2 - > s e t B o d y ( $ t e x t o )
3 3 ;
3 4
3 5 $ t h i s - > m a i l e r - > s e n d ( $ m e s s a g e ) ;
3 6 }
3 7 }
El constructor de la clase (lneas 11-15) recibe los objetos $ m o d e l y $ m a i l e r (o dicho de
otra forma; los servicios j a m a b . m o d e l y m a i l e r ) ya creados y bien configurados, y
simplemente los asigna como atributos para que puedan utilizarlos los mtodos que se irn
aadiendo al servicio.
El mtodo s e n d ( ) del servicio (lneas 17-36), recibe como parmetros un trmino de
bsqueda y una direccin de correos. Entonces, utiliza el servicio j a m a b . m o d e l (lnea 19)
para realizar una busqueda de alimentos en el sistema. El resultado es usado para construir
una cadena que servir como cuerpo del e-mail (lneas 21-26). A continuacin se prepara un
mensaje de e-mail (lneas 28-33) con los campos correspondientes; subject, from, to y body,
y se usa el servicio de mailing de Symfony2 (lnea 35) para enviar el mensaje.
Nota
El servicio de mailing incorporado con Symfony2 esta basado en la librera SwiftMailer,
y para el envo de mensaje utiliza el mtodo s e n d ( ) cuyo argumento debe ser una
instancia de S w i f t _ M e s s a g e . La creacin de esta instancia es lo que se realiza en las
lneas 28-33.
Algo importante y que hemos de resaltar. Durante el proceso de construccin de la clase
I n f o S e n d e r no nos hemos preocupados por detalles de la configuracin de los servicios,
simplemente sabamos que existian y conocamos sus funciones, las hemos utilizado y se
acab. Es la consecuencia de haber separado la configuracin del uso. Gracias a ello, escribir
un servicio que dependa de otro, o simplemente utilizar un servicio es realmente sencillo.
El prximo paso es integrar la clase que acabamos de construir como servicio del framework.
Si has estado atento a todo lo que llevamos dicho en esta unidad, sabrs que esto se hace
aadindola al fichero de configuracin s e r v i c e s . y m l del bundle.
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
1 p a r a m e t e r s :
2 j a m a b . m o d e l . c l a s s : J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l \ M o d e l
3 j a m a b . i n f o s e n d e r . c l a s s : J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l \ I n f o S e n d e r
4
5 s e r v i c e s :
6 j a m a b . m o d e l :
7 c l a s s : % j a m a b . m o d e l . c l a s s %
8 a r g u m e n t s : [ % d a t a b a s e _ n a m e % , % d a t a b a s e _ u s e r % , % d a t a b a s e _ p a s s w o r d % , % d a t a b a s e _ h o s t % ]
9
1 0 j a m a b . i n f o s e n d e r :
1 1 c l a s s : % j a m a b . i n f o s e n d e r . c l a s s %
1 2 a r g u m e n t s : [ @ j a m a b . m o d e l , @ m a i l e r ]
Servicios al poder!
75
La nica explicacin que merece el cdigo anterior es que hay que tener en cuenta que los
argumentos que son servicios se representan anteponiendo el carcter @ a su nombre.
El ltimo paso es asegurarnos de que todos los servicios que estamos usando estn bien
configurados. Los de nuestra factura; j a m a b . m o d e l y j a m a b . i n f o s e n d e r , obviamente lo
estn (vase el cdigo anterior). Nos falta comprobar el servicio de mailing. Este servicio se
configura en el archivo de configuracin global a p p / c o n f i g / c o n f i g . y m l , en la seccin
s w i f t m a i l e r .Echale un vistazo y fjate que hace referencia a los parmetros
% m a i l e r _ t r a n s p o r t % , % m a i l e r _ h o s t % , % m a i l e r _ u s e r % y % m a i l e r _ p a s s w o r d % . Adivinas
donde se definen tales parmetros? Espero que as sea, significa que has estado leyendo la
unidad con atencin. Se hace en el archivo s r c / c o n f i g / p a r a m e t e r s . i n i , aunque podramos
colocar los valores concretos directamente en el fichero c o n f i g . y m l , los administradores de
sistemas que van a instalar nuestra aplicacin agredecern muchsimo que todos los
parmetros de configuracin que tengan que ver con los servicios del sistema se encuentren
juntitos en un slo fichero (cuidado que ahora la palabra servicio la estamos usando en un
contexto de sistemas de informacin, esto es, nos referimos a servicios de base da datos, de
correo, etctera).
Hemos utilizado una cuenta de Gmail para enviar emails. Para configurarla es preciso aadir
dos parmetros ms: el tipo de encriptacin y el modo de autenticacin (esto lo dice la
documentacin de Gmail). Por otro lado, en la documentacin oficial de Symfony2 se dice
que estos datos son mapeados por swiftmailer a travs de los parmetros e n c r y p t i o n y
a u t h _ m o d e . La seccin s w i f t m a i l e r del archivo de configuracin global c o n f i g . y m l queda
as:
a p p / c o n f i g / c o n f i g . y m l
. . .
s w i f t m a i l e r :
t r a n s p o r t : % m a i l e r _ t r a n s p o r t %
u s e r n a m e : % m a i l e r _ u s e r %
p a s s w o r d : % m a i l e r _ p a s s w o r d %
. . .
Y la declaracin de los parmetros:
a p p / c o n f i g / p a r a m e t e r s . i n i
1 . . .
2 m a i l e r _ t r a n s p o r t = g m a i l
3 m a i l e r _ u s e r = t u u s e r n a m e
4 m a i l e r _ p a s s w o r d = t u p a s s w o r d
5 . . .
Y ya tenemos el servicio de mailing bien configurado y listo para ser usado. Y ahora toca
probarlo. Construiremos una ruta y una accin asociada para ello. Estara bien que
intentases realizar tu mismo esta prueba sin mirar lo que viene a continuacin hasta que no
lo hayas conseguido o hasta que te hayas desesperado. Si vas asimilando todo lo que hasta
ahora hemos estudiado en el curso, deberas ser capaz de hacerlo.
En s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l
aadimos la ruta:
J A M A B _ t e s t i n f o s e n d e r :
p a t t e r n : / t e s t i n f o s e n d e r
d e f a u l t s : { _ c o n t r o l l e r : J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : t e s t I n f o S e n d e r }
Servicios al poder!
76
Y en el D e f a u l t C o n t r o l l e r implementamos la accin correspondiente:
s r c / J a z z y w e b / A u l a s M e n t o r / A l i m e n t o s B u n d l e / C o n t r o l l e r / D e f a u l t C o n t r o l l e r . p h p
< ? p h p
. . .
p u b l i c f u n c t i o n t e s t I n f o S e n d e r A c t i o n ( )
{
$ i n f o s e n d e r = $ t h i s - > g e t ( ' j a m a b . i n f o s e n d e r ' ) ;
$ i n f o s e n d e r - > s e n d ( ' % n a r a n j a % ' , ' j u a n d a l i b a b a @ g m a i l . c o m ' ) ;
r e t u r n n e w \ S y m f o n y \ C o m p o n e n t \ H t t p F o u n d a t i o n \ R e s p o n s e (
' < h t m l > < b o d y > < h 2 > S e h a e n v i a d o i n f o r m a c i n a
j u a n d a l i b a b a @ g m a i l . c o m < / h 2 > < / b o d y > < / h t m l > ' ) ;
}
. . .
Lanza esta ruta en tu navegador y se enviar informacin sobre el alimento naranja a la
direccin de e-mail que hayas elegido.
Nota
Sustituye t u u s e r n a m e , t u p a s s w o r d y q u i e n t u q u i e r a s @ d o n d e q u i e r a s . c o m por los
valores propioso de tu cuenta de e-mail y por la direccin de correo contra la que
deseas hacer pruebas.
Nota
Observa que no hemos construido una plantilla para pintar la accin. Como es una
prueba sencilla, hemos decidido construir directamente un objeto R e s p o n s e .
El uso del servicio j a m a b . i n f o s e n d e r , como todos los servicios de Symfony2 no puede ser
ms sencillo. Le dices al Contenedor de Servicios: "dame el servicio fulanito", l te lo
devuelve educadamente, sin hacerte preguntas, y tu lo usas en tu aplicacin. Que quieres
usar otro servidor para enviar los mensajes. No tienes que cambiar ni una lnea de tu cdigo.
Cambias la configuracin del servicio de mailing y todo sigue funcionando. Que ahora resulta
que no quieres enviar mails mientras ests haciendo pruebas. Aades a la configuracin de
swiftmailer el parmetro: d i s a b l e _ d e l i v e r y : t r u e y se deshabilita el envo. Son posibles
situaciones prcticas que se te pueden dar en el desarrollo de tus aplicaciones y que, gracias
a la inyeccin de dependencias, se resuelven de una manera muy sencilla y segura en el
sentido de que realizas grandes cambios y todo sigue funcionando, nada se rompe.
Ms servicios an
No te apures ya hemos terminado, tan slo queremos recalcar que Symfony2 est
compuesto por muchos servicios como los que hemos estudiado en esta unidad. Y que, por
supuesto, los bundles de terceros ms famosos utilizan extensiva e intensivamente la
inyeccin de dependencia. Comprender bien este concepto te facilitar el aprendizaje y tu
Ms servicios an
77
experiencia con Symfony2.
Nota
Si quieres saber todos los servicios que vienen con Symfony2 ejecuta este comando:
p h p a p p / c o n s o l e c o n t a i n e r : d e b u g
Es verdad que se pueden desarrollar aplicaciones con Symfony2 sin necesidad de "echar
mucha cuenta" a la inyeccin de dependencias. Basta con conocer los servicios ms
importantes proporcionados por Symfony2, instanciarlos en las acciones del controlador
cuando los necesitas y utilizarlos. Sin embargo, si no prestas suficiente atencin a este
concepto y lo dejas un poco de lado, sentirs que no consigues controlar del todo al
framework y en ocasiones se te escapa. Comprender la inyeccin de dependencia, el
concepto de servicio y de contenedor de servicios, es fundamental para tomar las riendas
del framework y cabalgar sobre l confortablemente. Este es el motivo esta unidad y de su
ubicacin a la mitad del curso, justo antes de comenzar a desarrollar una aplicacin ms
compleja que servir de vehculo vertebrador para aprender a utilizar Symfony2.
En las unidades anteriores ya hemos utilizado los servicios de Symfony2 sin haberlos
mencionados. Cuando renderizamos los resultados de una accin con una plantilla, estamos
haciendo uso del servicio t e m p l a t i n g a travs del wrapper r e n d e r ( ) de la clase
C o n t r o l l e r . De hecho hacer esto:
r e t u r n $ t h i s - > r e n d e r ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g ' , $ p a r a m s ) ;
es equivalente a esto:
$ t = $ t h i s - > g e t ( ' t e m p l a t i n g ' ) ;
r e t u r n $ t - > r e n d e r R e s p o n s e ( ' J a z z y w e b A u l a s M e n t o r A l i m e n t o s B u n d l e : D e f a u l t : i n d e x . h t m l . t w i g ' , $ p a r a m s ) ;
Pero ms cortito.
En las prximas unidades estudiaremos los servicios ms importantes que ofrece Symfony2.
Con ellos se puede hacer casi de todo. No obstante a veces tendremos que ampliar el
framework con nuevas funcionalidades. Y esto, si queremos hacerlo bien, debemos hacerlo
usando la inyeccin de dependencias y los servicios.
La unidad en chuletas
Servicio. Un tipo de objetos que realizan algn tipo de tarea global en el sistema.
Las clases que se quieran integrar como servicios, se definen en el archivo
R e s o u r c e s / c o n f i g / s e r v i c e s . y m l del bundle.
Ese archivo tiene dos secciones: p a r a m e t e r s y s e r v i c e s . La primera sirve para definir
parmetros de configuracin del bundle (que pueden ser utilizados por los servicios o en
otras partes del bundle), y la segunda para declarar los servicios.
Las clases que vayan a incorporarse como servicios deben "inyectar" sus dependencias
(parmetros y otros servicios) a travs de su constructor o de algn setter.
Ejemplo:
La unidad en chuletas
78
p a r a m e t e r s :
j a m a b . d a t a b a s e _ n a m e : a l i m e n t o s
j a m a b . d a t a b a s e _ u s e r : r o o t
j a m a b . d a t a b a s e _ p a s s w o r d : r o o t
j a m a b . d a t a b a s e _ h o s t : l o c a l h o s t
j a m a b . m o d e l . c l a s s : J a z z y w e b \ A u l a s M e n t o r \ A l i m e n t o s B u n d l e \ M o d e l \ M o d e l
s e r v i c e s :
j a m a b . m o d e l :
c l a s s : % j a m a b . m o d e l . c l a s s %
a r g u m e n t s : [ % j a m a b . d a t a b a s e _ n a m e % , % j a m a b . d a t a b a s e _ u s e r % , % j a m a b . d a t a b a s e _ p a s s w o r d % , % j a m a b . d a t a b a s e _ h o s t % ]
Desde un controlador (que extienda la clase
S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r , se puede obtener
cualquier servicio usando el mtodo g e t ( ) :
$ s e r v i c i o = $ t h i s - > g e t ( ' n o m b r e _ d e l _ s e r v i c i o ' ) ;
Desde ese controlador tambin se puede obtener los valores de los parmetros usando
el mtodo clase g e t P a r a m e t e r ( ) :
$ p a r a m e t r o = $ t h i s - > g e t P a r a m e t e r ( ' n o m b r e _ d e l _ p a r a m e t r o ' ) ;
Los argumentos que son servicios se especifican colocando el carcter @ delante del
nombre:
. . .
s e r v i c e s :
j a m a b . i n f o s e n d e r :
c l a s s : % j a m a b . i n f o s e n d e r . c l a s s %
a r g u m e n t s : [ @ j a m a b . m o d e l , @ m a i l e r ]
. . .
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll para recorrer
todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Comentarios
79
Unidad 5: Desarrollo de la aplicacin MentorNotas (I).
Anlisis
Continuaremos el estudio de Symfony2 desarrollando una aplicacin web con unas
funcionalidades que permitan aplicar la mayor parte de las herramientas que el framework
ofrece. Tal aplicacin requiere de un anlisis previo que, mediante los modelos apropiados,
describa con cierto detalle las caractersticas de la misma. El primer objetivo de este captulo
es elaborar dicho anlisis.
En el anlisis abordaremos:
el catlogo de requisitos funcionales
el modelo de datos
el modelo de procesos
La tarea de diseo quedar reducida a proponer las pantallas que conformarn la aplicacin
y los elementos que deben incluir, ya que los elementos clave del diseo arquitectnico los
establece el propio framework. En la unidad 3 hemos estudiado los elementos ms bsicos
de esta arquitectura y se irn completando a medida que avancemos en la aplicacin.
Por ltimo facilitaremos tambin los recursos que van ms all del cdigo de los
controladores, entidades y plantillas que conforman el grueso de la aplicacin:
las CSS's para dotar de estilo a la aplicacin.
las libreras javascript para enriquecer la interfaz de usuario.
Algunos recursos grficos (logotipo, iconos, ).
Estos recursos se denominan activos (assets) de la aplicacin, y son servidos por el
servidor web al cliente directamente, sin ningn tipo de proceso intermedio.
En definitiva, en esta unidad desarrollaremos rpidamente pero con sufciente precisin, las
fases previas a la construccin del software; anlisis y diseo, as como la elaboracin de la
interfaz grfica de usuario. La intencin es poder centrarnos en la fase de construccin,
donde un framework de desarrollo de aplicaciones es realmente til.
Descripcin de la aplicacin
A partir de este momento y a lo largo del resto del curso, nuestra labor ser el desarrollo de
una aplicacin para la gestin de notas (al estilo de los post-it). Nos hemos inspirado en una
herramienta que ltimamente est teniendo mucho xito entre los usuarios de plataformas
mviles (smartphones y tabletas). Se trata de EverNote
11
, una aplicacin que permite a
sus usuarios almacenar notas y etiquetarlas para facilitar su bsqueda y recuperacin
posterior. Esta aplicacin se distribuye para los principales sistemas operativos y
plataformas, tanto mviles como fijas, y tambin ofrecen una aplicacin web que puede ser
ejecutada desde los navegadores ms populares. Posiblemente la caracterstica ms
sobresaliente de EverNote, sea la posibilidad de mantener sincronizadas las notas de un
usuario en todos los dispositivos en los que utilice la aplicacin, gracias a la existencia de un
repositorio remoto donde se almacenan sus notas.
La versin web de EverNote nos ha servido como motivo de inspiracin para la aplicacin
que analizaremos en esta unidad y construiremos en las siguientes. Obviamente no
pretendemos ni hacer una copia, ni reinventar la rueda, ni hacer la competencia ni nada
parecido. Nuestro objetivo es fundamentalmente pedaggico; queremos que aprendas a
desarrollar aplicaciones web con Symfony2, y estamos convencidos de que la mejor manera
de aprender a programar con una determinada tecnologa es mediante la elaboracin de una
aplicacin con suficiente cuerpo, que plantee en su desarrollo los principales problemas a
los que te enfrentars como programador web. Dada su popularidad, EverNote nos ha
Unidad 5: Desarrollo de la aplicacin MentorNotas (I). Anlisis
80
servido como un buen modelo. Por otro lado, dado nuestro inters pedaggico, nuestra
aplicacin ser bastante ms modesta, aunque, como podrs comprobar, es totalmente
profesional e incluso apta para ser usada en un entorno de produccin.
Por ltimo hemos optado por un gestor de notas como aplicacin vertebradora del curso,
porque engloba un amplio conjunto de funcionalidades para tratar los aspectos ms
relevantes de Symfony2. Adems es una aplicacin eminentemente prctica, a pesar de que
la hemos concebido desde una perspectiva pedaggica. De hecho, y gracias a las
posibilidades de extensin de Symfony2, una vez finalizado el curso, la aplicacin puede ser
mejorada, modificada y/o adaptada hasta el punto que uno desee. Tambin podr ser
utilizada como modelo para el desarrollo de otras aplicaciones que, en principio, no tienen
nada que ver con la gestin de notas; basta con que el programador tenga la suficiente
capacidad de abstraccin para identificar estructuras anlogas en dominios distintos.
Descripcin general
Con MentorNotas los usuarios registrados podrn crear, editar y eliminar notas al estilo de
post-it's enriquecidos. El usuario podr asociar las etiquetas que desee a sus notas, con el
objetivo de facilitar la bsqueda y recuperacin de las mismas. Los usuarios podrn
registrarse facilitando una direccin de correo electrnico. Adems, podrn tener acceso a
caractersticas premium si se abonan a alguno de los planes ofrecidos por el sistema.
El sistema ser administrado por una aplicacin que permitir gestionar los usuarios, los
planes de pago para el uso de caractersticas premium, y los banners de publicidad que
sern presentados a los usuarios no premium.
Utilizando la jerga clsica de los desarrolladores web, llamaremos backend a la aplicacin de
administracin, y frontend a la aplicacin en s, es decir al gestor de notas que usan los
usuarios registrados.
Catlogo de requisitos
Requisitos del frontend
1. Desde una pgina pblica, los usuarios podrn:
entrar en la aplicacin presentando sus credenciales (nombre de usuario y clave),
si an no est registrado, crear una nueva cuenta a partir de una direccin de
correo electrnico.
2. Existen dos tipos de usuarios:
registrados, simplemente han obtenido una cuenta con su correo electrnico.
premium, se han abonado a alguno de los planes de pago
3. El usuario podr crear, editar y borrar sus notas. Dichas notas contendrn los siguientes
elementos:
ttulo,
fecha, que pondr automticamente el sistema,
texto enriquecido, es decir, con posibilidad de formato,
un archivo adjunto (nicamente para usuarios registrados)
4. En el momento de crear una nota el usuario podr asociarle las etiquetas que desee.
Para ello podr utilizar las etiquetas que ya existan o crear otras nuevas sobre la
marcha, mientras est creando la nota.
Descripcin general
81
5. En una misma pantalla se mostrarn:
un listado con todas las etiquetas del usuario,
un buscador mediante el que se podrn buscar notas que contengan un trmino de
bsqueda en el texto y/o en el ttulo,
un listado con las notas que satisfacen el criterio de bsqueda que el usuario haya
elegido: todas las notas asociadas a una etiqueta o todas las notas que contienen
un trmino de bsqueda,
un detalle con la nota seleccionada, o con la primera nota que se encuentre si no se
ha seleccionado ninguna.
6. Cuando el usuario haga click en una etiqueta, el listado de notas mostrar todas las
notas pertenecientes a dicha categora.
7. Cuando el usuario haga una bsqueda, el listado de notas mostrar todas las notas que
contienen en su texto o ttulo ese trmino.
8. Cuando el usuario haga click en una nota del listado de notas, se mostrar el detalle de
esa nota en un espacio bien visible.
9. La aplicacin mostrar a los usuarios no premium banners de publicidad.
10. Los usuarios premium dispondrn de un apartado para consultar sus pagos y otros
elementos propios de la cuenta premium
11. Se habilitar un servicio RSS para difundir noticias relacionadas con la aplicacin
Requisitos del backend
Slo los usuarios administradores podrn acceder al backend para:
gestionar usuarios,
gestionar publicidad,
gestionar planes de pago
Gestin de usuarios
12. Crear y modificar los datos de los usuarios.
13. Borrar usuarios.
14. Activar/desactivar cuentas.
15. Asociar permisos (administracin, cuenta premium). Los usuarios podrn tener ms de
un permiso.
16. Consultar los pagos realizados por el usuario.
Gestin de publicidad
17. La publicidad consistir en banners que son imgenes en formato j p g , p n g o g i f .
18. Desde el backend se podrn crear, modificar y borrar estos banners.
Gestin de planes de pagos
19. Cada plan de pago consistir en un precio para un periodo de tiempo.
20. Desde el backend se podrn crear nuevos planes de pago. Se podrn modificar o borrar
si no existe ningn usuario abonado.
Requisitos del backend
82
Modelo de datos
La siguiente figura representa el modelo de datos propuesto:
Modelo de datos
Cada usuario puede tener las notas que quiera, pero cada nota pertenece a un slo usuario.
Las etiquetas de las notas son tambin propias de cada usuario. Las notas pueden tener
asociadas tantas etiquetas como se deseen, a la vez que una misma etiqueta puede ser
compartida entre las notas siempre que pertenezcan a un mismo usuario.
Un mismo usuario podr pertenecer a distintos grupos y, evidentemente, un grupo podr ser
compartido por los usuarios.
Cada usuario podr realizar uno o muchos contratos, y cada contrato tendr aplicada una
sla de las tarifas propuestas.
Por ltimo la publicidad no tiene relacin con el resto de las entidades. Simplemente ser un
repositorio de banners que sern mostrado de manera aleatoria a los usuarios no premium.
Descripcin de los procesos
Registro de nuevos usuarios
El usuario introduce en un formulario su direccin de correo electrnico, su nombre de
usuario y el password (por duplicado) con el que desea entrar en la aplicacin.
Si el formulario es vlido:
se crear un nuevo usuario en la aplicacin con estado inactivo y al que se le asignar
un token nico.
se le enviar a la direccin de correo electrnico que ha facilitado un mensaje de
bienvenida con una url que lleva adjunta dicho token nico para activar su cuenta.
Si el formulario no es vlido volver a mostrarse indicando la razn del error.
Cuando el usuario enlaza con la URL facilitada en el mensaje de bienvenida, la aplicacin
comprobar el token facilitado, y si es correcto se activar la cuenta. A partir de ese
momento el usuario podr entrar en la aplicacin a travs del formulario de login.
Creacin de una nota
El usuario rellena un formulario con los siguientes datos:
ttulo de la nota,
fecha, que se autorrellenar con la fecha del sistema, aunque podr ser cambiada por el
usuario.
Modelo de datos
83
el texto de la nota, con posibilidad de darle formato.
las etiquetas de la nota,
si es usuario premium, podr subir un archivo.
El proceso de asociar etiquetas a las notas ser como sigue:
En un cuadro de texto el usuario escribir todas las notas. A medida que escribe cada nota,
la aplicacin ir autocompletando sugiriendo las etiquetas que haya utilizado en otras notas
anteriores y que coincidan con el patrn que escribe. Al pulsar la tecla intro, la etiqueta se
habr terminado de escribir y podr escribir una nueva. El proceso se repite hasta que el
usuario se harte.
Cuando termine y guarde la nota, las etiquetas nuevas sern dadas de alta en el sistema y
sern asociadas a la nota.
Contratacin de una cuenta premium
El usuario podr contratar una cuenta premium picando en un enlace bien visible que se
habilitar para ello. Cuando el usuario pique en dicho enlace, ser redirigido a una pgina
donde se le informe de las tarifas y las funcionalidades premium. El usuario elige la tarifa
que desea y es redirigido al servicio de pago virtual, donde realiza el pago. Una vez realizado
el pago, dicho servicio vuelve a redirigir al usuario a la aplicacin indicando que se ha
realizado el pago correctamente. Entonces, se actualizar a premium la cuenta del usuario
por el periodo de tiempo que haya abonado. A partir de ese momento podr asociar archivos
a sus notas.
Presentacin de la publicidad
La publicidad ser presentada a los usuarios que no dispongan de cuenta premium de una
manera aleatoria, segn una distribucin de probabilidad uniforme.
Interfaz de usuario
En este apartado mostraremos mediante bocetos de pantallas, los distintos escenarios que
presentar la aplicacin:
Pantalla de login
.
Pantalla de login
Pantalla de registro
.
Contratacin de una cuenta premium
84
Pantalla de registro
Pantalla principal (inicio)
.
Pantalla de inicio
Pantalla de creacin de notas
.
Pantalla de creacin de notas
Pantalla de modificacin de notas
.
Pantalla principal (inicio)
85
Pantalla de modificacin de notas
Pantalla de planes de pago
.
Pantalla de planes de pago
Men de la aplicacin de administracin
.
Men de la aplicacin de administracin
Gestin de entidades
.
La gestin de las entidades usuarios, grupos, planes de pago y publicidad es, esencialmente,
idntica. Se trata de poder crearlas, modificarlas y listarlas. Por tanto utilizaremos slo una
de ellas para mostrar los bocetos.
Listado de usuarios
.
Listado de usuarios
Pantalla de planes de pago
86
Creacin de usuarios
.
Creacin de usuarios
Edicin de usuarios
.
Edicin de usuarios
Recursos para la construccin de la aplicacin
Queremos que nuestra aplicacin tenga un aspecto profesional y moderno. Por ello, para
construir las pginas, iremos ms all del HTML y utilizaremos jQuery
12
para ayudarnos en
la construccin de la interfaz grfica de usuario.
Como dicen en la pgina oficial, jQuery es una librera javascript, rpida y concisa que
simplifica el manejo del HTML, la manipulacin de eventos, la animacin, y la interaccin
Ajax con el fin de desarrollar aplicaciones web rpidamente.
Adems utilizaremos jQuery UI, que es una librera construida sobre jQuery y que ofrece
tiles elementos para la confeccin de interfaces de usuario (widgets), tales como mens,
desplegables de varios tipos, calendarios, etctera.
Por ltimo, para confeccionar el layout con tres columnas, cabecera y pie que se especifica
en el anlisis, utilizaremos un plugin de jQuery denominado jQuery UI Layout Plugin
13
.
Tanto jQuery UI como jQuery UI Layout Plugin, al ser extensiones de jQuery destinadas al
enriquecimiento de interfaces web (HTML), cuentan con sus propias CSS's, y son las que
utilizaremos para confeccionar la interfaz de usuario de nuestra aplicacin.
En el archivo interfaz_de_usuario.zip tienes disponibles los layouts (plantillas) HTML que,
enriquecidos con estas libreras, implementan las pantallas que hemos esbozado en el
anlisis realizado en esta unidad.
La estructura de directorio, una vez desplegado es la siguiente:
Creacin de usuarios
87
u i
l a y o u t s - > l a y o u t s * H T M L *
| i m a g e s - > i m g e n e s u t i l i z a d a s p o r l o s l a y o u t s a n t e r i o r e s
s r c
c s s - > * C S S * d e s a r r o l l a d a s p o r n o s o t r o s
i m a g e s - > i m g e n e s d e s a r r o l l a d a s p o r n o s o t r o s
j s - > j a v a s c r i p t d e s a r r o l l a d o s p o r n o s o t r o s
v e n d o r s - > l i b r e r a s * j a v a s c r i p t * d e t e r c e r o s , e s t a s
C L E d i t o r 1 _ 3 _ 0 c u e n t a n c o n s u s p r o p i a s * C S S ' s * e i m g e n e s
j q u e r y
j q u e r y - l a y o u t
t a g i t
Si abres directamente con el navegador los archivos del directorio layouts:
inicio.html
inicio-editar_nota.html
login.html
registro.html
podrs ver la interfaz grfica completamente acabada pero, obviamente, sin
funcionalidades. El objetivo de las siguientes unidades es, precisamente dotar de
funcionalidades a esta interfaz utilizando Symfony2. Estos archivos nos servirn como base
para elaborar las plantillas de la aplicacin.
Conclusin
En esta unidad hemos realizado un anlisis de la aplicacin cuya construccin nos servir
durante el resto del curso para estudiar Symfony2. Hemos planteado un catlogo de
requisitos, un modelo de datos, unos proceso. Hemos propuesto unos bocetos para la
interfaz de usuario. Y por ltimo se han facilitado los assets (CSS's, javascripts e imgenes) y
las plantillas HTML con las que construiremos la interfaz grfica de la aplicacin.
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll para recorrer
todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Conclusin
88
Unidad 6: Desarrollo de la aplicacin MentorNotas (II).
Rutas y Controladores
Advertencia
Atencin!!!! Falta por colocar la url del curso de symfony 1.4 en una nota del final.
La construccin de la aplicacin que se ha analizado en la unidad anterior puede llevarse a
cabo por muchos caminos. Lo importante es que elegir alguno de los que llevan a "Roma" sin
sufrir demasiado.
El camino que vamos a seguir en las prximas unidades no es el ms corto, ya que su diseo
obedece a criterios pedgogicos, en el sentido de que intentamos presentar los conceptos
de manera secuencial. Sin embargo, en la prctica esto no es lo ms adecuado: lo ideal es
tener un conocimiento global de la herramienta y utilizar cada una de sus partes en los
momentos adecuados. Esperamos que cuando finalices el curso tengas los conocimientos
sufciente para usar Symfony2 eficientemente y construyas aplicaciones a la velocidad del
rayo. Pero por lo pronto tendremos que dar algunos rodeos con el propsito de presentar
conceptos fundamentales.
Lo primero: el bundle
En Symfony2 todo es un bundle. Aunque es una frase simplista nos recuerda que lo primero
que necesitamos para desarrollar la aplicacin es, por lo menos, un bundle donde escribir su
cdigo.
En el anlisis de la unidad anterior se especific que el sistema deba contar con una parte
para los usuarios, que es la aplicacin de notas en s, y otra parte de administracin para los
responsables de la aplicacin. A la primera se le suele denominar frontend y a la segunda
backend. La primera decisin que hemos tomado en el diseo de la aplicacin es dividirla en
dos bundles, uno para las funcionalidades del frontend y otro para las del backend. Esta
divisin no es estrictamente necesaria, se puede meter todas las funcionalidades en un solo
bundle. Pero atendiendo al principio de la Separation of Concerns, preferimos separar el
cdigo del frontend y del backend.
Nota
Por lo pronto, si alguien que no conoce la aplicacin comienza a explorarla y a
modificarla, tendr claro donde debe ir para ver el cdigo de cada parte.
Para desarrollar la aplicacion utilizaremos el mismo proyecto Symfony2 que venimos
utizando en el curso. As podrs ver como un slo proyecto puede dar cabida a muchas
aplicaciones.
Tambin utilizaremos el espacio de nombres de la empresa ficticia Jazzyweb y la categoria
AulasMentor para la ubicacin de los bundles de la aplicacin MentorNotas. As todo el
cdigo del curso quedar en un slo proyecto. Si ms adelante quieres separarlo,
nicamente tienes que crear un nuevo proyecto de Symfony2 y enchufarle los bundles que
utilicen la aplicacin que quieras separar.
Unidad 6: Desarrollo de la aplicacin MentorNotas (II). Rutas y Controladores
89
Este primer conjunto de decisiones de diseo nos permiten emprender la primera accin:
crear los bundles. Utiliza el generador de bundles de Symfony2 tal como hicimos en la
unidad 3.
p h p a p p / c o n s o l e g e n e r a t e : b u n d l e
Y usa los siguientes valores para contestar a las preguntas:
Parmetro Valor
Bundle namespace Jazzyweb/AulasMentor/NotasFrontendBundle
Bundle name JAMNotasFrontendBundle
Target directory /tu/ruta/a/cursosf2/src
Configuration format yml
directory structure yes
automatic update of your Kernel yes
automatic update of the Routing yes
Por comodidad, hemos acortado un poco el nombre del bundle. Si todo ha ido bien cdigo
del bundle se ha generado en
/ t u / r u t a / a / c u r s o s f 2 / s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e , se ha debido
registrar en el fichero a p p / A p p K e r n e l . p h p , y debe haber una entrada en el fichero
a p p / c o n f i g / r o u t i n g . y m l que incluye el routing del bundle:
J A M N o t a s F r o n t e n d B u n d l e :
r e s o u r c e : " @ J A M N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l "
p r e f i x : /
Ahora construimos el bundle para las funcionalidades del backend. Vuelve a lanzar el
generador de bundles y contesta con los siguientes datos:
Parmetro Valor
Bundle namespace Jazzyweb/AulasMentor/NotasBackendBundle
Bundle name JAMNotasBackendBundle
Target directory /tu/ruta/a/cursosf2/src
Configuration format yml
directory structure yes
automatic update of your Kernel yes
automatic update of the Routing yes
Aunque ya hemos generado el esqueleto para el bundle donde se implementar el backend,
este ltimo ser desarrollado en la ltima unidad del curso. Desde ahora y hasta la unidad
10, todo el desarrollo tendr que ver con el frontend.
Para que no haya colisin con las rutas del bundle de ejemplo AcmeDemoBundle, vamos a
cambiar la ruta _ w e l c o m e del archivo a p p / c o n f i g / r o u t i n g _ d e v . y m l por la siguiente:
a p p / c o n f i g / r o u t i n g _ d e v . y m l
Unidad 6: Desarrollo de la aplicacin MentorNotas (II). Rutas y Controladores
90
_ w e l c o m e :
p a t t e r n : / w e l c o m e
d e f a u l t s : { _ c o n t r o l l e r : A c m e D e m o B u n d l e : W e l c o m e : i n d e x }
Y ya tenemos la ruta / disponible para nuestra aplicacin. Esa ruta es muy especial, es la
ruta inicial, y debe ser la de entrada en nuestra aplicacin. Por eso la queremos y se la
quitamos a la accin A c m e D e m o B u n d l e : W e l c o m e : i n d e x , que al fin y al cabo es de ejemplo y
no tiene tanta importancia como nuestra aplicacin.
Definimos las rutas y sus acciones asociadas
La siguiente decisin que hemos tomado es agrupar las acciones de nuestro frontend en los
siguientes controladores:
N o t a s C o n t r o l l e r , para las acciones relacionadas con la manipulacin de notas.
L o g i n C o n t r o l l e r , para las acciones relacionadas con el proceso de registro de
usuarios.
C o n t r a t o s C o n t r o l l e r , para las acciones relacionadas con los contratos premium
De nuevo, todas las acciones pueden ubicarse en un mismo controlador, pero las buenas
prcticas de programacin recomiendan separar las funcionalidades en grupos bien
definidos cuando esto sea posible.
Tras el estudio del anlisis de la aplicacin proponemos el siguiente conjunto de rutas:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l
1 j a m n _ h o m e p a g e :
2 p a t t e r n : /
3 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x }
4 r e q u i r e m e n t s :
5 _ m e t h o d : G E T
6
7 j a m n _ c o n e t i q u e t a :
8 p a t t e r n : / c o n e t i q u e t a / { e t i q u e t a }
9 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x }
1 0 r e q u i r e m e n t s :
1 1 i d : \ d +
1 2 _ m e t h o d : G E T
1 3
1 4 j a m n _ b u s c a r :
1 5 p a t t e r n : / b u s c a r
1 6 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x , e t i q u e t a : t e r m i n o }
1 7 r e q u i r e m e n t s :
1 8 _ m e t h o d : P O S T
1 9
2 0 j a m n _ n o t a :
2 1 p a t t e r n : / n o t a / { i d }
2 2 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x }
2 3 r e q u i r e m e n t s :
2 4 i d : \ d +
2 5 _ m e t h o d : G E T
2 6
2 7 j a m n _ n u e v a :
2 8 p a t t e r n : / n u e v a
2 9 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : n u e v a }
3 0
Definimos las rutas y sus acciones asociadas
91
3 1 j a m n _ e d i t a r :
3 2 p a t t e r n : / e d i t a r / { i d }
3 3 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e d i t a r }
3 4 r e q u i r e m e n t s :
3 5 i d : \ d +
3 6
3 7 j a m n _ b o r r a r :
3 8 p a t t e r n : / b o r r a r / { i d }
3 9 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : b o r r a r }
4 0 r e q u i r e m e n t s :
4 1 i d : \ d +
4 2
4 3 j a m n _ e s p a c i o _ p r e m i u m :
4 4 p a t t e r n : / m i e s p a c i o
4 5 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e s p a c i o P r e m i u m }
4 6
4 7 j a m n _ r s s :
4 8 p a t t e r n : / r s s
4 9 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : N o t a s : r s s }
5 0 r e q u i r e m e n t s :
5 1 _ m e t h o d : G E T
5 2 _ f o r m a t : x m l
5 3
5 4 j a m n _ r e g i s t r o :
5 5 p a t t e r n : / r e g i s t r o
5 6 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : L o g i n : r e g i s t r o }
5 7
5 8 j a m n _ a c t i v a r _ c u e n t a :
5 9 p a t t e r n : / a c t i v a r / { t o k e n }
6 0 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : L o g i n : a c t i v a r }
6 1
6 2 j a m n _ t a r i f a s :
6 3 p a t t e r n : / t a r i f a s
6 4 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : C o n t r a t o s : t a r i f a s P r e m i u m }
6 5 r e q u i r e m e n t s :
6 6 _ m e t h o d : G E T
6 7
6 8 j a m n _ c o n t r a t a r :
6 9 p a t t e r n : / c o n t r a t a r
7 0 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : C o n t r a t o s : c o n t r a t a r P r e m i u m }
Ya hemos hablado de las rutas, sabemos lo que son, para que sirven y como se especifican.
Pero estas rutas utilizan nuevas carctersticas del sistema de routing de Symfony2. Vamos
a verlas.
En muchas de las rutas se utiliza la directiva r e q u i r e m e n t s . Como su nombre indica,
establece un requisito que debe cumplir la ruta para que sea vlida. Uno de los usos ms
frecuentes de los requirements es limitar los valoresde los placeholders mediante
expresiones regulares
14
. Por ejemplo, en la ruta j a m n _ n o t a (lneas 20-25) el requisito
i d : \ d + , esta limitando ese placeholder a valores enteros positivos. De manera que la
siguiente URL:
h t t p : / / t u . s e r v i d o r / n o t a / m i _ n o t a
no casa con la ruta j a m n _ n o t a , pues m i _ n o t a no es un entero positivo, mientras que la
siguiente si lo hace:
Definimos las rutas y sus acciones asociadas
92
h t t p : / / t u . s e r v i d o r / n o t a / 2
El uso de requisitos proporciona una definicin ms precisa de la ruta. Otro requisito muy til
es _ m e t h o d que especifica el mtodo HTTP que debe utilizar la peticin (request) para casar
con la ruta. En nuestro caso, las rutas j a m n _ h o m e p a g e , j a m n _ c o n e t i q u e t a , j a m n _ n o t a ,
j a m n _ t a r i f a s y j a m n _ r s s casan con las URL's especificadas en ellas nicamente si la
peticin se ha realizado a travs del mtodo GET de HTTP.
Por ltimo el requisito _ f o r m a t indica al framework el formato de la peticin del objeto
Request. Esto, como veremos ms adelante nos servir para seleccionar el tipo de
documento que debemos construir en la respuesta.
Ahora que ya sabemos algo ms acerca del sistema de Routing de Symfony2, vamos a
describir la funcionalidad que debe implementar las acciones asociadas para satisfacer las
condiciones del anlisis.
Diseo de la lgica de control para las acciones del controlador N o t a s C o n t r o l l e r
Los requisito 3-8 de la aplicacin definen gran parte de la lgica de control de la aplicacin y
tambin especifica como deben mostrarse los datos. Las rutas y acciones diseadas
proponen una solucin posible a dichos requisitos.
Para mantener entre peticiones HTTP el filtro aplicado para obtener el listado de notas, y la
identificacin de la nota seleccionada cuyos detalles se muestran, utilizaremos el
mecanismo de sesin
15
.
Las rutas j a m n _ h o m e p a g e , j a m n _ c o n e t i q u e t a y j a m n _ b u s c a r se mapean sobre la misma
accin J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x , la diferencia entre ellas radica en los
parmetros que se pasan en la peticin.
Si la accin i n d e x A c t i o n ( ) es invocada por la ruta j a m n _ h o m e p a g e , buscar todas las notas
que coincidan con el filtro de bsqueda almacenado en la sesin y las mostrar en el listado
de notas. Si ese filtro an no existe (por ejemplo en la primera peticin) se mostrarn todas
las notas.
La ruta j a m n _ c o n e t i q u e t a pasa por la URL el valor de la variable e t i q u e t a , y ser
interpretado por la accin J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x como que debe buscar y
mostrar en el listado de notas slo aquellas asociadas a la etiqueta con i d especificado por
tal variable. Si el valor de esta variable es t o d a s mostrar todas las etiquetas. Esta accin
actualizar la sesin con el valor de la etiqueta que ha recibido.
La ruta j a m n _ b u s c a r pasa por POST la variable t e r m i n o . Esto ser interpretado por
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x como que debe buscar y mostrar todas las notas
que contengan en su ttulo o contenido la cadena definida por dicha variable. Esta accin
actualizar la sesin con el valor del trmino de bsqueda que ha recibido.
En todos los casos anteriores se mostrar el detalle de la nota seleccionada que se
especifique en la sesin. Si en la sesin no se especifica ninguna, mostrar la primera del
listado de notas.
La ruta j a m n _ n o t a tambin se mapea contra la accin
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x , la cual buscar la nota con el i d especificado por
la variable i d pasado por GET a travs de la URL y la mostrar en el lugar reservado para el
detalle de la nota seleccionada. El listado de notas se calcular de acuerdo a lo especificado
en la sesin. Esta accin actualizar el valor de la sesin correspondiente a la nota
seleccionada.
La ruta j a m n _ n u e v a se mapea contra la accin J A M N o t a s F r o n t e n d B u n d l e : N o t a s : n u e v a ,
mostrar un formulario para crear nuevas notas, y se encargar tambin de recibir los datos
del formulario para crear la nota.
Diseo de la lgica de control para las acciones del controlador NotasController
93
La ruta j a m n _ e d i t a r se mapea contra la accin J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e d i t a r ,
mostrar un formulario para editar los datos de la nota cuyo i d coincide con el valor de la
variable i d pasado por GET, y se encargar de grabar los datos recibidos para actualizar la
nota.
La ruta j a m n _ b o r r a r se mapea contra la accin J A M N o t a s F r o n t e n d B u n d l e : N o t a s : b o r r a r , y
servir para eliminar la nota especificada por el valor de la variable i d pasado por GET. Esta
accin borrar el valor de la sesin correspondiente a la nota seleccionada, ya que en el
momento de borrar la nota, era precisamente esa la nota seleccionada, y una vez borrada ya
no puede seguir sindolo.
Todas las acciones asociadas a estas rutas pintarn un listado con todas las etiquetas del
usuario y un listado de notas segn lo especificado en los filtros de bsqueda almacenados
en la sesin.
La ruta j a m n _ e s p a c i o _ p r e m i u m se mapea contra la accin
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e s p a c i o P r e m i u m y ser el punto de acceso a las
funcionalidades premium.
la ruta j a m n _ r s s se mapea contra J A M N o t a s F r o n t e n d B u n d l e : N o t a s : r s s y ser la
encargada de suministrar el documento XML con las noticias RSS.
Diseo de la lgica de control para las acciones del controlador L o g i n C o n t r o l l e r
La ruta j a m n _ r e g i s t r o se mapea contra la accin
J A M N o t a s F r o n t e n d B u n d l e : L o g i n : r e g i s t r o que se encargar de recoger los datos de un
nuevo usuario, registrarlo en la aplicacin y enviarle un e-mail de bienvenida con una URL
construida exclusivamente para el usuario y mediante la cual tendr que activar su cuenta.
La ruta j a m n _ a c t i v a r _ c u e n t a se mapea contra la accin
J A M N o t a s F r o n t e n d B u n d l e : L o g i n : a c t i v a r y servir para activar la cuenta de un usuario
que ha solicitado la activacin usando su URL de activacin.
Diseo de la lgica de control para las acciones del controlador
C o n t r a t o s C o n t r o l l e r
La ruta j a m n _ t a r i f a s se mapea contra la accin
J A M N o t a s F r o n t e n d B u n d l e : C o n t r a t o s : t a r i f a s P r e m i u m y servir para mostrar al usuario las
tarifas existentes.
La ruta j a m n _ c o n t r a t a r se mapea contra la accin
J A M N o t a s F r o n t e n d B u n d l e : C o n t r a t o s : c o n t r a t a r P r e m i u m , la cual realizar un nuevo
contrato premium.
Implemetacin de la lgica de control del controlador
N o t a s C o n t r o l l e r
El siguiente paso sera implementar las acciones y sus plantillas asociadas y fin del trabajo.
Suena bien pero las cosas no son siempre tan sencillas.
Para empezar an no hemos construido nuestro modelo con el que llevaremos a cabo las
operaciones propias de la lgica de la aplicacin (manipulacin de notas, registro, contratos)
y la persistencia de datos. Estudiaremos este tema en la siguiente unidad, que versar sobre
el servicio de persistencia de datos Doctrine.
Algunas de las acciones requieren la manipulacin de formularios y validaciones, tema que
ser estudiado en la unidad 8.
Tambin es preciso incorporar un sistema para el tratamiento de la seguridad, que garantice
que slo los usuarios registrados pueden usar la aplicacin, y dentro de estos, slo los
Diseo de la lgica de control para las acciones del controlador LoginController
94
premium que han pagado pueden usar las caractersticas premium. El tema de la
autenticacin y la autorizacin lo trataremos en la unidad 9.
Cuando hayamos estudiado todos estos aspectos, estaremos en condiciones de escribir el
cdigo definitivo de las acciones. Mientras tanto construiremos la lgica de control de las
acciones del controlador N o t a s C o n t r o l l e r . Para lo cual utilizaremos un modelo de negocio
simplificadsimo, carente de funcionalidad, pero suficiente para permitirnos construir la
lgica de control:
Las etiquetas sern representadas por arrays asociativos con las claves i d y n o m b r e , y las
notas con arrays asociativos con las clasve i d y t i t u l o . Para cualquier bsqueda realizada,
ya sea por etiqueta o por trmino de bsqueda, se devolver siempre el mismo conjunto de
notas.
Este modelo super simple, aunque es totalmente inservible como modelo defintivo, ser muy
til para centrarnos en el paso de datos entre cliente y servidor y construir una primera
versin de las acciones con la lgica de control totalmente funcional. Por otro lado, para
disear la jerarqua de plantillas twig tampoco es necesario contar con el modelo definitivo.
Por eso tambin propondremos una primera versin de plantillas twig en los siguientes
apartados.
Durante el desarrollo de estas primeras versiones de las acciones del controlador
N o t a s C o n t r o l l e r aprenderemos:
cmo se debe utilizar el servicio r e q u e s t , omnipresente en todos los controladores,
cmo se pasan parmetros a las plantillas desde las acciones,
cmo se construyen URL's correctas mediante el sistema de Routing, en las plantillas
twig y en los controladores,
la relacin entre las peticiones del cliente y las respuestas del servidor,
cmo se utiliza el servicio s e s s i o n para mantener constancia del cliente que est
utilizando la aplicacin y poder mantener el estado del mismo.
Comenzamos el trabajo cambiando el nombre al controlador creado por defecto durante la
generacin automtica del bundle. En lugar de D e f a u l t C o n t r o l l e r lo llamaremos
N o t a s C o n t r o l l e r . Esto significa cambiar el nombre del fichero
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / D e f a u l t C o n t r o l l e r . p h p
por
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / N o t a s C o n t r o l l e r . p h p , y
cambiar el nombre de clase que se declara en dicho fichero por N o t a s C o n t r o l l e r .
A continuacin copia el siguiente cdigo en su interior:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / N o t a s C o n t r o l l e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6
7 c l a s s N o t a s C o n t r o l l e r e x t e n d s C o n t r o l l e r
8 {
9
1 0 p u b l i c f u n c t i o n i n d e x A c t i o n ( )
1 1 {
1 2 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ; / / e q u i v a l e n t e a $ t h i s - > g e t ( ' r e q u e s t ' ) ;
Diseo de la lgica de control para las acciones del controlador LoginController
95
1 3 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
1 4
1 5 $ r u t a = $ r e q u e s t - > g e t ( ' _ r o u t e ' ) ;
1 6
1 7 s w i t c h ( $ r u t a )
1 8 {
1 9 c a s e ' j a m n _ h o m e p a g e ' :
2 0
2 1 b r e a k ;
2 2
2 3 c a s e ' j a m n _ c o n e t i q u e t a ' :
2 4 $ s e s s i o n - > s e t ( ' b u s q u e d a . t i p o ' , ' p o r _ e t i q u e t a ' ) ;
2 5 $ s e s s i o n - > s e t ( ' b u s q u e d a . v a l o r ' , $ r e q u e s t - > g e t ( ' e t i q u e t a ' ) ) ;
2 6 $ s e s s i o n - > s e t ( ' n o t a . s e l e c c i o n a d a . i d ' , ' ' ) ;
2 7
2 8 b r e a k ;
2 9
3 0 c a s e ' j a m n _ b u s c a r ' :
3 1 $ s e s s i o n - > s e t ( ' b u s q u e d a . t i p o ' , ' p o r _ t e r m i n o ' ) ;
3 2 $ s e s s i o n - > s e t ( ' b u s q u e d a . v a l o r ' , $ r e q u e s t - > g e t ( ' t e r m i n o ' ) ) ;
3 3 $ s e s s i o n - > s e t ( ' n o t a . s e l e c c i o n a d a . i d ' , ' ' ) ;
3 4
3 5 b r e a k ;
3 6 c a s e ' j a m n _ n o t a ' :
3 7 $ s e s s i o n - > s e t ( ' n o t a . s e l e c c i o n a d a . i d ' , $ r e q u e s t - > g e t ( ' i d ' ) ) ;
3 8 b r e a k ;
3 9 }
4 0
4 1 l i s t ( $ e t i q u e t a s , $ n o t a s , $ n o t a _ s e l e c c i o n a d a ) = $ t h i s - > d a m e E t i q u e t a s Y N o t a s ( ) ;
4 2
4 3 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x . h t m l . t w i g ' , a r r a y (
4 4 ' e t i q u e t a s ' = > $ e t i q u e t a s ,
4 5 ' n o t a s ' = > $ n o t a s ,
4 6 ' n o t a _ s e l e c c i o n a d a ' = > $ n o t a _ s e l e c c i o n a d a ,
4 7 ) ) ;
4 8 }
4 9
5 0 p u b l i c f u n c t i o n n u e v a A c t i o n ( )
5 1 {
5 2 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
5 3 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
5 4
5 5 i f ( $ r e q u e s t - > g e t M e t h o d ( ) = = ' P O S T ' ) {
5 6
5 7 / / s i l o s d a t o s q u e v i e n e n e n l a r e q u e s t s o n b u e n o s g u a r d a l a n o t a
5 8
5 9 $ s e s s i o n - > s e t F l a s h ( ' m e n s a j e ' , ' S e d e b e r a g u a r d a r l a n o t a : '
6 0 . $ r e q u e s t - > g e t ( ' n o m b r e ' ) . ' . C o m o a u n n o d i s p o n e m o s d e u n
6 1 s e r v i c i o p a r a p e r s i s t i r l o s d a t o s , m o s t r a m o s l a n o t a 1 ' ) ;
6 2
6 3 r e t u r n $ t h i s - > r e d i r e c t ( $ t h i s - > g e n e r a t e U r l ( ' j a m n _ n o t a ' , a r r a y ( ' i d ' = > 1 ) ) ) ;
6 4 }
6 5
6 6 l i s t ( $ e t i q u e t a s , $ n o t a s , $ n o t a _ s e l e c c i o n a d a ) = $ t h i s - > d a m e E t i q u e t a s Y N o t a s ( ) ;
6 7
6 8 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : n u e v a . h t m l . t w i g ' , a r r a y (
6 9 ' e t i q u e t a s ' = > $ e t i q u e t a s ,
7 0 ' n o t a s ' = > $ n o t a s ,
7 1 ' n o t a _ s e l e c c i o n a d a ' = > $ n o t a _ s e l e c c i o n a d a ,
7 2 ) ) ;
7 3 }
Diseo de la lgica de control para las acciones del controlador LoginController
96
7 4
7 5 p u b l i c f u n c t i o n e d i t a r A c t i o n ( )
7 6 {
7 7 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
7 8 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
7 9
8 0 / / S e r e c u p e r a l a n o t a q u e v i e n e e n l a r e q u e s t p a r a s e r e d i t a d a
8 1
8 2 $ n o t a = a r r a y (
8 3 ' i d ' = > $ r e q u e s t - > g e t ( ' i d ' ) ,
8 4 ' t i t u l o ' = > ' n o t a ' ,
8 5 ) ;
8 6
8 7
8 8 i f ( $ r e q u e s t - > g e t M e t h o d ( ) = = ' P O S T ' ) {
8 9
9 0 / / s i l o s d a t o s q u e v i e n e n e n l a r e q u e s t s o n b u e n o s g u a r d a l a n o t a
9 1
9 2 $ s e s s i o n - > s e t F l a s h ( ' m e n s a j e ' , ' S e d e b e r a e d i t a r l a n o t a : '
9 3 . $ r e q u e s t - > g e t ( ' t i t u l o ' ) .
9 4 ' . C o m o a n n o d i s p o n e m o s d e u n s e r v i c i o p a r a p e r s i s t i r l o s
9 5 d a t o s , l a n o t a p e r m a n e c e i g u a l ' ) ;
9 6
9 7 r e t u r n $ t h i s - > r e d i r e c t ( $ t h i s - > g e n e r a t e U r l ( ' j a m n _ n o t a ' , a r r a y ( ' i d ' = > $ r e q u e s t - > g e t ( ' i d ' ) ) ) ) ;
9 8 }
9 9
1 0 0 l i s t ( $ e t i q u e t a s , $ n o t a s , $ n o t a _ s e l e c c i o n a d a ) = $ t h i s - > d a m e E t i q u e t a s Y N o t a s ( ) ;
1 0 1
1 0 2 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e d i t a r . h t m l . t w i g ' , a r r a y (
1 0 3 ' e t i q u e t a s ' = > $ e t i q u e t a s ,
1 0 4 ' n o t a s ' = > $ n o t a s ,
1 0 5 ' n o t a _ a _ e d i t a r ' = > $ n o t a ,
1 0 6 ) ) ;
1 0 7 }
1 0 8
1 0 9 p u b l i c f u n c t i o n b o r r a r A c t i o n ( )
1 1 0 {
1 1 1 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
1 1 2 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
1 1 3
1 1 4 / / b o r r a d o d e l a n o t a $ r e q u e s t - > g e t ( ' i d ' ) ;
1 1 5
1 1 6 $ s e s s i o n - > s e t F l a s h ( ' m e n s a j e ' , ' S e d e b e r a b o r r a r l a n o t a ' . $ r e q u e s t - > g e t ( ' i d ' ) ) ;
1 1 7 $ s e s s i o n - > s e t ( ' n o t a . s e l e c c i o n a d a . i d ' , ' ' ) ;
1 1 8
1 1 9 r e t u r n $ t h i s - > f o r w a r d ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x ' ) ;
1 2 0 }
1 2 1
1 2 2 p u b l i c f u n c t i o n m i E s p a c i o A c t i o n ( )
1 2 3 {
1 2 4 $ p a r a m s = ' L o s d a t o s d e l a p g i n a d e i n i c i o d e l e s p a c i o p r e m i u m ' ;
1 2 5 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x ' , a r r a y ( ' p a r a m s ' = > $ p a r a m s ) ) ;
1 2 6 }
1 2 7
1 2 8 p u b l i c f u n c t i o n r s s A c t i o n ( )
1 2 9 {
1 3 0
1 3 1 }
1 3 2
1 3 3 / * *
1 3 4 * F u n c i n M o c k p a r a p o d e r d e s a r r o l l a r y p r o b a r l a l g i c a d e c o n t r o l .
1 3 5 *
1 3 6 * L a f u n c i n r e a l q u e f i n a l m e n t e s e i m p l e m e n t e , u t i l i z a r e l f i l t r o a l m a c e n a d o
1 3 7 * e n l a s e s i n y e l m o d e l o p a r a c a l c u l a r l a e t i q u e t a s , n o t a s y n o t a s e l e c c i o n a d a
1 3 8 * q u e e n c a d a m o m e n t o s e d e b a n p i n t a r .
1 3 9 * /
1 4 0 p r o t e c t e d f u n c t i o n d a m e E t i q u e t a s Y N o t a s ( )
1 4 1 {
Diseo de la lgica de control para las acciones del controlador LoginController
97
1 4 2 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
1 4 3
1 4 4 $ e t i q u e t a s = a r r a y (
1 4 5 a r r a y (
1 4 6 ' i d ' = > 1 ,
1 4 7 ' t e x t o ' = > ' e t i q u e t a 1 ' ,
1 4 8 ) ,
1 4 9 a r r a y (
1 5 0 ' i d ' = > 2 ,
1 5 1 ' t e x t o ' = > ' e t i q u e t a 2 ' ,
1 5 2 ) ,
1 5 3 a r r a y (
1 5 4 ' i d ' = > 3 ,
1 5 5 ' t e x t o ' = > ' e t i q u e t a 3 ' ,
1 5 6 ) ,
1 5 7 ) ;
1 5 8
1 5 9 $ n o t a s = a r r a y (
1 6 0 a r r a y (
1 6 1 ' i d ' = > 1 ,
1 6 2 ' t i t u l o ' = > ' n o t a 1 ' ,
1 6 3 ) ,
1 6 4 a r r a y (
1 6 5 ' i d ' = > 2 ,
1 6 6 ' t i t u l o ' = > ' n o t a 2 ' ,
1 6 7 ) ,
1 6 8 a r r a y (
1 6 9 ' i d ' = > 3 ,
1 7 0 ' t i t u l o ' = > ' n o t a 3 ' ,
1 7 1 ) ,
1 7 2 ) ;
1 7 3
1 7 4 $ n o t a _ s e l e c i o n a d a _ i d = $ s e s s i o n - > g e t ( ' n o t a . s e l e c c i o n a d a . i d ' ) ;
1 7 5 i f ( ! $ n o t a _ s e l e c i o n a d a _ i d )
1 7 6 {
1 7 7 $ n o t a _ s e l e c i o n a d a _ i d = 1 ;
1 7 8 }
1 7 9
1 8 0 $ n o t a _ s e l e c c i o n a d a = a r r a y (
1 8 1 ' i d ' = > $ n o t a _ s e l e c i o n a d a _ i d ,
1 8 2 ' t i t u l o ' = > ' n o t a ' . $ n o t a _ s e l e c i o n a d a _ i d ,
1 8 3 ) ;
1 8 4 r e t u r n a r r a y ( $ e t i q u e t a s , $ n o t a s , $ n o t a _ s e l e c c i o n a d a ) ;
1 8 5 }
1 8 6 }
Fjate como al principio de todas las acciones se le pide al contenedor de servicios de
Symfony2 una instancia de los servicios Request ($ t h i s - > g e t R e q u e s t ( ) ) y Session
($ t h i s - > g e t ( ' s e s s i o n ' ) ). El primero de ellos, Request, es un objeto que encapsula la
informacin obtenida de la peticin HTTP (request) y proporciona mtodos para manipularla.
El segundo, Session, es un objeto que encapsula y extiende con ms funcionalidades el
mecanismo de sesin nativo de PHP.
Diseo de la lgica de control para las acciones del controlador LoginController
98
Nota
Un problemita que tiene la tcnica de instanciar objetos a travs del contenedor de
servicios es que la asistencia automtica de cdigo de los IDE's (como Netbeans o
Eclipse), deja de funcionar, pues la herramienta no puede saber que tipo de objeto
devuelve el contenedor de servicios. Sin embargo la clase C o n t r o l l e r de Symfony2
implementa wrappers para los objetos ms usados que son funciones que devuelven
un determinado servicio o funcin del servicio llamando ellas mismas al contenedor de
servicio. Estos wrappers indican mediante comentarios de DocBlock
16
el tipo de
objeto devuelto y/o los argumentos que requiere la funcin. Esta informacin es usada
por los IDE's para la asistencia automtica.
Por ejemplo, estas dos lneas de cdigo devuelven el mismo objeto R e q u e s t :
$ r e q u e s t = $ t h i s - > g e t ( ' r e q u e s t ' ) ;
$ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
Pero si utilizas la segunda (que es el wrapper), la asistencia de cdigo automtica de
tu IDE funcionar sobre el objeto $ r e q u e s t .
Estos dos servicios son omnipresentes en las aplicaciones Symfony2. En la Request se
encuentran los datos enviados por el cliente web y todo lo relacionado con la peticin: la IP
del cliente, el navegador utilizado, los tipos de contenidos aceptados por el navegador, y
ms cosas. Lo mejor para saber todas estas cosas es revisar la documentacin sobre el
servicio Request en la API de Symfony2 (http://api.symfony.com).
De todas las cosas que se pueden hacer con l, lo ms habitual es pedir los datos enviados
por el usuario mediante su mtodo g e t ( ) . Es lo que se hace, por ejemplo en las lneas 32 y
37 del cdigo anterior. Otro uso interesante es obtener el nombre de la ruta correspondiente
a la URL de la peticin pasando _ r o u t e como parmetro a dicho mtodo. Esto se hace en la
lnea 15.
En la Session encontramos datos que la aplicacin va colocando en el lado del servidor y que
estn asociados a una c o o k i e , es decir, a un cliente. De esa manera la aplicacin puede
mantener estados y relacionar peticiones del mismo cliente a pesar de que HTTP es un
protocolo sin estado. De nuevo recomendamos echar un vistazo a la API de Symfony2 para
ver todas las cosas que se pueden hacer con este servicio. Lo ms utilizado son los
siguientes mtodos:
s e t ( s t r i n g $ n a m e , m i x e d $ v a l u e ) , para crear un parmetro en la sesin.
g e t ( s t r i n g $ n a m e , m i x e d $ d e f a u l t = n u l l ) para recuperar un parmetro de la
sesin. Si se proporciona el segundo argumento se utilizar por defecto en caso de que
no exista en la sesin tal parmetro.
s e t F l a s h ( s t r i n g $ n a m e , s t r i n g $ v a l u e ) , define un parmetro flash en la sesin.
Estos parmetros tienen un tiempo de vida de una peticin. A la segunda peticin
contada desde el momento en que se creo el parmetro, se eliminan de la sesin. Son
muy tiles para almacenar mensajes de error y otras notificaciones sin temor a llenar la
sesin de parmetros que son utilizados una sola vez.
g e t F l a s h ( s t r i n g $ n a m e , s t r i n g | n u l l $ d e f a u l t = n u l l ) , para recuperar un
parmetro flash de la sesin. Si se proporciona el segundo argumento se utilizar por
defecto en caso de que no exista en la sesin tal parmetro.
Diseo de la lgica de control para las acciones del controlador LoginController
99
Ya nos encontramos en disposicin de explicar el cdigo propuesto, en una primera versin,
para el controlador N o t a s C o n t r o l l e r .
La accin i n d e x A c t i o n ( )
Tal y como lo hemos planteado, la accin i n d e x A c t i o n ( ) es la que ms lgica de control
implementa. Lo primero que hace es comprobar desde qu ruta ha sido invocada (lnea 17).
Si la peticin viene de la ruta j a m n _ h o m e p a g e no hay que hacer nada especial (lneas 19-21).
Si viene de j a m _ c o n e t i q u e t a hay que definir el filtro de bsqueda en la sesin con el valor
del i d de la etiqueta pasada en la peticin (lneas 23-28). Si la ruta de origen es
j a m n _ b u s c a r , hay que definir el filtro de bsqueda en la sesin con el trmino de bsqueda
pasado en la peticin (lneas 30-33). Finalmente si es j a m n _ n o t a , hay que especificar en la
sesin la nota seleccionada para mostrar su detalle (lneas 36-38).
Nota
Una aclaracin sobre el diseo del filtro de bsqueda. Ya que la bsqueda puede
solicitarse de dos formas distintas: por etiqueta o por trmino de bsqueda, hemos
decidido usar el parmetro de sesin b u s q u e d a . t i p o para almacenar este dato.
Despus, el valor del i d de la etiqueta o del trmino de bsqueda se almacenar en
el parmetro de sesin b u s q u e d a . v a l o r . Conseguimos de esta manera un formato
homogneo y unvoco de expresar el filtro de bsqueda.
Una vez definido en la sesin el filtro de bsqueda, se obtienen el listado de etiquetas, el de
notas y la nota seleccionada que corresponda al filtro de bsqueda existente en ese
momento (lnea 41). Lo hacemos mediante la funcin d a m e E t i q u e t a s Y N o t a s . Fjate en su
implementacin. En ella se define y utiliza el modelo sencillo pero vaco de funcionalidad que
hemos propuesto ms arriba. Fjate que siempre devolver los mismos listados de etiquetas
y notas. En esto consiste el concepto de mockup; algo que presenta la interfaz pero no
implementa funcionalidad, o lo hace de manera muy simplificada.
Finalmente (lneas 43-47) se pasan los datos recuperados a la plantilla
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x . h t m l . t w i g que se encargar de construir un
documento HTML con toda esta informacin y enviarlo como respuesta al cliente. Despus
explicaremos como hemos diseado las plantillas de la aplicacin en esta primera versin.
La accin n u e v a A c t i o n
Esta accin es responsable de dos funcionalidades distintas: enviar al cliente un formulario
vaco para recoger los datos de la nueva nota, y procesar los datos que el cliente coloca en
dicho formulario. La accin sabe lo que tiene que hacer mirando el tipo de peticin que le
llega mediante la funcin g e t M e t h o d ( ) del objeto Request (lnea 55). Si el el mtodo HTTP
utilizado por el cliente en la peticin es GET, slo tiene que devolver el formulario. Si es
POST significa que se estn enviando los datos de la nueva nota y hay que guardarlos, hecho
lo cual realiza una redireccin a la ruta j a m n _ n o t a con el i d de la nota recien creada, para
que se muestre en el detalle.
Como an no disponemos de un servicio de persistencia ni de un modelo funcional, la
accin, en realidad, no guarda nada, pero construye un mensaje que almacena en un
parmetro flash de la sesin (lneas 59-61) que informa de este hecho.
La lnea 63 es muy interesante pues en ella se hace uso de dos carcteristicas muy tiles de
Symfony2; la redireccin y la generacin de una ruta desde un controlador. Una redireccin
consiste en enviar al cliente una respuesta con cdigo HTTP 300 y una URL de redireccin.
La accin indexAction()
100
Este cdigo indica al navegador que debe realizar una nueva peticin a la URL indicada. En
Symfony2 esto se realiza devolviendo una respuesta con la funcin r e d i r e c t ( ) del objeto
C o n t r o l l e r :
r e t u r n $ t h i s - > r e d i r e c ( ' h t t p : / / w w w . g o o g l e . e s ' ) ;
El cdigo anterior realizara una redireccin a la pgina principal de google.
Si queremos crear desde un controlador la URL de alguna de las rutas de nuestro proyecto
debemos utilizar la funcin g e n e r a t e U R L ( ) de la clase C o n t r o l l e r . Esta funcin es, en
realidad, un wrapper al mtodo g e n e r a t e ( ) del servicio Router de Symfony2.
Nota
Como ya te habrs imaginado, la clase C o n t r o l l e r implementa wrappers a los
servicios y funciones de servicios ms utilizados.
La lnea 63 es una combinacin de estas dos funciones; r e d i r e c t ( ) y g e n e r a t e U r l ( ) , y el
resultado es indicar al navegador cliente que realice una redireccin a la URL
correspondiente a la ruta j a m n _ n o t a . Esta redireccin no se realiza caprichosamente.
Gracias a ella se evita que, si una vez creada la nota el usuario recarga accidentalmente la
pgina en el navegador, se envin por segunda vez los datos del formulario, y se vuelva a
crear la misma nota de nuevo.
Esta accin se pintar con la plantilla J A M N o t a s F r o n t e n d B u n d l e : N o t a s : n u e v a . h t m l . t w i g .
Dicha plantilla, muestra los mismos elementos que la plantilla
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x . h t m l . t w i g , con la diferencia de que en el lugar del
detalle de la nota seleccionada se dibuja el formulario para la creacin de la nota. Por eso la
accin n u e v a A c t i o n ( ) necesita pasarle a su plantilla el listado de etiquetas y notas
correspondientes al filtro de bsqueda almacenado en la sesin. Ese es la razn de ser de la
lnea 66; idntica a la lnea 41.
La accin e d i t a r A c t i o n
Esta accin es prcticamente igual que la anterior. Tiene dos funciones: dibujar un
formulario para editar la nota que se haya seleccionado, y grabar los datos enviados por el
cliente para actualizar la nota. Para ello usa la misma estratgica que la accin
n u e v a A c t i o n ( ) : discriminar por el mtodo HTTP usado en la peticin mediante la funcin
g e t M e t h o d ( ) del objeto r e q u e s t .
Las diferencias son:
1. Debe recuperar la nota que viene en la peticin. Esto se hace en las lneas 82-85. Como
an no disponemos de un modelo funcional ni de una base de datos de donde recuperar
las entidades, hemos hecho una simulacin en la que se recoge el i d del objeto
r e q u e s t y se crea una nota segn el modelo desprovisto de funcionalidad (mock) que
venimos usando durante todo el rato. Al fin y al cabo nos estamos centrando en el paso
de variables entre cliente y servidor y para eso no necesitamos ms,
2. El tercer parmetro que se le pasa a su plantilla
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e d i t a r . h t m l . t w i g , es la nota a editar en lugar de la
nota seleccionada.
De nuevo la redireccin despus de actualizar la nota es importante para evitar reenvos
accidentales de datos por recarga de la pgina desde el navegador.
La accin editarAction
101
La plantilla asociada a esta accin tambin pintar el listado de etiquetas y notas
correspondientes al filtro que haya almacenado en la sesin. Dichos listados se recuperan en
la lnea 100 (igual a las lneas 41 y 66). El formulario de edicin se pintar en el lugar del
detalle de la nota.
La accin b o r r a r A c t i o n ( )
Esta accin recoge del objeto r e q u e s t el i d de la nota que se desea borrar y realiza la
operacin de borrado. Como an no disponemos de un modelo funcional, por lo pronto no
hace nada ms que indicar lo que se debe hacer en un parmetro flash de la sesin que se
mostrar en la respuesta. Una vez borrada la nota, se borra tambin el parmetro de sesin
n o t a . s e l e c c i o n a d a . i d , para que no se produzca un error cuando se vaya a pintar el detalle
de la nota (lnea 117). Finalmente se realiza una redireccin a la pgina principal. De nuevo
esta redireccin es importante para evitar reenvos accidentales de datos por recarga de la
pgina desde el navegador.
Las acciones m i E s p a c i o A c t i o n ( ) y r s s A c t i o n
La lgica de control de estas acciones no ofrece ningn problema. As que, por lo pronto, las
dejamos sin implementar.
Implemetacin de las plantillas del controlador N o t a s C o n t r o l l e r
Todas las acciones que acabamos de implemetar en una primera versin calculan el listado
de etiquetas y notas correspondiente al filtro de bsqueda que haya almacenado en la
sesin, ya que las pginas que generan deben mostrar dichos listados. La diferencia entre
ellas es que donde unas pintan un detalle de la nota seleccionada (i n d e x A c t i o n ( ) y
b o r r a r A c t i o n ( ) ), otras dibujan un formulario para recoger datos (n u e v a A c t i o n ( ) y
e d i t a r A c t i o n ( ) ).
Este hecho, junto con la herencia en tres niveles recomendada para organizar las plantillas y
el principio DRY (Don't Repeat Yourself), nos sugiere el siguiente diseo para las plantillas.
Una plantilla general para la aplicacin con la estructura HTML bsica:
a p p / R e s o u r c e s / v i e w s / b a s e . h t m l . t w i g .
Una plantilla que herede la anterior y que muestre el listado de etiquetas y notas y el formulario de bsqueda de
notas:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g .
Las plantillas especificadas en las acciones del controlador C o n t r o l l e r N o t a s que
heredarn de la anterior los elementos comunes. As no repetimos cdigo entre las
plantillas.
El resultado de aplicar estos principios es el siguiente cdigo para las plantillas:
a p p / R e s o u r c e s / v i e w s / b a s e . h t m l . t w i g
1 < ! D O C T Y P E h t m l >
2 < h t m l >
3 < h e a d >
4 < m e t a h t t p - e q u i v = " C o n t e n t - T y p e " c o n t e n t = " t e x t / h t m l ; c h a r s e t = u t f - 8 " / >
5 < t i t l e > { % b l o c k t i t l e % } W e l c o m e ! { % e n d b l o c k % } < / t i t l e >
6 { % b l o c k s t y l e s h e e t s % } { % e n d b l o c k % }
7 < l i n k r e l = " s h o r t c u t i c o n " h r e f = " { { a s s e t ( ' f a v i c o n . i c o ' ) } } " / >
8 < / h e a d >
9 < b o d y >
1 0 { % b l o c k b o d y % } { % e n d b l o c k % }
La accin borrarAction()
102
1 1 { % b l o c k j a v a s c r i p t s % } { % e n d b l o c k % }
1 2 < / b o d y >
1 3 < / h t m l >
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k s t y l e s h e e t s % }
4
5 < s t y l e >
6 . m a r c o {
7 b o r d e r - s t y l e : s o l i d ;
8 p a d d i n g : 5 p x ;
9 m a r g i n : 5 p x ;
1 0 f l o a t : l e f t ;
1 1 w i d t h : 5 0 0 p x ;
1 2 h e i g h t : 2 5 0 p x ;
1 3 }
1 4
1 5 . d e t a l l e _ n o t a {
1 6 b a c k g r o u n d - c o l o r : # D F D F D F ;
1 7 }
1 8
1 9 . m e n s a j e {
2 0 w i d t h : 1 0 2 5 p x ;
2 1 h e i g h t : 2 0 p x ;
2 2 b a c k g r o u n d - c o l o r : # f 6 6 ;
2 3
2 4 }
2 5
2 6 < / s t y l e >
2 7
2 8 { % e n d b l o c k % }
2 9
3 0 { % b l o c k b o d y % }
3 1
3 2 < h 1 > I m p l e m e n t a c i n d e l a l g i c a d e c o n t r o l d e l a a p l i c a c i n < / h 1 >
3 3
3 4 { % i f a p p . s e s s i o n . g e t F l a s h ( ' m e n s a j e ' ) % }
3 5 < d i v i d = " e t i q u e t a s " c l a s s = " m a r c o m e n s a j e " >
3 6 < b > { { a p p . s e s s i o n . g e t F l a s h ( ' m e n s a j e ' ) } } < / b >
3 7 < / d i v >
3 8 { % e n d i f % }
3 9
4 0 < d i v i d = " e t i q u e t a s " c l a s s = " m a r c o " >
4 1 < h 2 > L i s t a d o d e e t i q u e t a s < / h 2 >
4 2 < p > S i e m p r e s e m u e s t r a n t o d a s l a s e t i q u e t a s < / b > < / p >
4 3 < u l >
4 4 { % f o r e t i q u e t a i n e t i q u e t a s % }
4 5 < l i >
4 6 < a h r e f = " { { p a t h ( ' j a m n _ c o n e t i q u e t a ' , { e t i q u e t a : e t i q u e t a . i d } ) } } " >
4 7 { { e t i q u e t a . t e x t o } }
4 8 < / a >
4 9 < / l i >
5 0 { % e n d f o r % }
5 1 < / u l >
La accin borrarAction()
103
5 2 < / d i v >
5 3
5 4 < d i v i d = " n o t a s " c l a s s = " m a r c o " >
5 5 < h 2 > L i s t a d o d e n o t a s < / h 2 >
5 6 < p > S e m o s t r a r a n l a s n o t a s q u e c o i n c i d a n c o n e l s i g u i e n t e c r i t e r i o d e b s q u e d a : < / p >
5 7 t i p o d e b s q u e d a : < b > { { a p p . s e s s i o n . g e t ( ' b u s q u e d a . t i p o ' ) } } < / b > ,
5 8 v a l o r d e l a b s q u e d a : < b > { { a p p . s e s s i o n . g e t ( ' b u s q u e d a . v a l o r ' ) } } < / b >
5 9
6 0 < u l >
6 1 { % f o r n o t a i n n o t a s % }
6 2 < l i >
6 3 < a h r e f = " { { p a t h ( ' j a m n _ n o t a ' , { i d : n o t a . i d } ) } } " > { { n o t a . t i t u l o } } < / a >
6 4 < / l i >
6 5 { % e n d f o r % }
6 6 < / u l >
6 7
6 8 < / d i v >
6 9
7 0 < d i v i d = " b u s c a r " c l a s s = " m a r c o " >
7 1 < h 2 > F o r m u l a r i o d e b s q u e d a d e n o t a s < / h 2 >
7 2 < f o r m a c t i o n = " { { p a t h ( ' j a m n _ b u s c a r ' ) } } " m e t h o d = " P O S T " >
7 3
7 4 < i n p u t t y p e = " t e x t " i d = " t e r m i n o " n a m e = " t e r m i n o " / >
7 5 < i n p u t t y p e = " s u b m i t " v a l u e = " b u s c a r " / >
7 6
7 7 < / f o r m >
7 8 < / d i v >
7 9
8 0 { % e n d b l o c k % }
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / i n d e x . h t m l . t w i g .
1 { % e x t e n d s ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 { { p a r e n t ( ) } }
6
7 < d i v i d = " d e t a l l e _ n o t a " c l a s s = " m a r c o d e t a l l e _ n o t a " >
8 < h 2 > D e t a l l e d e n o t a < / h 2 >
9 { % i f n o t a _ s e l e c c i o n a d a % }
1 0 < a h r e f = " { { p a t h ( ' j a m n _ n u e v a ' ) } } " > N u e v a n o t a < / a >
1 1 < a h r e f = " { { p a t h ( ' j a m n _ e d i t a r ' , { ' i d ' : n o t a _ s e l e c c i o n a d a . i d } ) } } " > E d i t a r n o t a < / a >
1 2 < a h r e f = " { { p a t h ( ' j a m n _ b o r r a r ' , { ' i d ' : n o t a _ s e l e c c i o n a d a . i d } ) } } " > B o r r a r n o t a < / a >
1 3
1 4 < t a b l e b o r d e r = " 1 " >
1 5 < t r >
1 6 < t d > I D < / t d >
1 7 < t d > { { n o t a _ s e l e c c i o n a d a . i d } } < / t d >
1 8 < / t r >
1 9 < t r >
2 0 < t d > n o m b r e < / t d >
2 1 < t d > { { n o t a _ s e l e c c i o n a d a . t i t u l o } } < / t d >
2 2 < / t r >
2 3 < / t a b l e >
2 4 < / d i v >
2 5 { % e n d i f % }
2 6
2 7 { % e n d b l o c k % }
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / n u e v a . h t m l . t w i g .
La accin borrarAction()
104
1 { % e x t e n d s ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 { { p a r e n t ( ) } }
6
7 < d i v i d = " d e t a l l e _ n o t a " c l a s s = " m a r c o d e t a l l e _ n o t a " >
8 < h 2 > D e t a l l e d e n o t a < / h 2 >
9 < h 2 > F o r m u l a r i o c r e a r N u e v a N o t a < / h 2 >
1 0 < f o r m a c t i o n = " { { p a t h ( ' j a m n _ n u e v a ' ) } } " m e t h o d = " P O S T " >
1 1 n o m b r e : < i n p u t t y p e = " t e x t " i d = " n o m b r e " n a m e = " n o m b r e " / >
1 2 < i n p u t t y p e = " s u b m i t " v a l u e = " g u a r d a r " / >
1 3
1 4 < / f o r m >
1 5 < / d i v >
1 6
1 7 { % e n d b l o c k % }
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / e d i t a r . h t m l . t w i g .
1 { % e x t e n d s ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 { { p a r e n t ( ) } }
6
7 < d i v i d = " d e t a l l e _ n o t a " c l a s s = " m a r c o d e t a l l e _ n o t a " >
8 < h 2 > D e t a l l e d e n o t a < / h 2 >
9 < h 2 > F o r m u l a r i o e d i t a r N o t a { { n o t a _ a _ e d i t a r . i d } } < / h 2 >
1 0 < f o r m a c t i o n = " { { p a t h ( ' j a m n _ e d i t a r ' , { i d : n o t a _ a _ e d i t a r . i d } ) } } " m e t h o d = " P O S T " >
1 1 < i n p u t t y p e = " t e x t " i d = " t i t u l o " n a m e = " t i t u l o " v a l u e = " { { n o t a _ a _ e d i t a r . t i t u l o } } " / >
1 2 < i n p u t t y p e = " s u b m i t " v a l u e = " g u a r d a r " / >
1 3 < / f o r m >
1 4 < / d i v >
1 5
1 6 { % e n d b l o c k % }
Fjate en los bloques b o d y de las tres ltimas plantillas. Para que hereden el cdigo de la
plantilla padre no limitandose a cambiarlo, se utiliza la funcin de twig p a r e n t ( ) , cuya
funcin es colocar en su lugar el cdigo del bloque padre. Es la manera de aadir ms cdigo
a los bloques heredados en lugar de sustituirlos completamente.
Ya puedes comprobar el funcionamiento de la lgica de control que hemos propuesto y decir
que has terminado la unidad 6.
La unidad en chuletas
Requerimientos en el Routing
n o m b r e _ r u t a :
p a t t e r n : / p a t r o n / d e / l a / { r u t a } . { _ f o r m a t } / { c u l t u r e }
d e f a u l t s : { _ c o n t r o l l e r : N o m b r e B u n d l e : C o n t r o l a d o r : a c c i o n , _ f o r m a t : h t m l }
r e q u i r e m e n t s :
r u t a : ^ [ a - z A - Z 0 - 9 ] + $
_ m e t h o d : G E T
_ f o r m a t : h t m l | r s s
c u l t u r e : e s | f r | e n
Si un placeholder es incluido en la seccin d e f a u l t s se convierte en opcional.
La unidad en chuletas
105
Generando rutas
En una plantilla twig:
< ! - - R u t a r e l a t i v a - - >
< a h r e f = " { { p a t h ( ' n o m b r e _ r u t a ' , { ' p a r a m ' : ' v a l ' } ) } } " >
. . .
< / a >
< ! - - R u t a a b s o l u t a - - >
< a h r e f = " { { u r l ( ' n o m b r e _ r u t a ' , { ' p a r a m ' : ' v a l ' } ) } } " >
. . .
< / a >
En una accin de un controlador:
< ? p h p
. . .
$ r o u t e r = $ t h i s - > g e t ( ' r o u t e r ' ) ;
$ u r l = $ r o u t e r - > g e n e r a t e ( ' n o m b r e _ r u t a ' , a r r a y ( ' p a r a m ' = > ' v a l ' ) , t r u e ) ;
/ / e s e q u i v a l e n t e a u s a r e l w r a p p e r :
$ u r l = $ t h i s - > g e n e r a t e U r l ( ' n o m b r e _ r u t a ' , a r r a y ( ' p a r a m ' = > ' v a l ' ) , t r u e ) ;
Servicio Request
Desde una accin de un controlador:
< ? p h p
. . .
$ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ; / / e q u i v a l e n t e a $ t h i s - > g e t ( ' r e q u e s t ' ) ;
$ r e q u e s t - > g e t ( ' p a r a m ' , ' v a l o r _ d e f e c t o ' ) ; / / O b t i e n e e l p a r m e t r o ' p a r a m ' .
/ / U s a ' v a l o r _ d e f e c t o ' s i n o e x i s t e .
$ r e q u e s t - > g e t ( ' _ r o u t e ' ) ; / / o b t i e n e e l n o m b r e d e l a r u t a q u e h a d i s p a r a d o l a a c c i n .
$ r e q u e s t - > g e t M e t h o d ( ) ; / / o b t i e n e e l m t o d o H T T P q u e s e u s e n l a p e t i c i n
/ / M i r a l a A P I , h a y m u c h o s m s
Servicio Session
Desde la accin de un controlador:
< ? p h p
. . .
$ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ; / / e q u i v a l e n t e a $ t h i s - > g e t R e q u e s t ( ) - > g e t S e s s i o n ( ) ;
$ s e s s i o n - > g e t ( ' p a r a m ' , ' v a l o r _ d e f e c t o ' ) ; / / O b t i e n e e l p a r m e t r o d e s e s i n ' p a r a m ' .
/ / U s a ' v a l o r _ d e f e c t o ' s i n o e x i s t e .
$ s e s s i o n - > s e t ( ' p a r a m ' , ' v a l ' ) ; / / D e f i n e u n p a r m e t r o d e l a s s e s i n
Generando rutas
106
$ s e s s i o n - > g e t F l a s h ( ' p a r a m ' ) ; / / O b t i e n e e l p a r m e t r o d e s e s i n f l a s h ' p a r a m ' .
$ s e s s i o n - > s e t F l a s h ( ' p a r a m ' , ' v a l ' ) ; / / D e f i n e u n p a r m e t r o f l a s h d e l a s e s i n
Ampliando el cdigo de un bloque heredado en una plantilla twig
Para ello se usa la funcin p a r e n t ( ) .
. . .
{ % b l o c k b o d y % }
{ { p a r e n t ( ) } }
c d i g o n u e v o
{ % e n d b l o c k % }
Generando redirecciones desde un controlador
Haciendo forwarding a otra accin
. code-block: php
<?php
return $this->forward('JAMNotasFrontendBundle:Notas:index');
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll para recorrer
todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Ampliando el cdigo de un bloque heredado en una plantilla twig
107
Unidad 7: Desarrollo de la aplicacin MentorNotas (III).
El modelo y la persistencia de datos.
Ya hemos comentado en un par de ocasiones que Symfony2 no es un framework MVC ya que
nicamente proporciona servicios para la parte del controlador y la vista, dejando al
programador la tarea de construir el modelo sus herramientas asociadas.
Sin embargo la distribucin standard de Symfony2, que es la que estamos utilizando en este
curso, incorpora como herramienta de terceros (vendors) un potente framework para el
tratamiento de la persistencia de datos denominado Doctrine2. No obstante optar por su uso
para la construccin del modelo, no hace que la aplicacin sea ms o menos "symfnica".
Podemos utilizar otras herramientas
17
concebidas para acceder y manipular el sistema de
informacin que utilicemos como backend de persistencia de nuestra aplicacin.
Gran parte de las funcionalidades del modelo de una aplicacin web tienen que ver con los
datos persistentes, es decir, con los datos que se deben almacenar en algn tipo de base de
datos. Por ello es muy frecuente identificar el modelo con la herramienta que se utilice para
acceder a los datos y con la estructura datos en s, aunque esto no sea estrictamente cierto.
Por ello a veces se habla de Doctrine2 como el modelo de la aplicacin cuando en realidad
es una herramienta que nos ayuda a construir y manipular nuestro modelo.
Esta unidad trata de la construccin del modelo y de la persistencia de los datos con
Doctrine2. Al final de la misma tendremos disponibles una serie de clases, denominadas
entidades, que responden al modelo de datos especificado en el anlisis de la aplicacin
(unidad 5) y un servicio con el que podemos manipular y persistir en la base de datos tales
entidades.
El Object Relational Mapping (ORM)
Doctrine2 es un Object Relational Mapping, ORM a partir de ahora. Sin entrar a proponer una
traduccin en castellano para el trmino, diremos que la finalidad de un ORM es la de
proporcionar una capa orientada a objetos encima de una base de datos relacional, de
manera que las filas o tuplas de las tablas sean tratados como objetos. La asociacin
existente entre dichos objetos se corresponde con las relaciones entre las tablas de la base
de datos. La siguiente imagen, tomada del libro oficial de Symfony2, ilustra bastante bien lo
que acabamos de decir:
Object Relational Mapping
La gran ventaja de usar un ORM es la reutilizacin de los objetos en distintas partes de la
aplicacin e incluso en distintas aplicaciones, proporcionando un modelo que encaja
perfectamente en un desarrollo orientado a objetos. La manera de recuperar, introducir y
modificar datos es independiente del sistema gestor de base de datos que utilicemos; la
sintaxis utilizada es siempre la misma y es el propio ORM quien construye la query (SQL)
Unidad 7: Desarrollo de la aplicacin MentorNotas (III). El modelo y la persistencia de datos.
108
adecuada al sistema gestor de base de datos que en cada momento utilice la aplicacin.
Esto proporciona una gran portabilidad a nuestros proyectos, ya que si deseamos hacerlos
funcionar con otro sistema de base de datos distinto bastar con indicarlo en los ficheros de
configuracin del ORM, y no tendremos que cambiar nada de nuestro cdigo.
Los elementos comunes a cualquier ORM son las entidades (es as como se suelen
denominar a los objetos del modelo) y los metadatos que definen las relaciones entre las
entidades y la base de datos. Cada ORM, en su documentacin, debe explicar como se
deben crear dichas entidades, y como se deben especificar los metadatos que mapean las
entidades con las tablas de la base de datos. Por otro lado, el ORM debe proporcionar algn
tipo de mecanismo sencillo, flexible y versatil para realizar bsquedas de objetos que
satisfacen un criterio y para realizar operaciones de persistencia, esto es; guardar cambios
realizados en los objetos, crear otros nuevos y eliminarlos. En esta unidad estudiaremos
como se realiza todo esto con Doctrine2 desarrollando el modelo de nuestra aplicacin
MentorNotas.
Antes de continuar tenemos que aclarar que Doctrine2 es un producto muy potente, y por
ello, bastante complejo. Su estudio en profundidad abarcara un curso completo dedicado en
exclusiva a Doctrine2. Para que te hagas una idea de lo que estamos hablando, echa un
vistazo a la documentacin oficial.
No tengas la menor duda de que si decides usar Doctrine2 como ORM para construir tu
modelo tendrs que visitar esta web en ms de una ocasin. Por tanto, el estudio que
hacemos en esta unidad no puede considerarse como un compendio completo de Doctrine2,
se trata ms bien de una introduccin a fondo que trata la mayor parte de los conceptos
bsicos imprescindibles para manejarse con Doctrine2 en una aplicacin construida con
Symfony2.
Las entidades
El modelo de datos especificado en el anlisis de la unidad 5 presenta las entidades que
modelan nuestra aplicacin y las relaciones de asociacin entre ellas. Cada entidad esta
compuesta por unos atributos o campos y, posiblemente, por relaciones con otras entidades.
As, por ejemplo, la entidad Nota est compuesta por los atributos t i t u l o , t e x t o y f e c h a y
adems esta relacionada con la entidad Usuario de manera que muchas Nota's pueden
pertenecer a un nico Usuario (relacin muchos a uno), y con la entidad Etiqueta de
manera que muchas Etiqueta's pueden estar asociadas a muchas Nota's (relacin muchos a
muchos). Adems, estas entidades, como objetos que son, pueden definir los mtodos
necesarios para dotarlas de funcionalidad.
Doctrine2 no exige nada especial sobre las entidades del modelo, de manera que pueden ser
implementadas como objetos planos de PHP, es decir no tienen por que extender ninguna
clase propia del ORM como se hace comunmente en la mayora de ORM's existentes. Este
hecho proporciona una gran flexibilidad al programador as como la posibilidad de reutilizar
estos objetos en otras partes de la aplicacin que no tienen que ver nada con la persistencia
o incluso en otras aplicaciones.
Lo nico que exige Doctrine2 a las entidades que van a ser persistidas en la base de datos
es que dispongan de mtodos getter's y setter's tpicos de cualquier objeto para poder
acceder a sus atributos. Es decir, si una entidad tiene un atributo llamado a t r i b u t o , debe
definir un mtodo g e t A t r i b u t o ( ) para recuperarlo, y otro s e t A t r i b u t o ( $ v a l o r ) para
definirlo. Por supuesto, la entidad puede definir todos los mtodos que se deseen, aunque no
se vayan a utilizar nunca con Doctrine2.
Para concretar un poco; la siguiente clase, a falta de implementar sus mtodos, sera una
entidad vlida para ser persistida por Doctrine2:
Las entidades
109
< ? p h p
c l a s s N o t a
{
p r i v a t e i d ;
p r i v a t e t i t u l o ;
p r i v a t e t e x t o ;
p r i v a t e f e c h a ;
p u b l i c f u n c t i o n g e t I d ( )
{ . . . }
p u b l i c f u n c t i o n s e t I d ( $ i d )
{ . . . }
p u b l i c f u n c t i o n g e t T i t u l o ( )
{ . . . }
p u b l i c f u n c t i o n s e t T i t u l o ( $ t )
{ . . . }
p u b l i c f u n c t i o n g e t T e x t o ( )
{ . . . }
p u b l i c f u n c t i o n s e t T e x t o ( $ t )
{ . . . }
p u b l i c f u n c t i o n g e t F e c h a ( )
{ . . . }
p u b l i c f u n c t i o n s e t F e c h a ( $ f )
{ . . . }
}
Como ves no tiene nada de especial. Entonces, cmo sabe Doctrine2 de qu forma la
entidad y sus atributos deben ser mapeados sobre la base de datos? Tranquilos por que no
hay magia por ningn lado, los chicos de Symfony2 y Doctrine2 no son partidarios de los
objetos mgicos y prefieren que sea el programador quien controle todos los aspectos de la
aplicacin. La relacin entre el objeto y la base de datos relacional, es decir el Object
Relational Mapping, se lleva a cabo a travs de la configuracin. Te suena de algo esto de
separar la configuracin del uso?. En efecto, nos estamos refiriendo al patrn Injeccin de
Dependencias.
Una vez especificadas en la configuracin las relaciones entre entidades y base de datos, el
servicio de persistencia Doctrine2 ya est listo para manipular los datos de la base de datos
a travs de las entidades (objetos) del modelo.
As pues, la secuencia de pasos para poder utilizar Doctrine2 es:
1. Disear el modelo de datos especificando las asociaciones entre sus entidades (uno a
muchos, muchos a uno, muchos a muchos).
2. Construir las entidades como objetos planos de PHP con sus getters y setters de acceso
a las propiedades.
Las entidades
110
3. Especificar por configuracin las relaciones entre las entidades y la base de datos. Se
trata de indicar con qu tabla se corresponde cada entidad y de qu tipos son sus
atributos.
En las siguientes secciones propondremos la estrategia que hemos seguido para construir un
modelo segn lo especificado en el anlisis y persistible con Doctrine2.
Construccin de las entidades. El generador de entidades de
Symfony2
En principio, para crear las clases correspondientes a las entidades de nuestro modelo no es
necesario ms que el editor de textos de tu IDE. Sin embargo Symfony2 nos puede ayudar
bastante si utilizamos el generador automtico de entidades de Doctrine2. Se trata de una
herramienta que nos proporciona una primera versin de las entidades con los atributos y la
configuracin requerida para el mapeo sobre la base de datos. Las asociaciones entre
entidades debemos resolverlas posteriormente a mano.
Lanza el siguiente comando en la raz del proyecto:
p h p a p p / c o n s o l e g e n e r a t e : d o c t r i n e : e n t i t y
Lee con atencin las preguntas que te hace y contesta con las siguientes respuestas:
E n t i t y s h o r t c u t n a m e : J A M N o t a s F r o n t e n d B u n d l e : N o t a
C o n f i g u r a t i o n f o r m a t : a n n o t a t i o n
N e w f i e l d n a m e : t i t u l o
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : t e x t o
F i e l d t y p e : t e x t
N e w f i e l d n a m e : f e c h a
F i e l d t y p e : d a t e t i m e
N e w f i e l d n a m e : p a t h
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
g e n e r a t e a n e m p t y r e p o s i t o r y c l a s s : y e s
El Entity shortcut name es el nombre de la entidad junto con el bundle donde que la
alojar. Es la misma idea que los nombres lgicos para los controladores y las plantillas
que hemos visto en unidades anteriores. Las entidades se generan siempre en el
directorio E n t i t y del bundle. Por eso, para especificar su nombre corto (o lgico) slo
se require el nombre del bundle y el de la propia entidad.
El Configuration format establece la manera en que se van a especificar los metadatos
con la informacin de mapeo entre la entidad y la base de datos, es decir, como se va a
especificar la configuracin. Como todos los ficheros de configuracin en Symfony2 se
puede optar entre yml, xml, php y anotaciones. Esta ltima forma, las anotaciones, es
distinta conceptualmente a las dems. Estamos acostumbrado a que las
configuraciones se especifiquen en ficheros dedicados, ya sean yml, xml, php, sin
Construccin de las entidades. El generador de entidades de Symfony2
111
embargo las anotaciones plantean especificar la configuracin en el propio archivo en el
que se declara y define una clase. Si se trata de una entidad, en el propio archivo de la
entidad, utilizando una sintaxis especial en los bloques de comentarios que es capaz de
interpretar el contenedos de servicios. Esto no siempre tiene sentido, pero para el caso
de los metadatos de mapeos de entidades en base de datos es especialmente til, ya
que de esa manera tenemos en un mismo fichero la declaracin de la clase con los
atributos y la manera en que debe realizarse el mapeo sobre la base de datos.
Indicamos, por ltimo, que el conjunto de todos estos metadatos se conoce en el
contexto de los ORM's con el nombre de schema.
A continuacin vamos introduciendo los atributos de nuestra entidad y los tipos
asociados. Con esta informacin, el generador de entidades automtico, crear la
informacin del schema, esto es, los metadatos de mapeo.
Cuando hemos insertado el ltimo atributo, pulsamos una vez ms r e t u r n y
contestamos a la ltmima pregunta: "generate an empty repository class?", si
contestamos afirmativamente, adems de una clase para la entidad, se generar un
archivo cuya finalidad ser la implementacin de mtodos para la recuperacin de
colecciones de objetos de esa entidad que satisfacen distintos criterios. Al conjunto de
estos mtodos Doctrine2 les llama Repositorio.
Ahora debes tener en el bundle un nuevo directorio denominado E n t i t y con dos archivos:
N o t a . p h p y N o t a R e p o s i t o r y , el primero representa la entidad, y el segundo su repositorio
asociado (por lo pronto vaco). Vamos a echar un vistazo al fichero
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / E n t i t y / N o t a s . p h p .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / E n t i t y / N o t a s . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y ;
4
5 u s e D o c t r i n e \ O R M \ M a p p i n g a s O R M ;
6
7 / * *
8 * J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ N o t a
9 *
1 0 * @ O R M \ T a b l e ( )
1 1 * @ O R M \ E n t i t y ( r e p o s i t o r y C l a s s = " J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ N o t a R e p o s i t o r y " )
1 2 * /
1 3 c l a s s N o t a
1 4 {
1 5 / * *
1 6 * @ v a r i n t e g e r $ i d
1 7 *
1 8 * @ O R M \ C o l u m n ( n a m e = " i d " , t y p e = " i n t e g e r " )
1 9 * @ O R M \ I d
2 0 * @ O R M \ G e n e r a t e d V a l u e ( s t r a t e g y = " A U T O " )
2 1 * /
2 2 p r i v a t e $ i d ;
2 3
2 4 / * *
2 5 * @ v a r s t r i n g $ t i t u l o
2 6 *
2 7 * @ O R M \ C o l u m n ( n a m e = " t i t u l o " , t y p e = " s t r i n g " , l e n g t h = 2 5 5 )
2 8 * /
2 9 p r i v a t e $ t i t u l o ;
3 0
3 1 / * *
3 2 * @ v a r t e x t $ t e x t o
3 3 *
Construccin de las entidades. El generador de entidades de Symfony2
112
3 4 * @ O R M \ C o l u m n ( n a m e = " t e x t o " , t y p e = " t e x t " )
3 5 * /
3 6 p r i v a t e $ t e x t o ;
3 7
3 8 / * *
3 9 * @ v a r d a t e t i m e $ f e c h a
4 0 *
4 1 * @ O R M \ C o l u m n ( n a m e = " f e c h a " , t y p e = " d a t e t i m e " )
4 2 * /
4 3 p r i v a t e $ f e c h a ;
4 4
4 5 / * *
4 6 * @ v a r s t r i n g $ p a t h
4 7 *
4 8 * @ O R M \ C o l u m n ( n a m e = " p a t h " , t y p e = " s t r i n g " , l e n g t h = 2 5 5 )
4 9 * /
5 0 p r i v a t e $ p a t h ;
5 1
5 2
5 3 / * *
5 4 * G e t i d
5 5 *
5 6 * @ r e t u r n i n t e g e r
5 7 * /
5 8 p u b l i c f u n c t i o n g e t I d ( )
5 9 {
6 0 r e t u r n $ t h i s - > i d ;
6 1 }
6 2
6 3 / * *
6 4 * S e t t i t u l o
6 5 *
6 6 * @ p a r a m s t r i n g $ t i t u l o
6 7 * /
6 8 p u b l i c f u n c t i o n s e t T i t u l o ( $ t i t u l o )
6 9 {
7 0 $ t h i s - > t i t u l o = $ t i t u l o ;
7 1 }
7 2
7 3 / * *
7 4 * G e t t i t u l o
7 5 *
7 6 * @ r e t u r n s t r i n g
7 7 * /
7 8 p u b l i c f u n c t i o n g e t T i t u l o ( )
7 9 {
8 0 r e t u r n $ t h i s - > t i t u l o ;
8 1 }
8 2
8 3 / * *
8 4 * S e t t e x t o
8 5 *
8 6 * @ p a r a m t e x t $ t e x t o
8 7 * /
Construccin de las entidades. El generador de entidades de Symfony2
113
8 8 p u b l i c f u n c t i o n s e t T e x t o ( $ t e x t o )
8 9 {
9 0 $ t h i s - > t e x t o = $ t e x t o ;
9 1 }
9 2
9 3 / * *
9 4 * G e t t e x t o
9 5 *
9 6 * @ r e t u r n t e x t
9 7 * /
9 8 p u b l i c f u n c t i o n g e t T e x t o ( )
9 9 {
1 0 0 r e t u r n $ t h i s - > t e x t o ;
1 0 1 }
1 0 2
1 0 3 / * *
1 0 4 * S e t f e c h a
1 0 5 *
1 0 6 * @ p a r a m d a t e t i m e $ f e c h a
1 0 7 * /
1 0 8 p u b l i c f u n c t i o n s e t F e c h a ( $ f e c h a )
1 0 9 {
1 1 0 $ t h i s - > f e c h a = $ f e c h a ;
1 1 1 }
1 1 2
1 1 3 / * *
1 1 4 * G e t f e c h a
1 1 5 *
1 1 6 * @ r e t u r n d a t e t i m e
1 1 7 * /
1 1 8 p u b l i c f u n c t i o n g e t F e c h a ( )
1 1 9 {
1 2 0 r e t u r n $ t h i s - > f e c h a ;
1 2 1 }
1 2 2
1 2 3 / * *
1 2 4 * S e t p a t h
1 2 5 *
1 2 6 * @ p a r a m s t r i n g $ p a t h
1 2 7 * /
1 2 8 p u b l i c f u n c t i o n s e t P a t h ( $ p a t h )
1 2 9 {
1 3 0 $ t h i s - > p a t h = $ p a t h ;
1 3 1 }
1 3 2
1 3 3 / * *
1 3 4 * G e t p a t h
1 3 5 *
1 3 6 * @ r e t u r n s t r i n g
1 3 7 * /
1 3 8 p u b l i c f u n c t i o n g e t P a t h ( )
1 3 9 {
1 4 0 r e t u r n $ t h i s - > p a t h ;
1 4 1 }
Construccin de las entidades. El generador de entidades de Symfony2
114
1 4 2 }
La lnea 3 ubica a la clase que se est definiendo en el archivo al espacio de nombres
J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y , reservado para las entidades. Y en
la lnea 5 se hace uso de las clases perteneciente al espacio de nombres
D o c t r i n e \ O R M \ M a p p i n g , definiendo el alias O R M para referirse al mismo en el resto del
fichero. Fjate bien en los bloques de comentario. Como comentarios que son, no afectan
para nada a la clase en s. Sin embargo utilizan una sintaxis especial para describir
informacin de mapeo de la entidad en la base de datos.
En la lnea 8 se establece el nombre completo de la entidad.
En la lnea 10 se especifica que la entidad se corresponde con una tabla
(@ O R M \ T a b l e ( ) ). Como no se le ha pasado ningn argumento, el nombre de la tabla
ser igual que el nombre de la entidad: N o t a . Si quisiramos cambiarlo
especificaramos: O R M \ T a b l e ( ' n a m e = ' n o m b r e _ d e _ l a _ t a b l a ' ) .
En la lnea 11, se indica el nombre del repositorio asociado a la entidad. El repositorio no
es ms que una clase que contendr mtodos para recuperar colecciones de objetos de
esa entidad.
En cada uno de los atributos de la clase, se especifica la forma en que se debe mapear
el atributo en una columna de la tabla de la base de datos. Lo mejor para explorar la
sintaxis de mapeo en los distintos formatos de configuracin es consultando la
documentacin oficial sobre el mapping. Examina detalladamente cada uno de los
atributos creados y sus metadatos de mapeo correspondientes. Hay que aclarar que el
primer atributo: i d , lo ha insertado el generador de entidades automticamente, con el
fin de que sirva de clave principal en la tabla asociada.
Por ltimo, se han generado los getters y setters correspondientes a cada atributo.
Generamos a continuacin el resto de las entidades. Lanza para cada una de ellas el
comando:
p h p a p p / c o n s o l e g e n e r a t e : d o c t r i n e : e n t i t y
Y sigue estas indicaciones para la definicin de las entidades:
C o n t r a t o
E n t i t y s h o r t c u t n a m e : J A M N o t a s F r o n t e n d B u n d l e : C o n t r a t o
C o n f i g u r a t i o n f o r m a t : a n n o t a t i o n s
N e w f i e l d n a m e : f e c h a
F i e l d t y p e : d a t e
N e w f i e l d n a m e : r e f e r e n c i a
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
g e n e r a t e a n e m p t y r e p o s i t o r y c l a s s : y e s
E t i q u e t a
E n t i t y s h o r t c u t n a m e : J A M N o t a s F r o n t e n d B u n d l e : E t i q u e t a
C o n f i g u r a t i o n f o r m a t : a n n o t a t i o n s
Construccin de las entidades. El generador de entidades de Symfony2
115
N e w f i e l d n a m e : t e x t o
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
g e n e r a t e a n e m p t y r e p o s i t o r y c l a s s : y e s
G r u p o
E n t i t y s h o r t c u t n a m e : J A M N o t a s F r o n t e n d B u n d l e : G r u p o
C o n f i g u r a t i o n f o r m a t : a n n o t a t i o n s
N e w f i e l d n a m e : n o m b r e
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : r o l
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 0
g e n e r a t e a n e m p t y r e p o s i t o r y c l a s s : y e s
P u b l i c i d a d
E n t i t y s h o r t c u t n a m e : J A M N o t a s F r o n t e n d B u n d l e : P u b l i c i d a d
C o n f i g u r a t i o n f o r m a t : a n n o t a t i o n s
N e w f i e l d n a m e : n o m b r e
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : t e x t o
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : p a t h
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
g e n e r a t e a n e m p t y r e p o s i t o r y c l a s s : y e s
T a r i f a
E n t i t y s h o r t c u t n a m e : J A M N o t a s F r o n t e n d B u n d l e : T a r i f a
C o n f i g u r a t i o n f o r m a t : a n n o t a t i o n s
N e w f i e l d n a m e : n o m b r e
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : p e r i o d o
F i e l d t y p e : i n t e g e r
Construccin de las entidades. El generador de entidades de Symfony2
116
N e w f i e l d n a m e : p r e c i o
F i e l d t y p e : f l o a t
N e w f i e l d n a m e : v a l i d o D e s d e
F i e l d t y p e : d a t e
N e w f i e l d n a m e : v a l i d o H a s t a
F i e l d t y p e : d a t e
g e n e r a t e a n e m p t y r e p o s i t o r y c l a s s : y e s
U s u a r i o
E n t i t y s h o r t c u t n a m e : J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o
C o n f i g u r a t i o n f o r m a t : a n n o t a t i o n s
N e w f i e l d n a m e : n o m b r e
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : a p e l l i d o s
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : s a l t
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : u s e r n a m e
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : p a s s w o r d
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : e m a i l
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
N e w f i e l d n a m e : i s A c t i v e
F i e l d t y p e : b o o l e a n
N e w f i e l d n a m e : t o k e n R e g i s t r o
F i e l d t y p e : s t r i n g
F i e l d l e n g t h : 2 5 5
g e n e r a t e a n e m p t y r e p o s i t o r y c l a s s : y e s
Con esto ya tenemos una primera versin del modelo con anotaciones sobre como se debe
realizar el mapeo en la base de datos. An tenemos que definir las asociaciones entre las
entidades para que el modelo sea completo. Sin embargo, antes de dar este paso vamos a
crear una primera versin de la base de datos correspondiente a este modelo. Ms adelante,
Construccin de las entidades. El generador de entidades de Symfony2
117
cuando hayamos completado el modelo con la informacin de las asociaciones, volveremos
a generar la base de datos. Esto nos dar una idea de la potencia y versatilidad de Doctrine2
para manipular y modificar la base de datos incrementalmente a partir de los metadatos de
mapeo.
Creacin de la base de datos
Antes de crear la base de datos hay que configurar el servicio Doctrine2 con los parmetros
de conexin correctos. Esto se hace en el archivo global a p p / c o n f i g / p a r a m e t e r s . i n i (te
acuerdas de l?).
a p p / c o n f i g / p a r a m e t e r s . i n i
; T h e s e p a r a m e t e r s c a n b e i m p o r t e d i n t o o t h e r c o n f i g f i l e s
; b y e n c l o s i n g t h e k e y w i t h % ( l i k e % d a t a b a s e _ u s e r % )
; C o m m e n t s s t a r t w i t h ' ; ' , a s i n p h p . i n i
[ p a r a m e t e r s ]
d a t a b a s e _ d r i v e r = p d o _ m y s q l
d a t a b a s e _ h o s t = l o c a l h o s t
d a t a b a s e _ p o r t =
d a t a b a s e _ n a m e = m e n t o r n o t a s
d a t a b a s e _ u s e r = r o o t
d a t a b a s e _ p a s s w o r d = r o o t
. . .
Para que las operaciones que vamos a hacer a continuacin puedan llevarse a cabo,
asegurate de que el usuario de la base de datos tenga permisos para crear bases de datos
en el sistema.
Nota
Nosotros estamos utilizando el usuario r o o t de MySQL, de esa manera no tendremos
problemas con los permisos. Pero atentos, esto es justificable nicamente en el
entorno de desarrollo, donde podemos relajarnos con el tema de la seguridad. Una vez
que pasemos la aplicacin a un entorno de produccin, estas licencias no deben ser
permitidas.
Para crear la base de datos (vaca) lanza el comando de consola:
p h p a p p / c o n s o l e d o c t r i n e : d a t a b a s e : c r e a t e
Comprueba que se ha creado una base de datos con nombre m e n t o r n o t a s en tu sistema
gestor de base de datos.
Y ahora con este comando se generarn las tablas de acuerdo a la informacin especificada
en las anotaciones de cada entidad:
p h p a p p / c o n s o l e d o c t r i n e : s c h e m a : c r e a t e
chale un vistazo a la base de datos. Ya tiene tablas. Es posible que, ms adelante, nos
demos cuenta de que algunos campos deberan poder ser nulos. O que nos hemos
equivocado en el tipo. No pasa nada; cambiamos las anotaciones pertinentes y actualizamos
Creacin de la base de datos
118
el schema, es decir, la definicin de la base de datos, mediante:
p h p a p p / c o n s o l e d o c t r i n e : s c h e m a : u p d a t e
De hecho, durante al inicio de la construccin del modelo, esta instruccin se suele lanzar
muchas veces pues estamos continuamente refinando el modelo hasta que lo dejamos como
realmente queremos. A medida que la aplicacin se estabiliza, estas actualizaciones son
menos frecuentes, pero se siguen haciendo. La gracia de Doctrine2 es que lanza sobre la
base de datos los comandos necesarios para realizar los cambios incrementalmente sin
perder datos. De todas formas hay que tener cuidado con esto pues no siempre funciona.
Por eso todos los cambios que se realicen deben hacerse en el entorno de desarrollo (lo cual
es una norma general en cualquier mbito de la informtica).
Aunque lo normal ahora sera terminar de construir el modelo aadiendo las asociaciones
entre las entidades, no lo vamos a hacer. Nos conformaremos con el modelo inclompleto que
tenemos en este momento para mostrar como se crean, editan y eliminan objetos usando el
servicio de persistencia de Doctrine2 en combinacin con las entidades recien generadas.
Despus, cuando tengamos algunos datos en la base de datos, vamos a completar el modelo
y actualizar la estructura de la base de datos con la nueva informacin sobre las
asociaciones (o relaciones en el contexto de la base de datos). El objetivo que perseguimos
con este procedimiento "desordenado", es ilustrar lo que suele ocurrir en la prctica: que
una vez que tenemos el modelo funcionando con su base de datos asociada, hay que
realizar cambios que afectan a la estructura de la base de datos. Veremos como Doctrine2
nos proporciona un ayuda inestimable a la hora de ejecutar dichos cambios estructurales
(que son los ms fastidiosos) sin que haya prdida de datos.
El servicio de persistencia Doctrine2
Para no contaminar el controlador N o t a s C o n t r o l l e r con los ejemplos que vamos a utilizar
en esta unidad para estudiar el ORM, crearemos otro nuevo controlador denominado
E s t u d i o O R M C o n t r o l l e r y en l escribiremos el cdigo que es ajeno a la aplicacin. La ruta
de este controlador ser:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o O R M C o n t r o l l e r . p h p .
Tambin crearemos un directorio para almacenar las vistas de este controlador. Su ruta
ser: s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w / E s t u d i o O R M .
Ahora vamos a crear una ruta y una accin asociada para mostrar la manera de crear
entidades y persistirlas con Doctrine2.
Aade al archivo
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l , la
siguiente ruta:
j a m n _ O R M _ c r e a r :
p a t t e r n : / e s t u d i o _ o r m / c r e a r
d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : c r e a r }
y la accin c r e a r A c t i o n ( ) al controlador E s t u d i o O R M C o n t r o l l e r .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o O R M C o n t r o l l e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ;
4
El servicio de persistencia Doctrine2
119
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6 u s e \ D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
7 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ N o t a ;
8
9 c l a s s E s t u d i o O R M C o n t r o l l e r e x t e n d s C o n t r o l l e r
1 0 {
1 1 p u b l i c f u n c t i o n c r e a r A c t i o n ( )
1 2 {
1 3 $ n o t a 1 = n e w N o t a ( ) ;
1 4 $ n o t a 1 - > s e t F e c h a ( n e w \ D a t e T i m e ( ) ) ;
1 5 $ n o t a 1 - > s e t T i t u l o ( ' N o t a d e p r u e b a 1 ' ) ;
1 6 $ n o t a 1 - > s e t T e x t o ( ' T e x t o p a r a l a n o t a d e p r u e b a s 1 ' ) ;
1 7 $ n o t a 1 - > s e t P a t h ( ' r u t a / a / n o t a 1 ' ) ;
1 8
1 9 $ n o t a 2 = n e w N o t a ( ) ;
2 0 $ n o t a 2 - > s e t F e c h a ( n e w \ D a t e T i m e ( ) ) ;
2 1 $ n o t a 2 - > s e t T i t u l o ( ' N o t a d e p r u e b a 2 ' ) ;
2 2 $ n o t a 2 - > s e t T e x t o ( ' T e x t o p a r a l a n o t a d e p r u e b a s 2 ' ) ;
2 3 $ n o t a 2 - > s e t P a t h ( ' r u t a / a / n o t a 2 ' ) ;
2 4 / / P o r l o p r o n t o n o l e a s o c i a m o s u s u a r i o
2 5 / / A h o r a h a y q u e p e r s i s t i r l o
2 6 / / s o l i c i t a m o s e l s e r v i c i o d e p e r s i s t e n c i a ( O R M )
2 7 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
2 8 / / L e e n v i a m o s a d i c h o s e r v i c i o e l o b j e t o q u e q u e r e m o s p e r s i s t i r ( n o s e
2 9 / / r e a l i z a q u e r y a n
3 0 $ e m - > p e r s i s t ( $ n o t a 1 ) ;
3 1 $ e m - > p e r s i s t ( $ n o t a 2 ) ;
3 2
3 3 / / A l f i n a l d e l p r o c e s o , c u a n d o h a y a m o s e n v i a d o a t o d o s l o s o b j e t o s
3 4 / / a l s e r v i c i o d e p e r s i s t e n c i a , l o e n v i a m o s e f e c t i v a m e n t e a l a b a s e d e
3 5 / / d a t o s ( i n s e r t )
3 6 $ e m - > f l u s h ( ) ;
3 7
3 8 $ n o t a s = n e w A r r a y C o l l e c t i o n ( ) ;
3 9
4 0 $ n o t a s - > a d d ( $ n o t a 1 ) ;
4 1 $ n o t a s - > a d d ( $ n o t a 2 ) ;
4 2
4 3 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : c r e a r . h t m l . t w i g ' ,
4 4 a r r a y ( ' n o t a s ' = > $ n o t a s ) ) ;
4 5 }
4 6 }
Por ltimo la plantilla asociada es:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / E s t u d i o O R M / c r e a r . h t m l . t w i g
{ % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
{ % b l o c k b o d y % }
< h 1 > P r u e b a d e l O R M p a r a c r e a r o b j e t o s < / h 1 >
< h 2 > C r e a c i n d e n o t a s < / h 2 >
< h 3 > S e h a n c r e a d o y p e r s i s t i d o e n b a s e d e d a t o s l a n o t a s s i g u i e n t e s : < / h 3 >
< u l >
El servicio de persistencia Doctrine2
120
{ % f o r n o t a i n n o t a s % }
< l i > { { n o t a . t i t u l o } } < / l i >
{ % e n d f o r % }
< / u l >
{ % e n d b l o c k % }
Vamos a analizar la accin que acabamos de crear.
La lnea 6 indica que se va a usar la clase A r r a y C o l l e c t i o n de Doctrine2, que es una
implementacin ms eficiente de un array . Doctrine2 aconseja usar este A r r a y C o l l e c t i o n
para construir colecciones de objetos.
La lnea 7 indica que se va a usar la entidad N o t a de nuestro modelo.
En las lneas 13-17 y 19-23 se crean dos objetos de la clase N o t a , y se les define sus
propiedades utilizando los setters. En ese momento ya disponemos de dos notas en
memoria. Si no queremos perderlas cuando finalice el script debemos persistirlas en la base
de datos. Ah es donde entra en juego el servicio de persistencia Doctrine2. En la lnea 28 se
obtiene, a travs del contenedor de servicios, un objeto E n t i t y M a n a g e r con el cual se
realiza la persistencia de las entidades (lineas 31-32) usando su mtodo p e r s i s t ( ) . En este
punto el servicio de persistencia ya tiene informacin de que se desea persistir los objetos
n o t a 1 y n o t a 2 , pero la escritura en la base de datos no se lleva a cabo hasta la la lnea 37
donde se invoca el mtodo f l u s h ( ) .
Como regla general, no se debe usar f l u s h ( ) hasta que no se hayan realizado todos los
p e r s i s t ( ) sobre los objetos que se estn creando, actualizando y/o borrando. Doctrine2
hace esto as por razones de eficiencia. Con los p e r s i s t ( ) , se van preparando las query's, y
con el f l u s h ( ) se lanzan todas de una vez de manera optimizada.
En las lneas 38-41, se crea un tipo de array ms apropiado para las colecciones de objetos
que el array propio de PHP. Hemos usado este tipo de array con el objetivo de introducirlos,
ya que las colecciones de objetos devueltos por los repositorios de Doctrine2 son de este
tipo.
Por ltimo, en las lneas 43-44, se invoca a la plantilla que va a pintar el resultado. El cdigo
de esta plantilla, que reproducimos ms arriba, no merece ninguna explicacin especial.
Ejecuta la accin a travs de la URL:
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / e s t u d i o _ o r m / c r e a r
Y comprueba que se hayan insertado los dos registros en la tabla N o t a .
Como ves la creacin de entidades no tiene mucha complicacin, se trata de "inyectarlas" en
el servicio de persistencia Doctrine2 mediante el mtodo p e r s i s t ( ) y realizar un f l u s h ( )
antes de que se termine la accin. Ms tarde veremos como la actualizacin de objetos y el
borrado se realiza de forma anloga.
Ahora vamos a estudiar como se recuperan objetos de la base de datos. Para ello se utiliza
otro elemento de Doctrine2 denominado repositorio (repository). Cada entidad tiene
asociada por defecto un repositorio, que es un objeto de la clase
D o c t r i n e \ O R M \ E n t i t y R e p o s i t o r y , con un conjunto comn de mtodos para recuperar
objetos de la base de datos. Adems, cada entidad, puede declarar su propio repositorio,
que es una clase que extiende a D o c t r i n e \ O R M \ E n t i t y R e p o s i t o r y , para aadir nuevos
mtodos con criterios de bsqueda ms elaborados o sencillamente que no esten
disponibles en la clase padre. Estos repositorios especificos de cada entidad son los que
hemos generado junto con las entidades al usar el generador de entidades automtico.
El servicio de persistencia Doctrine2
121
Los mtodos comunes del repositorio son:
f i n d ( $ i d ) , recupera el objeto con el i d especificado por el argumento.
f i n d O n e B y { A t r i b u t o } ( $ v a l o r ) , recupera en primer objeto que tienen como A t r i b u t o
el parmetro pasado en el argumento. Por ejemplo, f i n d O n e B y T i t u l o ( ) , o
f i n d O n e B y T e x t o ( ) son dos mtodos de este tipo disponibles en el repositorio de la
entidad N o t a .
f i n d B y { A t r i b u t o } ( $ v a l o r ) , recupera en un A r r a y C o l l e c t i o n todos los objetos que
tienen como A t r i b u t o el parmetro pasado en el argumento.
f i n d A l l ( ) recupera todos los objetos.
Vemoslo en la prctica. Aade las siguientes ruta, accin y plantilla.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l
j a m n _ O R M _ r e c u p e r a r :
p a t t e r n : / e s t u d i o _ o r m / r e c u p e r a r / { i d }
d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r }
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o O R M C o n t r o l l e r . p h p
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n r e c u p e r a r A c t i o n ( )
4 {
5 $ i d = $ t h i s - > g e t R e q u e s t ( ) - > g e t ( ' i d ' ) ;
6
7 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
8
9 $ n o t a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d ( $ i d ) ;
1 0 i f ( ! $ n o t a ) {
1 1 t h r o w $ t h i s - > c r e a t e N o t F o u n d E x c e p t i o n ( ' N o e x i s t e n o t a c o n i d ' . $ i d ) ;
1 2 }
1 3
1 4
1 5 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r . h t m l . t w i g ' ,
1 6 a r r a y ( ' n o t a ' = > $ n o t a ) ) ;
1 7 }
1 8 . . .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / E s t u d i o O R M / r e c u p e r a r . h t m l . t w i g
{ % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
{ % b l o c k b o d y % }
< h 1 > P r u e b a d e l O R M p a r a m o s t r a r u n a n o t a < / h 1 >
< h 2 > N o t a { { n o t a . i d } } < / h 2 >
< t a b l e b o r d e r = " 1 " >
< t r >
< t d > T i t u l o : < / t d >
< t d > { { n o t a . t i t u l o } } < / t d >
< / t r >
El servicio de persistencia Doctrine2
122
< t r >
< t d > T e x t o : < / t d >
< t d > { { n o t a . t e x t o } } < / t d >
< / t r >
< t r >
< t d > P a t h : < / t d >
< t d > { { n o t a . p a t h } } < / t d >
< / t r >
< / t a b l e >
{ % e n d b l o c k % }
En la lnea 5 de la accin r e c u p e r a r A c t i o n ( ) se recupera el i d que se ha pasado a travs
de la URL. A continuacin pedimos un objeto E n t i t y M a n a g e r de Doctrine2 (lnea 7). En la
lnea 9 obtenemos el repositorio de la entidad J A M N o t a s F r o n t e n d B u n d l e : N o t a y utilizamos
su mtodo f i n d ( ) para recuperar el objeto N o t a con ese i d asociado. Podra ocurrir que no
hubiese ningn objeto asociado a tal i d , en ese caso la variable $ n o t a de la lnea 9 sera
n u l l . Entonces disparamos una excepcin N o t F o u n d E x c e p t i o n (lneas 10-13) con el mtodo
c r e a t e N o t F o u n d E c e p t i o n ( ) de la clase C o n t r o l l e r . El framework, cuando atrapa esta
excepcin construye y enva una respuesta HTTP 400 (page not found). Pero si existe una
nota asociada a ese i d , en la lnea 9 se recuperar un objeto vlido y lo pintaremos con la
plantilla r e c u p e r a r . h t m l . t w i g (lneas 15-16). El cdigo de dicha plantilla no necesita
ninguna explicacin.
Prueba la accin que acabamos de crear.
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / e s t u d i o _ o r m / r e c u p e r a r / 1
Esta es la forma rpida de recuperar objetos si se conoce su i d . Ahora vamos a recuperar
una coleccin con todas las notas almacenadas.
Aade la ruta, la accin y la plantilla siguientes al proyecto.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l
j a m n _ O R M _ r e c u p e r a r _ n o t a s :
p a t t e r n : / e s t u d i o _ o r m / r e c u p e r a r _ n o t a s
d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r N o t a s }
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o O R M C o n t r o l l e r . p h p
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n r e c u p e r a r N o t a s A c t i o n ( )
4 {
5 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
6
7 $ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d A l l ( ) ;
8 i f ( ! $ n o t a s ) {
9 t h r o w $ t h i s - > c r e a t e N o t F o u n d E x c e p t i o n ( ' N o e x i s t e n n o t a s ' ) ;
1 0 }
El servicio de persistencia Doctrine2
123
1 1
1 2
1 3 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r N o t a s . h t m l . t w i g ' ,
1 4 a r r a y ( ' n o t a s ' = > $ n o t a s ) ) ;
1 5 }
1 6 . . .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / E s t u d i o O R M / r e c u p e r a r N o t a s . h t m l . t w i g
{ % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
{ % b l o c k b o d y % }
< h 1 > P r u e b a d e l O R M p a r a m o s t r a r t o d a s l a s n o t a < / h 1 >
< h 2 > N o t a s < / h 2 >
{ % f o r n o t a i n n o t a s % }
< t a b l e b o r d e r = " 1 " >
< t r >
< t d > T i t u l o : < / t d >
< t d > { { n o t a . t i t u l o } } < / t d >
< / t r >
< t r >
< t d > T e x t o : < / t d >
< t d > { { n o t a . t e x t o } } < / t d >
< / t r >
< t r >
< t d > P a t h : < / t d >
< t d > { { n o t a . p a t h } } < / t d >
< / t r >
< / t a b l e >
< h r / >
{ % e n d f o r % }
{ % e n d b l o c k % }
Simplemente hemos utilizado el mtodo f i n d A l l ( ) del repositorio (lnea 7), y hemos
pintado en la plantilla una coleccin de objetos N o t a . Ahora puedes probar t con los
mtodos f i n d B y N o m b r e ( ) , f i n d B y T e x t o ( ) y f i n d B y P a t h ( ) , que tambin devuelven
colecciones y con los mtodos f i n d O n e B y N o m b r e ( ) , f i n d O n e B y T e x t o ( ) y f i n d O n e B y P a t h ( )
que devuelven un slo objeto (el primero que se encuentra).
Prueba la accin que acabamos de crear.
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / e s t u d i o _ o r m / r e c u p e r a r _ n o t a s
Si una vez que hemos recuperado un objeto, queremos modificarlo, no tenemos ms que
utilizar sus mtodos s e t t e r s para cambiar las propiedades y persistirlo con los mtodos
p e r s i s t ( ) y f l u s h ( ) del E n t i t y M a n a g e r :
El servicio de persistencia Doctrine2
124
1 < ? p h p
2 . . .
3
4 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
5
6 $ n o t a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d ( $ i d ) ;
7 i f ( ! $ n o t a ) {
8 t h r o w $ t h i s - > c r e a t e N o t F o u n d E x c e p t i o n ( ' N o e x i s t e e s a n o t a ' ) ;
9 }
1 0
1 1 $ n o t a - > s e t T i t u l o ( ' n u e v o t i t u l o ' ) ;
1 2 $ n o t a - > s e t T e x t o ( ' n u e v o t e x t o ' ) ;
1 3
1 4 $ e m - > p e r s i s t ( $ n o t a ) ;
1 5
1 6 $ e m - > f l u s h ( ) ;
1 7
1 8 . . .
Si lo que queremos es borrar el objeto:
1 < ? p h p
2 . . .
3
4 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
5
6 $ n o t a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d ( $ i d ) ;
7 i f ( ! $ n o t a ) {
8 t h r o w $ t h i s - > c r e a t e N o t F o u n d E x c e p t i o n ( ' N o e x i s t e e s a n o t a ' ) ;
9 }
1 0
1 1 $ e m - > r e m o v e ( $ n o t a ) ;
1 2
1 3 $ e m - > f l u s h ( ) ;
1 4
1 5 . . .
Refinamos el modelo. Las asociaciones entre objetos
Ya sabemos como persistir nuestras entidades en la base de datos y como recuperarlas
cuando el criterio de bsqueda encaja dentro de lo que ofrecen los mtodos "encontrar Por
los atributos" de un objeto (f i n d B y { A t t r i b u t o } ). Pero recuerda que el modelo que hemos
creado con la ayuda del generador automtico de entidades no cumple lo especificado en el
anlisis, pues no hemos tenido en cuenta las asociaciones entre objetos. Es lo que haremos
en este apartado.
Los objetos pueden asociarse, o relacionarse, de distintas formas:
un slo objeto del tipo A puede estar asociado a muchos objetos del tipo B , pero uno del
tipo B solo puede estar asociado a uno del tipo A . Esto es una relacin uno-a-muchos
(one to many),
muchos objetos del tipo A pueden estar asociados a un slo objeto del tipo B , y uno del
tipo B puede estar asociados a muchos del tipo A . Esto es una relacin muchos-a-uno
(many to one), y obviamente es la opuesta a la anterior,
Refinamos el modelo. Las asociaciones entre objetos
125
uno o varios objetos del tipo A pueden estar asociados a uno o varios objetos del tipo B
y viceversa. Esto es una relacin muchos a muchos (many to many),
un slo objeto del tipo A puede estar asociado con un slo objeto del tipo B . Esto es una
relacin uno a uno (one to one).
La relacin semntica de cada asociacin viene determinada por el contexto y la naturaleza
de cada entidad. Identfiquemos algunas de las relaciones entre las entidades de nuestro
modelo:
Una N o t a puede contener varios A r c h i v o 's y un A r c h i v o slo puede ser de una N o t a .
Se trata de una relacin one to many desde la N o t a al A r c h i v o .
Varias N o t a 's pueden pertenecer a un U s u a r i o , y un U s u a r i o puede poseer muchas
N o t a 's . Es una relacin many to one desde la N o t a al U s u a r i o .
Una o varias N o t a 's pueden tener una o varias E t i q u e t a 's asociadas, y al revs, una o
varias E t i q u e t a 's pueden etiquetar a una o varias N o t a 's. Es una relacin many to
many.
En las anteriores sentencias se ha marcado en negrita la cualidad semntica de la relacin.
Como ejercicio puedes continuar describiendo el resto de relaciones. Observa que cada
relacin one to many tiene su inversa many to one. Por ejemplo, la inversa de la primera
relacin sera: varios A r c h i v o 's pueden pertenecer a una sla N o t a .
Para completar la implementacin del modelo con las asociaciones entre objetos, debemos
agregar los atributos correspondientes a las entidades asociadas y las anotaciones
pertinentes para poder mapearlas sobre la base de datos. Doctrine2 permite especificar la
informacin de mapeo con un alto grado de precisin, por lo que la sintaxis de las
anotaciones para las asociaciones puede llegar a ser bastante compleja. En este apartado
vamos a dar unas reglas sencillas que funcionan en la mayor parte de las situaciones. Pero
debe quedar claro que Doctrine2 permite ms posibilidades y que, posiblemente en alguno
de tus proyectos, tengas que recurrir a la documentacin oficial sobre asociaciones.
La relacin One to Many
Tomemos como ejemplo la relacin: "un U s u a r i o posee muchas N o t a 's", el cdigo que hay
que aadir a la clase U s u a r i o sera (cdigo PHP y anotacin):
< ? p h p
. . .
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
c l a s s U s u a r i o {
. . .
/ * *
* @ O R M \ O n e T o M a n y ( t a r g e t E n t i t y = " N o t a " , m a p p e d B y = " u s u a r i o " )
* /
p r i v a t e $ n o t a s ;
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > n o t a s = n e w A r r a y C o l l e c t i o n ( ) ;
}
La relacin One to Many
126
Es importante en la relacin One to Many, inicializar la parte Many como una colleccin de
objetos Doctrine2 (A r r a y C o l l e c t i o n ). Esto se hace utilizando el constructor de la clase. En
la anotacin se debe especificar a travs de la directiva m a p p e d B y , como se llama el atributo
(u s u a r i o ) en la clase target (N o t a ).
La relacin Many to One
Tomemos como ejemplo la inversa a la anterior: "varias N o t a 's pueden pertenecer a un slo
U s u a r i o . el cdigo que hay que aadir a la clase U s u a r i o sera (cdigo PHP y anotacin):
< ? p h p
. . .
c l a s s N o t a {
. . .
/ * *
* @ O R M \ M a n y T o O n e ( t a r g e t E n t i t y = " U s u a r i o " )
* /
p r i v a t e $ u s u a r i o ;
. . .
El resultado de estas dos anotaciones complementarias cuando se mapean en la base de
datos es la creacin de un campo denominado u s u a r i o _ i d en la tabla N o t a que ser clave
foranea del campo i d de la tabla U s u a r i o .
La relacin Many to Many
Tomemos como ejemplo la relacin: "varias N o t a s pueden ser etiquetadas con varias
E t i q u e t a 's y viceversa". Este tipo de relacin es completamente simtrica e implica a los
dos objetos. Por ello hay que agregar el siguiente cdigo a la clase N o t a :
< ? p h p
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
. . .
c l a s s N o t a {
. . .
/ * *
* @ O R M \ M a n y T o M a n y ( t a r g e t E n t i t y = " E t i q u e t a " , i n v e r s e d B y = " n o t a s " )
* /
p r i v a t e $ e t i q u e t a s ;
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > e t i q u e t a s = n e w A r r a y C o l l e c t i o n ( ) ;
}
Y a la clase etiquetas:
< ? p h p
. . .
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
c l a s s E t i q u e t a {
. . .
La relacin Many to One
127
/ * *
* @ O R M \ M a n y T o M a n y ( t a r g e t E n t i t y = " E t i q u e t a " , m a p p e d B y = " e t i q u e t a s " )
* /
p r i v a t e $ n o t a s ;
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > n o t a s = n e w A r r a y C o l l e c t i o n ( ) ;
}
En ambos casos es necesario declarar en el constructor las entidades asociadas como
A r r a y C o l l e c t i o n , pues en los dos casos los atributos asociados son colecciones.
El resultado de estas dos anotaciones complementarias cuando se mapean en la base de
datos es la creacin de una tabla intermedia denominada n o t a _ e t i q u e t a , con dos campos
n o t a _ i d y e t i q u e t a _ i d que son clave foranea de los campos i d de las tablas N o t a y
E t i q u e t a respectivamente. Es decir, la estrategia que se sigue habitualmente para
implementar las relaciones N-M (otra forma ms propia del lenguaje de las bases de datos
para decir Many to Many) en una base de datos relacional.
Siguiendo estas reglas podemos completar el resto de las entidades con las asociaciones
que establece el modelo.
s r c / J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / E n t i t y / C o n t r a t o
< ? p h p
. . .
c l a s s C o n t r a t o {
. . .
/ / / / A S O C I A C I O N E S / / / /
/ * *
* @ O R M \ M a n y T o O n e ( t a r g e t E n t i t y = " T a r i f a " )
* /
p r i v a t e $ t a r i f a ;
/ * *
* @ O R M \ M a n y T o O n e ( t a r g e t E n t i t y = " U s u a r i o " )
* /
p r i v a t e $ u s u a r i o ;
/ / / / F I N A S O C I A C I O N E S / / / /
. . .
s r c / J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / E n t i t y / E t i q u e t a
< ? p h p
. . .
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
c l a s s E t i q u e t a {
. . .
/ / / / A S O C I A C I O N E S / / / /
La relacin Many to One
128
/ * *
* @ O R M \ M a n y T o M a n y ( t a r g e t E n t i t y = " N o t a " , m a p p e d B y = " e t i q u e t a s " )
* /
p r i v a t e $ n o t a s ;
/ * *
* @ O R M \ M a n y T o O n e ( t a r g e t E n t i t y = " U s u a r i o " )
* /
p r i v a t e $ u s u a r i o ;
/ / / / F I N A S O C I A C I O N E S / / / /
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > n o t a s = n e w A r r a y C o l l e c t i o n ( ) ;
}
s r c / J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / E n t i t y / G r u p o
< ? p h p
. . .
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
c l a s s G r u p o {
. . .
/ / / / A S O C I A C I O N E S / / / /
/ * *
* @ O R M \ M a n y T o M a n y ( t a r g e t E n t i t y = " U s u a r i o " , m a p p e d B y = " g r u p o s " )
* /
p r i v a t e $ u s u a r i o s ;
/ / / / F I N A S O C I A C I O N E S / / / /
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > u s u a r i o s = n e w A r r a y C o l l e c t i o n ( ) ;
}
s r c / J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / E n t i t y / N o t a
< ? p h p
. . .
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
c l a s s N o t a {
. . .
/ / / / A S O C I A C I O N E S / / / /
/ * *
* @ O R M \ M a n y T o O n e ( t a r g e t E n t i t y = " U s u a r i o " )
* /
La relacin Many to One
129
p r i v a t e $ u s u a r i o ;
/ * *
* @ O R M \ M a n y T o M a n y ( t a r g e t E n t i t y = " E t i q u e t a " , i n v e r s e d B y = " n o t a s " )
* /
p r i v a t e $ e t i q u e t a s ;
/ / / / F I N A S O C I A C I O N E S / / / /
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > e t i q u e t a s = n e w A r r a y C o l l e c t i o n ( ) ;
}
s r c / J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / E n t i t y / T a r i f a
< ? p h p
. . .
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
c l a s s T a r i f a {
. . .
/ / / / A S O C I A C I O N E S / / / /
/ * *
* @ O R M \ O n e T o M a n y ( t a r g e t E n t i t y = " C o n t r a t o " , m a p p e d B y = " t a r i f a " )
* /
p r i v a t e $ c o n t r a t o s ;
/ / / / F I N A S O C I A C I O N E S / / / /
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > c o n t r a t o s = n e w A r r a y C o l l e c t i o n ( ) ;
}
s r c / J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / E n t i t y / U s u a r i o
< ? p h p
. . .
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
c l a s s U s u a r i o {
/ / / / A S O C I A C I O N E S / / / /
/ * *
* @ O R M \ O n e T o M a n y ( t a r g e t E n t i t y = " N o t a " , m a p p e d B y = " u s u a r i o " )
* /
p r i v a t e $ n o t a s ;
/ * *
* @ O R M \ O n e T o M a n y ( t a r g e t E n t i t y = " C o n t r a t o " , m a p p e d B y = " u s u a r i o " )
* /
La relacin Many to One
130
p r i v a t e $ c o n t r a t o s ;
/ * *
* @ O R M \ O n e T o M a n y ( t a r g e t E n t i t y = " E t i q u e t a " , m a p p e d B y = " u s u a r i o " )
* /
p r i v a t e $ e t i q u e t a s ;
/ * *
* @ O R M \ M a n y T o M a n y ( t a r g e t E n t i t y = " G r u p o " , i n v e r s e d B y = " u s u a r i o s " )
* /
p r i v a t e $ g r u p o s ;
/ / / / F I N A S O C I A C I O N E S / / / /
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > n o t a s = n e w A r r a y C o l l e c t i o n ( ) ;
$ t h i s - > c o n t r a t o s = n e w A r r a y C o l l e c t i o n ( ) ;
$ t h i s - > e t i q u e t a s = n e w A r r a y C o l l e c t i o n ( ) ;
$ t h i s - > g r u p o s = n e w A r r a y C o l l e c t i o n ( ) ;
}
Una vez declaradas las asociaciones, tanto como atributos de las entidades como
anotaciones para el mapeo relacional, debemos crear los mtodos para acceder a ellas, es
decir, los getters y setters. Podemos hacerlo a mano, o mejor an, utilizando el siguiente
comando de consola:
p h p a p p / c o n s o l e d o c t r i n e : g e n e r a t e : e n t i t i e s J A M N o t a s F r o n t e n d B u n d l e
Este comando crea, a partir de la configuracin de las entidades, es decir, de las anotaciones
en nuestro caso, los atributos y mtodos de acceso getters y setters que falten por definir en
la entidad. Comprubalo mirando el cdigo de las entidades despus de lanzar este
comando.
Nota
Recuerda que adems de anotaciones, los metadatos que definen el mapeo se pueden
declarar en un fichero de configuracin YAML, XML o PHP.
Y ahora que ya tenemos las entidades completamente definidas, actualizamos la estructura
de la base de datos mediante el comando:
p h p a p p / c o n s o l e d o c t r i n e : s c h e m a : u p d a t e - - f o r c e
Esta operacin es comprometida, se trata de actualizar la estructura de una base de datos, y
ello podra dar lugar a una prdida de datos. Por ello Doctrine2 exige indicar si realmente
quieres atacar a la base de datos usando el modificador - - f o r c e , o si slo quieres volcar en
pantalla las sentencias SQL mediante el modificador - - d u m p - s q l , de manera que puedas
examinarlas antes de lanzarlas.
La relacin Many to One
131
Comprueba como despus de lanzar el comando se ha actualizado la base de datos con
todas las relaciones que modelan las asociaciones entre objetos y, adems, no ha habido
prdida de datos (mira el contenido de la tabla N o t a ). Y es que Doctrine2, adems de ser un
explendido ORM es una gran herramienta para la manipulacin de bases de datos.
Ms all de los mtodos f i n d B y . El lenguaje DQL
Los mtodos que ofrece el repositorio por defecto asociado a cada entidad son realmente
tiles, y se pueden hacer muchas cosas con ellos. Sin embargo en muchas ocasiones se te
van a quedar cortos. Tendrs que recuperar una serie de objetos que cumplan un criterio
ms complejo que el simple "buscarPor" (f i n d B y ). Dicho en lenguaje de base de datos,
habr que realizar consultas con clausulas where ms complicadas y/o con joins entre
tablas.
Ningn ORM estara completo si no proporcionase algn mecanismo para realizar cualquier
tipo de consultas sobre la base de datos. O dicho ahora en el lenguaje de los ORM's, de
recuperar colecciones de objetos que satisfagan criterios de de busquedas de cualquier tipo.
El mecanismo de consulta de Doctrine2 se llama DQL, acrnimo de Doctrine Query
Language. Se trata de un lenguaje de consulta sobre objetos
18
. Y hemos de tener muy
presente la palabra objeto, pues el hecho de que DQL utilice las construcciones
caractersticas del SQL: SELECT, UPDATE y DELETE, tiende a confundir al principiante que lo
utiliza como si fuera una variante de este ltimo.
Sugerencia
No debes pensar en trmino de tablas, columnas y joins entre tablas, si no en
trminos de tu modelo de objetos. Lo veremos con los ejemplos.
Realizaremos un estudio del DQL en la onda de esta unidad, es decir, presentando los
elementos bsicos que cubren un amplio espectro de situaciones, pero dejando claro que
Doctrine2 da para mucho ms (parece que un curso de Doctrine2 no vendra mal ;-)).
Cuando precises ms detalles para construir esa fastidiosa query que necesitas para tu
ltimo proyecto, tendrs que consultar la documentacin oficial sobre DQL.
Comienza por crear una ruta llamada j a m n _ O R M _ D Q L y asociala a la accin d q l A c t i o n ( ) del
controlador e s t u d i o O R M . Seguro que ya sabes cmo se hace esto no?.
Comenzamos por algo sencillo; recuperar todas las notas, es decir, lo mismo que se
conseguira con el mtodo f i n d A l l ( ) del repositorio;
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o O R M C o n t r o l l e r . p h p
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n d q l A c t i o n ( )
4 {
5 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
6
7 / / T o d a s l a n o t a s u s a n d o D Q L
8 $ q u e r y = $ e m - > c r e a t e Q u e r y (
9 ' S E L E C T n F R O M J A M N o t a s F r o n t e n d B u n d l e : N o t a n ' ) ;
1 0
1 1 $ n o t a s T o d a s = $ q u e r y - > g e t R e s u l t ( ) ;
Ms all de los mtodos findBy. El lenguaje DQL
132
1 2
1 3 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r N o t a s . h t m l . t w i g ' ,
1 4 a r r a y ( ' n o t a s ' = > $ n o t a s T o d a s ) ) ;
1 5
1 6 }
En la lnea 5 obtenemos un objeto E n t i t y M a n a g e r a travs de nuestro todopoderoso
Contenedor de Servicios. Entonces creamos una consulta en DQL haciendo uso de su
mtodo c r e a t e Q u e r y ( ) (lneas 8-9). Finalmente obtenemos la coleccin de objetos
correspondiente a la consulta en la lnea 11. Como la accin recupera una coleccin de
notas, lo mismo que la accin r e c u p e r a r N o t a s A c t i o n ( ) , hemos reutilizado la plantilla
J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r N o t a s . h t m l . t w i g para pintar el
resultado.
Seguro que el prrafo anterior no te ha servido de mucho, el cdigo es bastante
autoexplicativo. Lo que nos interesa es analizar la sintaxis de la consulta DQL. Si conoces
aunque sea un poco de SQL (esperemos que as sea) encontrs muchas similitudes entre el
DQL y el SQL. Y las hay. Pero an as no te dejes llevar demasiado por los parecidos y no
pienses en trminos de columnas. Debemos pensar en recuperar objetos. En la consulta
anterior simplemente se dice que se seleccionen todos los objetos
J a z z y w e b M e n t o r N o t a s B u n d l e : N o t a identificadas por el alias n . Fjate que no se pide ninguna
columna en concreto, pues ese trmino no tiene sentido en el contexto de los objetos. Se
devolvern, simplemente, objetos notas, con todas sus cosas. Una vez que los tengas en las
manos, puedes pedirles lo que quieras, o ms bien, lo que te permitan, usando sus mtodos
getters. La consulta devuelve todos los objetos pues no se le ha lmitado con ninguna
condicin. Vamos a por una un poquito ms compleja; todas las notas que tienen en el ttulo
la palabra "ipsum".
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n d q l A c t i o n ( )
4 {
5 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
6 / / T o d a s l a s n o t a s q u e t i e n e n e n e l t t u l o l a p a l a b r a " i p s u m " c o n D Q L
7 $ q u e r y = $ e m - > c r e a t e Q u e r y (
8 " S E L E C T n F R O M J A M N o t a s F r o n t e n d B u n d l e : N o t a n w h e r e n . t e x t o L I K E : t e r m i n o " )
9 - > s e t P a r a m e t e r ( ' t e r m i n o ' , ' % i p s u m % ' ) ;
1 0
1 1 $ n o t a s I p s u m = $ q u e r y - > g e t R e s u l t ( ) ;
1 2
1 3 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r N o t a s . h t m l . t w i g ' ,
1 4 a r r a y ( ' n o t a s ' = > $ n o t a s I p s u m ) ) ;
1 5 }
Ahora pedimos (S E L E C T ) todos los objetos J A M N o t a s F r o n t e n d B u n d l e : N o t a identificados por
el alias n , que tienen en su propiedad t e x t o el trmino i p s u m . La forma de referenciar una
propiedad del objeto es mediante la sintaxis punto (n . t e x t o ). La clausula L I K E tiene el
mismo significado que en SQL. Y por ltimo se ha optado por parametrizar la consulta. Para
ello se precede con el carcter : el nombre del parmetro y posteriormente se establece su
valor mediante el mtodo s e t P a r a m e t e r ( ) . De nuevo reutilizamos la plantilla
J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r N o t a s . h t m l . t w i g .
Vamos a por otro ejemplo. Todas las notas del usuario a l b e r t o .
Ms all de los mtodos findBy. El lenguaje DQL
133
Nota
Para poder probar lo que viene a continuacin, introduce en la base de datos con tu
cliente MySQL favorito, un usuario con username a l b e r t o , algunas etiquetas
asociadas a ese usuario y algunas notas asociadas al usuario y etiquetadas con
algunas de las etiquetas anteriores.
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n d q l A c t i o n ( )
4 {
5 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
6
7 / / T o d a s l a s n o t a s d e l u s u a r i o a l b e r t o c o n D Q L
8 $ q u e r y = $ e m - > c r e a t e Q u e r y (
9 " S E L E C T n F R O M J A M N o t a s F r o n t e n d B u n d l e : N o t a n J O I N n . u s u a r i o u W H E R E
1 0 u . u s e r n a m e = : u s e r n a m e " )
1 1 - > s e t P a r a m e t e r ( ' u s e r n a m e ' , ' a l b e r t o ' ) ;
1 2
1 3 $ n o t a s A l b e r t o = $ q u e r y - > g e t R e s u l t ( ) ;
1 4
1 5 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r N o t a s . h t m l . t w i g ' ,
1 6 a r r a y ( ' n o t a s ' = > $ n o t a s A l b e r t o ) ) ;
1 7 }
De nuevo queremos recuperar objetos J A M N o t a s F r o n t e n d B u n d l e : N o t a . Y es lo que pedimos
en la S E L E C T . Pero esta vez queremos filtrar por la propiedad u s u a r i o , y resulta que esta
propiedad es a su vez un objeto del tipo J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o . Por ello
debemos usar la clausula J O I N , para "unir" unos objetos con otros. A los objetos n . u s u a r i o
los referenciamos con el alias u el cual utilizamos a continuacin para establecer la
condicin W H E R E u . u s e r n a m e = : u s e r n a m e , es decir "donde la propiedad ``username`` del
objeto usuario sea lo especificado por el parmetro ``:username``". Este ltimo parmetro,
como hemos visto antes, se establece a travs del mtodo s e t P a r a m e t e r ( ) .
Vamos aumentando la complejidad de la consulta. Ahora vamos a recuperar todas las notas
del usuario a l b e r t o que tienen la etiqueta p h p .
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n d q l A c t i o n ( )
4 {
5 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
6 / / T o d a s l a n o t a s d e l u s u a r i o ' a l b e r t o ' q u e t i e n e n e t i q u e t a ' p h p ' c o n D Q L
7 $ q u e r y = $ e m - > c r e a t e Q u e r y (
8 " S E L E C T n F R O M J A M N o t a s F r o n t e n d B u n d l e : N o t a n
9 J O I N n . u s u a r i o u
1 0 J O I N n . e t i q u e t a s e
1 1 W H E R E e . t e x t o = : t e x t o A N D u . u s e r n a m e = : u s e r n a m e " )
1 2 - > s e t P a r a m e t e r s ( a r r a y ( ' t e x t o ' = > ' p h p ' , ' u s e r n a m e ' = > ' a l b e r t o ' ) ) ;
1 3
1 4 $ n o t a s A l b e r t o = $ q u e r y - > g e t R e s u l t ( ) ;
1 5
1 6 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o O R M : r e c u p e r a r N o t a s . h t m l . t w i g ' ,
1 7 a r r a y ( ' n o t a s ' = > $ n o t a s A l b e r t o ) ) ;
1 8 }
Ms all de los mtodos findBy. El lenguaje DQL
134
De nuevo pedimos en el S E L E C T los objetos de tipo J A M N o t a s F r o n t e n d B u n d l e : N o t a . Esta
vez queremos unirlos con los objectos J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o y
J A M N o t a s F r o n t e n d B u n d l e : E t i q u e t a , ambos relacionados con los objetos de tipo
J A M N o t a s F r o n t e n d B u n d l e : N o t a a travs de sus propiedades u s u a r i o y e t i q u e t a s
respectivamente. Eso es lo que se hace en las lneas 9 y 10 del cdigo anterior, donde
adems se identifican con los alias u y e . Luego, en la lnea 11 se establecen las condiciones
(W H E R E ) sobre las propiedades t e x t o y u s e r n a m e de tales objetos. Cuando utilizamos ms
de un parmetro en la consulta, como es el caso en esta ocasin, utilizamos el mtodo
s e t P a r a m e t e r s ( ) , el cual recibe como argumento un array asociativo con los nombre y
valores de los parmetros.
Fjate en esta ltima consulta. En ningn momento se ha hecho un J O I N sobre la tabla
intermedia que relaciona las notas con las etiquetas. Sencillamente por que DQL no trata de
tablas y columnas. Y no hay nada en el modelo de datos (entidades) parecido a una tabla
intermedia. De hecho, repetimos hasta correr el riesgo de que nos llamen "pesao", que la
palabra "tabla" no tiene sentido en DQL. Olvdala. Lo que tiene sentido es que una nota
puede tener asociadas una o muchas etiquetas y un usuario. Y eso est representado en la
entidad a travs de sus propiedades e t i q u e t a s y u s u a r i o s . Y es en eso en lo que debemos
pensar.
En resumen: En las sentencias DQL pedimos la recuperacin (S E L E C T ) de objetos de alguno
de los tipos de nuestras entidades y los referenciamos con un alias (F R O M ). Posteriormente
podemos unir (J O I N ) las propiedades que, a su vez sean objetos, con las entidades
correspondientes, y filtrar (W H E R E ) por el valor de las propiedades escalares (las que no son
objetos).
Son muchas las posibilidades del DQL, incluyendo clausulas para borrar (D E L E T E ) y actualizar
(U P D A T E ) objetos que vienen muy bien cuando queremos hacer una operacin de borrado o
actualizado en masa. Sin embargo en la mayor parte de las ocasiones, los mtodos
p e r s i s t ( ) , d e l e t e ( ) y f l u s h ( ) son suficiente para borrar y actualizar objetos.
Organizamos las consultas en repositorios
Nada nos impide utilizar en las acciones del controlador sentencias DQL para recuperar
objetos. Tal y como lo hemos hecho en la seccin anterior. Nada salvo las buenas prcticas
de programacin y el deseo de que cada cosa est en su sitio, localizable, testeable, fcil de
mantener y sin contaminar la lgica de control de las acciones con sentencias DQL que, por
muy bonitas que sean, no dejan de ser otro lenguaje embebido en el cdigo principal.
Este es el papel de los repositorios asociados a las entidades. Ya hemos visto que cada
entidad cuenta con un repositorio por defecto con una serie de funciones f i n d B y que
resuelven muchos casos. Podemos extender dicho repositorio con nuevas funciones
desarrolladas por nosotros que encapsulan consultas DQL, y utilizarlas en las acciones de la
misma forma que hemos utilizado en secciones anteriores los mtodos f i n d B y .
Veremos como se hace esto aprovechando la explicacin para completar el modelo de
nuestra aplicacin MentorNotas. Una de las cosas que necesitaremos es recuperar todas las
notas de un usuario que tengan asociada una determinada etiqueta. Abre el archivo
s r c / J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / E n t i t y / N o t a R e p o s i t o r y . p h p y
aade el siguiente cdigo:
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y ;
4
5 u s e D o c t r i n e \ O R M \ E n t i t y R e p o s i t o r y ;
6
Organizamos las consultas en repositorios
135
7 / * *
8 * N o t a R e p o s i t o r y
9 *
1 0 * T h i s c l a s s w a s g e n e r a t e d b y t h e D o c t r i n e O R M . A d d y o u r o w n c u s t o m
1 1 * r e p o s i t o r y m e t h o d s b e l o w .
1 2 * /
1 3 c l a s s N o t a R e p o s i t o r y e x t e n d s E n t i t y R e p o s i t o r y
1 4 {
1 5
1 6 p u b l i c f u n c t i o n f i n d B y U s u a r i o A n d E t i q u e t a ( $ u s u a r i o , $ e t i q u e t a )
1 7 {
1 8 i f ( $ u s u a r i o i n s t a n c e o f U s u a r i o ) {
1 9 $ u s e r n a m e = $ u s u a r i o - > g e t U s e r n a m e ( ) ;
2 0 } e l s e {
2 1 $ u s e r n a m e = $ u s u a r i o ;
2 2 }
2 3
2 4 i f ( $ e t i q u e t a i n s t a n c e o f E t i q u e t a ) {
2 5 $ i d _ e t i q u e t a = $ e t i q u e t a - > g e t I d ( ) ;
2 6 } e l s e {
2 7 $ i d _ e t i q u e t a = $ e t i q u e t a ;
2 8 }
2 9
3 0 $ q u e r y = $ t h i s - > g e t E n t i t y M a n a g e r ( ) - > c r e a t e Q u e r y (
3 1 " S E L E C T n F R O M J A M N o t a s F r o n t e n d B u n d l e : N o t a n
3 2 J O I N n . e t i q u e t a s e
3 3 J O I N n . u s u a r i o u
3 4 W H E R E e . i d = : i d _ e t i q u e t a a n d u . u s e r n a m e = : u s e r n a m e " )
3 5 - > s e t P a r a m e t e r s ( a r r a y ( ' u s e r n a m e ' = > $ u s e r n a m e , ' i d _ e t i q u e t a ' = > $ i d _ e t i q u e t a ) ) ;
3 6
3 7 r e t u r n $ q u e r y - > g e t R e s u l t ( ) ;
3 8 }
3 9
4 0 }
La funcin f i n d B y U s u a r i o A n d E t i q u e t a ( ) recibe dos argumentos. Se ha construido de
manera que puedan ser tanto objetos como cadenas. Las condiciones de las lneas 18-22 y
24-28, se encargan de extraer los trminos de bsqueda correcto segn se hayan pasado
objetos o cadenas. La parte importante est en las lneas 30-35, donde se construye la
consulta DQL de la misma manera que hemos hecho en la seccin anterior. La diferencia es
que ahora el objeto E n t i t y M a n a g e r se obtiene directamente del objeto N o t a R e p o s i t o r y , es
decir, no es necesario utilizar el Contenedor de Servicios.
Nota
Ni es necesario, ni es posible, ya que este objeto no proporciona ninguna manera de
acceder al Contenedor de Servicios de Symfony2
Finalmente (lnea 37) se devuelve la coleccin de notas que cumplen el criterio de bsqueda.
Ahora ya podemos utilizar esta funcin en cualquier accin de un controlador de la siguiente
manera:
< ? p h p
. . .
$ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' )
- > f i n d B y U s u a r i o A n d E t i q u e t a ( $ u s u a r i o , $ e t i q u e t a ) ;
. . .
Organizamos las consultas en repositorios
136
Otras funciones que necesitaremos en nuestro repositorio sern las que nos permitan buscar
las notas de un usuario ordenadas por fechas y las notas de un usuario que contienen un
determinado trmino de bsqueda. Esto es lo que hacen los siguientes mtodos del
repositorio de la entidad N o t a .
< ? p h p
. . .
c l a s s N o t a R e p o s i t o r y e x t e n d s E n t i t y R e p o s i t o r y
{
. . .
p u b l i c f u n c t i o n f i n d B y U s u a r i o O r d e r e d B y F e c h a ( $ u s u a r i o )
{
$ q u e r y = $ t h i s - > g e t E n t i t y M a n a g e r ( ) - > c r e a t e Q u e r y (
" S E L E C T n F R O M J A M N o t a s F r o n t e n d B u n d l e : N o t a n
J O I N n . u s u a r i o u
W H E R E u . u s e r n a m e = : u s e r n a m e O R D E R B Y n . f e c h a D E S C " )
- > s e t P a r a m e t e r s ( a r r a y ( ' u s e r n a m e ' = > $ u s u a r i o - > g e t U s e r n a m e ( ) ) ) ;
r e t u r n $ q u e r y - > g e t R e s u l t ( ) ;
}
p u b l i c f u n c t i o n f i n d B y U s u a r i o A n d T e r m i n o ( $ u s u a r i o , $ t e r m i n o )
{
i f ( $ u s u a r i o i n s t a n c e o f U s u a r i o ) {
$ u s e r n a m e = $ u s u a r i o - > g e t U s e r n a m e ( ) ;
} e l s e {
$ u s e r n a m e = $ u s u a r i o ;
}
$ q u e r y = $ t h i s - > g e t E n t i t y M a n a g e r ( ) - > c r e a t e Q u e r y (
" S E L E C T n F R O M J A M N o t a s F r o n t e n d B u n d l e : N o t a n
J O I N n . u s u a r i o u
W H E R E u . u s e r n a m e = : u s e r n a m e A N D ( n . t e x t o L I K E : t e r m i n o O R n . t i t u l o L I K E : t e r m i n o ) " )
- > s e t P a r a m e t e r s ( a r r a y ( ' u s e r n a m e ' = > $ u s e r n a m e , ' t e r m i n o ' = > ' % ' . $ t e r m i n o . ' % ' ) ) ;
r e t u r n $ q u e r y - > g e t R e s u l t ( ) ;
}
}
Tambin necesitaremos la coleccin de etiquetas de un determinado usuario. La funcin
f i n d B y U s u a r i o O r d e r e d B y T e x t o ( ) del repositorio de la entidad E t i q u e t a resuelve este
problema:
s r c / J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / E n t i t y / E t i q u e t a R e p o s i t o r y . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y ;
4
5 u s e D o c t r i n e \ O R M \ E n t i t y R e p o s i t o r y ;
6
7 / * *
8 * E t i q u e t a R e p o s i t o r y
9 *
1 0 * T h i s c l a s s w a s g e n e r a t e d b y t h e D o c t r i n e O R M . A d d y o u r o w n c u s t o m
1 1 * r e p o s i t o r y m e t h o d s b e l o w .
1 2 * /
1 3 c l a s s E t i q u e t a R e p o s i t o r y e x t e n d s E n t i t y R e p o s i t o r y
1 4 {
1 5 p u b l i c f u n c t i o n f i n d B y U s u a r i o O r d e r e d B y T e x t o ( $ u s e r n a m e )
1 6 {
1 7 $ q u e r y = $ t h i s - > g e t E n t i t y M a n a g e r ( ) - > c r e a t e Q u e r y (
1 8 " S E L E C T e F R O M J A M N o t a s F r o n t e n d B u n d l e : E t i q u e t a e
Organizamos las consultas en repositorios
137
1 9 J O I N e . u s u a r i o u w h e r e u . i d = : u s e r n a m e O R D E R B Y e . t e x t o A S C " )
2 0 - > s e t P a r a m e t e r s ( a r r a y ( ' u s e r n a m e ' = > $ u s e r n a m e ) ) ;
2 1
2 2 r e t u r n $ q u e r y - > g e t R e s u l t ( ) ;
2 3 }
2 4 }
Incorporamos el modelo a la aplicacin
A lo largo de la unidad hemos procurado ofrecer algo ms que una introduccin a Doctrine2.
El objetivo principal ha sido ensearte como puedes utilizarlo en tus proyectos y, a la vez,
construir un modelo completamente funcional para nuestra aplicacin. Ahora que lo tenemos
es el momento de incorporarlo al cdigo del controlador N o t a s C o n t r o l l e r . Recuerda que la
funcin d a m e E t i q u e t a s Y N o t a s ( ) de este controlador es la que se encarga de calcular la
coleccin de etiquetas, notas y la nota seleccionada que debe mostrarse en el detalle, en
funcin del filtro de bsqueda que haya definido en la sesin. Hasta ahora estbamos
usando un modelo sin funcionalidades que nos permiti implementar la lgica de control.
Ahora vamoso a usar en su lugar el modelo que hemos desarrollado a lo largo de esta
interminable unidad. El cdigo que proponemos es el siguiente:
1 < ? p h p
2 . . .
3 p r o t e c t e d f u n c t i o n d a m e E t i q u e t a s Y N o t a s ( )
4 {
5 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
6 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
7
8 $ u s u a r i o = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o ' )
9 - > f i n d O n e B y U s e r n a m e ( ' a l b e r t o ' ) ;
1 0
1 1
1 2 $ b u s q u e d a _ t i p o = $ s e s s i o n - > g e t ( ' b u s q u e d a . t i p o ' ) ;
1 3
1 4 $ b u s q u e d a _ v a l o r = $ s e s s i o n - > g e t ( ' b u s q u e d a . v a l o r ' ) ;
1 5
1 6 / / E t i q u e t a s . S e p i l l a n t o d a s
1 7 $ e t i q u e t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : E t i q u e t a ' ) - >
1 8 f i n d B y U s u a r i o O r d e r e d B y T e x t o ( $ u s u a r i o ) ;
1 9
2 0 / / N o t a s . S e p i l l a n s e g n e l f i l t r o a l m a c e n a d o e n l a s e s i n
2 1 i f ( $ b u s q u e d a _ t i p o = = ' p o r _ e t i q u e t a ' & & $ b u s q u e d a _ v a l o r ! = ' t o d a s ' ) {
2 2 $ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - >
2 3 f i n d B y U s u a r i o A n d E t i q u e t a ( $ u s u a r i o , $ b u s q u e d a _ v a l o r ) ;
2 4 } e l s e i f ( $ b u s q u e d a _ t i p o = = ' p o r _ t e r m i n o ' ) {
2 5 $ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - >
2 6 f i n d B y U s u a r i o A n d T e r m i n o ( $ u s u a r i o , $ b u s q u e d a _ v a l o r ) ;
2 7 } e l s e {
2 8 $ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - >
2 9 f i n d B y U s u a r i o O r d e r e d B y F e c h a ( $ u s u a r i o ) ;
3 0 }
3 1
3 2
3 3 $ n o t a _ s e l e c i o n a d a _ i d = $ s e s s i o n - > g e t ( ' n o t a . s e l e c c i o n a d a . i d ' , ' 1 ' ) ;
3 4
Incorporamos el modelo a la aplicacin
138
3 5 $ n o t a _ s e l e c c i o n a d a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - >
3 6 f i n d O n e B y I d ( $ n o t a _ s e l e c i o n a d a _ i d ) ;
3 7
3 8
3 9 r e t u r n a r r a y ( $ e t i q u e t a s , $ n o t a s , $ n o t a _ s e l e c c i o n a d a ) ;
4 0 }
La lneas 6 y 7 recuperan el servicio s e s s i o n y el E n t i t y M a n a g e r . La lnea 7 se declara el
usuario que ha iniciado la sesin en la aplicacin. Como an no hemos tratado el tema del
inicio de sesin y la autentificacin, proponemos uno cualquiera "a capn". Es decir, usamos
de nuevo un objeto mock. Cuando, dentro de dos unidades, hayamos aprendido a obtener el
usario que ha iniciado la sesin, cambiaremos este comportamiento sin funcionalidad por el
correcto. Por lo pronto nos vale as.
Las lneas 14-15 obtienen la coleccin de etiquetas completa del usuario que ha iniciado la
sesin. Para lo cual utilizamos la funcin f i n d B y U s u a r i o O r d e r e d B y T e x t o del repositorio de
la entidad E t i q u e t a .
En las lneas 18-27 se lleva a cabo la bsqueda de notas en funcin del filtro que est
almacenado en la sesin. El cdigo es autoexplicativo. El nico detalle digno de ser
comentado es que cuando buscamos por etiqueta, y el valor de bsqueda es t o d a s , es
equivalente a que no haya ningn filtro en la sesin y, por tanto, haya que recuperar todas
las notas del usuario.
En las lneas 30-33 se recupera la nota seleccionada que ser mostrada en el detalle. Y
finalmente, en la lnea 36 de devuelve el resultado de todos los clculos.
Prueba la aplicacin. Con esto ya tenemos gran parte de la lgica de negocio de la aplicacin
implementada.
La unidad en chuletas
Construir entidades
p h p a p p / c o n s o l e g e n e r a t e : d o c t r i n e : e n t i t y
Genera en el directorio E n t i t y del bundle la entidad cuyo nombre especifiquemos y,
opcionalmente una clase R e p o s i t o r y donde podemos escribir consultas que obedezcan a
distintos criterios.
Configuracin de la base de datos
Se realiza en el fichero a p p / c o n f i g / c o n f i g . y m l . Pero all puedes ver que se utilizan los
parmetros % d a t a b a s e _ d r i v e r % , % d a t a b a s e _ h o s t % , % d a t a b a s e _ p o r t % , % d a t a b a s e _ n a m e % ,
% d a t a b a s e _ u s e r % , % d a t a b a s e _ p a s s w o r d % . que a su vez se encuentran en el fichero
a p p / c o n n f i g / p a r a m e t e r s . i n i .
Crear la base de datos y las tablas
1. Crear la base de datos
p h p a p p / c o n s o l e d o c t r i n e : d a t a b a s e : c r e a t e
2. Crear el esquema (conjunto de tablas y sus relaciones) de la base de datos
La unidad en chuletas
139
p h p a p p / c o n s o l e d o c t r i n e : s c h e m a : c r e a t e
Las tablas se crean segn lo especificado en los metadatos de las entidades. Estos
metadatos se pueden especificar mediante anotaciones o ficheros de configuracin YAML,
XML o PHP.
3. Actualizar el esquema
p h p a p p / c o n s o l e d o c t r i n e : s c h e m a : u p d a t e - - f o r c e
Persistir un objeto en la base de datos
$ n o t a 1 = n e w N o t a ( ) ;
$ n o t a 1 - > s e t F e c h a ( n e w \ D a t e T i m e ( ) ) ;
$ n o t a 1 - > s e t T i t u l o ( ' N o t a d e p r u e b a 1 ' ) ;
$ n o t a 1 - > s e t T e x t o ( ' T e x t o p a r a l a n o t a d e p r u e b a s 1 ' ) ;
$ n o t a 1 - > s e t P a t h ( ' r u t a / a / n o t a 1 ' ) ;
$ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
/ / L e e n v i a m o s a d i c h o s e r v i c i o e l o b j e t o q u e q u e r e m o s p e r s i s t i r ( n o s e
/ / r e a l i z a q u e r y a n
$ e m - > p e r s i s t ( $ n o t a 1 ) ;
$ e m - > f l u s h ( ) ; / / s e l a n z a a l f i n a l , c u a n d o t o d o s l o s o b j e t o s h a y a n s i d o p e r s i s t i d o s
Recuperar objetos con mtodos f i n d
$ i d = $ t h i s - > g e t R e q u e s t ( ) - > g e t ( ' i d ' ) ;
$ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
$ n o t a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d ( $ i d ) ;
$ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d B y T i t u l o ( ' e l t i t u l o ' ) ;
$ n o t a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d O n e B y T i t u l o ( ' e l t i t u l o ' ) ;
$ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d B y L o Q u e Q u i e r a s Y C o m o Q u i e r a s ( $ i d ) ;
Los mtodos f i n d B y devuelven colecciones y los mtodos f i n d O n e B y recuperan un solo
objeto. Atencin a esto que es causa comn de fallos. Por otro lado, en los repositorios
podemos implementar todos los mtodos f i n d que queramos y como queramos
Borrar un objeto
< ? p h p
. . .
$ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
$ n o t a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d ( $ i d ) ;
i f ( ! $ n o t a ) {
t h r o w $ t h i s - > c r e a t e N o t F o u n d E x c e p t i o n ( ' N o e x i s t e e s a n o t a ' ) ;
}
$ e m - > r e m o v e ( $ n o t a ) ;
Persistir un objeto en la base de datos
140
$ e m - > f l u s h ( ) ;
Especificacin de las relaciones entre entidades (forma simple y prctica)
1. One to Many
< ? p h p
. . .
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
c l a s s U s u a r i o {
. . .
/ * *
* @ O R M \ O n e T o M a n y ( t a r g e t E n t i t y = " N o t a " , m a p p e d B y = " u s u a r i o " )
* /
p r i v a t e $ n o t a s ;
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
$ t h i s - > n o t a s = n e w A r r a y C o l l e c t i o n ( ) ;
}
2. Many to One
< ? p h p
. . .
c l a s s N o t a {
. . .
/ * *
* @ O R M \ M a n y T o O n e ( t a r g e t E n t i t y = " U s u a r i o " )
* /
p r i v a t e $ u s u a r i o ;
. . .
2. Many to Many
< ? p h p
u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
. . .
c l a s s N o t a {
. . .
/ * *
* @ O R M \ M a n y T o M a n y ( t a r g e t E n t i t y = " E t i q u e t a " , i n v e r s e d B y = " n o t a s " )
* /
p r i v a t e $ e t i q u e t a s ;
p u b l i c f u n c t i o n _ _ c o n s t r u c t ( )
{
Especificacin de las relaciones entre entidades (forma simple y prctica)
141
$ t h i s - > e t i q u e t a s = n e w A r r a y C o l l e c t i o n ( ) ;
}
Implementacin de un mtodo de repositorio
< ? p h p
. . .
p u b l i c f u n c t i o n f i n d B y U s u a r i o A n d T e r m i n o ( $ u s u a r i o , $ t e r m i n o )
{
i f ( $ u s u a r i o i n s t a n c e o f U s u a r i o ) {
$ u s e r n a m e = $ u s u a r i o - > g e t U s e r n a m e ( ) ;
} e l s e {
$ u s e r n a m e = $ u s u a r i o ;
}
$ q u e r y = $ t h i s - > g e t E n t i t y M a n a g e r ( ) - > c r e a t e Q u e r y (
" S E L E C T n F R O M J A M N o t a s F r o n t e n d B u n d l e : N o t a n
J O I N n . u s u a r i o u
W H E R E u . u s e r n a m e = : u s e r n a m e A N D ( n . t e x t o L I K E : t e r m i n o O R n . t i t u l o L I K E : t e r m i n o ) " )
- > s e t P a r a m e t e r s ( a r r a y ( ' u s e r n a m e ' = > $ u s e r n a m e , ' t e r m i n o ' = > ' % ' . $ t e r m i n o . ' % ' ) ) ;
r e t u r n $ q u e r y - > g e t R e s u l t ( ) ;
}
. . .
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll para recorrer
todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Implementacin de un mtodo de repositorio
142
Unidad 8: Desarrollo de la aplicacin MentorNotas (IV).
Validacin y Formularios
Con lo que llevamos visto hasta el momento ya podemos crear pginas web que presenten
formularios al usuario y recoger los datos que este enva para que nuestra aplicacin los
procese. Lo hemos hecho en la unidad 3. Sin embargo con Symfony2 se puede hacer mejor.
El servicio de formularios junto con el de validacin facilitan enormente esta tarea, a la vez
que ayudan a tener un cdigo mejor organizado y legible.
El propsito del servicio de validacin es comprobar si los atributos de un objeto cumplen o
no una sere de reglas que hemos definido previamente. Mientras que la finalidad del
servicio de formularios es la de crear cmodamente formularios que, por una parte sern
enviados en un documento HTML en la respuesta, y por otra servirn para asociar los datos
que el cliente enva en la peticin HTTP y comprobar su validez antes de realizar ninguna
operacin. Para esta ltima funcin el servicio de formulario usa (depende de) el servicio de
validacin.
Como puedes imaginar despus de todo lo que llevamos, ambos servicios son accesibles a
travs del contenedor de servicios. El funcionamientos bsico de los dos sigue el mismo
patrn que el servicio de persistencia: partimos de un objeto plano de PHP que deseamos
procesar (persistir, validad o crear un formulario), lo inyectamos, es decir, lo pasamos como
argumento al servicio en cuestin, y este ltimo se encarga de procesarlo (persistirlo,
validarlo o crear un formulario). Para que el servicio pueda realizar su funcin
adecuadamente necesita cierta informacin que se le proporciona a travs de ficheros de
configuracin (YAML, XML, PHP) o anotaciones.
Nota
Un objeto planto es un objeto que no tiene nada que ver con el framework sino ms
bien con nuestra lgica de negocio. Nuestro objeto N o t a es un buen ejemplo.
El servicio de validacin
Cuando aplicamos el servicio validador sobre un objeto, este lee la/s regla/s que se han
asociado a sus atributos mediante la configuracin o las anotaciones, verificando si dicho
dato las cumple. Si el resultado es negativo, el validador devuelve un array de errores que
puedes utilizar para, por ejemplo, mostrarlos al usuario en la respuesta HTTP.
La mejor manera de ver el funcionamiento de este servicio es mediante un ejemplo. Vamos
a realizar la validacin de datos sobre nuestro objeto U s u a r i o . Recordemos los atributos de
este objeto:
1 < ? p h p
2 . . .
3 c l a s s U s u a r i o
4 {
5
6 p r i v a t e $ i d ;
7 p r i v a t e $ n o m b r e ;
8 p r i v a t e $ a p e l l i d o s ;
9 p r i v a t e $ u s e r n a m e ;
1 0 p r i v a t e $ s a l t ;
Unidad 8: Desarrollo de la aplicacin MentorNotas (IV). Validacin y Formularios
143
1 1 p r i v a t e $ p a s s w o r d ;
1 2 p r i v a t e $ e m a i l ;
1 3 p r i v a t e $ i s A c t i v e ;
1 4 p r i v a t e $ t o k e n R e g i s t r o ;
1 5
1 6 . . .
Nota
Para centrarnos en lo que nos interesa y ahorrar espacio hemos eliminado las
anotaciones para Doctrine. No obstante tu no tienes que hacer esto. Mantn las
anotaciones de este objeto, son fundamentales para que funcione Doctrine!
Lo primero que tenemos que hacer es definir (configurar) las reglas que aplicaremos a cada
uno de sus atributos. Symfony2 dispone de un amplio conjunto de reglas (constraints) que
puedes utilizar. Adems tambin permite que definas las tuyas propias con lo que las
posibilidades de validacin son totales. En la documentacin oficial de Symfony2 puedes ver
todas estas reglas de validacin junto con su manera de uso.
Utilizaremos las reglas que nos vayan haciendo falta a medida que avanzamos en la unidad.
Pretendemos que los atributos del objeto U s u a r i o satisfagan los siguientes requisitos:
n o m b r e No se puede quedar vaco y no puede tener ms de 255 caracteres
a p e l l i d o s No puede tener ms de 255 caracteres
u s e r n a m e No se puede quedar vaco y no puede tener ms de 20 caracteres
alfanumricos o guiones
s a l t No puede tener ms de 255 caracteres
p a s s w o r d No se puede quedar vaco y no puede tener ms de 20 caracteres
alfanumricos o guiones
e m a i l Debe ser una direccin de email vida
i s A c t i v e Debe ser un valor booleano
t o k e n R e g i s t r o Nada en especial
Vamos a utilizar como mtodo de configuracin las anotaciones, ya que nos parecen ms
tiles, pues podemos verlas directamente en el mismo fichero fuente donde se declara el
objeto.
Nota
Para utilizar anotaciones hay que indicarlo explicitamente en el fichero
a p p / c o n f i g / c o n f i g . y m l
. . .
f r a m e w o r k :
v a l i d a t i o n : { e n a b l e _ a n n o t a t i o n s : t r u e }
Unidad 8: Desarrollo de la aplicacin MentorNotas (IV). Validacin y Formularios
144
. . .
Adems hemos de usar el espacio de nombres
S y m f o n y \ C o m p o n e n t \ V a l i d a t o r \ C o n s t r a i n t s en el fichero fuente donde se declare el
objeto que vamos a validar :
< ? p h p
. . .
u s e S y m f o n y \ C o m p o n e n t \ V a l i d a t o r \ C o n s t r a i n t s a s A s s e r t ;
. . .
El siguiente trozo de cdigo muestra como se aplicaran, mediante anotaciones, las reglas
necesarias para satisfacer los requisitos de la tabla anterior.
1 < ? p h p
2 . . .
3 u s e S y m f o n y \ C o m p o n e n t \ V a l i d a t o r \ C o n s t r a i n t s a s A s s e r t ;
4 . . .
5 / * *
6 * @ A s s e r t \ N o t B l a n k ( )
7 * @ A s s e r t \ M a x L e n g t h ( 2 5 5 )
8 * /
9 p r i v a t e $ n o m b r e ;
1 0
1 1 / * *
1 2 * @ A s s e r t \ M a x L e n g t h ( 2 5 5 )
1 3 * /
1 4 p r i v a t e $ a p e l l i d o s ;
1 5
1 6 / * *
1 7 * @ A s s e r t \ N o t B l a n k ( )
1 8 * @ A s s e r t \ M a x L e n g t h ( 2 5 5 )
1 9 * @ A s s e r t \ R e g e x (
2 0 * p a t t e r n = " / ^ [ \ w - ] + $ / " ,
2 1 * m e s s a g e = " E l n o m b r e d e u s u a r i o n o p u e d e c o n t e n e r m s q u e c a r a c t e r e s a l f a n u m r i c o s y g u i o n e s " )
2 2 * /
2 3 p r i v a t e $ u s e r n a m e ;
2 4
2 5 / * *
2 6 * @ A s s e r t \ M a x L e n g t h ( 2 5 5 )
2 7 * /
2 8 p r i v a t e $ s a l t ;
2 9
3 0 / * *
3 1 * @ A s s e r t \ N o t B l a n k ( )
3 2 * @ A s s e r t \ M a x L e n g t h ( 2 5 5 )
3 3 * @ A s s e r t \ R e g e x (
3 4 * p a t t e r n = " / ^ [ \ w - ] + $ / " ,
3 5 * m e s s a g e = " E l p a s s w o r d n o p u e d e c o n t e n e r m s q u e c a r a c t e r e s a l f a n u m r i c o s y g u i o n e s " )
3 6 * /
3 7 p r i v a t e $ p a s s w o r d ;
3 8
3 9 / * *
4 0
4 1 * @ A s s e r t \ N o t B l a n k ( )
4 2 * @ A s s e r t \ M a x L e n g t h ( 2 5 5 )
4 3 * @ A s s e r t \ E m a i l (
Unidad 8: Desarrollo de la aplicacin MentorNotas (IV). Validacin y Formularios
145
4 4 * m e s s a g e = " L a d i r e c c i n ' { { v a l u e } } ' n o e s v l i d a . " )
4 5 * /
4 6 p r i v a t e $ e m a i l ;
4 7
4 8 / * *
4 9 * @ A s s e r t \ T y p e ( t y p e = " b o o l " , m e s s a g e = " E l v a l o r { { v a l u e } } d e b e s e r { { t y p e } } . " )
5 0 * /
5 1 p r i v a t e $ i s A c t i v e ;
Nota
Estas anotaciones se aaden a las que ya tenamos definidas para el servicio
Doctrine2. Cada servicio conoce qu anotaciones del objeto debe utilizar. No las
mostramos aqu por que nos estamos centrando exclusivamente en la validacin. Al
final de la unidad presentaremos el cdigo fuente de la clase N o t a s completo, las
anotaciones para su configuracin.
Un ejercicio que deberas hacer es contrastar el conjunto de anotaciones que hemos
utilizado con la documentacin oficial de las reglas de validacin. Como ves no todas las
reglas tienen el mismo grado de complejidad, algunas (N o t B l a n k por ejemplo) son de
aplicacin inmediatas, mientras que otras (R e g e x por ejemplo) requieren ms datos para ser
definidas. Por otro lado, todas las reglas admiten la opcin m e s s a g e para definir el mensaje
generado cuando la regla no se cumple.
Entre todas las reglas que hemos utilizado, merece la pena destacar R e g e x , que permite
aplicar expresiones regulares para validar los datos.
Una vez que ya hemos configurado las reglas que se aplicarn a cada atributo de la clase
U s u a r i o , podemos utilizar el servicio de validacin. La forma de utilizar el servicio de
validacin es la siguiente. Supongamos que estamos en una accin y tenemos un objeto
U s u a r i o ya creado:
1 < ? p h p
2 . . .
3
4 $ v a l i d a d o r = $ t h i s - > g e t ( ' v a l i d a t o r ' ) ;
5
6 $ e r r o r s = $ v a l i d a d o r - > v a l i d a t e ( $ u s u a r i o ) ;
Una vez que el contenedor de servicio nos proporciona el servicio de validacin (lnea 4),
pasamos el objeto que queremos validad ($ u s u a r i o ) al mtodo v a l i d a t e del servicio (lnea
6). Si el objeto no cumple lo que se ha especificado en las anotaciones de validacin, la
variable $ e r r o r recogera en un array una lista de mensajes que indican las reglas que no
se han cumplido.
Veamos esto en la prctica. Igual que hicimos cuando estudiamos Doctrine2, vamos a crear
un controlador para realizar el estudio de los validadores y formularios. Lo llamaremos
E s t u d i o V a l i d a c i o n Y F o r m u l a r i o s C o n t r o l l e r . Creamos en l la accin
v a l i d a U s u a r i o A c t i o n ( ) y la mapeamos en una ruta:
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l
Unidad 8: Desarrollo de la aplicacin MentorNotas (IV). Validacin y Formularios
146
. . .
j a m n _ E V F :
p a t t e r n : / e s t u d i o _ v a l y f o r m s / v a l i d a _ u s u a r i o
d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : v a l i d a U s u a r i o }
En el siguiente cdigo validamos varios objetos U s u a r i o que no cumplen las reglas
impuestas. En la plantilla asociada se muestran los errores generados por el servicio de
validacin basndose en las reglas que hemos definido en las anotaciones.
J a z z y w e b / A u l a s M e n t o r / J A M N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o C o n t r o l l e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ; ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ;
7
8 c l a s s E s t u d i o V a l i d a c i o n Y F o r m u l a r i o C o n t r o l l e r e x t e n d s C o n t r o l l e r
9 {
1 0
1 1 p u b l i c f u n c t i o n v a l i d a U s u a r i o A c t i o n ( )
1 2 {
1 3 $ u s u a r i o 1 = n e w U s u a r i o ( ) ;
1 4
1 5 / / P r i m e r o v a l i d a m o s u n u s u a r i o s i n p r o p i e d a d e s
1 6
1 7 $ v a l i d a t o r = $ t h i s - > g e t ( ' v a l i d a t o r ' ) ;
1 8
1 9 $ e r r o r s 1 = $ v a l i d a t o r - > v a l i d a t e ( $ u s u a r i o 1 ) ;
2 0
2 1 / / V e m o s q u e l a n i c a p r o p i e d a d q u e h a p a s a d o e s A p e l l i d o s
2 2 / / R e l l e n a m o s l a s d e m s :
2 3 $ u s u a r i o 2 = n e w U s u a r i o ( ) ;
2 4
2 5 $ u s u a r i o 2 - > s e t N o m b r e ( ' A l b e r t o P a b l o ' ) ;
2 6 $ u s u a r i o 2 - > s e t E m a i l ( ' a l b e r t o p a b l o ' ) ;
2 7 $ u s u a r i o 2 - > s e t I s A c t i v e ( 1 0 . 2 3 ) ;
2 8 $ u s u a r i o 2 - > s e t S a l t ( ' l a s a l t ' ) ;
2 9 $ u s u a r i o 2 - > s e t U s e r n a m e ( ' a l b e r t o p a b l o ' ) ;
3 0 $ u s u a r i o 2 - > s e t P a s s w o r d ( ' a l b e r t o p a b l o ' ) ;
3 1
3 2 $ e r r o r s 2 = $ v a l i d a t o r - > v a l i d a t e ( $ u s u a r i o 2 ) ;
3 3
3 4 / / P a s l a p r o p i e d a d n o m b r e y s a l t
3 5
3 6 $ u s u a r i o 3 = n e w U s u a r i o ( ) ;
3 7
3 8 $ u s u a r i o 3 - > s e t N o m b r e ( ' A l b e r t o P a b l o ' ) ;
3 9 $ u s u a r i o 3 - > s e t E m a i l ( ' a l b e r t o p a b l o @ k k . e s ' ) ;
4 0 $ u s u a r i o 3 - > s e t I s A c t i v e ( t r u e ) ;
4 1 $ u s u a r i o 3 - > s e t S a l t ( ' l a s a l t ' ) ;
4 2 $ u s u a r i o 3 - > s e t U s e r n a m e ( ' a l b e r t o p a b l o ' ) ;
4 3 $ u s u a r i o 3 - > s e t P a s s w o r d ( ' a l b e r t o p a b l o ' ) ;
4 4
Unidad 8: Desarrollo de la aplicacin MentorNotas (IV). Validacin y Formularios
147
4 5 $ e r r o r s 3 = $ v a l i d a t o r - > v a l i d a t e ( $ u s u a r i o 3 ) ;
4 6
4 7 $ u s u a r i o s = a r r a y (
4 8 a r r a y ( ' n u m ' = > 1 , ' u s e r ' = > $ u s u a r i o 1 , ' e r r o r s ' = > $ e r r o r s 1 ) ,
4 9 a r r a y ( ' n u m ' = > 2 , ' u s e r ' = > $ u s u a r i o 2 , ' e r r o r s ' = > $ e r r o r s 2 ) ,
5 0 a r r a y ( ' n u m ' = > 3 , ' u s e r ' = > $ u s u a r i o 3 , ' e r r o r s ' = > $ e r r o r s 3 ) ,
5 1 ) ;
5 2
5 3 r e t u r n $ t h i s - > r e n d e r (
5 4 ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : v a l i d a U s u a r i o . h t m l . t w i g ' ,
5 5 a r r a y ( ' u s u a r i o s ' = > $ u s u a r i o s ) ) ;
5 6 }
5 7
5 8 }
La plantilla
' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : v a l i d a U s u a r i o . h t m l . t w i g ,
la ubicamos, atendiendo a su nombre lgico, en el directorio
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d / R e s o u r c e s / v i e w / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d / R e s o u r c e s / v i e w s / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o / v a l i d a U s u a r i o . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 < h 1 > P r u e b a d e v a l i d a c i n d e o b j e t o s U s u a r i o < / h 1 >
6
7 { % f o r u i n u s u a r i o s % }
8
9 < h 3 > D a t o s d e l u s u a r i o { { u . n u m } } < / h 3 >
1 0
1 1 < t a b l e b o r d e r = " 1 " >
1 2 < t r >
1 3 < t d > i d : < / t d >
1 4 < t d > { { u . u s e r . i d } } < / t d >
1 5 < / t r >
1 6 < t r >
1 7 < t d > n o m b r e : < / t d >
1 8 < t d > { { u . u s e r . n o m b r e } } < / t d >
1 9 < / t r >
2 0 < t r >
2 1 < t d > a p e l l i d o s : < / t d >
2 2 < t d > { { u . u s e r . a p e l l i d o s } } < / t d >
2 3 < / t r >
2 4 < t r >
2 5 < t d > u s e r n a m e : < / t d >
2 6 < t d > { { u . u s e r . u s e r n a m e } } < / t d >
2 7 < / t r >
2 8 < t r >
2 9 < t d > p a s s w o r d : < / t d >
3 0 < t d > { { u . u s e r . p a s s w o r d } } < / t d >
3 1 < / t r >
3 2 < t r >
3 3 < t d > s a l t : < / t d >
3 4 < t d > { { u . u s e r . s a l t } } < / t d >
Unidad 8: Desarrollo de la aplicacin MentorNotas (IV). Validacin y Formularios
148
3 5 < / t r >
3 6 < t r >
3 7 < t d > e m a i l : < / t d >
3 8 < t d > { { u . u s e r . e m a i l } } < / t d >
3 9 < / t r >
4 0 < t r >
4 1 < t d > e s A c t i v o : < / t d >
4 2 < t d > { { u . u s e r . i s A c t i v e } } < / t d >
4 3 < / t r >
4 4
4 5 < / t a b l e >
4 6
4 7 { % i f u . e r r o r s | l e n g t h > 0 % }
4 8 E l u s u a r i o n o e s v l i d o p o r q u e :
4 9 < u l >
5 0 { % f o r e r r o r i n u . e r r o r s % }
5 1 < l i > { { e r r o r . p r o p e r t y P a t h } } { { e r r o r . m e s s a g e } } < / l i >
5 2 { % e n d f o r % }
5 3 < / u l >
5 4 { % e l s e % }
5 5 E l u s u a r i o s i e s v l i d o
5 6 { % e n d i f % }
5 7
5 8 < h r / >
5 9 { % e n d f o r % }
6 0
6 1 { % e n d b l o c k % }
Introduce la ruta recin creada en tu navegador y observa el funcionamiento de los
validadores que hemos utilizado en la accin.
Existen muchas ms reglas de validacin que puedes utilizar. Pero la filosofa de uso es
siempre la misma. En los ejercicios de esta unidad tendrs ocasin de explorar muchas de
ellas. Pero la teora acerca de la validacin se termina aqu.
Para terminar diremos que el uso ms frecuente de la validacin en las aplicaciones web es
la comprobacin de datos enviados por el cliente a travs de formularios. Por ello, como
veremos en breve, el servicio de formularios utiliza el servicio de validacin de forma
trasparente para realizar esta tarea. Lo cual significa que casi nunca se utiliza el servicio de
validacin en exclusiva. Sin embargo, en ocasiones, puedes que tengas que utilizarlo para
otros menesteres distintos a la validacin de datos de un formulario. Por ejemplo, supn que
tu aplicacin tiene una funcin muy compleja cuyo resultado debemos rechazar si no cumple
ciertas restricciones. El servicio de validacin puede servirnos perfectamente para
comprobar tales restricciones. Otro ejemplo que tambin puede darse es la validacin de
datos devueltos por posibles servicios web consultados por la aplicacin.
Por ltimo, adems de las reglas propuestas por el servicio, tambin puedes crear tus
propias reglas. Para saber como hacer esto puedes mirar: como crear reglas a medida
El servicio de formularios
Un formulario HTML consiste en un conjunto de campos de entrada en los que el usuario
puede introducir datos de distintas maneras: escribindolo, seleccionndolo de una lista,
picando con el ratn en alguna/s casilla/s, etctera. Una de los objetivos del framework de
formularios de Symfony2 es ayudarnos a construir dicho conjunto de campos.
El servicio de formularios
149
Por otro lado, una vez que el usuario introduce los datos en el formulario y los enva al
servidor, por cuestiones de seguridad, la aplicacin debe validarlos antes de realizar ninguna
accin que pueda comprometer al sistema. El servicio de formularios tambin se utiliza para
realizar dichas comprobaciones.
En Symfony2 podemos crear los formularios de dos maneras:
1. directamente en una accin y,
2. definiendo en una clase un tipo (T y p e ) que recoge los tipos de datos de cada campo del
formulario, y creando el formulario a travs de dicho tipo.
Aunque la primera estrategia es ms directa, la segunda es ms adecuada pues permite la
reutilizacin de formularios en distintas partes del cdigo de la aplicacin, e incluso entre
distintas aplicaciones.
Comenzaremos estudiando como se crea un formulario directamente en la accin.
Formularios definidos en la accin
En primer lugar, un formulario debera estar asociado a un objeto plano de nuestro modelo
19
al que se le hayan asociado reglas de validacin. Por tanto lo primero es asignar reglas de
validacin al objeto que nos servir como semilla del formulario. A continuacin inyectamos
dicho objeto al servicio de formularios y definimos los campos y tipos de campos que
deseamos aadir al formulario. Entonces ya tendremoso disponible un objeto formulario que
puede ser renderizado en la plantilla y usado tambin para realizar la validacin de los
datos.
De nuevo, una imagn vale ms que mil palabras. Vamos lo que acabamos de decir en
funcionamiento. Utilizaremos como ejemplo el objeto U s u a r i o cuyas reglas de validacin ya
hemos definido en la seccin anterior. Define la siguiente ruta:
j a m n _ E V F _ f o r m _ u s u a r i o :
p a t t e r n : / e s t u d i o _ v a l y f o r m s / f o r m _ u s u a r i o
d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : f o r m U s u a r i o }
Y aade la accin f o r m U s u a r i o A c t i o n ( ) al controlador
J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o C o n t r o l l e r
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o C o n t r o l l e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ;
7
8 c l a s s E s t u d i o V a l i d a c i o n Y F o r m s C o n t r o l l e r e x t e n d s C o n t r o l l e r
9 {
1 0 . . .
1 1
1 2 p u b l i c f u n c t i o n f o r m U s u a r i o A c t i o n ( )
1 3 {
1 4 $ u s u a r i o = n e w U s u a r i o ( ) ;
1 5
1 6 $ u s u a r i o - > s e t N o m b r e ( ' A l b e r t o ' ) ;
1 7 $ u s u a r i o - > s e t A p e l l i d o s ( ' E i n s t e i n ' ) ;
Formularios definidos en la accin
150
1 8 $ u s u a r i o - > s e t E m a i l ( ' a l b e r t o p a b l o @ k k . e s ' ) ;
1 9 $ u s u a r i o - > s e t I s A c t i v e ( t r u e ) ;
2 0 $ u s u a r i o - > s e t U s e r n a m e ( ' a l b e r t o ' ) ;
2 1 $ u s u a r i o - > s e t P a s s w o r d ( ' a l b e r t o ' ) ;
2 2
2 3 $ f o r m = $ t h i s - > c r e a t e F o r m B u i l d e r ( $ u s u a r i o )
2 4 - > a d d ( ' n o m b r e ' , ' t e x t ' )
2 5 - > a d d ( ' a p e l l i d o s ' , ' t e x t ' )
2 6 - > a d d ( ' e m a i l ' , ' e m a i l ' )
2 7 - > a d d ( ' i s A c t i v e ' , ' c h e c k b o x ' )
2 8 - > a d d ( ' u s e r n a m e ' , ' t e x t ' )
2 9 - > a d d ( ' p a s s w o r d ' , ' p a s s w o r d ' )
3 0 - > g e t F o r m ( ) ;
3 1
3 2 r e t u r n $ t h i s - > r e n d e r (
3 3 ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : f o r m U s u a r i o . h t m l . t w i g ' ,
3 4 a r r a y (
3 5 ' f o r m ' = > $ f o r m - > c r e a t e V i e w ( ) ,
3 6 ) ) ;
3 7 }
3 8
3 9 }
4 0 ? >
En las lneas 14-21 hemos creado un usuario y le hemos definido algunos de sus campos. A
partir de este objeto U s u a r i o , creamos un formulario en las lnea 23. Para ello usamos el
mtodo c r e a t e F o r m B u i l d e r ( ) , que es un wrapper al contenedor de servicios de Symfony2,
pasndole como argumento el objeto que acabamos de crear. A continuacin (lneas 24-29)
aadimos los campos que constituirn el formulario mediante los mtodos a d d ( ) . El primer
argumento de este mtodo es el nombre del atributo del objeto que estamos utilizando
($ u s u a r i o en nuestro caso), y el segundo el tipo de campo que queremos acociarle.
Tambin podemos pasarle un tercer argumento con un array de opciones. Estas opciones
dependen de cada tipo de campo. Los tipos de campos y sus opciones, pueden consultarse
en la documentacin oficial de Symfony2.
Fjate que no hemos aadido todos los campos del objeto U s u a r i o . No es ni necesario ni
adecuado, que el formulario disponga de todos los campos del objeto que utiliza como
semilla. Este objeto es utilizado por el formulario para
1. Asignar los valores de sus atributos a los campos del formulario
2. Utilizar las reglas de validacin asociadas a sus atributos en el proceso de validacin.
As que es el programador quien, en funcin de sus necesidades, decide qu campos
contendr el formulario.
Los objetos formularios pueden convertirse en variables para las plantillas mediante el
mtodo c r e a t e V i e w ( ) . Es lo que hacemos en la lnea 34. En la plantilla podemos usar la
variable f o r m de tres maneras distintas para pintar el formulario. La ms sencilla, pero
menos flexible, es a travs de la funcin f o r m _ w i d g e t ( ) de twig, la cual se encarga de
generar todo el cdigo de los campos del formulario automticamente.
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d / R e s o u r c e s / v i e w s / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o / f o r m U s u a r i o . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 < h 1 > C o m o s e p i n t a n l o s f o r m u l a r i o s : E l f o r m u l a r i o u s u a r i o < / h 1 >
6
Formularios definidos en la accin
151
7 < h 2 > L a m a n e r a m s r p i d a , p e r o c o n m e n o s c o n t r o l s o b r e l o s w i d g e t s < / h 2 >
8 < f o r m a c t i o n = " # " m e t h o d = " p o s t " { { f o r m _ e n c t y p e ( f o r m ) } } >
9 { { f o r m _ w i d g e t ( f o r m ) } }
1 0
1 1 < i n p u t t y p e = " s u b m i t " / >
1 2 < / f o r m >
1 3
1 4 { % e n d b l o c k % }
La lnea 9 es la encargada de pintar
todos los campos del formulario,
las etiquetas de los campos
los errores asociados a los campos en caso de que la validacin no haya sido
satisfactoria,
los errores generales del formulario
un campo oculto que se genera automticamente para proteger el formulario contra
ataques CSRF.
Es decir, lo hace todo. El problema es que el cdigo HTML generado no lo controlamos
nosotros y, aunque para hacer pruebas puede venir muy bien, es un problema para afinar
una versin definitiva de la plantilla. Otra manera de pintar el formulario que proporciona
ms flexibilidad en el cdigo de la plantilla es utilizando en combinacin las funciones
f o r m _ e r r o r s ( ) , f o r m _ r o w ( ) y f o r m _ r e s t ( ) . La plantilla anterior quedara as:
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d / R e s o u r c e s / v i e w / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o / f o r m U s u a r i o . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 < h 1 > C o m o s e p i n t a n l o s f o r m u l a r i o s : E l f o r m u l a r i o u s u a r i o < / h 1 >
6
7 < h 2 > U n a m a n e r a m s " v e r b o s a " , p e r o c o n m s c o n t r o l s o b r e l o s w i d g e t s < / h 2 >
8 < f o r m a c t i o n = " # " m e t h o d = " p o s t " { { f o r m _ e n c t y p e ( f o r m ) } } >
9 { { f o r m _ e r r o r s ( f o r m ) } }
1 0
1 1 { { f o r m _ r o w ( f o r m . n o m b r e ) } }
1 2 { { f o r m _ r o w ( f o r m . a p e l l i d o s ) } }
1 3 { { f o r m _ r o w ( f o r m . e m a i l ) } }
1 4 { { f o r m _ r o w ( f o r m . i s A c t i v e ) } }
1 5 { { f o r m _ r o w ( f o r m . u s e r n a m e ) } }
1 6 { { f o r m _ r o w ( f o r m . p a s s w o r d ) } }
1 7
1 8 { { f o r m _ r e s t ( f o r m ) } }
1 9
2 0 < i n p u t t y p e = " s u b m i t " / >
2 1 < / f o r m >
2 2
2 3 { % e n d b l o c k % }
Ahora se han pintado por separado los errores generales del formulario (lnea 9), las filas
correspondientes a los campos del formulario se pintan por separado en las lneas 11-16,
Formularios definidos en la accin
152
cada lnea se compone de la etiqueta del formulario, el mensaje de error cuando no pasa la
validacin, y el campo en s. Por ltimo en la lnea 18 se genera automticamente el resto de
los campos del formulario que no se hayan pintado con f o r m _ r o w . El uso de f o r m _ r e s t
importante para pintar el campo oculto que utiliza Symfony2 para el control de ataques
CSRF.
Hemos ganado algo de flexibilidad pero en muchos casos tampoco ser suficiente. Para esos
casos podemos utilizar las funciones f o r m _ l a b e l ( ) , f o r m _ e r r o r s ( ) y f o r m _ w i d g e t ( ) para
desglosar cada una de los campos.
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d / R e s o u r c e s / v i e w / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o / f o r m U s u a r i o . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 < h 1 > C o m o s e p i n t a n l o s f o r m u l a r i o s : E l f o r m u l a r i o u s u a r i o < / h 1 >
6
7 < h 2 > L a m a n e r a m s " v e r b o s a " , p e r o c o n m s c o n t r o l s o b r e l o s w i d g e t s < / h 2 >
8 < f o r m a c t i o n = " # " m e t h o d = " p o s t " { { f o r m _ e n c t y p e ( f o r m ) } } >
9 { { f o r m _ e r r o r s ( f o r m ) } }
1 0
1 1 < d i v >
1 2 { { f o r m _ l a b e l ( f o r m . n o m b r e ) } }
1 3 { { f o r m _ e r r o r s ( f o r m . n o m b r e ) } }
1 4 { { f o r m _ w i d g e t ( f o r m . n o m b r e ) } }
1 5 < / d i v >
1 6
1 7 < d i v >
1 8 { { f o r m _ l a b e l ( f o r m . a p e l l i d o s ) } }
1 9 { { f o r m _ e r r o r s ( f o r m . a p e l l i d o s ) } }
2 0 { { f o r m _ w i d g e t ( f o r m . a p e l l i d o s ) } }
2 1 < / d i v >
2 2
2 3 < d i v >
2 4 { { f o r m _ l a b e l ( f o r m . e m a i l ) } }
2 5 { { f o r m _ e r r o r s ( f o r m . e m a i l ) } }
2 6 { { f o r m _ w i d g e t ( f o r m . e m a i l ) } }
2 7 < / d i v >
2 8
2 9 < d i v >
3 0 { { f o r m _ l a b e l ( f o r m . i s A c t i v e ) } }
3 1 { { f o r m _ e r r o r s ( f o r m . i s A c t i v e ) } }
3 2 { { f o r m _ w i d g e t ( f o r m . i s A c t i v e ) } }
3 3 < / d i v >
3 4
3 5 < d i v >
3 6 { { f o r m _ l a b e l ( f o r m . u s e r n a m e ) } }
3 7 { { f o r m _ e r r o r s ( f o r m . u s e r n a m e ) } }
3 8 { { f o r m _ w i d g e t ( f o r m . u s e r n a m e ) } }
3 9 < / d i v >
4 0
4 1 < d i v >
4 2 { { f o r m _ l a b e l ( f o r m . p a s s w o r d ) } }
4 3 { { f o r m _ e r r o r s ( f o r m . p a s s w o r d ) } }
Formularios definidos en la accin
153
4 4 { { f o r m _ w i d g e t ( f o r m . p a s s w o r d ) } }
4 5 < / d i v >
4 6
4 7 { { f o r m _ r e s t ( f o r m ) } }
4 8
4 9 < i n p u t t y p e = " s u b m i t " / >
5 0 < / f o r m >
5 1
5 2 { % e n d b l o c k % }
Definicin de tipos para crear formularios
Si tuvisemos que utilizar el mismo formulario que acabamos de construir en otra parte de la
aplicacin, tendramos que copiarlo y pegarlo all donde lo necesitamos. Supongo que al leer
la frase anterior se te habrn erizado todos los bellos de la piel. En efecto, repetir cdigo por
doquier no es una buena idea. Si queremos reutilizar un formulario la solucin es crear lo
que en Symfony2 se conoce como un tipo (T y p e ).
Un tipo es una clase que deriva de S y m f o n y \ C o m p o n e n t \ F o r m \ A b s t r a c t T y p e en la que se
definen los campos que debe poseer cualquier formulario construido a partir de dicho tipo. El
servicio de formularios de Symfony2 proporciona el mtodo c r e a t e F o r m ( ) el cual, tomando
como argumentos un T y p e y un objeto del modelo, construye un formulario.
Vamos a construir el mismo formulario para el objeto U s u a r i o utilizando esta estrategia
ms adecuada para la reusabilidad. Comenzamos por definir un tipo para el objeto U s u a r i o .
Es decir, creamos una clase que llamaremos U s u a r i o T y p e y que extiende a
S y m f o n y \ C o m p o n e n t \ F o r m \ A b s t r a c t T y p e . La ubicaremos en el directorio
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d / F o r m / T y p e .
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d / F o r m / T y p e / U s u a r i o T y p e . p h p .
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e ;
4
5 u s e S y m f o n y \ C o m p o n e n t \ F o r m \ A b s t r a c t T y p e ;
6 u s e S y m f o n y \ C o m p o n e n t \ F o r m \ F o r m B u i l d e r ;
7
8 c l a s s U s u a r i o T y p e e x t e n d s A b s t r a c t T y p e
9 {
1 0
1 1 p u b l i c f u n c t i o n b u i l d F o r m ( F o r m B u i l d e r $ b u i l d e r , a r r a y $ o p t i o n s )
1 2 {
1 3 $ b u i l d e r - > a d d ( ' n o m b r e ' , ' t e x t ' )
1 4 - > a d d ( ' a p e l l i d o s ' , ' t e x t ' )
1 5 - > a d d ( ' e m a i l ' , ' e m a i l ' )
1 6 - > a d d ( ' i s A c t i v e ' , ' c h e c k b o x ' )
1 7 - > a d d ( ' u s e r n a m e ' , ' t e x t ' )
1 8 - > a d d ( ' p a s s w o r d ' , ' p a s s w o r d ' ) ;
1 9 }
2 0
2 1 p u b l i c f u n c t i o n g e t N a m e ( )
2 2 {
2 3 r e t u r n ' u s u a r i o ' ;
2 4 }
Definicin de tipos para crear formularios
154
2 5
2 6 / / E s t o n o e s s i e m p r e n e c e s a r i o , p e r o p a r a c o n s t r u i r f o r m u l a r i o s e m b e b i d o s
2 7 / / e s i m p r e s c i n d i b l e s , a s q u e n o c u e s t a n a d a a c o s t u m b r a r s e a p o n e r l o
2 8 p u b l i c f u n c t i o n g e t D e f a u l t O p t i o n s ( a r r a y $ o p t i o n s )
2 9 {
3 0 r e t u r n a r r a y (
3 1 ' d a t a _ c l a s s ' = > ' J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ' ,
3 2 ) ;
3 3 }
3 4 }
El elemento fundamental de un tipo es su mtodo b u i l d F o r m ( ) donde se especifican los
campos que debe incluir el formulario que se construya a partir de dicho tipo. El mtodo
g e t N a m e ( ) debe devolver un nombre nico que identifique al tipo . Y el mtodo
g e t D e f a u l t O p t i o n s ( ) , aunque no es estrictamente necesario para la mayor parte de
operaciones que se hacen con los tipos, es requerido cuando se utilizan formularios
embebidos. Por ello no viene mal acostumbrarse a ponerlo.
Ahora que tenemos el tipo U s u a r i o T y p e podemos utilizarlo para construir un formulario de
usuarios en cualquier accin de nuestra aplicacin. La accin f o r m U s u a r i o ( ) de la seccin
anterior quedara ahora as:
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o C o n t r o l l e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ;
7 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e \ U s u a r i o T y p e ;
8
9 c l a s s E s t u d i o V a l i d a c i o n Y F o r m s C o n t r o l l e r e x t e n d s C o n t r o l l e r
1 0 {
1 1 . . .
1 2
1 3 p u b l i c f u n c t i o n f o r m U s u a r i o A c t i o n ( )
1 4 {
1 5 $ u s u a r i o = n e w U s u a r i o ( ) ;
1 6
1 7 $ u s u a r i o - > s e t N o m b r e ( ' A l b e r t o ' ) ;
1 8 $ u s u a r i o - > s e t A p e l l i d o s ( ' E i n s t e i n ' ) ;
1 9 $ u s u a r i o - > s e t E m a i l ( ' a l b e r t o p a b l o @ k k . e s ' ) ;
2 0 $ u s u a r i o - > s e t I s A c t i v e ( t r u e ) ;
2 1 $ u s u a r i o - > s e t U s e r n a m e ( ' a l b e r t o ' ) ;
2 2 $ u s u a r i o - > s e t P a s s w o r d ( ' a l b e r t o ' ) ;
2 3
2 4 $ f o r m = $ t h i s - > c r e a t e F o r m ( n e w U s u a r i o T y p e ( ) , $ u s u a r i o ) ;
2 5
2 6 r e t u r n $ t h i s - > r e n d e r (
2 7 ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : f o r m U s u a r i o . h t m l . t w i g ' ,
2 8 a r r a y (
2 9 ' f o r m ' = > $ f o r m - > c r e a t e V i e w ( ) ,
3 0 ) ) ;
3 1 }
3 2
3 3 }
Y el resultado es exactamente igual que antes. La diferencia es que
1. El cdigo es ms legible, no se contamina con los detalles de creacin del formulario.
Definicin de tipos para crear formularios
155
2. El formulario es reutilizable. Podemos crear un formulario usuario en cualquier otra
parte de la aplicacin. Si tenemos que cambiarlo, aadiendo por ejemplo un nuevo
campo, bastara con modificar la clase U s u a r i o T y p e para que todos los formularios
construidos a partir de ella se actualicen.
Importante
No olvides aadir la lnea: u s e
J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e \ U s u a r i o T y p e ; en el
controlador para poder usar la clase U s u a r i o T y p e .
Validacin de formularios
Si una aplicacin enva un formulario al cliente, es por que espera recibir datos para realizar
algn proceso con ellos. Las aplicaciones web, normalmente estn ms expuestas que las
aplicaciones de usuario, ya que utilizan alguna red para dar su servicio, y son muchos los
usuarios que pueden acceder a la red aumentando el riesgo de ataques. Por ello es muy
importante en el desarrollo de aplicaciones web que todos los datos enviados desde el
cliente sean cuidadosamente validados antes de ser procesados, pues cualquier tipo de
exploit de una aplicacin se produce por sus vas de entradas de datos.
Vamos a explicar esto con ms detalle:
La funcin de los formularios HTML es recoger datos del usuario cliente y enviarlos al
servidor para hacer algo con ellos (almacenarlos en una base de datos, realizar un complejo
clculo para confeccionar una carta astral o cualquier otra cosa).
El navegador web enva los datos realizando una peticin HTTP al servidor, la cual, segn la
especificacin del protocolo HTTP, puede ser de varios tipos en funcin de lo que el cliente
tenga intencin de hacer con los datos enviados sobre el servidor. Las operaciones HTTP
ms conocidas que un servidor web pone a disposicin de los clientes a travs de su interfaz
uniforme son GET, POST, PUT y DELETE, la cuales pueden ser comparadas semnticamente
con las operaciones en una base de datos:
Mtodo
HTTP
Operacin en base de
datos Descripcin
GET Retrieve Recupera un recurso/registro. No hay modificacin del
mismo
POST Update Modifica un recurso/registro.
PUT Create Crear un recurso/registro
DELETE Delete Elimina un recurso/registro
Los navegadores web actuales tan slo soportan las dos primeras operaciones (GET y POST)
para realizar peticiones al servidor y enviarle datos. Ahora bien, cul de las dos debemos
utilizar en nuestros formularios HTML? La respuesta ya la hemos insinuado unas lnea ms
atrs al comparar los mtodos HTTP con las operaciones de una base de datos: si en el
proceso de servidor que recibe los datos se va a llevar a cabo algn tipo de modificacin (ya
sea en bases de datos, ficheros o cualquier otro tipo de recurso) debemos utilizar POST que
se correspondera con una operacin de actualizacin, pero si dicho proceso utiliza los datos
enviados tan solo para recuperar algn recurso, sin que haya ningn tipo de modificacin en
los datos que gestiona la aplicacin, entonces debemos utilizar GET.
Validacin de formularios
156
Adems de los matices semnticos de ambas operaciones, existe una diferencia bien visible:
En el caso de una peticin del tipo GET, los datos se envan como parmetros que forman
parte de la URL, tal y como mostramos en este ejemplo:
h t t p : / / w w w . e l s e r v i d o r . e s / r e c u r s o ? p a r a m 1 = v a l o r 1 & p a r a m 2 = v a l o r 2
mientras que en las peticiones POST los datos viajan encapsulados en la seccin de datos de
la peticin HTTP. Adems mediante POST se pueden enviar ficheros al servidor indicando en
la peticin que el tipo de contenido (content-type) de los datos enviados es
m u l t i p a r t / f o r m - d a t a . Esta indicacin se realiza a travs del parmetro e n c t y p e del
elemento f o r m .
Es muy importante que comprendamos que las peticiones HTTP se realizan desde un cliente
sobre el cual, obviamente, el servidor no tiene ningn tipo de control directo, de manera que
el cliente puede enviar al servidor los parmetros que quiera, saltndose lo prescrito por el
formulario. Expliquemos esto con ms detalle. La secuencia normal que seguira una
aplicacin web para pedir datos al cliente y procesarlos sera:
1. El servidor enva un formulario HTML al cliente con los campos que el usuario utilizar
para introducir los datos.
2. El navegador interpreta el documento HTML y presenta el formulario al usuario
3. El usuario introduce los datos y enva el formulario relleno, es decir, pica en el botn
submit y el navegador construye una peticin HTTP al proceso de servidor indicado en el
parmetro action del formulario y con los datos que el usuario ha introducido.
4. El servidor recibe la peticin con los datos esperados y los procesa.
Sin embargo no hay nada que impida saltarse el paso 3 y construir en el cliente, sin utilizar
el navegador, una peticin HTTP al proceso de servidor con cualquier tipo de datos. Esto
significa que el servidor nunca debe fiarse de los datos que traen las peticiones, ya que han
podido ser manipuladas en el cliente y no tienen por que obedecer a lo que el programador
espera, dando lugar a posibles brechas de seguridad en la aplicacin.
Conclusin: Los procesos de servidor deben validar TODOS los datos que le llegan antes de
realizar ninguna operacin con ellos.
Nota
Observa que el hecho de introducir validadores en el cliente utilizando cdigo
javascript no vale de nada, ya que como acabamos de indicar, el usuario puede
puentear completamente el uso del navegador, y por tanto del formulario enviado,
para construir y lanzar la peticin con los datos que desee. Este hecho junto con el
desconocimiento, la pereza o la escasa destreza del programador para blindar sus
aplicaciones con los adecuados validadores del lado del servidor, dan lugar a una de
las vulnerabilidades ms comunes de las aplicaciones web.
Nota
Sobre la validacin HTML5.
Validacin de formularios
157
Desde la introduccin del HTML5 muchos navegadores pueden realizar nativamente
una validacin en el lado del cliente. Para activarla se aade el atributo r e q u i r e d al
campo que se desea validar. Symfony2 aade automticamente dicho atributo en los
campos que son requeridos, por lo que si utilizas un navegador compatible con HTML5
se producir una validacin en el cliente antes de enviar los datos al servidor. An as,
y por las razones expuesta ms arriba, hay que implementar la validacin de los
formularios en el lado del servidor.
La validacin en el lado del cliente se puede deshabilitar aadiendo el atributo
n o v a l i d a t e a la etiqueta f o r m , o f o r m n o v a l i d a t e a la etiqueta s u b m i t . Hacer esto es
especialmente importante cuando queremos testear la validacin en el lado del
servidor. Es decir, lo que nos porponemos hacer ahora. Por tanto aadiremos el
atributo n o v a l i d a t e a la etiqueta f o r m en nuestras plantillas.
Importante
Aade el atributo n o v a l i d a t e a la etiqueta f o r m en la plantilla
f o r m U s u a r i o . h t m l . t w i g :
. . .
< f o r m n o v a l i d a t e = " t r u e " a c t i o n = " # " m e t h o d = " p o s t " { { f o r m _ e n c t y p e ( f o r m ) } } >
. . .
A continuacin vamos a explicar un procedimiento que puedes aplicar siempre que necesites
recoger y validar datos del usuario mediante formularios. Utilizaremos, como no, el objeto
U s u a r i o y su tipo asociado U s u a r i o T y p e como ejemplo.
La estrategia consiste en utilizar una misma accin tanto para mostrar el formulario como
para procesar los datos que el usuario ha introducido en l. Si la peticin de la accin se ha
realizado con el mtodo GET, interpretaremos que el usuario est solicitando el formulario.
Pero si la peticin se hace va POST querr decir que el usuario ha enviado datos y entonces
tenemos que validarlo.
Vamos a modificar la accin f o r m U s u a r i o A c t i o n ( ) para implementar esta estrategia:
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o C o n t r o l l e r
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ;
7 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e \ U s u a r i o T y p e ;
8
9 c l a s s E s t u d i o V a l i d a c i o n Y F o r m s C o n t r o l l e r e x t e n d s C o n t r o l l e r
1 0 {
1 1 . . .
1 2
Validacin de formularios
158
1 3 p u b l i c f u n c t i o n f o r m U s u a r i o A c t i o n ( )
1 4 {
1 5 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
1 6 $ u s u a r i o = n e w U s u a r i o ( ) ;
1 7
1 8 $ f o r m = $ t h i s - > c r e a t e F o r m ( n e w U s u a r i o T y p e ( ) , $ u s u a r i o ) ;
1 9
2 0 i f ( $ r e q u e s t - > g e t M e t h o d ( ) = = ' P O S T ' )
2 1 {
2 2 $ f o r m - > b i n d R e q u e s t ( $ r e q u e s t ) ;
2 3 i f ( $ f o r m - > i s V a l i d ( ) )
2 4 {
2 5 / / S e p r o c e s a e l f o r m u l a r i o
2 6
2 7 $ t h i s - > g e t ( ' s e s s i o n ' ) - > s e t F l a s h ( ' m e n s a j e ' , ' E l f o r m u l a r i o e r a v l i d o ' ) ;
2 8 r e t u r n $ t h i s - > r e d i r e c t ( $ t h i s - > g e n e r a t e U r l ( ' j a m n _ E V F _ f o r m _ u s u a r i o ' ) ) ;
2 9 }
3 0 }
3 1
3 2 r e t u r n $ t h i s - > r e n d e r (
3 3 ' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : f o r m U s u a r i o . h t m l . t w i g ' ,
3 4 a r r a y (
3 5 ' f o r m ' = > $ f o r m - > c r e a t e V i e w ( ) ,
3 6 ) ) ;
3 7 }
3 8
3 9 }
Para completar el procedimiento hay que indicar en el atributo a c t i o n del formulario HTML,
la URL donde se enviarn los datos. En la lnea 8 de la plantilla siguiente se usa la funcin
p a t h de twig para establecer dicha URL. Tambin hemos incluido el atributo n o v a l i d a t e a
la etiqueta f o r m , y la visualizacin condicional del mensaje establecido en una variable flash
de sesin que indica que el formulario era vlido.
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d / R e s o u r c e s / v i e w / E s t u d i o V a l i d a c i o n Y F o r m u l a r i o / f o r m U s u a r i o . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4
5 < h 1 > C o m o s e p i n t a n l o s f o r m u l a r i o s : E l f o r m u l a r i o u s u a r i o < / h 1 >
6
7 { % i f a p p . s e s s i o n . h a s F l a s h ( ' m e n s a j e ' ) % }
8 < d i v s t y l e = " b a c k g r o u n d - c o l o r : r o s y b r o w n " >
9 { { a p p . s e s s i o n . f l a s h ( ' m e n s a j e ' ) } }
1 0 < / d i v >
1 1 { % e n d i f % }
1 2 < h 2 > U n a m a n e r a m s " v e r b o s a " , p e r o c o n m s c o n t r o l s o b r e l o s w i d g e t s < / h 2 >
1 3 < f o r m n o v a l i d a t e = " t r u e " a c t i o n = " { { p a t h ( ' j a m n _ E V F _ f o r m _ u s u a r i o ' ) } } " m e t h o d = " p o s t " { { f o r m _ e n c t y p e ( f o r m ) } } >
1 4 { { f o r m _ e r r o r s ( f o r m ) } }
1 5
1 6 { { f o r m _ r o w ( f o r m . n o m b r e ) } }
1 7 { { f o r m _ r o w ( f o r m . a p e l l i d o s ) } }
1 8 { { f o r m _ r o w ( f o r m . e m a i l ) } }
1 9 { { f o r m _ r o w ( f o r m . i s A c t i v e ) } }
2 0 { { f o r m _ r o w ( f o r m . u s e r n a m e ) } }
2 1 { { f o r m _ r o w ( f o r m . p a s s w o r d ) } }
2 2
2 3 { { f o r m _ r e s t ( f o r m ) } }
2 4
2 5 < i n p u t t y p e = " s u b m i t " / >
2 6 < / f o r m >
2 7
2 8 { % e n d b l o c k % }
Ahora ya podemos probar el procedimiento y explicarlo. Cuando la peticin se realiza va
mtodo GET, la accin interpreta que se est solicitando el envo del formulario para
rellenar. Nosotros lo hemos enviado vaco, pero si se quiere enviar con algn campo relleno,
Validacin de formularios
159
basta con definir en el objeto $ u s u a r i o el atributo correspondiente antes de construir el
formulario (lnea 16 de la accin). Pero si la peticin se ha realizado va mtodo POST,
significa que el usuario envi el formulario picando en el botn s u b m i t . Entonces se rellena
el formulario con los valores que vienen en la peticin (lnea 22) y acto seguido se validan
con las reglas definidas para el objeto U s u a r i o (lnea 23). Si la validacin no es correcta,
entonces se vuelve a pintar el formulario con los mismos datos que se enviaron y adems
con los mensaje de error que explican porqu no es vlido el formulario. Por ltimo, si se
pasa la validacin, entonces se realiza el proceso de los datos y se realiza una redireccin a
alguna accin. Aunque, como en este caso, la redireccin se haga a la misma accin, es muy
importante realizarla para evitar que, al refrescar por accidente el navegador, se vuelvan a
enviar por POST los mismos datos y vuelvan a procesarse (ten en cuenta que una
redireccin siempre se realiza mediante la operacin GET).
Este procedimiento, con ms o menos variacin, lo vas a encontrar continuamente en el
cdigo de las aplicaciones Symfony2 que utilicen formularios.
La unidad en chuletas
El servicio de validacin
Para definir reglas de validacin sobre un objeto plano hay que aadir en el fichero donde se
define la clase:
< ? p h p
. . .
u s e S y m f o n y \ C o m p o n e n t \ V a l i d a t o r \ C o n s t r a i n t s a s A s s e r t ;
. . .
Para saber cuales son las regas de validacin, consulta este enlace:
reglas de validacin
Para usarlas en una accin:
< ? p h p
. . .
$ u s u a r i o = n e w U s u a r i o ( ) ;
$ v a l i d a t o r = $ t h i s - > g e t ( ' v a l i d a t o r ' ) ;
$ e r r o r s 1 = $ v a l i d a t o r - > v a l i d a t e ( $ u s u a r i o 1 ) ;
. . .
Y para pintarlas en una plantilla:
. . .
{ % i f u . e r r o r s | l e n g t h > 0 % }
E l u s u a r i o n o e s v l i d o p o r q u e :
< u l >
{ % f o r e r r o r i n u . e r r o r s % }
< l i > { { e r r o r . p r o p e r t y P a t h } } { { e r r o r . m e s s a g e } } < / l i >
{ % e n d f o r % }
< / u l >
{ % e l s e % }
E l u s u a r i o s i e s v l i d o
{ % e n d i f % }
< h r / >
La unidad en chuletas
160
{ % e n d f o r % }
. . .
El servicio de formularios
Para definir un formulario en la accin:
p u b l i c f u n c t i o n f o r m U s u a r i o A c t i o n ( )
{
$ u s u a r i o = n e w U s u a r i o ( ) ;
$ u s u a r i o - > s e t N o m b r e ( ' A l b e r t o ' ) ;
$ u s u a r i o - > s e t A p e l l i d o s ( ' E i n s t e i n ' ) ;
$ u s u a r i o - > s e t E m a i l ( ' a l b e r t o p a b l o @ k k . e s ' ) ;
$ u s u a r i o - > s e t I s A c t i v e ( t r u e ) ;
$ u s u a r i o - > s e t U s e r n a m e ( ' a l b e r t o ' ) ;
$ u s u a r i o - > s e t P a s s w o r d ( ' a l b e r t o ' ) ;
$ f o r m = $ t h i s - > c r e a t e F o r m B u i l d e r ( $ u s u a r i o )
- > a d d ( ' n o m b r e ' , ' t e x t ' )
- > a d d ( ' a p e l l i d o s ' , ' t e x t ' )
- > a d d ( ' e m a i l ' , ' e m a i l ' )
- > a d d ( ' i s A c t i v e ' , ' c h e c k b o x ' )
- > a d d ( ' u s e r n a m e ' , ' t e x t ' )
- > a d d ( ' p a s s w o r d ' , ' p a s s w o r d ' )
- > g e t F o r m ( ) ;
r e t u r n $ t h i s - > r e n d e r (
' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : f o r m U s u a r i o . h t m l . t w i g ' ,
a r r a y (
' f o r m ' = > $ f o r m - > c r e a t e V i e w ( ) ,
) ) ;
}
Para pintarlo en una plantilla (manera ms "verbosa"):
{ % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
{ % b l o c k b o d y % }
< h 1 > C o m o s e p i n t a n l o s f o r m u l a r i o s : E l f o r m u l a r i o u s u a r i o < / h 1 >
< h 2 > L a m a n e r a m s " v e r b o s a " , p e r o c o n m s c o n t r o l s o b r e l o s w i d g e t s < / h 2 >
< f o r m a c t i o n = " # " m e t h o d = " p o s t " { { f o r m _ e n c t y p e ( f o r m ) } } >
{ { f o r m _ e r r o r s ( f o r m ) } }
< d i v >
{ { f o r m _ l a b e l ( f o r m . n o m b r e ) } }
{ { f o r m _ e r r o r s ( f o r m . n o m b r e ) } }
{ { f o r m _ w i d g e t ( f o r m . n o m b r e ) } }
< / d i v >
< d i v >
{ { f o r m _ l a b e l ( f o r m . a p e l l i d o s ) } }
{ { f o r m _ e r r o r s ( f o r m . a p e l l i d o s ) } }
{ { f o r m _ w i d g e t ( f o r m . a p e l l i d o s ) } }
El servicio de formularios
161
< / d i v >
< d i v >
{ { f o r m _ l a b e l ( f o r m . e m a i l ) } }
{ { f o r m _ e r r o r s ( f o r m . e m a i l ) } }
{ { f o r m _ w i d g e t ( f o r m . e m a i l ) } }
< / d i v >
< d i v >
{ { f o r m _ l a b e l ( f o r m . i s A c t i v e ) } }
{ { f o r m _ e r r o r s ( f o r m . i s A c t i v e ) } }
{ { f o r m _ w i d g e t ( f o r m . i s A c t i v e ) } }
< / d i v >
< d i v >
{ { f o r m _ l a b e l ( f o r m . u s e r n a m e ) } }
{ { f o r m _ e r r o r s ( f o r m . u s e r n a m e ) } }
{ { f o r m _ w i d g e t ( f o r m . u s e r n a m e ) } }
< / d i v >
< d i v >
{ { f o r m _ l a b e l ( f o r m . p a s s w o r d ) } }
{ { f o r m _ e r r o r s ( f o r m . p a s s w o r d ) } }
{ { f o r m _ w i d g e t ( f o r m . p a s s w o r d ) } }
< / d i v >
{ { f o r m _ r e s t ( f o r m ) } }
< i n p u t t y p e = " s u b m i t " / >
< / f o r m >
{ % e n d b l o c k % }
Para definirlo en una clase como tipo. Se crea la clase en el directorio F o r m \ T y p e del bundle:
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e ;
u s e S y m f o n y \ C o m p o n e n t \ F o r m \ A b s t r a c t T y p e ;
u s e S y m f o n y \ C o m p o n e n t \ F o r m \ F o r m B u i l d e r ;
c l a s s U s u a r i o T y p e e x t e n d s A b s t r a c t T y p e
{
p u b l i c f u n c t i o n b u i l d F o r m ( F o r m B u i l d e r $ b u i l d e r , a r r a y $ o p t i o n s )
{
$ b u i l d e r - > a d d ( ' n o m b r e ' , ' t e x t ' )
- > a d d ( ' a p e l l i d o s ' , ' t e x t ' )
- > a d d ( ' e m a i l ' , ' e m a i l ' )
- > a d d ( ' i s A c t i v e ' , ' c h e c k b o x ' )
- > a d d ( ' u s e r n a m e ' , ' t e x t ' )
- > a d d ( ' p a s s w o r d ' , ' p a s s w o r d ' ) ;
El servicio de formularios
162
}
p u b l i c f u n c t i o n g e t N a m e ( )
{
r e t u r n ' u s u a r i o ' ;
}
/ / E s t o n o e s s i e m p r e n e c e s a r i o , p e r o p a r a c o n s t r u i r f o r m u l a r i o s e m b e b i d o s
/ / e s i m p r e s c i n d i b l e s , a s q u e n o c u e s t a n a d a a c o s t u m b r a r s e a p o n e r l o
p u b l i c f u n c t i o n g e t D e f a u l t O p t i o n s ( a r r a y $ o p t i o n s )
{
r e t u r n a r r a y (
' d a t a _ c l a s s ' = > ' J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ' ,
) ;
}
}
Y se utiliza en una accin:
< ? p h p
. . .
u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e \ U s u a r i o T y p e ;
. . .
$ u s u a r i o = n e w U s u a r i o ( ) ;
$ u s u a r i o - > s e t N o m b r e ( ' A l b e r t o ' ) ;
$ u s u a r i o - > s e t A p e l l i d o s ( ' E i n s t e i n ' ) ;
$ f o r m = $ t h i s - > c r e a t e F o r m ( n e w U s u a r i o T y p e ( ) , $ u s u a r i o ) ;
Mtodo para la validacin de formularios.
En una accin:
< ? p h p
. . .
p u b l i c f u n c t i o n f o r m U s u a r i o A c t i o n ( )
{
$ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
$ u s u a r i o = n e w U s u a r i o ( ) ;
$ f o r m = $ t h i s - > c r e a t e F o r m ( n e w U s u a r i o T y p e ( ) , $ u s u a r i o ) ;
i f ( $ r e q u e s t - > g e t M e t h o d ( ) = = ' P O S T ' )
{
$ f o r m - > b i n d R e q u e s t ( $ r e q u e s t ) ;
i f ( $ f o r m - > i s V a l i d ( ) )
{
/ / S e p r o c e s a e l f o r m u l a r i o
$ t h i s - > g e t ( ' s e s s i o n ' ) - > s e t F l a s h ( ' m e n s a j e ' , ' E l f o r m u l a r i o e r a v l i d o ' ) ;
r e t u r n $ t h i s - > r e d i r e c t ( $ t h i s - > g e n e r a t e U r l ( ' j a m n _ E V F _ f o r m _ u s u a r i o ' ) ) ;
}
}
Mtodo para la validacin de formularios.
163
r e t u r n $ t h i s - > r e n d e r (
' J A M N o t a s F r o n t e n d B u n d l e : E s t u d i o V a l i d a c i o n Y F o r m u l a r i o : f o r m U s u a r i o . h t m l . t w i g ' ,
a r r a y (
' f o r m ' = > $ f o r m - > c r e a t e V i e w ( ) ,
) ) ;
}
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll para recorrer
todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Comentarios
164
Unidad 9: Desarrollo de la aplicacin MentorNotas (V).
Seguridad - Autentificacin y Autorizacin
La seguridad en una aplicacin es un proceso que consiste en dos pasos sucesivos:
Autentificacin y Autorizacin.
La autentificacin es el proceso mediante el cual la aplicacin comprueba si el usuario que
pretende utilizarla es realmente quien dice ser. Es un proceso de identificacin. Para ello la
aplicacin solicita al usuario ciertos parmetros que lo identifiquen y, mediante algn tipo de
comprobacin, decide si lo considera identificado en el sistema o no.
La autorizacin es un proceso mediante el cual la aplicacin decide qu funcionalidades
puede utilizar el usuario que la maneja y qu informacin le puede presentar. La aplicacin
toma tal decisin basndose en la identificacin del usuario, esto es; decidir qu recursos
puede ofrecerle una vez que ha admitido la autentificacin del usuario. Es, por tanto, un
segundo nivel de seguridad en el control de acceso.
Algunas aplicaciones seguras ofrecen todos sus recursos a cualquier usuario autentificado,
en cuyo caso la autorizacin se confunde con la autentificacin, pero en la mayora de las
aplicaciones no es as. La que estamos desarrollando exige en sus requisitos este doble nivel
de seguridad, ya que dependiendo del perfil que tenga asociado, el usuario podr utilizar
ms o menos recursos de la aplicacin.
Symfony2 proporciona un magnfico servicio para el tratamiento de la seguridad en las
aplicaciones web, de forma que podemos resolver gran parte de los problemas que esta
plantea a travs de un fichero de configuracin. A lo largo de esta unidad estudiaremos este
componente y lo aplicaremos a la aplicacin que estamos desarrollando.
Componentes y funcionamiento del servicio de seguridad de
Symfony2.
La estructura bsica del funcionamiento de los mecanismos de seguridad en aplicaciones
web es prcticamente la misma en todas ellas.
En primer lugar, cuando el usuario solicite algn recurso protegido, la aplicacin debe iniciar
el proceso de autentificacin, solicitando de alguna manera al usuario las credenciales que lo
identifican. A continuacin la aplicacin contrasta dichas credenciales utilizando algn tipo
de sistema de infomacin persistente (base de datos, directorio tipo LDAP, fichero de
passwords, etctera) donde residen los datos de identificacin y posiblemente otros datos
acerca del usuario (permisos asociados, por ejemplo).
Nota
El procedimiento tpico para autentificar a un usuario consiste en solicitarle un nombre
de usuario (username) y una contrasea (password) asociada, que constituyen un par
conocido tan slo por el usuario para evitar suplantaciones de identidad. A pesar de
ser el mecanismo ms utilizado, es el ms inseguro, pues la filtracin de password
obtenidos por distintas tcnicas, desde las ms rudimentaras como passwords que
quedan anotados en papeles que se tiran a la papelera, hasta las ms sofisticadas
como la inyecccin de troyanos que leen lo que el usuario teclea (keyloggers), est a
la orden del da.
Por ello hay una tendencia y un inters creciente por adoptar mecanismos basados en
certificados digitales pertenecientes y custodiados por del usuario y protegidos por
clave. El DNI electrnico contiene un certificado de este tipo, siendo uno de los
Unidad 9: Desarrollo de la aplicacin MentorNotas (V). Seguridad - Autentificacin y
Autorizacin
165
propsitos del mismo identificar a las personas en aplicaciones informticas. Estos
mtodos son bastante ms seguros que el basado en username y password, sin
embargo no estn exentos de ser vulnerados.
Un nivel an ms seguro lo constituyen los mecanismos de control biomtrico, como
puede ser la lectura de la huella dactilar, que complementa o sustituye a los que
acabamos de describir.
No obstante, en el momento de escribir estas lneas, la mayor parte de las
aplicaciones web basan su control de identidad en el par de parmetros nombre de
usuario y contrasea y ser este el que utilizaremos en nuestra aplicacin.
Una vez admitida la identidad del usuario, se obtienen del sistema de informacin los datos
del usuario y se hace uso de la sesin para mantener en las sucesivas peticiones del usuario
su estado autentificado y sus permisos. De esa manera la aplicacin sabr en cada peticin
qu recursos puede ofrecer al usuario (autorizacin) sin preguntarle de nuevo sus
credenciales de identificacin.
Cuando el usuario solicite finalizar la sesin, o pase un determinado tiempo sin actividad, la
aplicacin destruir la sesin, de manera que al realizar una nueva peticin a un recurso
protegido, la aplicacin volver a pedir las credenciales al usuario repitindose el proceso.
El servicio de seguridad de Symfony2 sigue este mismo patrn de funcionamiento. Para lo
cual utiliza una serie de conceptos que separan las distintas funcionalidades del proceso. En
primer lugar presentaremos y describiremos dichos conceptos sin entrar en el detalle de
como se especifican en el cdigo. Es importante, antes de nada, entenderlos bien. Despus,
utilizando nuestra aplicacin de ejemplo, veremos con detalle como se aplican tales
conceptos.
Los proveedores de usuarios (user providers)
Un proveedor de usuarios es un objeto que se comunica con el sistema de informacin
persistente donde se almacenan los datos de los usuarios relativos a la seguridad y que se
utiliza para contrastar las credenciales de acceso y obtener los permisos del usuario
(denominados roles en Symfony2). Este sistema de informacin puede ser:
un archivo,
una base de datos
un directorio LDAP
un directorio activo
un servicio externo como Facebook o twitter
un proveedor de identidad de alguna federacin de aplicaciones
etctera
La edicin estndar de Symfony2 proporciona dos proveedores de usuarios: uno basado en
el fichero de configuracin y otro que utiliza alguna de las base de datos configuradas en el
framework. Sin embargo, dado el diseo modular y extensible del framework es posible
aadir nuevos proveedores de usuarios al sistema.
Los cortafuegos (firewalls)
Los recursos de una aplicacin Symfony2 se protegen mediante cortafuegos (firewalls). Un
cortafuegos es un conjunto de directivas o parmetros de configuracin que definen
Los proveedores de usuarios (user providers)
166
1. qu recursos (rutas) de la aplicacin estn protegidos,
2. qu mecanismo de autentificacin debe utilizarse para recoger las credenciales del
usuario y
3. sobre qu proveedor de usuarios se deben constrastar dichas credenciales.
Symfony2 incorpora, por lo pronto, los siguientes mecanismos de autentificacin. No
obstante, dado su diseo modular y extensible, se podran aadir sin mucho esfuerzo otros
nuevos.
1. Autentificacin bsica HTTP (basic HTTP autentication). En este tipo de autentificacin el
servidor enva al navegador la orden de pedir en un formulario un nombre de usuario y
una contrasea. Por ello es el propio navegador el que proporciona el formulario de
login en una ventana emergente.
2. Autentificacin digest HTTP (digest HTTP authentication), que es similar al anterior pero
ms seguro, ya que aplica funciones hash a las contraseas antes de enviarlas por la
red.
3. Autentificacin mediante un formulario HTML. Es el mecanismo clsico. En este caso el
desarrollador tendr que construir dicho formulario e indicar al cortafuegos como se
accede a l.
4. Autentificacin mediante certificados X.509. Es un mecanismo ms seguro basado en
certificados digitales X.509, como los emitidos por la FNMT (Fbrica Nacional de Moneda
y Timbre) o el que proporciona el DNI electrnico.
En la configuracin de los cortafuegos tambin se pueden establecer otros procedimientos y
parmetros propios del proceso de autentificacin como por ejemplo; el proceso de logout
(fin de sesin), la incorporacin de un filtro Remember me o la URL de la pgina de error.
En definitiva, los cortafuegos son los responsables del proceso de autentificacin en el
servicio de seguridad de Symfony2 y utilizan algn proveedor de usuarios para contrastar las
credenciales del usuario.
Importante
La aplicacin puede definir todos los cortafuegos que precise. Cada cortafuego puede
proteger un conjunto de rutas diferentes, utilizar el mecanismo de autentificacin que
le venga bien, y contrastar las credenciales contra uno de los proveedores de usuarios
disponibles en la aplicacin.
El control de acceso (access control) y la jerarqua de roles (role hierarchy)
Una vez que el el usuario ha conseguido demostrar su identidad durante el proceso de
autentificacin, entra en juego el proceso de autorizacin.
La autorizacin permite proteger el acceso a los recursos (rutas) mediante el concepto de
rol, el cual es sencillamente una palabra clave que nos inventamos para asociarla a los
usuarios y a los recursos que deseamos proteger. De manera que el acceso a un recurso que
tenga asociado un rol (o una combinacin lgica de roles), ser permitido nicamente a los
usuarios que posean dicho rol (o cumplan la combinacin lgica de roles).
Por ello la configuracin del proceso de autorizacin tiene dos partes:
El control de acceso (access control) y la jerarqua de roles (role hierarchy)
167
1. La asignacin de roles a los usuarios, para lo que se utiliza el sistema de informacin
persisitente donde se encuentra informacin acerca de los roles (permisos) asociados al
usuario.
2. La asociacin de roles a los recursos, que se definen en la configuracin del servicio de
seguridad de Symfony2.
Symfony2 permite una mayor granularidad en el proceso de autorizacin, siendo posible
proteger con roles incluso partes concretas de las acciones y/o plantillas y los atributos de
las entidades mapeadas con Doctrine2.
El servicio de seguridad de Symfony2 permite organizar los roles en jerarquas, de manera
que un slo rol puede agrupar a varios y el proceso de autorizacin tratar al usuario que lo
posea como si tuviese todos los roles que se hayan definido en la jerarqua.
Adems el concepto de autorizacin va ms all de la proteccin por roles. Tambin se
puede exigir que los recursos sean accedidos nicamente desde ciertas IP's y/o dominos y
que sea necesario utilizar web segura (https).
Los codificadores (encoders)
Sea cual sea el sistema de informacin utilizado, las contraseas de los usuarios deberan
almacenarse codificadas. De esta manera se previene un eventual filtrado de contraseas
por parte de quien tiene acceso a dichos sistemas de informacin, ya sea legalmente por ser
administrador o ilegalmente por que ha conseguido penetrar en ellos.
La encriptacin o codificacin de las contraseas se realiza mediante algoritmos hash cuya
principal caracterstica es que codifican cadenas de caracteres en un nico sentido. Es decir,
no es posible la decodificacin. Por ello, si se almacena la versin codificada de la
contrasea, no se puede obtener a partir de esta la contrasea original.
Entonces, cmo se puede utilizar las contraseas codificadas en el proceso de
autentificacin?. Muy sencillo, se trata de codificar la cadena que el usuario facilita con el
mismo algoritmo que se utiliz para su almacenamiento, y comparar el resultado con la
constrasea codificada en el proveedor de usuarios.
El sistema de seguridad de Symfony2 tambin nos ayuda con la tarea de codificar la
contrasea en el proceso de autentificacin. Proporciona para ello un servicio de codificacin
(encoders) que se aplica sobre cada proveedor de usuarios para codificar la contrasea
facilitada por el usuario con el mismo algoritmo que se utiliz en el sistema de informacin
asociado al proveedor de usuarios en cuestin.
La seguridad en accin
Lleg el momento de aplicar estos conceptos en la prctica. La aplicacin y definicin de la
seguridad en Symfony2 se hace a travs del fichero a p p / c o n f i g / s e c u r i t y . y m l . Ah
realizaremos la configuracin de todos los elementos que acabamos de ver para proteger
nuestra aplicacin, es decir, la configuracin de los servicios que constituyen el sistema de
seguridad.
Nota
En la distribucin estndar de Symfony2, el fichero s e c u r i t y . y m l es incluido desde el
fichero de configuracin a p p / c o n f i g / c o n f i g . y m l . Por lo que tambin se podra
realizar la configuracin de la seguridad directamente en este ltimo archivo.
Los codificadores (encoders)
168
Abre el fichero a p p / c o n f i g / s e c u r i t y . y m l que trae como ejemplo la distribucin estndard
de Symfony2. chale un vistazo e identifica los conceptos que hemos estudiado en la
seccin anterior: user providers, firewalls, access control, role hierarchy y encoders. Para
poder comenzar a jugar necesitamos, por lo pronto un proveedor de usuarios. Ya dijimos que
Symfony2 proporciona dos tipos; uno basado en el propio fichero de configuracin y otro que
utiliza una base de datos. Comenzaremos utilizando el primero, denominado en Symfony2:
proveedor de usuarios in memory, pues el sistema lee la informacin de los usuarios del
propio fichero de configuracin y la ubica en memoria durante la ejecucin del script.
Una vez que tengamos varios usuarios en nuestro sistema, mostraremos como proteger los
recursos de nuestra aplicacin, utilizando para recoger las credenciales del usuario, primero
el mecanismo de autentificacin HTTP Basic y despus un formulario HTML. Ser este ltimo
el que utilizaremos finalmente en nuestra aplicacin.
Despus utilizaremos el mecanismo de autorizacin para proteger ciertas recursos de la
aplicacin con roles (permisos). Tambin veremos como podemos exigir que el acceso a los
recursos que deseemos se realice desde las IP's, dominios y canal (seguro/inseguro, esto es
http/https) que deseemos.
Finalmente sustituiremos el proveedor de usuarios in memory por una base de datos con las
claves debidamente codificadas que constituir el proveedor de usuarios definitivo de
nuestra aplicacin.
Al final de la unidad tendremos disponible el sistema de seguridad de nuestra aplicacin
debidamente configurado, habiendo dado un paso ms para su acabado definitivo.
El proveedor de usuarios in memory
Este proveedor de usuarios utiliza el ms simple de todos los sistemas de informacin, un
archivo de texto. Concretamente el mismo archivo s e c u r i t y . y m l .
En el ejemplo que viene de serie con la distribucin estndar de Symfony2 puedes ver como
se utiliza:
a p p / c o n f i g / s e c u r i t y . y m l
s e c u r i t y :
. . .
p r o v i d e r s :
i n _ m e m o r y :
u s e r s :
u s e r : { p a s s w o r d : u s e r p a s s , r o l e s : [ ' R O L E _ U S E R ' ] }
a d m i n : { p a s s w o r d : a d m i n p a s s , r o l e s : [ ' R O L E _ A D M I N ' ] }
. . .
La sintxis requiere pocas explicaciones. Bajo el item u s e r s del proveedor de usuarios
i n _ m e m o r y se aaden arrays asociativos cuyas claves son los usernames de los usuario y
cuyos valores son otros arrays asociativos con informacin de las contraseas y de los roles.
Vamos a adaptar este proveedor de usuario a nuestra aplicacin. Siguiendo los requisitos de
la unidad 5 llegamos a la conclusin de que necesitamos tres roles: usuario registrado,
usuario premium y administrador. Vamos a crear un usuario con cada tipo de rol. Nos
quedara lo siguiente:
a p p / c o n f i g / s e c u r i t y . y m l
s e c u r i t y :
. . .
El proveedor de usuarios in memory
169
p r o v i d e r s :
i n _ m e m o r y :
u s e r s :
a l b e r t o : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ R E G I S T R A D O ' ] }
m a r i a : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ P R E M I U M ' ] }
m i g u e l : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ A D M I N ' ] }
. . .
Pues ya est. Ya tenemos una fuente de datos para la identificacin en nuestra aplicacin.
Realmente sencilla no?. En efecto es demasiado sencilla. Si el nmero de usuarios es alto y
necesitamos almacenar ms atributos del usuario, este proveedor de usuarios no es el ms
adecuado. Pero puede ser suficiente para determinadas aplicaciones que no requieran
muchos usuarios y/o no precisen ms datos que los nombres de usuarios.
Por lo pronto, con objeto de no despistar demasiado, vamos a dejar las contraseas sin
codificar. Ms adelante veremos como hacerlo.
Autentificacin
Nota
Queremos mostrar en esta unidad como pueden coexistir en un mismo proyecto de
Symfony2 varios bundles con sus propios mecanismos de seguridad. Sin embargo,
como suele ocurrir en casi cualquier aspecto de la vida, pedir ms significa pagar ms.
En efecto, para evitar colisiones entre las rutas de nuestro bundle y las del que viene
de ejemplo con Symfony2, tendremos que anteponer un prefijo (n o t a s , por ejemplo) a
todas las rutas de nuestro bundle. Tampoco es que haya que realizar un esfuerzo
sobrehumano. Basta con colocar en el archivo a p p / c o n f i g / r o u t i n g . y m l el prefijo
n o t a s en la definicin de las rutas del bundle JAMNotasFrontendBundle:
. . .
J A M N o t a s F r o n t e n d B u n d l e :
r e s o u r c e : " @ J A M N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l "
p r e f i x : / n o t a s
. . .
La cosa es que da un poco de "coraje" tener que alargar la ruta. No obstante, si no
quisiramos mantener funcionando el bundle de ejemplo, no sera necesario esta
pequea modificacin. Pero, repetimos, la hacemos porque queremos mostrar la
enorme escalabilidad y flexibilidad del framework.
Antes de poder configurar los cortafuegos y los controles de acceso, necesitamos un modelo
para la seguridad de nuestra aplicacin. La tabla siguente recoge qu recursos se deben
proteger y qu roles se requieren para accederlos.
Recurso Acceso
/ n o t a s / n o t a s / c o n e t i q u e t a
/ n o t a s / b u s c a r / n o t a s / n o t a
/ n o t a s / b o r r a r
Para usuarios registrados, pero los usuarios no
premium tendrn publicidad
Autentificacin
170
/ n o t a s / n u e v a n o t a s / e d i t a r Para usuarios registrados , pero los premium
podrn subir archivos
/ n o t a s / m i e s p a c i o Para usuarios premium
n o t a s / r s s Pblico, pero contenidos extras en funcin del
perfil
/ n o t a s / r e g i s t r o / n o t a s / a c t i v a r
/ n o t a s / t a r i f a s / n o t a s / c o n t r a t a r
Pblico
/ n o t a s / a d m i n i s t r a c i o n Para administradores del sistema
Por otro lado, los usuario premium tendrn disponibles todas las funcionalidades de los
registrados y algunas extras ms. Los administradores, slo pueden acceder a la
administracin de la aplicacin.
Como ya hemos dicho anteriormente, podemos definir tantos cortafuegos como queramos,
cada uno puede utilizar un mecanismo de autentificacin distinto, un proveedor de usuarios
distintos, y por supuesto, configuraciones distintas. Lo importante es que no protejan los
mismos recursos, pues habra una colisin que se resolvera seleccionando el primer
cortafuegos que tenga una ruta coincidente con la que se ha solicitado. Observa que es el
mismo comportamiento que tiene el servicio Routing.
Fjate que en el archivo a p p / c o n f i g / s e c u r i t y . y m l hay tres cortafuegos definidos (d e v ,
l o g i n y s e c u r e d _ a r e a ). Son los que vienen de serie con el bundle AcmeDemoBundle. Lo que
haremos ser aadir otro cortafuegos para las rutas de nuestros bundles. Lo llamaremos
j a m n _ a r e a _ p r o t e g i d a .
Nota
Los cortafuegos que vienen de ejemplo no son en absoluto necesarios para nuestra
aplicacin. Los mantenemos para ilustrar que varios cortafuegos pueden convivir sin
ningn problema siempre que no colisionen las rutas que protegen.
Los cortafuegos admiten muchos parmetros. Al final de esta seccin mostraremos un
listado con todos ellos. No obstante son imprescindibles los siguientes:
p a t t e r n , que mediante una expresin regular expresa las rutas que se van a proteger
con dicho cortafuegos,
Uno de los siguientes: x 5 0 9 , h t t p _ b a s i c , h t t p _ d i g e s t o l o g i n _ f o r m , para indicar el
mecanismo que se utilizar para pedir las credenciales de identificacin. A su vez, estos
parmetros tienen distintas opciones.
Si hubiera ms de un proveedor de usuarios habra que indicarlo con el parmetro p r o v i d e r .
Como solo tenemos uno, no es necesario por lo pronto.
Autentificar con HTTP Basic
Con todo esto en mente vamos a proponer la configuracin del cortafuegos que nos parece
ms acertada. Hay que aclarar que hay ms de una que resuelve el problema. Aade el
siguiente cortafuegos al fichero s e c u r i t y . y m l :
a p p / c o n f i g / s e c u r i t y . y m l
Autentificar con HTTP Basic
171
1 f i r e w a l l s :
2 . . .
3
4 j a m n _ a r e a _ p r o t e g i d a :
5 p a t t e r n : ^ / n o t a s /
6 h t t p _ b a s i c :
7 r e a l m : " A r e a p r o t e g i d a "
8 . . .
Nota
Ten cuidado con las identaciones en este archivo, fjate que el cortafuegos
j a m n _ a r e a _ p r o t e g i d a pertenece a la seccin f i r e w a l l s y que antes hay varios
cortafuegos definidos representados por los puntos suspensivos. El item
j a m n _ a r e a _ p r o t e g i d a , debe estar al mismo nivel que los otros cortafuegos que
vienen de ejemplo (s e c u r e d _ a r e a , l o g i n y d e v )
Por lo pronto, hemos protegido todas las rutas de la aplicacin. Es decir el parmetro
p a t t e r n es ^ / n o t a s / . El carcter ^ al principio indica al patrn que las rutas coincidentes
son las que comienzan por / n o t a s / , y no las que contienen / n o t a s .
Adems hemos utilizado como mtodo de autentificacin el bsico de HTTP. Si ahora
intentas acceder a cualquier ruta del bundle JAMNotasFrontendBundle el navegador te
solicitar que ingreses un nombre de usuario y una contrasea. Para acceder puedes utilizar
cualquiera de los usuarios que tenemos en nuestro proveedor de usuarios.
Nota
Con la autentificacin HTTP, una vez que te hayas autentificado el navegador
mantendr el token de autentificacin hasta que lo cierres y lo vuelvas a abrir. Ten
esto en cuenta cuando hagas pruebas. Puedes entrar en una ruta protegida cuando
esperas que te salga el formulario de autentificacin y volverte un poco loco hasta que
te das cuenta de que tienes que reiniciar el navegador para cerrar la sesin.
El problema es que de esta manera hemos protegido tambin las rutas que deben ser
pblicas, como por ejemplo / n o t a s / r e g i s t r o / . La solucin a este problema consiste en
aadir un "anti-cortafuegos", que no es ms que otro cortafuegos en el que se indica un
patrn de rutas al que no se debe activar la autentificacin:
a p p / c o n f i g / s e c u r i t y . y m l
1 f i r e w a l l s :
2 . . .
3 j a m n _ a r e a _ p u b l i c a :
4 p a t t e r n : ^ / n o t a s / ( r e g i s t r o | a c t i v a r | t a r i f a s | c o n t r a t a r )
5 s e c u r i t y : f a l s e
6
Autentificar con HTTP Basic
172
7 j a m n _ a r e a _ p r o t e g i d a :
8 p a t t e r n : ^ / n o t a s /
9 h t t p _ b a s i c :
1 0 r e a l m : " A r e a p r o t e g i d a "
1 1 . . .
El nuevo cortafuegos (lneas 3-5) acta, como anti-cortafuegos del que le sigue (lneas 7-10),
desprotegiendo, es decir, haciendo pblicas las rutas
^ / n o t a s / r e g i s t r o
^ / n o t a s / a c t i v a r
^ / n o t a s / t a r i f a s
^ / n o t a s / c o n t r a t a r
pues son las que coinciden con la expresin regular especificada en su parmetro p a t t e r n s .
El parmetro s e c u r i t y : f a l s e (lnea 5) es el que desactiva la proteccin de estas rutas.
Importante
Para que esto funcione es imprescindible que el cortafuegos j a m n _ a r e a _ p u b l i c a se
declare antes que j a m n _ a r e a _ p r o t e g i d a , pues al ser el patrn del primero ms
especfico que el del segundo, si lo colocamos despus nunca entrara en
funcionamiento.
Nota
Esta estrategia de proteger todo y, posteriormente, abrir las puerta que se precisen es
muy tpica en el mundo de la seguridad y concretamente en la configuracin de
cortafuegos de red reales.
Nota
Cuando hagas pruebas, vers que las rutas que hemos desprotegido arrojan un error,
pero es debido a que an no hemos escrito sus acciones asociadas. No tienen nada
que ver con el cortafuegos. Lee bien el error.
Ahora el problema que tenemos es que cualquier usuario autentificado puede acceder a
todos los recursos protegidos de la aplicacin. Pero eso no es un problema de la
autentificacin, sino de la autorizacin.
Autentificar con un formulario de Login tradicional
Antes de resolver el problema de la autorizacin, vamos a cambiar el mecanismo de
autentificacin por un formulario HTML que podremos disear a nuestro gusto y que estar
Autentificar con un formulario de Login tradicional
173
ms integrado con la aplicacin.
Para ello cambiamos el parmetro h t t p _ b a s i c por f o r m _ l o g i n . Y agregamos la
configuracin mnima que necesita este mecanismos de autentificacin para funcionar: una
ruta que pinte el formulario de login, y otra ruta a la que enviar las credenciales introducidas
por el usuario en dicho formulario. El fichero s e c u r i t y . y m l quedara as:
a p p / c o n f i g / s e c u r i t y . y m l
1 f i r e w a l l s :
2 . . .
3 j a m n _ a r e a _ p u b l i c a :
4 p a t t e r n : ^ / n o t a s / ( d e m o | r e g i s t r o | a c t i v a r | t a r i f a s | c o n t r a t a r | l o g i n $ )
5 s e c u r i t y : f a l s e
6
7 j a m n _ a r e a _ p r o t e g i d a :
8 p a t t e r n : ^ / n o t a s /
9 f o r m _ l o g i n :
1 0 l o g i n _ p a t h : / n o t a s / l o g i n
1 1 c h e c k _ p a t h : / n o t a s / l o g i n _ c h e c k
1 2 . . .
En las lneas 9-11 se realiza la especificacin del formulario de login. Se trata de definir dos
rutas: l o g i n _ p a t h y c h e c k _ p a t h , la primera debe dirigir a la accin que muestre el
formulario de login, y la segunda se utiliza para comprobar las credenciales enviadas por
dicho formulario.
El mecanismo de autentificacin funciona ahora de la siguiente manera: cuando un usuario
no autentificado solicita un recurso protegido por el cortafuegos, este le redirige a la URL
asociada a la ruta l o g i n _ p a t h , la cual se encarga de pintar el formulario de login. El atributo
a c t i o n de este formulario debe corresponderse con la URL de la ruta asociada al parmetro
c h e c k _ p a t h . As pues, los datos facilitados por el usuario son enviados a esta URL, la cual es
muy especial, ya que es el sistema de seguridad de Symfony2 quien se encarga de
interceptarla para llevar a cabo el proceso de autentificacin. Por eso no hay que asociar
ningn controlador a la ruta c h e c k _ p a t h , tan solo hay que aadirla al fichero r o u t i n g . y m l .
Aadimos, por tanto, al fichero de rutas de nuestro bundle las siguientes rutas (lo hacemos
al principio del mismo):
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l
1 j a m n _ l o g i n :
2 p a t t e r n : / l o g i n
3 d e f a u l t s : { _ c o n t r o l l e r : J A M N o t a s F r o n t e n d B u n d l e : L o g i n : l o g i n }
4
5 j a m n _ l o g i n _ c h e c k :
6 p a t t e r n : / l o g i n _ c h e c k
Para que el sistema funcione es muy importante:
1. Que la ruta asociada al parmetro l o g i n _ p a t h sea pblica. En caso contrario el proceso
quedara atrapado en un bucle infinito. Por esta razn se ha aadido a la expresin
regular del p a t t e r n del (anti)cortafuegos j a m n _ a r e a _ p u b l i c a la ruta n o t a s / l o g i n
(final de la lnea 4).
2. Que la ruta asociada al parmetro c h e c k _ p a t h est detrs del cortafuegos. En caso
contrario el sistema se quejara diciendo que no hay ningn controlador asociado a la
ruta. Por eso se ha aadido el cdigo $ en la expresin regular de la lnea 4. Sin ese
Autentificar con un formulario de Login tradicional
174
cdigo la ruta n o t a s / l o g i n _ c h e c k tambin cumplira la expresin regular y, por tanto,
pertenecera al (anti)cortafuegos.
3. Que las variables asociadas a los campos username y password se llamen _ u s e r n a m e y
_ p a s s w o r d respectivamente (se puede cambiar por configuracin estos nombres).
Tan slo nos queda crear la accin y la plantilla que pinta el formulario de login y que estn
asociadas a la ruta j a m n _ l o g i n (es decir, asociada al parmetros del cortafuegos
l o g i n _ p a t h ). Como puedes ver en la definicin de esta ruta, hemos optado por crear la
accin de login en un controlador distinto denominado L o g i n C o n t r o l l e r . El cdigo de este
controlador quedara as:
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / L o g i n C o n t r o l l e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ;
4
5 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
6 u s e S y m f o n y \ C o m p o n e n t \ S e c u r i t y \ C o r e \ S e c u r i t y C o n t e x t ;
7
8 c l a s s L o g i n C o n t r o l l e r e x t e n d s C o n t r o l l e r
9 {
1 0
1 1 p u b l i c f u n c t i o n l o g i n A c t i o n ( )
1 2 {
1 3 i f ( $ t h i s - > g e t ( ' r e q u e s t ' ) - > a t t r i b u t e s - > h a s ( S e c u r i t y C o n t e x t : : A U T H E N T I C A T I O N _ E R R O R ) ) {
1 4 $ e r r o r = $ t h i s - > g e t ( ' r e q u e s t ' ) - > a t t r i b u t e s - > g e t ( S e c u r i t y C o n t e x t : : A U T H E N T I C A T I O N _ E R R O R ) ;
1 5 } e l s e {
1 6 $ e r r o r = $ t h i s - > g e t ( ' s e s s i o n ' ) - > g e t ( S e c u r i t y C o n t e x t : : A U T H E N T I C A T I O N _ E R R O R ) ;
1 7 }
1 8
1 9 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : L o g i n : l o g i n . h t m l . t w i g ' , a r r a y (
2 0 ' l a s t _ u s e r n a m e ' = > $ t h i s - > g e t ( ' r e q u e s t ' ) - > g e t S e s s i o n ( ) - > g e t ( S e c u r i t y C o n t e x t : : L A S T _ U S E R N A M E ) ,
2 1 ' e r r o r ' = > $ e r r o r ,
2 2
2 3 ) ) ;
2 4 }
2 5 }
Lo nico que hace esta accin es comprobar si en la Request existe el atributo
S e c u r i t y C o n t e x t : : A U T H E N T I C A T I O N _ E R R O R . En caso afirmativo se recupera de la propia
Request un array con los errores de autentificacin, y en caso contrario, estos errores se
recuperan de la sesin. Dichos errores se pasan, junto con el ltimo nombre de usuario
almacenado en la sesin, a la plantilla. Obviamente si no hay errores, el array est vaco.
La plantilla correspondiente a la accin anterior sera:
J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w / L o g i n / l o g i n . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4 < h 1 > F o r m u l a r i o d e L o g i n < / h 1 >
5
6 { % i f e r r o r % }
7 < d i v c l a s s = " e r r o r " > { { e r r o r . m e s s a g e } } < / d i v >
8 { % e n d i f % }
9
1 0 < f o r m a c t i o n = " { { p a t h ( " j a m n _ l o g i n _ c h e c k " ) } } " m e t h o d = " p o s t " i d = " l o g i n " >
1 1 < d i v >
1 2 < l a b e l f o r = " u s e r n a m e " > U s e r n a m e < / l a b e l >
1 3 < i n p u t t y p e = " t e x t " i d = " u s e r n a m e " n a m e = " _ u s e r n a m e " v a l u e = " { { l a s t _ u s e r n a m e } } " / >
1 4 < / d i v >
Autentificar con un formulario de Login tradicional
175
1 5
1 6 < d i v >
1 7 < l a b e l f o r = " p a s s w o r d " > P a s s w o r d < / l a b e l >
1 8 < i n p u t t y p e = " p a s s w o r d " i d = " p a s s w o r d " n a m e = " _ p a s s w o r d " / >
1 9 < / d i v >
2 0
2 1 < i n p u t t y p e = " s u b m i t " c l a s s = " s y m f o n y - b u t t o n - g r e y " v a l u e = " L O G I N " / >
2 2 < / f o r m >
2 3 { % e n d b l o c k % }
Observa como las variables asociadas a los campos username y password se llaman
_ u s e r n a m e y _ p a s s w o r d respectivamente (lneas 13 y 18), y como el atributo a c t i o n del
formulario se corresponde con la ruta apuntada por c h e c k _ p a t h , es decir;
j a m n l _ l o g i n _ c h e c k (lnea 10).
Y ya tenemos habilitado un formulario HTML para el proceso de autentificacin de nuestra
aplicacin. En la siguiente unidad mejoraremos este formulario utilizando el servicio de
formularios de Symfony2. As podremos aadir reglas de validacin fcilmente a cada uno de
los campos. Tambin integraremos el formulario con el diseo de la aplicacin. Por lo pronto,
para los propsitos de esta unidad, este nos vale tal y como est. Ya puedes probar el nuevo
mecanismo de autenticacin. Recuerda borrar la cach de tu navegador para borrar la
sesin.
Saliendo de la sesin (logout)
Otro elemento muy til que se puede definir en los cortafuegos es el mecanismo de cierre de
sesin (logout). Activarlo es realmente sencillo. Basta con indicar la ruta que realizar el
logout y aadirla al fichero r o u t i n g . y m l . No tenemos que asociar ningn controlador a esta
ruta puesto que el mecanismo de seguridad de Symfony2 la intercepta y pone en marcha el
fin de la sesin.
a p p / c o n f i g / s e c u r i t y . y m l
1 f i r e w a l l s :
2 . . .
3 j a m n _ a r e a _ p u b l i c a :
4 p a t t e r n : ^ / n o t a s / ( d e m o | r e g i s t r o | a c t i v a r | t a r i f a s | c o n t r a t a r | l o g i n $ )
5 s e c u r i t y : f a l s e
6
7 j a m n _ a r e a _ p r o t e g i d a :
8 p a t t e r n : ^ / n o t a s
9 f o r m _ l o g i n :
1 0 l o g i n _ p a t h : / n o t a s / l o g i n
1 1 c h e c k _ p a t h : / n o t a s / l o g i n _ c h e c k
1 2 l o g o u t :
1 3 p a t h : / n o t a s / l o g o u t
1 4 t a r g e t : / n o t a s
Las lneas 12-14 activan el proceso de logout. Se definen dos rutas, la que inicia el proceso
(p a t h ) y la ruta a la que se redirige la aplicacin una vez finalizada la sesin (t a r g e t ).
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g . y m l
1 . . .
2 j a m n _ l o g o u t :
Saliendo de la sesin (logout)
176
3 p a t t e r n : / l o g o u t
4 . . .
Ahora podemos cerrar la sesin solicitando la ruta j a m n _ l o g o u t . Lo ms adecuado es incluir
un enlace a esta ruta en el layout de la aplicacin, para que el usuario sepa como puede
hacer logout.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g ,
1 . . .
2 { % b l o c k b o d y % }
3 < a h r e f = " { { p a t h ( ' j a m n _ l o g o u t ' ) } } " > C e r r a r s e s i n < / a >
4 < h 1 > I m p l e m e n t a c i n d e l a l g i c a d e c o n t r o l d e l a a p l i c a c i n < / h 1 >
5
6 . . .
Autorizacin
La autorizacin se configura en las secciones a c c e s s _ c o n t r o l y r o l e _ h i e r a r c h y del
archivo s e c u r i t y . y m l . Vamos a comenzar por definir nuestra jerarqua de roles.
Segn hemos establecido en el modelo de seguridad: los usuario premium tendrn
disponibles todas las funcionalidades de los registrados y algunas extras ms. Los
administradores, slo pueden acceder a la administracin de la aplicacin.
Estos requisitos pueden resolverse de varias formas. La jerarqua de roles nos ayuda
permitiendo que el rol R O L _ P R E M I U M contenga al rol R O L _ R E G I S T R A D O .
a p p / c o n f i g / s e c u r i t y . y m l
1 . . .
2 r o l e _ h i e r a r c h y :
3 R O L E _ P R E M I U M : [ R O L E _ R E G I S T R A D O ]
4 . . .
Con esta configuracin los usuarios que dispongan del rol R O L E _ P R E M I U M tambin se
comportar de cara al proceso de autorizacin como si tuvieran el rol R O L E _ R E G I S T R A D O .
Dependiendo de las necesidades de la aplicacin respecto a la autorizacin, nuestra
jerarqua de roles puede ser ms o menos compleja. En ciertos casos puede que resulte
innecesaria. Pero la verdad es que, incluso para casos sencillos como el de nuestra
aplicacin, resulta muy util para organizar los permisos.
Protegiendo los recursos de la aplicacin
Ahora vamos a utilizar la tabla con el modelo de seguridad que hemos presentado en esta
misma unidad para llevar a cabo la configuracin del control de accesos. El resultado es el
siguiente:
a p p / c o n f i g / s e c u r i t y . y m l
1 . . .
2 a c c e s s _ c o n t r o l :
3 - { p a t h : ^ / n o t a s / m i e s p a c i o , r o l e s : R O L E _ P R E M I U M }
4 - { p a t h : ^ / n o t a s / a d m i n i s t r a c i o n , r o l e s : R O L E _ A D M I N }
5 - { p a t h : ^ / n o t a s , r o l e s : R O L E _ R E G I S T R A D O }
Autorizacin
177
Advertencia
El orden en el que hemos puesto las rutas es imprescidible para que nuestro modelo
de autorizacin funcione correctamente. Es decir, hay que colocar las rutas de forma
que las expresiones regulares vayan desde las ms especficas a las ms generales,
pues si se hace al revs siempre se cumplir la ms general antes que las ms
especfica, facilitando la entrada en recursos con los permisos no deseados.
Protegiendo los controladores y plantillas de la aplicacin
En el modelo de seguridad establecimos que las rutas:
j a m n _ h o m e p a g e
j a m n _ c o n e t i q u e t a
j a m n _ b u s c a r
j a m n _ n o t a
j a m n _ b o r r a r
mostraran publicidad a los usuarios no premium, y las rutas
j a m n _ n u e v a
j a m n _ e d i t a r
permitiran a los usuarios premium adjuntar archivos en las notas.
Esta mayor granularidad en el acceso a los recursos se puede conseguir mediante el servicio
s e c u r i t y . c o n t e x t de Symfony2. La manera de comprobar si un usuario tiene un
determinado rol en una accin es:
< ? p h p
u s e S y m f o n y \ C o m p o n e n t \ S e c u r i t y \ C o r e \ E x c e p t i o n \ A c c e s s D e n i e d E x c e p t i o n ;
. . .
p u b l i c f u n c t i o n h o l a A c t i o n ( )
{
i f ( f a l s e = = = $ t h i s - > g e t ( ' s e c u r i t y . c o n t e x t ' ) - > i s G r a n t e d ( ' R O L E _ A D M I N ' ) )
{
/ / A q u e l c d i g o p a r a e l u s u a r i o q u e n o t e n g a e l r o l , p o r
/ / e j e m p l o l a n z a r u n a e x c e p c i n d e a c c e s o d e n e g a d o :
/ / t h r o w n e w A c c e s s D e n i e d E x c e p t i o n ( ) ;
}
. . .
}
Tambin podemos poner condiciones de control de acceso en las plantillas twig mediante la
funcin i s _ g r a n t e d :
Protegiendo los controladores y plantillas de la aplicacin
178
{ % i f i s _ g r a n t e d ( ' R O L E _ A D M I N ' ) % }
< a h r e f = " . . . " > D e l e t e < / a >
{ % e n d i f % }
Modificando la plantilla l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g , de esta manera conseguimos
cumplir el requisito referente a la presentacin de publicidad a usuarios no premium:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g ,
1 . . .
2 { % b l o c k b o d y % }
3
4 < h 1 > I m p l e m e n t a c i n d e l a l g i c a d e c o n t r o l d e l a a p l i c a c i n < / h 1 >
5
6 { % i f n o t i s _ g r a n t e d ( ' R O L E _ P R E M I U M ' ) % }
7 < h 2 > P U B L I C I D A D A Q U < / h 2 >
8 { % e n d i f % }
9 . . .
En la siguiente unidad le daremos la forma definitiva a esta funcionalidad y tambin
incorporaremos la subida de adjuntos segn el rol. Por lo pronto valga esto para mostrar
como se puede proteger el acceso a partes de controladores y plantillas.
Exigiendo canal seguro
En las especificaciones de la aplicacin no se deca nada acerca de usar web segura. Sin
embargo es una buena idea exigir este requisito, al menos en la parte de administracin. La
manera de hacer esto es aadindo el parmetro r e q u i r e s _ c h a n n e l a la ruta del
a c c e s s _ c o n t r o l que queramos exigir un canal seguro:
a p p / c o n f i g / s e c u r i t y . y m l
1 . . .
2 a c c e s s _ c o n t r o l :
3 - { p a t h : ^ / m i e s p a c i o , r o l e s : R O L E _ P R E M I U M }
4 - { p a t h : ^ / a d m i n i s t r a c i o n , r o l e s : R O L E _ A D M I N , r e q u i r e s _ c h a n n e l : h t t p s }
5 - { p a t h : ^ / , r o l e s : R O L E _ R E G I S T R A D O }
Tambin podemos exigir que se acceda nicamente desde ciertas IP's y/o desde ciertos
dominios con los parmetros i p y h o s t respectivamente.
La base de datos como proveedor de usuarios
En esta seccin vamos a cambiar el sencillo pero til proveedor de usuarios i n - m e m o r y , por
uno ms sofisticado y completo basado en una clase entidad de Doctrine2 que, por tanto,
utiliza una base de datos para comprobar y obtener los datos de los usuarios. Y lo bueno es
que todo lo que hemos hecho para crear el mecanismo de autentificacin basado en un
formulario HTML seguir siendo vlido.
Cada proveedor de usuarios tiene asociada una clase U s e r que "sabe" manipular los datos
del usuario en el sistema de informacin que utilice.
Exigiendo canal seguro
179
Nota
Para el proveedor i n - m e m o r y que hemos estudiado ms arriba, esta clase es
S y m f o n y \ C o m p o n e n t \ S e c u r i t y \ C o r e \ U s e r \ U s e r . Si tienes curiosidad puedes verla en
el archivo
v e n d o r / s y m f o n y / s r c / S y m f o n y / C o m p o n e n t / S e c u r i t y / C o r e / U s e r / U s e r . p h p .
En el caso del proveedor i n - m e m o r y dicha clase est perfectamente construida y acabada, y
ni siquiera es necesario conocer su existencia para utilizarlo. Basta con definir correctamete
su configuracin en el fichero s e c u r i t y . y m l .
Sin embargo, en el caso del proveedor de usuarios basado en entidad, la clase U s e r la tiene
que construir el programador. Esto es as puesto que el proveedor de usuarios no conoce a
priori como estn organizados los datos del usuario en la base de datos en cuestin. Por ello,
hay que "ensearle" para que pueda realizar su funcin con xito.
Como esta clase entidad, ser utilizada internamente en los procesos de autorizacin y
autentificacin, los diseadores del componente de seguridad de Symfony2 han tenido la
precaucin de definir una Interfaz de PHP que obligatoriamente debe ser implementada por
la entidad que vayamos a utilizar en el proceso.
En realidad el componente de seguridad proporciona dos interfaces que podemos utilizar
segn las funcionalidades que requiramos. La ms sencilla se denomina U s e r I n t e r f a c e , y
exige la implementacin de los siguientes mtodos:
e q u a l s ( $ u s e r ) , mtodo que debe devolver t r u e cuando el argumento sea igual al
objeto t h i s . El criterio de igualdad lo decide el programador siguiendo las
caractersticas de su modelo de usuarios.
g e t P a s s w o r d ( ) , debe devolver el password del usuario.
g e t U s e r n a m e ( ) , debe devolver el username del usuario.
g e t R o l e s ( ) , debe devolver un array con los roles del usuario.
g e t S a l t ( ) , debe devolver la s a l t utilizada para codificar el password del usuario (en
caso de que se haya codificado)
e r a s e C r e d e n t i a l s ( ) , para eliminar datos sensitivos que se puedan almacenar en el
objeto. No suele ser necesario implementar este mtodo.
La otra interfaz proporcionada se denomina A d v a n c e d U s e r I n t e r f a z , y define los mismos
mtodos que la anterior ms algunos referentes a las condiciones de
activacin/desactivacin de usuarios.
i s A c c o u n t N o n E x p i r e d ( ) , comprueba si la cuenta del usuario ha expirado.
i s A c c o u n t N o n L o c k e d ( ) , comprueba si la cuenta del usuario ha sido bloqueada.
i s C r e d e n t i a l s N o n E x p i r e d ( ) , comprueba si la credencial (password, por ejemplo) ha
expirado.
i s E n a b l e d ( ) , comprueba si el usuario est habilitado.
La semntica de las operaciones anteriores, es decir lo que signifique cuenta no expirada,
cuenta bloqueada, credencial no expirada y usuario habilitado, la proporciona el
programador de la aplicacin en funcin de su modelo de usuarios.
Si decidimos utilizar la interfaz avanzada de usuarios (A d v a n c e d U s e r I n t e r f a z ), el proceso
de autentificacin realizar comprobaciones sobre la activacin del usuario utilizando los
Exigiendo canal seguro
180
mtodos anterioriores. Si utilizamos la interfaz ms sencilla U s e r I n t e r f a z , tales
comprobaciones no se llevarn a cabo.
Utilizaremos la interfaz avanzada de usuarios para construir nuestra entidad de usuarios. Por
un poquito ms de trabajo tendremos habilitado un completo sistema de control de nuestros
usuarios.
En realidad, la entidad usuario ya la tenemos casi lista. En efecto, podemos utilizar nuestra
entidad J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o para conectar al proveedor de usuarios con
nuestra base de datos. Simplemente debemos hacer que dicha clase implemente la interfaz
A d v a n c e d U s e r I n t e r f a c e . Fjate que muchos de los mtodos exigidos por esta ltima ya los
tenemos construidos. As que solo tendremos que aadir y definir los que nos falten, es
decir:
g e t R o l e s ( )
e r a s e C r e d e n t i a l s ( )
i s A c c o u n t N o n E x p i r e d ( )
i s A c c o u n t N o n L o c k e d ( )
i s C r e d e n t i a l s N o n E x p i r e d ( )
i s E n a b l e d ( )
A continuacin mostramos como quedara nuestra entidad
J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o tras la implementacin de la interfaz.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / E n t i t y / U s u a r i o . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y ;
4
5 u s e D o c t r i n e \ O R M \ M a p p i n g a s O R M ;
6 u s e D o c t r i n e \ C o m m o n \ C o l l e c t i o n s \ A r r a y C o l l e c t i o n ;
7 u s e S y m f o n y \ C o m p o n e n t \ V a l i d a t o r \ C o n s t r a i n t s a s A s s e r t ;
8 u s e S y m f o n y \ C o m p o n e n t \ S e c u r i t y \ C o r e \ U s e r \ U s e r I n t e r f a c e ;
9 u s e S y m f o n y \ C o m p o n e n t \ S e c u r i t y \ C o r e \ U s e r \ A d v a n c e d U s e r I n t e r f a c e ;
1 0
1 1 / * *
1 2 * J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o
1 3 *
1 4 * @ O R M \ T a b l e ( )
1 5 * @ O R M \ E n t i t y
1 6 * /
1 7 c l a s s U s u a r i o i m p l e m e n t s A d v a n c e d U s e r I n t e r f a c e
1 8 {
1 9
2 0 / / A q u t o d o s l o s m t o d o s y a i m p l e m e n t a d o s . . .
2 1
2 2 / / / / M t o d o s d e l a i n t e r f a z A d v a n c e d U s e r I n t e r f a c e q u e f a l t a n / / / /
2 3
2 4 p u b l i c f u n c t i o n e r a s e C r e d e n t i a l s ( )
2 5 {
2 6
2 7 }
2 8
Exigiendo canal seguro
181
2 9 f u n c t i o n e q u a l s ( U s e r I n t e r f a c e $ u s e r )
3 0 {
3 1 r e t u r n $ u s e r - > g e t U s e r n a m e ( ) = = = $ t h i s - > u s e r n a m e ;
3 2 }
3 3
3 4 p u b l i c f u n c t i o n g e t R o l e s ( )
3 5 {
3 6 $ r o l e s = a r r a y ( ) ;
3 7 f o r e a c h ( $ t h i s - > g r u p o s a s $ g )
3 8 {
3 9 $ r o l e s [ ] = $ g - > g e t R o l ( ) ;
4 0 }
4 1
4 2 r e t u r n $ r o l e s ;
4 3 }
4 4
4 5 p u b l i c f u n c t i o n i s A c c o u n t N o n E x p i r e d ( )
4 6 {
4 7 r e t u r n t r u e ;
4 8 }
4 9
5 0 p u b l i c f u n c t i o n i s A c c o u n t N o n L o c k e d ( )
5 1 {
5 2 r e t u r n t r u e ;
5 3 }
5 4
5 5 p u b l i c f u n c t i o n i s C r e d e n t i a l s N o n E x p i r e d ( )
5 6 {
5 7 r e t u r n t r u e ;
5 8 }
5 9
6 0 p u b l i c f u n c t i o n i s E n a b l e d ( )
6 1 {
6 2 r e t u r n $ t h i s - > g e t I s A c t i v e ( ) ;
6 3 }
6 4 }
En nuestro modelo cada grupo tiene definido un rol, as que la construccin del array de
roles en el mtodo g e t R o l e s ( ) es muy sencilla.
Nota
Un modelo ms potente y flexible sera aquel en el que los roles estn en otra tabla y
se asocian con los grupos mediante una tabla intermedia.
Por otro lado, como nuestro modelo no tiene en cuenta ni la caducidad ni el bloqueo de
cuentas de usuarios, los mtodos i s A c c o u n t N o n E x p i r e d ( ) , i s A c c o u n t N o n L o c k e d ( ) y
i s C r e d e n t i a l s N o n E x p i r e d ( ) , devuelven directamente un valor t r u e . Por ltimo, para
nosotroso un usuario habilitado es un usuario activo, as que el mtodo i s E n a b l e d ( )
devuelve precisamente el valor del atributo i s A c t i v e .
Exigiendo canal seguro
182
El prximo paso es aadir al fichero s e c u r i t y . y m l un nuevo proveedor de usuarios
asociado a la entidad que acabamos de crear, e indicar en el cortafuegos de nuestra
aplicacin que deseamos utilizar este nuevo proveedor de usuarios. El fichero s e c u r i t y . y m l
queda finalmente as:
a p p / c o n f i g / s e c u r i t y . y m l
1 s e c u r i t y :
2 e n c o d e r s :
3 S y m f o n y \ C o m p o n e n t \ S e c u r i t y \ C o r e \ U s e r \ U s e r : p l a i n t e x t
4 J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o : p l a i n t e x t
5
6 r o l e _ h i e r a r c h y :
7 R O L E _ A D M I N : R O L E _ U S E R
8 R O L E _ S U P E R _ A D M I N : [ R O L E _ U S E R , R O L E _ A D M I N , R O L E _ A L L O W E D _ T O _ S W I T C H ]
9 R O L E _ P R E M I U M : [ R O L E _ R E G I S T R A D O ]
1 0
1 1 p r o v i d e r s :
1 2 i n _ m e m o r y :
1 3 u s e r s :
1 4 a l b e r t o : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ R E G I S T R A D O ' ] }
1 5 m a r i a : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ P R E M I U M ' ] }
1 6 m i g u e l : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ A D M I N ' ] }
1 7
1 8 b a s e _ d a t o s :
1 9 e n t i t y : { c l a s s : J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o , p r o p e r t y : u s e r n a m e }
2 0
2 1 f i r e w a l l s :
2 2 d e v :
2 3 p a t t e r n : ^ / ( _ ( p r o f i l e r | w d t ) | c s s | i m a g e s | j s ) /
2 4 s e c u r i t y : f a l s e
2 5
2 6 l o g i n :
2 7 p a t t e r n : ^ / d e m o / s e c u r e d / l o g i n $
2 8 s e c u r i t y : f a l s e
2 9
3 0 s e c u r e d _ a r e a :
3 1 p a t t e r n : ^ / d e m o / s e c u r e d /
3 2 f o r m _ l o g i n :
3 3 c h e c k _ p a t h : / d e m o / s e c u r e d / l o g i n _ c h e c k
3 4 l o g i n _ p a t h : / d e m o / s e c u r e d / l o g i n
3 5 l o g o u t :
3 6 p a t h : / d e m o / s e c u r e d / l o g o u t
3 7 t a r g e t : / d e m o /
3 8 # a n o n y m o u s : ~
3 9 # h t t p _ b a s i c :
4 0 # r e a l m : " S e c u r e d D e m o A r e a "
4 1 j a m n _ a r e a _ p u b l i c a :
4 2 p a t t e r n : ^ / n o t a s / ( d e m o | r e g i s t r o | a c t i v a r | t a r i f a s | c o n t r a t a r | l o g i n $ )
4 3 s e c u r i t y : f a l s e
4 4
4 5 j a m n _ a r e a _ p r o t e g i d a :
4 6 p a t t e r n : ^ / n o t a s
4 7 p r o v i d e r : b a s e _ d a t o s
4 8 f o r m _ l o g i n :
4 9 l o g i n _ p a t h : / n o t a s / l o g i n
5 0 c h e c k _ p a t h : / n o t a s / l o g i n _ c h e c k
5 1
Exigiendo canal seguro
183
5 2 l o g o u t :
5 3 p a t h : / n o t a s / l o g o u t
5 4 t a r g e t : / n o t a s
5 5
5 6 a c c e s s _ c o n t r o l :
5 7 - { p a t h : ^ / n o t a s / m i e s p a c i o , r o l e s : R O L E _ P R E M I U M }
5 8 - { p a t h : ^ / n o t a s / a d m i n i s t r a c i o n , r o l e s : R O L E _ A D M I N , r e q u i r e s _ c h a n n e l : h t t p s }
5 9 - { p a t h : ^ / n o t a s , r o l e s : R O L E _ R E G I S T R A D O }
La definicin del nuevo proveedor de usuarios basado en la entidad de Doctrine2 se realiza
en las lneas 18-19. Como ves simplemente hay que indicar el nombre de la entidad que
implementa la interfaz de usuario. Luego, en la lnea 47, se exige al cortafuegos
j a m n _ a r e a _ p r o t e g i d a que use como proveedor de usuarios el que acabamos de crear.
Observa tambin que hemos aadido un e n c o d e r en la lnea 4 para la entidad
J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o , ya que es obligatorio definir un e n c o d e r para las clases
U s e r asociadas a los proveedores de identidad. Por lo pronto hemos utilizado el ms sencillo,
que deja el password sin codificar. En la prxima seccin lo cambiaremos por uno ms
robusto y explicaremos como utilizarlo.
Para que puedas probar el nuevo proveedor de usuarios, tienes que introducir algunos
usuarios en la base de datos rellenando, al menos, el campo u s e r n a m e y p a s s w o r d . Adems
debes aadir en la tabla g r u p o s los tres registros siguientes:
Tabla Grupo
nombre rol
registrado ROLE_REGISTRADO
premium ROLE_PREMIUM
admin ROLE_ADMIN
Y asociar grupos a los usuarios, a travs de la tabla u s u a r i o _ g r u p o .
Nota
Si no ests utilizando el mecanismos de Fixtures que hemos explicado en los ejercicios de la unidad 7, puedes utilizar el siguiente script SQL para
insertar usuario y grupos.
I N S E R T I N T O ` G r u p o ` ( ` i d ` , ` n o m b r e ` , ` r o l ` ) V A L U E S
( 1 , ' r e g i s t r a d o ' , ' R O L E _ R E G I S T R A D O ' ) ,
( 2 , ' p r e m i u m ' , ' R O L E _ P R E M I U M ' ) ,
( 3 , ' a d m i n i s t r a d o r ' , ' R O L E _ A D M I N ' ) ;
I N S E R T I N T O ` U s u a r i o ` ( ` i d ` , ` n o m b r e ` , ` a p e l l i d o s ` , ` s a l t ` , ` u s e r n a m e ` , ` p a s s w o r d ` , ` e m a i l ` , ` i s A c t i v e ` , ` t o k e n R e g i s t r o ` ) V A L U E S
( 1 , ' A l b e r t o ' , ' E i n s t e i n ' , ' ' , ' a l b e r t o ' , ' p r u e b a s ' , ' a l b e r t o @ m e n t o r n o t a s . e s ' , 1 , ' ' ) ,
( 2 , ' M a r i a ' , ' C u r i e ' , ' ' , ' m a r i a ' , ' p r u e b a s ' , ' m a r i a @ m e n t o r n o t a s . e s ' , 1 , ' ' ) ,
( 3 , ' M i g u e l ' , ' F a r a d a y ' , ' ' , ' m i g u e l ' , ' p r u e b a s ' , ' m i g u e l @ m e n t o r n o t a s . e s ' , 1 , ' ' ) ;
I N S E R T I N T O ` u s u a r i o _ g r u p o ` ( ` u s u a r i o _ i d ` , ` g r u p o _ i d ` ) V A L U E S
( 1 , 1 ) ,
( 2 , 2 ) ,
( 3 , 3 ) ;
El servicio de codificacin. Los e n c o d e r s
La funcin de los e n c o d e r s qued clara al principio de la unidad. En esta seccin veremos
como utilizarlos en la prctica.
Por lo general, los passwords son almacenados en los sistemas de informacin codificados
mediante algn algoritmo hash. Por eso cada proveedor de usuarios debe conocer qu
El servicio de codificacin. Los encoders
184
algoritmo aplicar al password facilitado por el mecanismo de autentificacin para contrastar
con el valor almacenado en el sistema de informacin. Y esa es precisamente la funcin del
servicio de codificacin: especificar el algoritmo que se debe aplicar al password.
En la seccin e n c o d e r s del fichero s e c u r i t y . y m l se debe especificar un algoritmo por cada
proveedor de usuarios utilizado. O ms bien dicho, por cada entidad U s e r asociada a los
proveedores de usuarios que estemos utilizando.
Symfony2 proporciona los algoritmos sha1 y sha512, y se aplican de la siguiente manera:
. . .
e n c o d e r s :
J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o :
a l g o r i t h m : s h a 1
i t e r a t i o n s : 1
e n c o d e _ a s _ b a s e 6 4 : f a l s e
. . .
Nota
El algoritmo puede ser s h a 1 o s h a 5 1 2 .
Ahora, para que los usuarios que definiste en la seccin anterior puedan entrar en la
aplicacin, debes cambiar el campo p a s s w o r d por la cadena que resulta de aplicar este
algoritmo a dicho p a s w o r d . Por ejemplo, si el password de un usuario era p r u e b a s , ahora
debes cambiarlo por 6 2 0 a 7 d e 8 2 7 6 3 5 2 7 4 0 6 a 4 1 3 c a 7 e e 2 6 7 8 1 6 d 3 3 2 8 1 1 , que es la cadena
resultante de aplicar sha1 a p r u e b a s .
Por otro lado, en algn momento habr que implementar un proceso de registro de usuarios
de la aplicacin. En ese proceso se le pedir al usuario que introduzca su password y
debemos codificarlo con este algoritmo. Para realizar tal operacin podemos (y debemos)
utilizar el propio servicio de codificacin en la accin que implemente el registro:
< ? p h p
/ / / . . . e n a l g n l u g a r d e l a a c c i n d e r e g i s t r o
$ f a c t o r y = $ t h i s - > g e t ( ' s e c u r i t y . e n c o d e r _ f a c t o r y ' ) ;
$ u s e r = n e w J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ( ) ;
$ e n c o d e r = $ f a c t o r y - > g e t E n c o d e r ( $ u s e r ) ;
$ p a s s w o r d = $ e n c o d e r - > e n c o d e P a s s w o r d ( ' p r u e b a s ' , $ u s e r - > g e t S a l t ( ) ) ;
$ u s e r - > s e t P a s s w o r d ( $ p a s s w o r d ) ;
En la prxima unidad utilizaremos este snnipet para construir el proceso de registro.
Recuperando el objeto Usuario
Una vez que la authentificacin se ha llevado a cabo, podemos acceder a los datos del
usuario mediante el objeto U s e r asociado. Desde una accin se puede hacer as:
Recuperando el objeto Usuario
185
p u b l i c f u n c t i o n i n d e x A c t i o n ( )
{
$ u s e r = $ t h i s - > g e t ( ' s e c u r i t y . c o n t e x t ' ) - > g e t T o k e n ( ) - > g e t U s e r ( ) ;
}
O incluso de manera ms corta:
p u b l i c f u n c t i o n i n d e x A c t i o n ( )
{
$ u s e r = $ t h i s - > g e t U s e r ( ) ;
}
En una plantilla twig este objeto puede se accedido a travs de la clave a p p . u s e :
< p > U s e r n a m e : { { a p p . u s e r . u s e r n a m e } } < / p >
Recuerda que hasta ahora siempre mostrbamos las notas del usuario a l b e r t o . Ya es hora
de arreglar esto y recuperar las notas del usuario que inicia sesin. Para lo cual basta con
cambiar el mtodo d a m e E t i q u e t a s Y N o t a s ( ) del controlador N o t a s C o n t r o l l e r .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / N o t a s C o n t r o l l e r . p h p
< ? p h p
. . .
p r o t e c t e d f u n c t i o n d a m e E t i q u e t a s Y N o t a s ( )
{
$ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
$ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
/ / S u s t i t u i m o s e s t a l n e a s :
/ / $ u s u a r i o = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o ' )
/ / - > f i n d O n e B y U s e r n a m e ( ' a l b e r t o ' ) ;
/ / p o r e s t a :
$ u s u a r i o = $ t h i s - > g e t ( ' s e c u r i t y . c o n t e x t ' ) - > g e t T o k e n ( ) - > g e t U s e r ( ) ;
. . .
La unidad en chuletas
Users providers
s e c u r i t y :
. . .
p r o v i d e r s :
i n _ m e m o r y :
u s e r s :
a l b e r t o : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ R E G I S T R A D O ' ] }
m a r i a : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ P R E M I U M ' ] }
m i g u e l : { p a s s w o r d : p r u e b a s , r o l e s : [ ' R O L E _ A D M I N ' ] }
La unidad en chuletas
186
b a s e _ d a t o s :
e n t i t y : { c l a s s : J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o , p r o p e r t y : u s e r n a m e }
. . .
Cortafuegos
Mecanismo de autentificacin HTTP
f i r e w a l l s :
. . .
j a m n _ a r e a _ p r o t e g i d a :
p a t t e r n : ^ / n o t a s /
h t t p _ b a s i c :
r e a l m : " A r e a p r o t e g i d a "
. . .
Mecanismo de autentificacin con Formulario login
j a m n _ a r e a _ p r o t e g i d a :
p a t t e r n : ^ / n o t a s /
f o r m _ l o g i n :
l o g i n _ p a t h : / n o t a s / l o g i n
c h e c k _ p a t h : / n o t a s / l o g i n _ c h e c k
l o g o u t :
p a t h : / n o t a s / l o g o u t
t a r g e t : / n o t a s
Controlador y plantilla para formulario de login
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ;
u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
u s e S y m f o n y \ C o m p o n e n t \ S e c u r i t y \ C o r e \ S e c u r i t y C o n t e x t ;
c l a s s L o g i n C o n t r o l l e r e x t e n d s C o n t r o l l e r
{
p u b l i c f u n c t i o n l o g i n A c t i o n ( )
{
i f ( $ t h i s - > g e t ( ' r e q u e s t ' ) - > a t t r i b u t e s - > h a s ( S e c u r i t y C o n t e x t : : A U T H E N T I C A T I O N _ E R R O R ) ) {
$ e r r o r = $ t h i s - > g e t ( ' r e q u e s t ' ) - > a t t r i b u t e s - > g e t ( S e c u r i t y C o n t e x t : : A U T H E N T I C A T I O N _ E R R O R ) ;
} e l s e {
$ e r r o r = $ t h i s - > g e t ( ' s e s s i o n ' ) - > g e t ( S e c u r i t y C o n t e x t : : A U T H E N T I C A T I O N _ E R R O R ) ;
}
r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : L o g i n : l o g i n . h t m l . t w i g ' , a r r a y (
' l a s t _ u s e r n a m e ' = > $ t h i s - > g e t ( ' r e q u e s t ' ) - > g e t S e s s i o n ( ) - > g e t ( S e c u r i t y C o n t e x t : : L A S T _ U S E R N A M E ) ,
' e r r o r ' = > $ e r r o r ,
) ) ;
}
}
{ % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
{ % b l o c k b o d y % }
Cortafuegos
187
< h 1 > F o r m u l a r i o d e L o g i n < / h 1 >
{ % i f e r r o r % }
< d i v c l a s s = " e r r o r " > { { e r r o r . m e s s a g e } } < / d i v >
{ % e n d i f % }
< f o r m a c t i o n = " { { p a t h ( " j a m n _ l o g i n _ c h e c k " ) } } " m e t h o d = " p o s t " i d = " l o g i n " >
< d i v >
< l a b e l f o r = " u s e r n a m e " > U s e r n a m e < / l a b e l >
< i n p u t t y p e = " t e x t " i d = " u s e r n a m e " n a m e = " _ u s e r n a m e " v a l u e = " { { l a s t _ u s e r n a m e } } " / >
< / d i v >
< d i v >
< l a b e l f o r = " p a s s w o r d " > P a s s w o r d < / l a b e l >
< i n p u t t y p e = " p a s s w o r d " i d = " p a s s w o r d " n a m e = " _ p a s s w o r d " / >
< / d i v >
< i n p u t t y p e = " s u b m i t " c l a s s = " s y m f o n y - b u t t o n - g r e y " v a l u e = " L O G I N " / >
< / f o r m >
{ % e n d b l o c k % }
Anticortafuego:
j a m n _ a r e a _ p u b l i c a :
p a t t e r n : ^ / n o t a s / ( d e m o | r e g i s t r o | a c t i v a r | t a r i f a s | c o n t r a t a r | l o g i n $ )
s e c u r i t y : f a l s e
Autorizacin
Definicin de roles
r o l e _ h i e r a r c h y :
R O L E _ A D M I N : R O L E _ U S E R
R O L E _ S U P E R _ A D M I N : [ R O L E _ U S E R , R O L E _ A D M I N , R O L E _ A L L O W E D _ T O _ S W I T C H ]
R O L E _ P R E M I U M : [ R O L E _ R E G I S T R A D O ]
Proteccin de recursos
a c c e s s _ c o n t r o l :
- { p a t h : ^ / n o t a s / m i e s p a c i o , r o l e s : R O L E _ P R E M I U M }
- { p a t h : ^ / n o t a s / a d m i n i s t r a c i o n , r o l e s : R O L E _ A D M I N }
- { p a t h : ^ / n o t a s , r o l e s : R O L E _ R E G I S T R A D O }
Encoders
e n c o d e r s :
S y m f o n y \ C o m p o n e n t \ S e c u r i t y \ C o r e \ U s e r \ U s e r : p l a i n t e x t
J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o :
a l g o r i t h m : s h a 1
i t e r a t i o n s : 1
e n c o d e _ a s _ b a s e 6 4 : f a l s e
Usar el servicio encoder en una accin
Autorizacin
188
< ? p h p
/ / / . . . e n a l g n l u g a r d e l a a c c i n d e r e g i s t r o
$ f a c t o r y = $ t h i s - > g e t ( ' s e c u r i t y . e n c o d e r _ f a c t o r y ' ) ;
$ u s e r = n e w J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ( ) ;
$ e n c o d e r = $ f a c t o r y - > g e t E n c o d e r ( $ u s e r ) ;
$ p a s s w o r d = $ e n c o d e r - > e n c o d e P a s s w o r d ( ' p r u e b a s ' , $ u s e r - > g e t S a l t ( ) ) ;
$ u s e r - > s e t P a s s w o r d ( $ p a s s w o r d ) ;
referencia oficial del componente securiy
Comentarios
Aqu puedes enviar comentarios, dudas y sugerencias. Utiliza la barra de scroll para recorrer
todos los mensajes. El formulario de envo se encuentra al final.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
Comentarios
189
Unidad 10: Desarrollo de la aplicacin MentorNotas
(VI). Esamblando todo el frontend
Lleg la hora de rematar la aplicacin que venimos desarrollando desde la unidad 5. Vamos
a comenzar haciendo un repaso de lo que ya llevamos hecho y as sabremos lo que an nos
falta.
Por lo pronto la aplicacin:
tiene operativo el inicio de sesin
muestra el listado de etiquetas del usuario que ha iniciado la sesin
muestra el ttulo de las notas de dicho usuario,
cuando se pica en una etiqueta se muestran las notas asociadas a esa etiqueta
tiene operativo el buscador de notas (por ttulo y/o texto)
muestra como detalle de la nota una tabla con el ID y el ttulo de la nota.
muestra unos enlaces para crear, editar y borrar una nota, pero no son completamente
funcionales.
As que an nos queda:
hacer funcional la creacin, edicin y borrado de notas, as como la subida de archivos
asociados a notas cuando el usuario sea premium.
presentar toda la informacin en el espacio reservado para el detalle de la nota.
implementar el proceso de registro de usuarios,
implementar el proceso de contrato de una cuenta premium,
mostrar publicidad cuando el usuario no sea premium,
adecentar el aspecto grfico de la aplicacin,
crear la parte de administracin,
En esta unidad completaremos todos estos puntos a excepcin del ltimo, que ser resuelto
en la siguiente unidad.
Podemos atacar cada uno de estos puntos en el orden que queramos. En realidad no hay
dependencia entre ellos. As que comenzaremos por poner bonita la aplicacin. Eso nos dar
animo para seguir adelante.
La eclosin de la crislida
El objetivo de este apartado es cambiar las plantillas twig de la aplicacin para adaptarlas a
la interfaz grfica HTML + javascript que se propuso en la unidad 5. Recuerda:
inicio.html
inicio-editar_nota.html
login.html
registro.html
Para ello fragmentaremos en plantillas twig dichos layouts, identificando sus partes comunes
y las que deben pintarse dinmicamente. Utilizando los mecanismos de herencia e inclusin
de las plantillas organizaremos todos estos fragmentos cuidando de que no haya
repeticiones de cdigo innecesarias.
Unidad 10: Desarrollo de la aplicacin MentorNotas (VI). Esamblando todo el frontend
190
Primero los activos (assets)
En primer lugar vamos a colocar los activos en el lugar que le corresponde. Si te acuerdas lo
que se dijo en la unidad 3, estos deben colocarse en el directorio R e s o u r c e s / p u b l i c . Los
organizaremos de la siguiente manera:
archivos u i archivos en el plugin
u i / s r c / c s s R e s o u r c e s / p u b l i c / c s s
u i / s r c / j s R e s o u r c e s / p u b l i c / j s
u i / s r c / i m a g e s R e s o u r c e s / p u b l i c / i m a g e s
u i / v e n d o r s R e s o u r c e s / p u b l i c / v e n d o r s
Ahora, para tenerlos accesibles directamente desde el servidor web, ejecutamos la siguiente
tarea:
a p p / c o n s o l e a s s e t s : i n s t a l l - - s y m l i n k w e b
Que crea enlaces simblicos en el directorio w e b hacia los assets que acabamos de aadir a
nuestro plugin. Comprubalo.
Y ya tenemos todas las libreras de activos que necesitamos para construir la interfaz
grfica.
Despus las plantillas
Para crear un conjunto de plantillas bien organizado que implemente la interfaz propuesta
en el tema 5, lo primero que tenemos que hacer es examinar bien la estructuras de dicha
interfaz para conocerla lo mejor que podamos. Slo as seremos capaces de realizar dicha
adaptacin. Por ello te animamos a que antes de seguir leyendo realices dicho estudio. Es
decir examina el cdigo HTML y procura extraer su estructura. Para realizar esta tarea
resultan muy tiles algunas herramientas de desarrollo web como Firebug y Web Developer,
ambas plugins muy conocidos de Firefox. Otros navegadores, como Chrome, incorporan de
serie algunas herramientas de desarrollo que tambin ayudan a conocer la estructura de un
documento HTML.
Terminste el estudio de la estructura HTML?. Muy bien, entonces te habrs dado cuenta de
que:
En la seccin h e a d se aaden las CSS's y las libreras javascript que acabamos de
incorporar al proyecto como assets. Aqu aparece un primer problema para la
adaptacin. Resulta que nuestra plantilla base incorpora las CSS's en la seccin h e a d ,
pero coloca los javascripts al final de la seccin b o d y . La solucin es sencilla; modificar
la plantilla base para que los javascripts estn en la seccin h e a d . Sin embargo no es
necesario hacer esto. Aunque las libreras se carguen al final de la seccin b o d y , la
interfaz seguir funcionando perfectamente, ya que las funciones javascript de jQuery
no comenzarn a ejecutarse hasta que el documento est completamente cargado (ese
es el sentido de la sentencia $ ( d o c u m e n t ) . r e a d y ( ) con la que se inician nuestros
javascript). As que dejamos tal cual a la plantilla base.
Existen dos tipos de interfaces, es decir, pantallas con dos tipos de estructura distintas;
las encargadas de mostrar la informacin sobre las notas,
Primero los activos (assets)
191
y las encargadas de mostrar formularios de login y registro, que tambin servirn para
mostrar la pantalla de contrato premium y alguna otra.
Esto nos sugiere que tendremos que componer dos layouts distintos, o dicho de otra
forma, dos plantillas twig de nivel 2 en el modelos de herencia de 3 niveles que estamos
aplicando.
Primero los activos (assets)
192
Las pantallas que muestran las notas estn estructuradas en 5 zonas: la cabecera (capa
u i - l a y o u t - n o r t h ), el listado de etiquetas (capa u i - l a y o u t - w e s t ), el listado de notas
(capa u i - l a y o u t - c e n t e r ), el detalle de la nota, que tambin se usa para la edicin de
las notas (capa u i - l a y o u t - e a s t ) y el pie (capa u i - l a y o u t - s o u t h ). Todas estas capas
son interpretadas por la librera javascript j q u e r y . l a y o u t . m i n - 1 . 2 . 0 . j s la cual
enriquece el aspecto de la pgina colocando el contenido ordenadamente en tres
columnas con cabecera y pie.
De todas las zonas anteriores, la nica que cambia de aspecto es la que muestra el
detalle de la nota, que tambin se utiliza para mostrar el formulario de creacin y/o
edicin de una nota.
Comenzaremos por la adaptacin de las pantallas que muestran notas, ms complejas e
interesantes y que representan la parte nuclear de la aplicacin. Llamaremos a esta parte el
panel de notas.
Adaptacin de las pantallas del panel de notas
Como todas las acciones relacionadas con el panel de notas siempre pintan 4 de las 5 zonas
exactamente igual (cuidado, con igual nos referimos a estructuralmente igual, obviamente
los datos dinmicos cambiarn en funcin de la interaccin del usuario), podemos colocar en
la plantilla l a y o u t - e t i q u e t a s - n o t a s . t w i g . h t m l todas estas zonas comunes ms los
javascripts y CSS's.
El layout del panel de notas
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / l a y o u t - e t i q u e t a s - n o t a s . t w i g . h t m l :
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k s t y l e s h e e t s % }
4 < l i n k h r e f = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / j q u e r y / c s s / u i - l i g h t n e s s / j q u e r y - u i - 1 . 8 . 1 7 . c u s t o m . c s s ' ) } } " t y p e = " t e x t / c s s " r e l = " s t y l e s h e e t " / >
5 < l i n k h r e f = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / j q u e r y - l a y o u t / c s s / l a y o u t - d e f a u l t - l a t e s t . c s s ' ) } } " t y p e = " t e x t / c s s " r e l = " s t y l e s h e e t " / >
6 < l i n k h r e f = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / c s s / u i . c s s ' ) } } " t y p e = " t e x t / c s s " r e l = " s t y l e s h e e t " / >
7 { % e n d b l o c k % }
8
9 { % b l o c k j a v a s c r i p t s % }
1 0 < s c r i p t s r c = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / j q u e r y / j s / j q u e r y - 1 . 7 . 1 . m i n . j s ' ) } } " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t >
1 1 < s c r i p t s r c = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / j q u e r y / j s / j q u e r y - u i - 1 . 8 . 1 7 . c u s t o m . m i n . j s ' ) } } " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t >
1 2 < s c r i p t s r c = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / j q u e r y - l a y o u t / j s / j q u e r y . l a y o u t . m i n - 1 . 2 . 0 . j s ' ) } } " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t >
1 3 < s c r i p t s r c = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / j s / u i . j s ' ) } } " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t >
1 4 { % e n d b l o c k % }
1 5
1 6 { % b l o c k b o d y % }
1 7
1 8 { % i n c l u d e ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : c a b e c e r a . h t m l . t w i g ' % }
1 9
2 0 { % i n c l u d e ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e t i q u e t a s . h t m l . t w i g ' % }
2 1
2 2 { % i n c l u d e ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : n o t a s . h t m l . t w i g ' % }
2 3
2 4 { % i n c l u d e ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : p i e . h t m l . t w i g ' % }
2 5
2 6 { % b l o c k d e t a l l e _ y _ e d i c i o n % } { % e n d b l o c k % }
2 7
2 8 { % e n d b l o c k % }
En esta plantilla hemos redefinido los bloques s t y l e h e e t s y j a v a s c r i p t s de la plantilla
padre (: : b a s e . h t m l . t w i g ) aadiendo los assets del proyecto, y tambin se ha redefinido el
bloque b o d y . Dentro de este ltimo bloque deben ir las cinco capas de las que venimos
hablando, cuatro de ellas fijas y una que cambia segn estemos viendo una nota o
editndola. Por tanto se tratara de copiar aqu el cdigo de las capas u i - l a y o u t - n o r t h ,
u i - l a y o u t - w e s t , u i - l a y o u t - c e n t e r y u i - l a y o u t - s o u t h y adaptar el contenido dinmico
que viene de las acciones. Esto es lo que hemos hecho en las lneas 18-24 mediante el
mecanismo de inclusin. De esta manera la plantilla muestra ms claramente la estructura y
compondremos el cdigo concreto de cada zona en cada una de las plantillas:
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : c a b e c e r a . h t m l . t w i g ,
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e t i q u e t a s . h t m l . t w i g ,
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : n o t a s . h t m l . t w i g ,
Adaptacin de las pantallas del panel de notas
193
J A M N o t a s F r o n t e n d B u n d l e : N o t a s : p i e . h t m l . t w i g
Las acciones del panel de notas de nuestra aplicacin pintarn sus datos utilizando plantillas
que hereden de esta ltima y que implementen el bloque d e t a l l e _ y _ e d i c i o n . Esta solucin
cumple el principio DRY (Don't Repeat Yourself), adems de mostrar con claridad la
estructura de las pantallas.
A continuacin mostramos el cdigo de cada una de las plantillas incluidas por
l a y o u t - e t i q u e t a s - n o t a s . t w i g . h t m l .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / c a b e c e r a . h t m l . t w i g
1 < d i v c l a s s = " u i - l a y o u t - n o r t h u i - w i d g e t - c o n t e n t " >
2
3 < d i v c l a s s = " b a n n e r " >
4 { % i f n o t i s _ g r a n t e d ( ' R O L E _ P R E M I U M ' ) % }
5 < s p a n s t y l e = " f o n t - s i z e : x x - l a r g e " >
6 P U B L I C I D A D
7 < / s p a n >
8 < a c l a s s = " u i - b u t t o n u s u a r i o - t o o l p r e m i u m u i - c o r n e r - a l l " h r e f = " { { p a t h ( ' j a m n _ c o n t r a t a r ' ) } } " >
9 c o n t r a t a p r e m i u m , e l i m i n a l a p u b l i c i d a d y o b t e n n u m e r o s a s v e n t a j a s !
1 0 < / a >
1 1 { % e l s e % }
1 2 < s p a n s t y l e = " f o n t - s i z e : x x - l a r g e " >
1 3 B O T O N E R A P R E M I U M
1 4 < / s p a n >
1 5 < a c l a s s = " u i - b u t t o n u s u a r i o - t o o l p r e m i u m u i - c o r n e r - a l l " h r e f = " { { p a t h ( ' j a m n _ e s p a c i o _ p r e m i u m ' ) } } " >
1 6 M i e s p a c i o p r e m i u m
1 7 < / a >
1 8 { % e n d i f % }
1 9 < / d i v >
2 0
2 1 < d i v c l a s s = " l o g o " >
2 2 < s p a n > M e n t o r N o t a s < / s p a n >
2 3 < i m g s t y l e = " f l o a t : l e f t ; " h e i g h t = " 6 0 " s r c = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / i m a g e s / m e n t o r . p n g ' ) } } " > < / i m g >
2 4
2 5 < / d i v >
2 6
2 7 < d i v c l a s s = " s i g n o u t " >
2 8 < s p a n c l a s s = " u s u a r i o - n o m b r e " > { { a p p . u s e r . n o m b r e } } { { a p p . u s e r . a p e l l i d o s } } < / s p a n >
2 9 < a i d = " b t n _ s a l i r " c l a s s = " u i - b u t t o n u i - b u t t o n - t e x t - i c o n - p r i m a r y u s u a r i o - t o o l s a l i r u i - c o r n e r - a l l " h r e f = " { { p a t h ( ' j a m n _ l o g o u t ' ) } } " >
3 0 < s p a n c l a s s = " u i - b u t t o n - i c o n - p r i m a r y u i - i c o n u i - i c o n - p o w e r " > < / s p a n >
3 1 < s p a n c l a s s = " u i - b u t t o n - t e x t " > s a l i r < / s p a n > < / a >
3 2 < / d i v >
3 3
3 4 < / d i v >
Esta plantilla muestra la publicidad o las carcteristicas premium y un enlace para contratar
una tarifa premium o acceder al espacio premium, segn el tipo de usuario, el logo de
Mentor, el nombre del usuario que ha iniciado la sesin y el botn de salir. Observa la
incorporacin de estos elementos dinmicos en las lneas 4-18, 15, 23, 28 y 29. Presta
especial atencin al uso que hemos hecho de la funcin twig i s _ g r a n t e d ( ) para decidir si
pintamos publicidad o caractersticas premium.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / e t i q u e t a s . h t m l . t w i g
1 < d i v c l a s s = " u i - l a y o u t - w e s t e t i q u e t a s " >
2 < h 3 c l a s s = " u i - w i d g e t - h e a d e r " > E t i q u e t a s < / h 3 >
3 < d i v c l a s s = " u i - l a y o u t - c o n t e n t u i - w i d g e t - c o n t e n t " >
4 < u l >
5 < l i >
6 < s p a n c l a s s = " u i - i c o n u i - i c o n - s t a r " s t y l e = " f l o a t : l e f t ; " > < / s p a n >
7 < a h r e f = " { { p a t h ( " j a m n _ c o n e t i q u e t a " , { " e t i q u e t a " : ' t o d a s ' } ) } } " > t o d a s l a s n o t a s < / a >
8 < / l i >
9 { % f o r e t i q u e t a i n e t i q u e t a s % }
1 0 < l i >
1 1 < s p a n c l a s s = " u i - i c o n u i - i c o n - t a g " s t y l e = " f l o a t : l e f t ; " > < / s p a n >
1 2 < a h r e f = " { { p a t h ( " j a m n _ c o n e t i q u e t a " , { " e t i q u e t a " : e t i q u e t a . i d } ) } } " > { { e t i q u e t a . t e x t o } } < / a >
1 3 < / l i >
1 4 { % e n d f o r % }
1 5 < / u l >
1 6 < / d i v >
1 7 < / d i v >
Esta plantilla muestra un listado de enlaces de etiquetas (lneas 9-14). Tambin pinta un
enlace especial (lnea 7) que se utiliza para mostrar todas las notas.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / n o t a s . h t m l . t w i g
Adaptacin de las pantallas del panel de notas
194
1 < d i v c l a s s = " u i - l a y o u t - c e n t e r " >
2
3 < h 3 c l a s s = " u i - w i d g e t - h e a d e r " > L i s t a d o d e n o t a s < / h 3 >
4
5
6 < d i v c l a s s = " u i - w i d g e t m y - w i d g e t " >
7 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
8
9 < f o r m i d = " f o r m _ b u s c a r " a c t i o n = " { { p a t h ( " j a m n _ b u s c a r " ) } } " m e t h o d = " p o s t " >
1 0 < i n p u t c l a s s = " b u s c a r u i - c o r n e r - a l l " s i z e = " 2 0 " n a m e = " t e r m i n o " i d = " t e r m i n o " / >
1 1 < b u t t o n t y p e = s u b m i t " c l a s s = " b t n _ b u s c a r " > B u s c a r < / b u t t o n >
1 2 < / f o r m >
1 3 < / d i v >
1 4 < / d i v >
1 5
1 6 < d i v c l a s s = " s e p a r a d o r " > < / d i v >
1 7
1 8
1 9 < d i v c l a s s = " u i - l a y o u t - c o n t e n t u i - w i d g e t - c o n t e n t " >
2 0 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
2 1
2 2 { % f o r n o t a i n n o t a s % }
2 3 { % i f n o t a . i d = = n o t a _ s e l e c c i o n a d a . i d % }
2 4 < d i v c l a s s = " n o t a s e l e c c i o n a d a " >
2 5 { % e l s e % }
2 6 < d i v c l a s s = " n o t a " >
2 7 { % e n d i f % }
2 8 < a h r e f = " { { p a t h ( ' j a m n _ n o t a ' , { " i d " : n o t a . i d } ) } } " >
2 9 < d i v c l a s s = " n o t a - c o n t a i n e r " >
3 0 < d i v c l a s s = " n o t a - t i t u l o " >
3 1 { { n o t a . t i t u l o } }
3 2 < / d i v >
3 3 < d i v c l a s s = " n o t a - c o n t e n t " >
3 4 < s p a n c l a s s = " n o t a - f e c h a " > { { n o t a . f e c h a . d a t e } } < / s p a n >
3 5 { { n o t a . t e x t o | r a w } }
3 6
3 7 < / d i v >
3 8 < / d i v >
3 9 < / a >
4 0 < / d i v >
4 1
4 2 { % e l s e % }
4 3 < d i v c l a s s = " u i - s t a t e - h i g h l i g h t " >
4 4 < s p a n c l a s s = " u i - i c o n u i - i c o n - a l e r t " s t y l e = " f l o a t : l e f t ; " > < / s p a n >
4 5 < s p a n s t y l e = " p a d d i n g : 0 . 5 e m ; " > N o s e h a n e n c o n t r a d o n o t a s < / s p a n > < / d i v >
4 6 { % e n d f o r % }
4 7
4 8 < / d i v >
4 9 < / d i v >
5 0
5 1 < / d i v >
5 2
5 3 < / d i v >
Esta pantalla pinta un formulario para buscar notas y un listado de notas. El primero se pinta
en las lneas 9-12 y el segundo en las lneas 22-46. Dos detalles importantes a tener en
cuenta en la implementacin del listado de notas. En primer lugar, para poder resaltar la
nota seleccionada, se hace una comparacin del id de la nota que se est pintando con el id
de la nota seleccionada. Si coincide se usa la clase CSS n o t a _ s e l e c c i o n a d a en lugar de
n o t a . En segundo lugar, fjate en el { % e l s e % } de la lnea 42, no tiene su { % i f % }
correspondiente!. Esto, aunque lo parezca, no es un error, es una caracterstica del lenguaje
twig. El tema es que ese { % e l s e % } forma parte del bucle { % f o r n o t a i n n o t a s % } , y
significa que si no hay ninguna n o t a en la coleccin n o t a s se ejecute lo que viene despus
del { % e l s e % } , es decir, que se muestre un mensaje informando de ello.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / p i e . h t m l . t w i g
1 < d i v c l a s s = " u i - l a y o u t - s o u t h u i - w i d g e t - c o n t e n t " >
2 < d i v c l a s s = " p i e - d e - p a g i n a " >
Adaptacin de las pantallas del panel de notas
195
3 < d i v c l a s s = " c r " >
4 2 0 1 2 , J u a n D a v i d R o d r g u e z
5 < / d i v >
6 < d i v c l a s s = " p o w e r e d " >
7 < s p a n s t y l e = " f o n t - s t y l e : i t a l i c " > p o w e r e d b y < / s p a n >
8 < i m g h e i g h t = " 3 0 " s r c = " { { a s s e t ( " b u n d l e s / j a m n o t a s f r o n t e n d / i m a g e s / p h p - l o g o . j p g " ) } } " / >
9 < i m g h e i g h t = " 3 0 " s r c = " { { a s s e t ( " b u n d l e s / j a m n o t a s f r o n t e n d / i m a g e s / l o g o . p n g " ) } } " / >
1 0 < i m g h e i g h t = " 3 0 " s r c = " { { a s s e t ( " b u n d l e s / j a m n o t a s f r o n t e n d / i m a g e s / j q u e r y _ l o g o . p n g " ) } } " / >
1 1
1 2 < / d i v >
1 3 < / d i v >
1 4 < / d i v >
Por ltimo esta plantilla muestra informacin acerca del copyright y de las tecnologas que
se han utilizado en la confeccin de la aplicacin, es decir, lo tpico de un pie de pgina.
Las acciones del panel de notas
Y ahora vamos a por las acciones. Ya sabemos la filosofa: las acciones del panel de notas
pintarn sus datos sobre plantillas que hereden de l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g , y
que redefinirn el bloque d e t a l l e _ y _ e d i c i o n . Comencemos por la accin i n d e x A c t i o n ( )
que pintar sus datos sobre la plantilla i n d e x . h t m l . t w i g .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / N o t a s C o n t r o l l e r . p h p
1 < ? p h p
2 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ C o n t r o l l e r ;
3
4 u s e S y m f o n y \ B u n d l e \ F r a m e w o r k B u n d l e \ C o n t r o l l e r \ C o n t r o l l e r ;
5 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ N o t a ;
6
7 c l a s s N o t a s C o n t r o l l e r e x t e n d s C o n t r o l l e r
8 {
9
1 0 p u b l i c f u n c t i o n i n d e x A c t i o n ( )
1 1 {
1 2 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ; / / e q u i v a l e n t e a $ t h i s - > g e t ( ' r e q u e s t ' ) ;
1 3 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
1 4
1 5 $ r u t a = $ r e q u e s t - > g e t ( ' _ r o u t e ' ) ;
1 6
1 7 s w i t c h ( $ r u t a )
1 8 {
1 9 c a s e ' j a m n _ h o m e p a g e ' :
2 0
2 1 b r e a k ;
2 2
2 3 c a s e ' j a m n _ c o n e t i q u e t a ' :
2 4 $ s e s s i o n - > s e t ( ' b u s q u e d a . t i p o ' , ' p o r _ e t i q u e t a ' ) ;
2 5 $ s e s s i o n - > s e t ( ' b u s q u e d a . v a l o r ' , $ r e q u e s t - > g e t ( ' e t i q u e t a ' ) ) ;
2 6 $ s e s s i o n - > s e t ( ' n o t a . s e l e c c i o n a d a . i d ' , ' ' ) ;
2 7
2 8 b r e a k ;
2 9
3 0 c a s e ' j a m n _ b u s c a r ' :
3 1 $ s e s s i o n - > s e t ( ' b u s q u e d a . t i p o ' , ' p o r _ t e r m i n o ' ) ;
3 2 $ s e s s i o n - > s e t ( ' b u s q u e d a . v a l o r ' , $ r e q u e s t - > g e t ( ' t e r m i n o ' ) ) ;
3 3 $ s e s s i o n - > s e t ( ' n o t a . s e l e c c i o n a d a . i d ' , ' ' ) ;
Las acciones del panel de notas
196
3 4
3 5 b r e a k ;
3 6 c a s e ' j a m n _ n o t a ' :
3 7 $ s e s s i o n - > s e t ( ' n o t a . s e l e c c i o n a d a . i d ' , $ r e q u e s t - > g e t ( ' i d ' ) ) ;
3 8 b r e a k ;
3 9 }
4 0
4 1 l i s t ( $ e t i q u e t a s , $ n o t a s , $ n o t a S e l e c c i o n a d a ) = $ t h i s - > d a m e E t i q u e t a s Y N o t a s ( ) ;
4 2
4 3 / / c r e a m o s u n f o r m u l a r i o p a r a b o r r a r l a n o t a
4 4 i f ( $ n o t a S e l e c c i o n a d a i n s t a n c e o f N o t a ) {
4 5 $ d e l e t e F o r m = $ t h i s - > c r e a t e D e l e t e F o r m ( $ n o t a S e l e c c i o n a d a - > g e t I d ( ) ) - > c r e a t e V i e w ( ) ;
4 6 } e l s e {
4 7 $ d e l e t e F o r m = n u l l ;
4 8 }
4 9
5 0 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x . h t m l . t w i g ' , a r r a y (
5 1 ' e t i q u e t a s ' = > $ e t i q u e t a s ,
5 2 ' n o t a s ' = > $ n o t a s ,
5 3 ' n o t a _ s e l e c c i o n a d a ' = > $ n o t a S e l e c c i o n a d a ,
5 4 ' d e l e t e _ f o r m ' = > $ d e l e t e F o r m ,
5 5 ) ) ;
5 6 }
5 7
5 8 . . .
En esencia es la misma accin que ya habamos implementado a lo largo del curso. La
diferencia es que hemos aadido un nuevo elemento que pasamos a la plantilla: un
formulario para borrar la nota seleccionada (lneas 44-48). Dicho formulario es creado por
una funcin auxiliar que debes aadir al cdigo del N o t a s C o n t r o l l e r :
1 < ? p h p
2 . . .
3 p r o t e c t e d f u n c t i o n c r e a t e D e l e t e F o r m ( $ i d )
4 {
5 r e t u r n $ t h i s - > c r e a t e F o r m B u i l d e r ( a r r a y ( ' i d ' = > $ i d ) )
6 - > a d d ( ' i d ' , ' h i d d e n ' )
7 - > g e t F o r m ( )
8 ;
9 }
1 0 . . .
Por ltimo tenemos que reescribir la accin b o r r a r A c t i o n . Recuerda que el cdigo que
habamos escrito para dicha accin no borraba nada, tan solo avisaba de que en un futuro
tendra que arreglarse. Pues el futuro ya est aqu:
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n b o r r a r A c t i o n ( )
4 {
5 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
6 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
7 $ f o r m = $ t h i s - > c r e a t e D e l e t e F o r m ( $ r e q u e s t - > g e t ( ' i d ' ) ) ;
8
9 $ f o r m - > b i n d R e q u e s t ( $ r e q u e s t ) ;
1 0
1 1 i f ( $ f o r m - > i s V a l i d ( ) ) {
1 2 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
Las acciones del panel de notas
197
1 3 $ e n t i t y = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d ( $ r e q u e s t - > g e t ( ' i d ' ) ) ;
1 4
1 5 i f ( ! $ e n t i t y ) {
1 6 t h r o w $ t h i s - > c r e a t e N o t F o u n d E x c e p t i o n ( ' E s a n o t a n o e x i s t e . ' ) ;
1 7 }
1 8
1 9 $ e m - > r e m o v e ( $ e n t i t y ) ;
2 0 $ e m - > f l u s h ( ) ;
2 1
2 2 $ s e s s i o n - > s e t ( ' n o t a . s e l e c c i o n a d a . i d ' , ' ' ) ;
2 3 }
2 4 . . .
2 5
2 6
2 7
2 8 r e t u r n $ t h i s - > r e d i r e c t ( $ t h i s - > g e n e r a t e U r l ( ' j a m n _ h o m e p a g e ' ) ) ;
2 9 }
Adems, para que la plantilla J A M N o t a s F r o n t e n d B u n d l e : N o t a s : i n d e x . h t m l . t w i g funcione
correctamente hay que modificar ligeramente la funcin del N o t a s C o n t r o l l e r
d a m e E t i q u e t a s Y N o t a s la cual queda as:
1 < ? p h p
2 . . .
3 p r o t e c t e d f u n c t i o n d a m e E t i q u e t a s Y N o t a s ( )
4 {
5 $ s e s s i o n = $ t h i s - > g e t ( ' s e s s i o n ' ) ;
6 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
7
8 $ u s u a r i o = $ t h i s - > g e t ( ' s e c u r i t y . c o n t e x t ' ) - > g e t T o k e n ( ) - > g e t U s e r ( ) ;
9
1 0
1 1 $ b u s q u e d a _ t i p o = $ s e s s i o n - > g e t ( ' b u s q u e d a . t i p o ' ) ;
1 2
1 3 $ b u s q u e d a _ v a l o r = $ s e s s i o n - > g e t ( ' b u s q u e d a . v a l o r ' ) ;
1 4
1 5 / / E t i q u e t a s . S e p i l l a n t o d a s
1 6 $ e t i q u e t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : E t i q u e t a ' ) - >
1 7 f i n d B y U s u a r i o O r d e r e d B y T e x t o ( $ u s u a r i o ) ;
1 8
1 9 / / N o t a s . S e p i l l a n s e g n e l f i l t r o a l m a c e n a d o e n l a s e s i n
2 0 i f ( $ b u s q u e d a _ t i p o = = ' p o r _ e t i q u e t a ' & & $ b u s q u e d a _ v a l o r ! = ' t o d a s ' ) {
2 1 $ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - >
2 2 f i n d B y U s u a r i o A n d E t i q u e t a ( $ u s u a r i o , $ b u s q u e d a _ v a l o r ) ;
2 3 } e l s e i f ( $ b u s q u e d a _ t i p o = = ' p o r _ t e r m i n o ' ) {
2 4 $ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - >
2 5 f i n d B y U s u a r i o A n d T e r m i n o ( $ u s u a r i o , $ b u s q u e d a _ v a l o r ) ;
2 6 } e l s e {
2 7 $ n o t a s = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - >
2 8 f i n d B y U s u a r i o O r d e r e d B y F e c h a ( $ u s u a r i o ) ;
2 9 }
3 0
3 1 $ n o t a _ s e l e c c i o n a d a = n u l l ;
3 2 i f ( c o u n t ( $ n o t a s ) > 0 ) {
3 3 $ n o t a _ s e l e c i o n a d a _ i d = $ s e s s i o n - > g e t ( ' n o t a . s e l e c c i o n a d a . i d ' ) ;
3 4 i f ( ! i s _ n u l l ( $ n o t a _ s e l e c i o n a d a _ i d ) & & $ n o t a _ s e l e c i o n a d a _ i d ! = ' ' ) {
3 5 $ n o t a _ s e l e c c i o n a d a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - >
3 6 f i n d O n e B y I d ( $ n o t a _ s e l e c i o n a d a _ i d ) ;
3 7 } e l s e {
3 8 $ n o t a _ s e l e c c i o n a d a = $ n o t a s [ 0 ] ;
3 9 }
Las acciones del panel de notas
198
4 0 }
4 1
4 2 r e t u r n a r r a y ( $ e t i q u e t a s , $ n o t a s , $ n o t a _ s e l e c c i o n a d a ) ;
4 3 }
La modificacin consiste en las lneas 32-40. En ellas se elige como nota seleccionada la que
venga en la sesin, y la primera de la lista en el caso de que en la sesin no est disponible
este dato. Finalmente si no hay ninguna nota en la coleccin de notas, se devuelve un valor
nulo.
Finalmente, el cdigo de la plantilla queda:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / i n d e x . h t m l . t w i g
1 { % e x t e n d s ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g ' % }
2
3 { % b l o c k d e t a l l e _ y _ e d i c i o n % }
4
5 < d i v c l a s s = " u i - l a y o u t - e a s t " >
6 < h 3 c l a s s = " u i - w i d g e t - h e a d e r " > N o t a < / h 3 >
7
8 { % i f n o t a _ s e l e c c i o n a d a % }
9
1 0 < d i v i d = " c o n f i r m a - b o r r a d o " > < / d i v >
1 1
1 2 < d i v c l a s s = " u i - w i d g e t m y - w i d g e t " >
1 3 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
1 4 < s p a n c l a s s = " m y - b u t t o n s e t - l e f t " >
1 5 < a i d = " b t n _ c r e a r " h r e f = " { { p a t h ( ' j a m n _ n u e v a ' ) } } " > c r e a r < / a >
1 6 < / s p a n >
1 7
1 8 < s p a n c l a s s = " m y - b u t t o n s e t - r i g h t " >
1 9 < a i d = " b t n _ e d i t a r " h r e f = " { { p a t h ( ' j a m n _ e d i t a r ' , { ' i d ' : n o t a _ s e l e c c i o n a d a . i d } ) } } " > e d i t a r < / a >
2 0 < b u t t o n i d = " b t n _ b o r r a r " t y p e = " s u b m i t " > B o r r a r < / b u t t o n >
2 1 < f o r m i d = " f o r m _ b o r r a r " a c t i o n = " { { p a t h ( ' j a m n _ b o r r a r ' , { ' i d ' : n o t a _ s e l e c c i o n a d a . i d } ) } } " m e t h o d = " p o s t " >
2 2 { { f o r m _ w i d g e t ( d e l e t e _ f o r m ) } }
2 3 < / f o r m >
2 4 < / s p a n >
2 5 < / d i v >
2 6 < / d i v >
2 7
2 8 < d i v c l a s s = " s e p a r a d o r " > < / d i v >
2 9
3 0 < d i v c l a s s = " u i - l a y o u t - c o n t e n t u i - w i d g e t - c o n t e n t " >
3 1 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
3 2 < d i v i d = " n o t a - d e t a l l e " >
3 3
3 4 < d i v c l a s s = " n o t a - d e t a l l e - t i t u l o " >
3 5 { { n o t a _ s e l e c c i o n a d a . t i t u l o } }
3 6 < / d i v >
3 7
3 8 { % i f n o t a _ s e l e c c i o n a d a . p a t h % }
3 9 < d i v c l a s s = " n o t a - d e t a l l e - a d j u n t o " >
4 0 < s p a n c l a s s = " u i - i c o n u i - i c o n - d i s k " s t y l e = " f l o a t : l e f t ; " > < / s p a n >
4 1 { % s e t u r l F i l e = a s s e t ( ' u p l o a d s ' ) ~ ' / ' ~ a p p . u s e r . g e t U s e r n a m e ~ ' - ' ~ n o t a _ s e l e c c i o n a d a . p a t h % }
4 2 < a h r e f = " { { u r l F i l e } } " > { { n o t a _ s e l e c c i o n a d a . n o m b r e F i c h e r o } } < / a >
4 3
4 4 < / d i v >
4 5 { % e n d i f % }
4 6
4 7 < d i v c l a s s = " n o t a - d e t a l l e - e t i q u e t a s " >
4 8 { % f o r e t i q u e t a i n n o t a _ s e l e c c i o n a d a . e t i q u e t a s % }
4 9 < s p a n c l a s s = " n o t a - d e t a l l e - e t i q u e t a u i - c o r n e r - a l l " > { { e t i q u e t a . t e x t o } } < / s p a n >
5 0 { % e n d f o r % }
5 1
5 2 < / d i v >
5 3 < d i v c l a s s = " n o t a - d e t a l l e - c o n t e n t " >
5 4 { { n o t a _ s e l e c c i o n a d a . t e x t o | r a w } }
5 5 < / d i v >
5 6 < / d i v >
5 7 < / d i v >
5 8 < / d i v >
Las acciones del panel de notas
199
5 9 { % e l s e % }
6 0 < d i v c l a s s = " u i - w i d g e t m y - w i d g e t " >
6 1 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
6 2 < s p a n c l a s s = " m y - b u t t o n s e t - l e f t " >
6 3 < a i d = " b t n _ c r e a r " h r e f = " { { p a t h ( ' j a m n _ n u e v a ' ) } } " > c r e a r < / a >
6 4 < / s p a n >
6 5 < / d i v >
6 6 < / d i v >
6 7 { % e n d i f % }
6 8 < / d i v >
6 9
7 0 { % e n d b l o c k % }
En esta plantilla, si existe la nota seleccionada, se pinta una barra de botones con las
acciones c r e a r , e d i t a r y b o r r a r (lneas 12-26). Tambin se pinta un formulario para
borrar la nota que es activado mediante j a v a s c r i p t por el botn borrar (lneas 21-23). A
continuacin se pintan los detalles de la nota: el ttulo (lnea 35), el archivo adjunto, si lo
tiene (lneas 38-45), las etiquetas de la nota (lneas 47-52), el texto (lneas 53-55). Si la nota
seleccionada no existe, simplemente se pinta un botn para crear una nota nueva (lneas
59-67).
Ya puedes probar la accin i n d e x A c t i o n es decir
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / n o t a s
Comprueba que ya funciona la bsqueda por etiquetas y trminos, las seleccin de notas en
el listado de notas y el borrado de la nota seleccionada.
Ahora vamos a por la creacin y la edicin de notas. Recuerda que las acciones
n u e v a A c t i o n ( ) y e d i t a r A c t i o n ( ) ya estaban implementadas, pero al igual que suceda
con la accin b o r r a r A c t i o n ( ) , no funcionaban adecuadamente. Ahora vamos a darles su
forma definitiva.
Pero antes vamos a componer las plantillas correspondientes a estas acciones. Tanto en el
caso de la edicin como de la creacin, el nico cambio se producir en la zona del detalle
de la nota, que ser sustituida por un formulario de creacin y/o edicin. Por tanto las
plantillas para estas dos acciones heredarn de la plantilla
l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g . Esa es la idea que hemos desarrollado ms arriba.
Nada nuevo hasta ahora.
Si has analizado con detalle la interfaz de usuario propuesta en la unidad 5, el formulario de
edicin/creacin contiene:
Un editor enriquecido
Un etiquetador
Una botonera con los botones h e c h o , c a n c e l a r y b o r r a r , para el formulario de edicin
y h e c h o y c a n c e l a r para el formulario de creacin.
Estos elementos son enriquecidos mediantes nuevas libreras javascript. As que las
plantillas que hagamos tendrn que redefinir los bloques j a v a s c r i p t s y s t y l e s h e e t s para
aadir dichas libreras y sus CSS's correspondientes.
Por otro lado, el formulario de edicin y de creacin, son muy similares pero presentan
diferencias suficientes como para plantearse crear dos plantillas distintas para cada uno de
ellos. Lo que haremos, aludiendo al principio DRY, es componer una plantilla con los
elementos comunes que ser utilizada por ambas acciones. A dicha plantilla aadiremos la
lgica necesario para que incluya el formulario de creacin o edicin segn el caso.
Vemoslo en la prctica.
Las acciones del panel de notas
200
La plantilla c r e a r O E d i t a r . h t m l . t w i g ser la plantilla comn:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / i n d e x . h t m l . t w i g
1 { % e x t e n d s ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : l a y o u t - e t i q u e t a s - n o t a s . h t m l . t w i g ' % }
2
3 { % b l o c k j a v a s c r i p t s % }
4
5 { { p a r e n t ( ) } }
6 < s c r i p t s r c = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / C L E d i t o r 1 _ 3 _ 0 / j q u e r y . c l e d i t o r . m i n . j s ' ) } } " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t >
7 < s c r i p t s r c = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / t a g i t / j s / t a g - i t . j s ' ) } } " t y p e = " t e x t / j a v a s c r i p t " > < / s c r i p t >
8
9 < s c r i p t >
1 0 $ ( d o c u m e n t ) . r e a d y ( f u n c t i o n ( ) {
1 1 v a r t i t u l o B o x = $ ( " # n o t a _ t i t u l o " ) ;
1 2
1 3 $ ( " # b t n _ h e c h o _ c r e a r " ) . b u t t o n ( {
1 4 i c o n s : {
1 5 p r i m a r y : " u i - i c o n - d i s k "
1 6 }
1 7 } ) ;
1 8
1 9 $ ( " # b t n _ h e c h o _ c r e a r " ) . c l i c k ( f u n c t i o n ( ) {
2 0 $ ( " # f o r m _ c r e a r _ n o t a " ) . s u b m i t ( ) ;
2 1 } ) ;
2 2
2 3 $ ( " # b t n _ h e c h o _ e d i t a r " ) . b u t t o n ( {
2 4 i c o n s : {
2 5 p r i m a r y : " u i - i c o n - d i s k "
2 6 }
2 7 } ) ;
2 8
2 9 $ ( " # b t n _ h e c h o _ e d i t a r " ) . c l i c k ( f u n c t i o n ( ) {
3 0 $ ( " # f o r m _ e d i t a r _ n o t a " ) . s u b m i t ( ) ;
3 1 } ) ;
3 2
3 3 $ ( " # b t n _ c a n c e l a r " ) . b u t t o n ( {
3 4 i c o n s : {
3 5 p r i m a r y : " u i - i c o n - c a n c e l "
3 6 }
3 7 } ) ;
3 8
3 9
4 0
4 1 $ ( " # n o t a _ e t i q u e t a s " ) . t a g i t ( {
4 2 a v a i l a b l e T a g s : [ { % f o r e t i q u e t a i n e t i q u e t a s % } " { { e t i q u e t a . t e x t o } } " , { % e n d f o r % } ] ,
4 3
4 4 } ) ;
4 5
4 6
4 7 t i t u l o B o x . f o c u s ( f u n c t i o n ( ) {
4 8 i f ( $ ( t h i s ) . a t t r ( " v a l u e " ) = = t i t u l o B o x 1 D e f a u l t ) $ ( t h i s ) . a t t r ( " v a l u e " , " " ) ;
4 9 $ ( t h i s ) . a d d C l a s s ( " n o t a - t i t u l o - a c t i v o " ) ;
5 0 } ) ;
5 1
5 2 t i t u l o B o x . b l u r ( f u n c t i o n ( ) {
5 3 $ ( t h i s ) . r e m o v e C l a s s ( " n o t a - t i t u l o - a c t i v o " ) ;
5 4
5 5
5 6 } ) ;
5 7 $ ( " # n o t a _ t e x t o " ) . c l e d i t o r ( {
5 8 c o n t r o l s : " b o l d i t a l i c u n d e r l i n e s t r i k e t h r o u g h s u b s c r i p t s u p e r s c r i p t | f o n t s i z e " +
5 9 " s t y l e | c o l o r h i g h l i g h t r e m o v e f o r m a t | b u l l e t s n u m b e r i n g | o u t d e n t " +
6 0 " i n d e n t | a l i g n l e f t c e n t e r a l i g n r i g h t j u s t i f y | u n d o r e d o | " +
6 1 " r u l e i m a g e l i n k u n l i n k | p r i n t s o u r c e "
6 2 } ) ;
6 3 } )
6 4
6 5 < / s c r i p t >
6 6 { % e n d b l o c k % }
6 7
6 8 { % b l o c k s t y l e s h e e t s % }
6 9 { { p a r e n t ( ) } }
7 0 < l i n k r e l = " s t y l e s h e e t " t y p e = " t e x t / c s s " h r e f = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / C L E d i t o r 1 _ 3 _ 0 / j q u e r y . c l e d i t o r . c s s ' ) } } " / >
7 1 < l i n k r e l = " s t y l e s h e e t " t y p e = " t e x t / c s s " h r e f = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / t a g i t / c s s / j q u e r y . u i . a u t o c o m p l e t e . c u s t o m . c s s ' ) } } " / >
7 2 < l i n k r e l = " s t y l e s h e e t " t y p e = " t e x t / c s s " h r e f = " { { a s s e t ( ' b u n d l e s / j a m n o t a s f r o n t e n d / v e n d o r s / t a g i t / c s s / j q u e r y . t a g i t . c s s ' ) } } " / >
7 3
7 4 { % e n d b l o c k % }
7 5
7 6
7 7 { % b l o c k d e t a l l e _ y _ e d i c i o n % }
7 8
7 9 { % i f e d i t a % }
8 0 { % i n c l u d e ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : e d i t a r . h t m l . t w i g ' % }
8 1 { % e l s e % }
8 2 { % i n c l u d e ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : c r e a r . h t m l . t w i g ' % }
8 3 { % e n d i f % }
8 4
8 5
8 6
8 7 { % e n d b l o c k % }
Observa la inclusin de las nuevas libreras javascript (lneas 6-7), del script utilizado para
enriquecer el formulario (lneas 9-64) y la inclusin de las nuevas CSS's (lneas 69-71). Por
ltimo observa la lgica introducida en el bloque d e t a l l e _ y _ e d i c i o n , en funcin de la
existencia o no de una variable denominada e d i t a , se incluir la plantilla para el formulario
de edicin o de creacin. Vemos el cdigo de estos ltimos.
Las acciones del panel de notas
201
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / c r e a r . h t m l . t w i g
1 < d i v c l a s s = " u i - l a y o u t - e a s t " >
2 < h 3 c l a s s = " u i - w i d g e t - h e a d e r " > C r e a r N o t a < / h 3 >
3
4 < d i v i d = " c o n f i r m a - b o r r a d o " > < / d i v >
5
6 < d i v c l a s s = " u i - w i d g e t m y - w i d g e t " >
7 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
8 < s p a n c l a s s = " m y - b u t t o n s e t - l e f t " >
9 < b u t t o n i d = " b t n _ h e c h o _ c r e a r " > h e c h o < / b u t t o n >
1 0 < / s p a n >
1 1
1 2 < s p a n c l a s s = " m y - b u t t o n s e t - r i g h t " >
1 3 < a i d = " b t n _ c a n c e l a r " h r e f = " { { p a t h ( ' j a m n _ h o m e p a g e ' ) } } " > c a n c e l a r < / a >
1 4 < / s p a n >
1 5 < / d i v >
1 6 < / d i v >
1 7
1 8 < d i v c l a s s = " s e p a r a d o r " > < / d i v >
1 9
2 0 < d i v c l a s s = " u i - l a y o u t - c o n t e n t u i - w i d g e t - c o n t e n t " >
2 1 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
2 2
2 3 < f o r m i d = " f o r m _ c r e a r _ n o t a " a c t i o n = " { { p a t h ( ' j a m n _ n u e v a ' ) } } " m e t h o d = " p o s t " { { f o r m _ e n c t y p e ( n e w _ f o r m ) } } >
2 4
2 5 { % i f f o r m _ e r r o r s ( n e w _ f o r m ) % }
2 6 < d i v c l a s s = " u i - s t a t e - e r r o r " > { { f o r m _ e r r o r s ( n e w _ f o r m ) } } < / d i v >
2 7 { % e n d i f % }
2 8
2 9 < d i v c l a s s = " r o w " >
3 0 < d i v s t y l e = " f o n t - s i z e : s m a l l " > T t u l o : < / d i v >
3 1 { % i f f o r m _ e r r o r s ( n e w _ f o r m . t i t u l o ) % }
3 2 < d i v s t y l e = " w i d t h : 3 0 0 p x ; " c l a s s = " u i - s t a t e - e r r o r " > { { f o r m _ e r r o r s ( n e w _ f o r m . t i t u l o ) } } < / d i v >
3 3 { % e n d i f % }
3 4
3 5 { { f o r m _ w i d g e t ( n e w _ f o r m . t i t u l o , { ' a t t r ' : { ' c l a s s ' : ' n o t a - t i t u l o u i - c o r n e r - a l l ' , ' s t y l e ' : ' w i d t h : 3 0 0 p x ; ' } } ) } }
3 6 < / d i v >
3 7
3 8 < d i v c l a s s = " r o w " >
3 9 < s p a n s t y l e = " f o n t - s i z e : s m a l l " > E t i q u e t a s : < / s p a n >
4 0
4 1
4 2 < u l i d = " n o t a _ e t i q u e t a s " >
4 3 < / u l >
4 4 < / d i v >
4 5
4 6 < d i v c l a s s = " r o w " >
4 7 { % i f f o r m _ e r r o r s ( n e w _ f o r m . t e x t o ) % }
4 8 < d i v c l a s s = " u i - s t a t e - e r r o r " > { { f o r m _ e r r o r s ( n e w _ f o r m . t e x t o ) } } < / d i v >
4 9 { % e n d i f % }
5 0 { { f o r m _ w i d g e t ( n e w _ f o r m . t e x t o ) } }
5 1
5 2 < / d i v >
5 3
5 4 { % i f i s _ g r a n t e d ( ' R O L E _ P R E M I U M ' ) % }
5 5 < d i v c l a s s = " r o w " >
5 6 < d i v s t y l e = " f o n t - s i z e : s m a l l " > C a m b i a r F i c h e r o : < / d i v >
5 7 { % i f f o r m _ e r r o r s ( n e w _ f o r m . f i l e ) % }
5 8 < d i v c l a s s = " u i - s t a t e - e r r o r " > { { f o r m _ e r r o r s ( n e w _ f o r m . f i l e ) } } < / d i v >
5 9 { % e n d i f % }
6 0 { { f o r m _ w i d g e t ( n e w _ f o r m . f i l e ) } }
6 1 { % e n d i f % }
6 2 < / d i v >
6 3
6 4 { { f o r m _ w i d g e t ( n e w _ f o r m . _ t o k e n ) } }
6 5
6 6 < / f o r m >
6 7 < / d i v >
6 8 < / d i v >
6 9 < / d i v >
Y el de edicin:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / N o t a s / e d i t a r . h t m l . t w i g
1 < d i v c l a s s = " u i - l a y o u t - e a s t " >
2 < h 3 c l a s s = " u i - w i d g e t - h e a d e r " > E d i t a r N o t a < / h 3 >
3
4 < d i v i d = " c o n f i r m a - b o r r a d o " > < / d i v >
5
6 < d i v c l a s s = " u i - w i d g e t m y - w i d g e t " >
7 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
8 < s p a n c l a s s = " m y - b u t t o n s e t - l e f t " >
9 < b u t t o n i d = " b t n _ h e c h o _ e d i t a r " > h e c h o < / b u t t o n >
Las acciones del panel de notas
202
1 0 < / s p a n >
1 1
1 2 < s p a n c l a s s = " m y - b u t t o n s e t - r i g h t " >
1 3 < a i d = " b t n _ c a n c e l a r " h r e f = " { { p a t h ( ' j a m n _ n o t a ' , { ' i d ' : n o t a _ s e l e c c i o n a d a . i d } ) } } " > c a n c e l a r < / a >
1 4 < b u t t o n i d = " b t n _ b o r r a r " t y p e = " s u b m i t " > B o r r a r < / b u t t o n >
1 5 < f o r m i d = " f o r m _ b o r r a r " a c t i o n = " { { p a t h ( ' j a m n _ b o r r a r ' , { ' i d ' : n o t a _ s e l e c c i o n a d a . i d } ) } } " m e t h o d = " p o s t " >
1 6 { { f o r m _ w i d g e t ( d e l e t e _ f o r m ) } }
1 7 < / f o r m >
1 8 < / s p a n >
1 9 < / d i v >
2 0 < / d i v >
2 1
2 2 < d i v c l a s s = " s e p a r a d o r " > < / d i v >
2 3
2 4 < d i v c l a s s = " u i - l a y o u t - c o n t e n t u i - w i d g e t - c o n t e n t " >
2 5 < d i v c l a s s = " u i - w i d g e t - c o n t e n t m y - w i d g e t - c o n t e n t " >
2 6
2 7 < f o r m i d = " f o r m _ e d i t a r _ n o t a " a c t i o n = " { { p a t h ( ' j a m n _ e d i t a r ' , { ' i d ' : n o t a _ s e l e c c i o n a d a . i d } ) } } " m e t h o d = " p o s t " { { f o r m _ e n c t y p e ( e d i t _ f o r m ) } } >
2 8
2 9 { % i f f o r m _ e r r o r s ( e d i t _ f o r m ) % }
3 0 < d i v c l a s s = " u i - s t a t e - e r r o r " > { { f o r m _ e r r o r s ( e d i t _ f o r m ) } } < / d i v >
3 1 { % e n d i f % }
3 2
3 3 < d i v c l a s s = " r o w " >
3 4 < d i v s t y l e = " f o n t - s i z e : s m a l l " > T t u l o : < / d i v >
3 5 { % i f f o r m _ e r r o r s ( e d i t _ f o r m . t i t u l o ) % }
3 6 < d i v s t y l e = " w i d t h : 3 0 0 p x ; " c l a s s = " u i - s t a t e - e r r o r " > { { f o r m _ e r r o r s ( e d i t _ f o r m . t i t u l o ) } } < / d i v >
3 7 { % e n d i f % }
3 8
3 9 { { f o r m _ w i d g e t ( e d i t _ f o r m . t i t u l o , { ' a t t r ' : { ' c l a s s ' : ' n o t a - t i t u l o u i - c o r n e r - a l l ' , ' s t y l e ' : ' w i d t h : 3 0 0 p x ; ' } } ) } }
4 0 < / d i v >
4 1
4 2 < d i v c l a s s = " r o w " >
4 3 < s p a n s t y l e = " f o n t - s i z e : s m a l l " > E t i q u e t a s : < / s p a n >
4 4
4 5
4 6 < u l i d = " n o t a _ e t i q u e t a s " >
4 7 { % f o r e t i q u e t a i n n o t a _ s e l e c c i o n a d a . e t i q u e t a s % }
4 8 < l i > { { e t i q u e t a . t e x t o } } < / l i >
4 9 { % e n d f o r % }
5 0 < / u l >
5 1 < / d i v >
5 2
5 3 < d i v c l a s s = " r o w " >
5 4 { % i f f o r m _ e r r o r s ( e d i t _ f o r m . t e x t o ) % }
5 5 < d i v c l a s s = " u i - s t a t e - e r r o r " > { { f o r m _ e r r o r s ( e d i t _ f o r m . t e x t o ) } } < / d i v >
5 6 { % e n d i f % }
5 7 { { f o r m _ w i d g e t ( e d i t _ f o r m . t e x t o ) } }
5 8
5 9 < / d i v >
6 0
6 1 < d i v c l a s s = " r o w " >
6 2 < d i v >
6 3 < d i v s t y l e = " f o n t - s i z e : s m a l l " > F i c h e r o : < / d i v >
6 4
6 5 { % i f n o t a _ s e l e c c i o n a d a . p a t h % }
6 6 { % s e t u r l F i l e = a s s e t ( n o t a _ s e l e c c i o n a d a . w e b P a t h ( a p p . u s e r . g e t U s e r n a m e ) ) % }
6 7 < a h r e f = " { { u r l F i l e } } " > { { n o t a _ s e l e c c i o n a d a . p a t h } } < / a >
6 8 { % e n d i f % }
6 9 < / d i v >
7 0 < / d i v >
7 1
7 2 { % i f i s _ g r a n t e d ( ' R O L E _ P R E M I U M ' ) % }
7 3 < d i v c l a s s = " r o w " >
7 4 < d i v s t y l e = " f o n t - s i z e : s m a l l " > C a m b i a r F i c h e r o : < / d i v >
7 5 { % i f f o r m _ e r r o r s ( e d i t _ f o r m . f i l e ) % }
7 6 < d i v c l a s s = " u i - s t a t e - e r r o r " > { { f o r m _ e r r o r s ( e d i t _ f o r m . f i l e ) } } < / d i v >
7 7 { % e n d i f % }
7 8 { { f o r m _ w i d g e t ( e d i t _ f o r m . f i l e ) } }
7 9 { % e n d i f % }
8 0 < / d i v >
8 1
8 2 { { f o r m _ w i d g e t ( e d i t _ f o r m . _ t o k e n ) } }
8 3
8 4 < / f o r m >
8 5 < / d i v >
8 6 < / d i v >
8 7 < / d i v >
En ambas plantillas se utiliza un formulario que tiene los siguientes campos:
ttulo
texto
file
Este formulario se corresponde con el tipo N o t a T y p e , que ya hemos implementado en los
ejercicios de la unidad 8. Bueno, en realidad hay que aadirle el campo t e x t o :
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / F o r m / T y p e / N o t a T y p e
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e ;
4
Las acciones del panel de notas
203
5 u s e S y m f o n y \ C o m p o n e n t \ F o r m \ A b s t r a c t T y p e ;
6 u s e S y m f o n y \ C o m p o n e n t \ F o r m \ F o r m B u i l d e r ;
7
8 c l a s s N o t a T y p e e x t e n d s A b s t r a c t T y p e
9 {
1 0
1 1 p u b l i c f u n c t i o n b u i l d F o r m ( F o r m B u i l d e r $ b u i l d e r , a r r a y $ o p t i o n s )
1 2 {
1 3 $ b u i l d e r - > a d d ( ' t i t u l o ' , ' t e x t ' )
1 4 - > a d d ( ' t e x t o ' , ' t e x t a r e a ' )
1 5 - > a d d ( ' f i l e ' , ' f i l e ' ) ;
1 6 }
1 7
1 8 p u b l i c f u n c t i o n g e t N a m e ( )
1 9 {
2 0 r e t u r n ' n o t a ' ;
2 1 }
2 2
2 3 / / E s t o n o e s s i e m p r e n e c e s a r i o , p e r o p a r a c o n s t r u i r f o r m u l a r i o s e m b e b i d o s
2 4 / / e s i m p r e s c i n d i b l e s , a s q u e n o c u e s t a n a d a a c o s t u m b r a r s e a p o n e r l o
2 5 p u b l i c f u n c t i o n g e t D e f a u l t O p t i o n s ( a r r a y $ o p t i o n s )
2 6 {
2 7 r e t u r n a r r a y (
2 8 ' d a t a _ c l a s s ' = > ' J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ N o t a ' ,
2 9 ) ;
3 0 }
3 1
3 2 }
Otro elemento a destacar es el formulario de borrado de notas que aparece en las lneas
14-17 de la plantilla de edicin. Fjate que es la misma cosa que ya hicimos en la plantilla
i n d e x . h t m l . t w i g .
Vemos ahora como se tratan en ambos casos la gestin de etiquetas para cada nota que se
crea o edita con estos formularios. En las lneas 46-50 de la plantilla de edicin (42-43 de la
plantilla de creacin), se aade una lista desordenada (< u l > < / u l > ) con las etiquetas de cada
nota. Esta lista es enriquecida mediante la librera jQuery t a g - i t . j s , la cual se encarga de
convertir el listado en un elegante etiquetador. Cuando se enva el formulario al servidor, la
propia librera se encarga de pasar la coleccin de etiquetas que se hayan creado en una
variable de la request denominada i t e m . De esa manera, la accin correspondiente podr
recuperarla y asociar adecuadamente dichas etiquetas a la nota que se est creando o
editando.
Por ltimo observa como la subidad de ficheros (al final de ambas plantillas), que se hace tal
y como hemos estudiado en los ejercicios de la unidad 8, se habilita nicamente cuando el
usuario es de tipo premium.
Ahora vamos a por las acciones correspondientes a estas plantillas. Primero mostraremos el
cdigo y despus lo comentaremos.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / N o t a s C o n t r o l l e r
1 < ? p h p
2 . . .
3 p u b l i c f u n c t i o n n u e v a A c t i o n ( ) {
4 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
5
6 l i s t ( $ e t i q u e t a s , $ n o t a s , $ n o t a _ s e l e c c i o n a d a ) = $ t h i s - > d a m e E t i q u e t a s Y N o t a s ( ) ;
7
Las acciones del panel de notas
204
8 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
9
1 0 $ n o t a = n e w N o t a ( ) ;
1 1 $ n e w F o r m = $ t h i s - > c r e a t e F o r m ( n e w N o t a T y p e ( ) , $ n o t a ) ;
1 2
1 3 i f ( $ r e q u e s t - > g e t M e t h o d ( ) = = " P O S T " ) {
1 4
1 5 $ n e w F o r m - > b i n d R e q u e s t ( $ r e q u e s t ) ;
1 6
1 7 i f ( $ n e w F o r m - > i s V a l i d ( ) ) {
1 8 $ u s u a r i o = $ t h i s - > g e t ( ' s e c u r i t y . c o n t e x t ' ) - > g e t T o k e n ( ) - > g e t U s e r ( ) ;
1 9
2 0 $ i t e m = $ r e q u e s t - > g e t ( ' i t e m ' ) ;
2 1 $ t h i s - > a c t u a l i z a E t i q u e t a s ( $ n o t a , $ i t e m [ ' t a g s ' ] , $ u s u a r i o ) ;
2 2
2 3 $ n o t a - > s e t U s u a r i o ( $ u s u a r i o ) ;
2 4 $ n o t a - > s e t F e c h a ( n e w \ D a t e T i m e ( ) ) ;
2 5
2 6 i f ( $ n e w F o r m [ ' f i l e ' ] - > g e t D a t a ( ) ! = ' ' )
2 7 $ n o t a - > u p l o a d ( $ u s u a r i o - > g e t U s e r n a m e ( ) ) ;
2 8
2 9 $ e m - > p e r s i s t ( $ n o t a ) ;
3 0
3 1 $ e m - > f l u s h ( ) ;
3 2
3 3 r e t u r n $ t h i s - > r e d i r e c t ( $ t h i s - > g e n e r a t e U r l ( ' j a m n _ h o m e p a g e ' ) ) ;
3 4 }
3 5 }
3 6
3 7 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : c r e a r O E d i t a r . h t m l . t w i g ' , a r r a y (
3 8 ' e t i q u e t a s ' = > $ e t i q u e t a s ,
3 9 ' n o t a s ' = > $ n o t a s ,
4 0 ' n o t a _ s e l e c c i o n a d a ' = > $ n o t a ,
4 1 ' n e w _ f o r m ' = > $ n e w F o r m - > c r e a t e V i e w ( ) ,
4 2 ' e d i t a ' = > f a l s e ,
4 3 ) ) ;
4 4 }
4 5
4 6 p u b l i c f u n c t i o n e d i t a r A c t i o n ( ) {
4 7 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
4 8 $ i d = $ r e q u e s t - > g e t ( ' i d ' ) ;
4 9 l i s t ( $ e t i q u e t a s , $ n o t a s , $ n o t a _ s e l e c c i o n a d a ) = $ t h i s - > d a m e E t i q u e t a s Y N o t a s ( ) ;
5 0
5 1 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
5 2
5 3 $ n o t a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a ' ) - > f i n d ( $ i d ) ;
5 4
5 5 i f ( ! $ n o t a ) {
5 6 t h r o w $ t h i s - > c r e a t e N o t F o u n d E x c e p t i o n ( ' N o s e h a p o d i d o e n c o n t r a r e s a n o t a ' ) ;
5 7 }
5 8
5 9 $ e d i t F o r m = $ t h i s - > c r e a t e F o r m ( n e w N o t a T y p e ( ) , $ n o t a ) ;
6 0 $ d e l e t e F o r m = $ t h i s - > c r e a t e D e l e t e F o r m ( $ i d ) ;
6 1
6 2 i f ( $ t h i s - > g e t R e q u e s t ( ) - > g e t M e t h o d ( ) = = " P O S T " ) {
6 3
6 4 $ e d i t F o r m - > b i n d R e q u e s t ( $ r e q u e s t ) ;
6 5
6 6 i f ( $ e d i t F o r m - > i s V a l i d ( ) ) {
6 7 $ u s u a r i o = $ t h i s - > g e t ( ' s e c u r i t y . c o n t e x t ' ) - > g e t T o k e n ( ) - > g e t U s e r ( ) ;
Las acciones del panel de notas
205
6 8
6 9 $ i t e m = $ r e q u e s t - > g e t ( ' i t e m ' ) ;
7 0 $ t h i s - > a c t u a l i z a E t i q u e t a s ( $ n o t a , $ i t e m [ ' t a g s ' ] , $ u s u a r i o ) ;
7 1
7 2 $ n o t a - > s e t F e c h a ( n e w \ D a t e T i m e ( ) ) ;
7 3
7 4 i f ( $ e d i t F o r m [ ' f i l e ' ] - > g e t D a t a ( ) ! = ' ' )
7 5 $ n o t a - > u p l o a d ( $ u s u a r i o - > g e t U s e r n a m e ( ) ) ;
7 6
7 7 $ e m - > p e r s i s t ( $ n o t a ) ;
7 8
7 9 $ e m - > f l u s h ( ) ;
8 0
8 1 r e t u r n $ t h i s - > r e d i r e c t ( $ t h i s - > g e n e r a t e U r l ( ' j a m n _ h o m e p a g e ' ) ) ;
8 2 }
8 3 }
8 4
8 5 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : c r e a r O E d i t a r . h t m l . t w i g ' , a r r a y (
8 6 ' e t i q u e t a s ' = > $ e t i q u e t a s ,
8 7 ' n o t a s ' = > $ n o t a s ,
8 8 ' n o t a _ s e l e c c i o n a d a ' = > $ n o t a ,
8 9 ' e d i t _ f o r m ' = > $ e d i t F o r m - > c r e a t e V i e w ( ) ,
9 0 ' d e l e t e _ f o r m ' = > $ d e l e t e F o r m - > c r e a t e V i e w ( ) ,
9 1 ' e d i t a ' = > t r u e ,
9 2 ) ) ;
9 3 }
Ambas acciones comienzan igual que la accin i n d e x A c t i o n ( ) , recuperando el listado de
etiquetas y notas del usuario y la nota seleccionada (esta ltima no se necesita para la
creacin de notas). A continuacin se crea un objeto de tipo N o t a , que se inicializa en el caso
de edicin con la nota seleccionada, y que se utiliza para construir el formulario a partir del
tipo N o t a T y p e . Entonces se discrimina entre el caso de que la peticin sea por GET o POST.
En el primer caso solamente hay que pintar el formularioy en el segundo hay que procesar
los datos para modificar o crear la nota.
Tanto en la creacin como en la edicin de notas se utiliza una funcin auxiliar denominada
a c t u a l i z a E t i q u e t a s ( ) . Su cometido es asociar a la nota las etiquetas que vienen del
formulario a travs de la varible de la peticin i t e m . El cdigo de esta funcin es:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / N o t a s C o n t r o l l e r
1 p r o t e c t e d f u n c t i o n a c t u a l i z a E t i q u e t a s ( $ n o t a , $ t a g s , $ u s u a r i o ) {
2
3 i f ( c o u n t ( $ t a g s ) = = 0 ) {
4 $ t a g s = a r r a y ( ) ;
5 }
6 $ e m = $ t h i s - > g e t D o c t r i n e ( ) - > g e t E n t i t y M a n a g e r ( ) ;
7
8 $ n o t a - > g e t E t i q u e t a s ( ) - > c l e a r ( ) ;
9
1 0
1 1 f o r e a c h ( $ t a g s a s $ t a g ) {
1 2 $ e t i q u e t a = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : E t i q u e t a ' ) - > f i n d O n e B y T e x t o A n d U s u a r i o ( $ t a g , $ u s u a r i o ) ;
1 3
1 4 i f ( ! $ e t i q u e t a i n s t a n c e o f E t i q u e t a ) {
1 5 $ e t i q u e t a = n e w E t i q u e t a ( ) ;
1 6 $ e t i q u e t a - > s e t T e x t o ( $ t a g ) ;
1 7 $ e t i q u e t a - > s e t U s u a r i o ( $ u s u a r i o ) ;
1 8 $ e m - > p e r s i s t ( $ e t i q u e t a ) ;
1 9 }
2 0
2 1 $ n o t a - > a d d E t i q u e t a ( $ e t i q u e t a ) ;
2 2 }
2 3
2 4 $ e m - > f l u s h ( ) ;
2 5 }
Las acciones del panel de notas
206
Lo que hace este cdigo es borrar todas las etiquetas de la nota y, a continuacin asociar de
nuevo las etiquetas que se han pasado por el argumento. Es una manera sencilla de resolver
el problema de la actualizacin de atributos de una entidad.
Por ltimo se pasan los elementos necesarios para ser pintados por la plantilla
c r e a r O E d i t a r . h t m l . t w i g . Observa el valor de la variable e d i t a en cada una de las
acciones y como se corresponde con lo que hemos dicho ms arriba cuando plantebamos
las plantillas.
Y fin de la historia. Con esto ya tenemos el panel de notas completamente funcional.
El proceso de registro
En este apartado vamos a desarrollar el proceso de registro de nuevos usuarios en la
aplicacin. Comenzamos explicando el proceso:
En la pantalla de login de la aplicacin se aadir un enlace bien claro para que los usuarios
que an no dispongan de una cuenta en la aplicacin puedan crear una. Cuando el usuario
pique en este enlace se le presentar un formulario de registro en el que introducir su
nombre, apellidos, nombre de usuario, email y el password por duplicado.
Cuando enve el formulario al servidor, la aplicacin crear en la base de datos un registro
para el usuario con el campo i s A c t i v e definido como f a l s e y con el campo
t o k e n R e g i s t r o relleno con una cadena alfanumrica larga y nica. Adems se asociar el
usuario al grupo registrado.
Acto seguido se enviar un e-mail al usuario notificndole que ha solicitado crear una cuenta
en la aplicacin y que si desea activarla debe picar en un enlace que se le ofrece en el
propio email. Este enlace es una URL de la aplicacin que lleva como parmetro GET el
token que se ha generado anteriormente. Cuando el usuario pique en el enlace se ejecutar
la accin de activacin del usuario. Dicha accin comprobar si existe un registro usuario
con el token que se le ha enviado, y en caso afirmativo lo activar (pondr el campo
i s A c t i v e a t r u e ). De esa manera el usuario ya podr hacer login en la aplicacin y utilizarla
como usuario registrado.
Y esa es la esencia de cualquier proceso de (auto)registro de usuarios. Vemos ahora como
hemos resuelto el problema.
En primer lugar creamos un tipo nuevo para crear el formulario de registro, lo
denominaremos R e g i s t r o T y p e , y contendr todos los campos que hemos mencionado ms
arriba:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / F o r m / T y p e / R e g i s t r o T y p e . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e ;
4
5 u s e S y m f o n y \ C o m p o n e n t \ F o r m \ A b s t r a c t T y p e ;
6 u s e S y m f o n y \ C o m p o n e n t \ F o r m \ F o r m B u i l d e r ;
7
8 c l a s s R e g i s t r o T y p e e x t e n d s A b s t r a c t T y p e
9 {
1 0 p u b l i c f u n c t i o n b u i l d F o r m ( F o r m B u i l d e r $ b u i l d e r , a r r a y $ o p t i o n s )
1 1 {
1 2 $ b u i l d e r - > a d d ( ' n o m b r e ' , ' t e x t ' )
1 3 - > a d d ( ' a p e l l i d o s ' , ' t e x t ' )
1 4 - > a d d ( ' u s e r n a m e ' , ' t e x t ' )
El proceso de registro
207
1 5 - > a d d ( ' e m a i l ' , ' t e x t ' )
1 6 - > a d d ( ' p a s s w o r d ' , ' p a s s w o r d ' )
1 7 - > a d d ( ' p a s s w o r d _ a g a i n ' , ' p a s s w o r d ' ) ;
1 8 }
1 9
2 0 p u b l i c f u n c t i o n g e t N a m e ( )
2 1 {
2 2 r e t u r n ' r e g i s t r o ' ;
2 3 }
2 4
2 5 p u b l i c f u n c t i o n g e t D e f a u l t O p t i o n s ( a r r a y $ o p t i o n s )
2 6 {
2 7 r e t u r n a r r a y (
2 8 ' d a t a _ c l a s s ' = > ' J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ' ,
2 9 ) ;
3 0 }
3 1 }
Hemos asociado este tipo a la entidad U s u a r i o , pero para que esta asociacin sea correcta,
hay que aadir el campo p a s s w o r d _ a g a i n a dicha entidad:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / E n t i t y / U s u a r i o . p h p
1 < ? p h p
2
3 . . .
4 / * *
5 * @ v a r s t r i n g $ p a s s w o r d _ a g a i n
6 *
7 * @ A s s e r t \ N o t B l a n k ( )
8 * @ A s s e r t \ M a x L e n g t h ( 2 5 5 )
9 * @ A s s e r t \ R e g e x (
1 0 * p a t t e r n = " / ^ [ \ w - ] + $ / " ,
1 1 * m e s s a g e = " E l p a s s w o r d n o p u e d e c o n t e n e r m s q u e c a r a c t e r e s a l f a n u m r i c o s y g u i o n e s " )
1 2 * /
1 3 p r i v a t e $ p a s s w o r d _ a g a i n ;
1 4
1 5 / * *
1 6 * S e t p a s s w o r d _ a g a i n
1 7 *
1 8 * @ p a r a m s t r i n g $ p a s s w o r d _ a g a i n
1 9 * /
2 0 p u b l i c f u n c t i o n s e t P a s s w o r d A g a i n ( $ p a s s w o r d ) {
2 1 $ t h i s - > p a s s w o r d _ a g a i n = $ p a s s w o r d ;
2 2 }
2 3
2 4 / * *
2 5 * G e t p a s s w o r d _ a g a i n
2 6 *
2 7 * @ r e t u r n s t r i n g
2 8 * /
2 9 p u b l i c f u n c t i o n g e t P a s s w o r d A g a i n ( ) {
3 0 r e t u r n $ t h i s - > p a s s w o r d _ a g a i n ;
3 1 }
3 2 . . . *
Fjate que este nuevo atributo no se persiste en la base de datos, ya que no tiene ningn
tipo de metadato de mapeo asociado. Se trata de un campo que ser utilizado por el
formulario de registro. Adems, es importante que cuando se validen los campos del
formulario, nos aseguremos de que los campos u s e r n a m e y e m a i l no estn repetidos en la
base de datos, y de que los campos p a s s w o r d y p a s s w o r d _ a g a i n sean iguales. Para
conseguir esto simplemente tenemos que aadir las reglas de validacin adecuadas a esta
El proceso de registro
208
misma entidad. Mirando en la referencia de Symfony2 vemos que para exigir que los
atributos u s e r n a m e y e m a i l sean nicos hay que aadir estas lneas al principio del archivo
U s u a r i o . p h p :
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / E n t i t y / U s u a r i o . p h p
1 < ? p h p
2 . . .
3 / * *
4 * J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o
5 *
6 * @ O R M \ T a b l e ( )
7 * @ O R M \ E n t i t y ( r e p o s i t o r y C l a s s = " J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o R e p o s i t o r y " )
8 *
9 * @ U n i q u e E n t i t y ( " e m a i l " )
1 0 * @ U n i q u e E n t i t y ( " u s e r n a m e " )
1 1 * /
1 2 c l a s s U s u a r i o i m p l e m e n t s A d v a n c e d U s e r I n t e r f a c e {
1 3 . . . *
Y para que los atributos p a s s w o r d y p a s s w o r d _ a g a i n sean idnticos recurrimos a un tipo de
validador ms sofisticado basados en mtodos (funciones) que deben satisfacer alguna regla
de validacin y que no se corresponden con un atributo del objeto. El nombre de estas
funciones debe comenzar por "get" o "is". Aade el siguiente trozo de cdigo al fichero
U s u a r i o . p h p :
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / E n t i t y / U s u a r i o . p h p
1 . . .
2 / * *
3 * @ A s s e r t \ T r u e ( m e s s a g e = " H a s e s c r i t o d o s p a s s w o r d d i s t i n t o s " )
4 * /
5 p u b l i c f u n c t i o n i s P a s s w o r d O K ( ) {
6 r e t u r n ( $ t h i s - > p a s s w o r d = = = $ t h i s - > p a s s w o r d _ a g a i n ) ;
7 }
8 . . . *
La idea es sencilla, te creas una funcin que comience por "is" o "get", con la lgica que
quieras, y le indicas mediante una asercin (nosotros lo estamos haciendo con anotaciones)
el tipo de validacin que quieres hacer sobre esa funcin. En este caso simplemente
pedimos que el resultado sea t r u e . Este tipo de validadores son muy tiles cuando
necesitamos comprobar que se cumplen ciertas relaciones entre atributos del objeto.
Con esto ya tenemos disponible un tipo para crear el formulario de registro, y un objeto con
sus validadores definidos que podemos asociar a dicho tipo.
A continuacin vamos a implementar la accin de registro. Recuerda que esta accin ya la
tenemos enlazada en el routing del bundle (ruta j a m n _ r e g i s t r o ).
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / L o g i n C o n t r o l l e r . p h p
1 < ? p h p
2 . . .
3 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ;
4 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F o r m \ T y p e \ R e g i s t r o T y p e ;
5 . . .
6
7 p u b l i c f u n c t i o n r e g i s t r o A c t i o n ( ) {
8
El proceso de registro
209
9 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
1 0 $ u s u a r i o = n e w U s u a r i o ( ) ;
1 1
1 2 $ f o r m = $ t h i s - > c r e a t e F o r m ( n e w R e g i s t r o T y p e ( ) , $ u s u a r i o ) ;
1 3
1 4 i f ( $ r e q u e s t - > g e t M e t h o d ( ) = = " P O S T " ) {
1 5 $ f o r m - > b i n d R e q u e s t ( $ r e q u e s t ) ;
1 6 i f ( $ f o r m - > i s V a l i d ( ) ) {
1 7 $ s e r v i c e R e g i s t r o = $ t h i s - > g e t ( ' j a m _ n o t a s _ f r o n t e n d . r e g i s t r o ' ) ;
1 8 $ s e r v i c e R e g i s t r o - > r e g i s t r a ( $ u s u a r i o , $ f o r m - > g e t ( ' p a s s w o r d ' ) - > g e t D a t a ( ) ) ;
1 9
2 0 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : L o g i n : r e g i s t r o _ s u c c e s s . h t m l . t w i g ' , a r r a y ( ' u s u a r i o ' = > $ u s u a r i o ) ) ;
2 1 }
2 2 }
2 3
2 4 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : L o g i n : r e g i s t r o . h t m l . t w i g ' , a r r a y ( ' f o r m ' = > $ f o r m - > c r e a t e V i e w ( ) ) ) ;
2 5 }
Observa los u s e que hemos aadido para usar las clase U s u a r i o y R e g i s t r o T y p e en la
accin. Como ves hemos utilizado para el tratamiento de formularios el procedimiento
general que explicamos en la unidad 8. Lo interesante de este cdigo es lo que ocurre
cuando se envan los datos del formulario por POST y se pasa el proceso de validacin:
simplemente se lleva a cabo el registro. Mediante el contenedor de dependencias
instanciamos un servicio llamado j a m _ n o t a s _ f r o n t e n d . r e g i s t r o que se encargar de
insertar al usuario en la base de datos, con el campo i s A c t i v e a f a l s e y con un token
de registro.
enviar un correo electrnico al usuario indicndole que ha sido dado de alta en la
aplicacin y que puede activar su cuenta picando en un enlace que se proporciona en el
propio correo.
Obviamente este magnfico servicio hay que implementarlo. Pues vamos a ello. Observa que
el servicio debe ser capaz de acceder a la base de datos y de enviar e-mails. Para ello puede
hacer uso de los servicios existentes en el framework doctrine y mailer. Adems, cuando
vaya a insertar el password facilitado por el usuario debe hacerlo respetando el tipo de
codificacin que se haya definido en el fichero s e c u r i t y . y m l . Lo mejor es utilizar el servicio
de codificacin de Symfony2, que ya se ocupa de todos estos detalles. Por lo pronto hemos
identificado 3 dependencias de nuestro servicio de registro. Vamos a aadir una menos
obvia; utilizaremos tambin el servicio de templating para componer el texto del e-mail que
se enviar al usuario. Pues con esto en mente proponemos la siguiente clase como servicio
de registro:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / S e r v i c e s / R e g i s t r o . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ S e r v i c e s ;
4
5 c l a s s R e g i s t r o {
6
7 p u b l i c f u n c t i o n _ _ c o n s t r u c t ( $ d o c t r i n e , $ m a i l e r , $ t e m p l a t i n g , $ f a c t o r y _ e n c o d e r ) {
8 $ t h i s - > d o c t r i n e = $ d o c t r i n e ;
9 $ t h i s - > m a i l e r = $ m a i l e r ;
1 0 $ t h i s - > t e m p l a t i n g = $ t e m p l a t i n g ;
1 1 $ t h i s - > f a c t o r y _ e n c o d e r = $ f a c t o r y _ e n c o d e r ;
1 2 }
1 3
1 4 p u b l i c f u n c t i o n r e g i s t r a ( $ u s u a r i o , $ p a s s w o r d ) {
1 5 $ u s u a r i o - > s e t I s A c t i v e ( f a l s e ) ;
1 6 $ u s u a r i o - > s e t T o k e n R e g i s t r o ( s u b s t r ( m d 5 ( u n i q i d ( r a n d ( ) , t r u e ) ) , 0 , 3 2 ) ) ;
1 7
El proceso de registro
210
1 8 $ e m = $ t h i s - > d o c t r i n e - > g e t E n t i t y M a n a g e r ( ) ;
1 9
2 0 $ g r u p o = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : G r u p o ' )
2 1 - > f i n d O n e B y R o l ( ' R O L E _ R E G I S T R A D O ' ) ;
2 2
2 3 $ u s u a r i o - > a d d G r u p o ( $ g r u p o ) ;
2 4
2 5 $ e n c o d e r = $ t h i s - > f a c t o r y _ e n c o d e r - > g e t E n c o d e r ( $ u s u a r i o ) ;
2 6
2 7 $ s a l t = s u b s t r ( m d 5 ( u n i q i d ( r a n d ( ) , t r u e ) ) , 0 , 1 0 ) ;
2 8 $ u s u a r i o - > s e t S a l t ( $ s a l t ) ;
2 9 $ p a s s w o r d = $ e n c o d e r - > e n c o d e P a s s w o r d ( $ p a s s w o r d , $ u s u a r i o - > g e t S a l t ( ) ) ;
3 0
3 1 $ u s u a r i o - > s e t P a s s w o r d ( $ p a s s w o r d ) ;
3 2
3 3 $ e m - > p e r s i s t ( $ u s u a r i o ) ;
3 4
3 5 $ e m - > f l u s h ( ) ;
3 6
3 7 $ m e s s a g e = \ S w i f t _ M e s s a g e : : n e w I n s t a n c e ( )
3 8 - > s e t S u b j e c t ( ' A l t a e n l a a p l i c a c i n M e n t o r N o t a s ' )
3 9 - > s e t F r o m ( ' n o r e p l a y @ m e n t o r n o t a s . c o m ' )
4 0 - > s e t T o ( $ u s u a r i o - > g e t E m a i l ( ) )
4 1 - > s e t B o d y ( $ t h i s - > t e m p l a t i n g - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : L o g i n : e m a i l _ r e g i s t r o . h t m l . t w i g ' , a r r a y ( ' u s u a r i o ' = > $ u s u a r i o ) ) )
4 2 ;
4 3 $ t h i s - > m a i l e r - > s e n d ( $ m e s s a g e ) ;
4 4 }
4 5
4 6 }
Como ya estudiamos en la unidad 4, los servicios se crean inyectando las dependencias en
el constructor y aadiendo los mtodos que requieran. En nuestro caso, el mtodo
r e g i s t r a ( ) se encarga de llevar a cabo todas las acciones que hemos descrito ms arriba:
crear el usuario, hacerlo inactivo, aadirlo al grupo registrado, codificar adecuadamente su
password, crear su token, componer el mail y enviarlo. Todo ello apoyandose en los servicios
dependientes. Analiza el cdigo anterior identificando donde se hace cada una de estas
operaciones. Fjate que en la composicin del e-mail, se utiliza el renderizado de una
plantilla para rellenar el cuerpo del mensaje. El cdigo de esta plantilla se presenta a
continuacin:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / L o g i n / e m a i l _ r e g i s t r o . h t m l . t w i g
H o l a { { u s u a r i o . n o m b r e } } { { u s u a r i o . a p e l l i d o s } } ,
T o a l g u i e n h a c r e a d o u n a c u e n t a e n l a a p l i c a c i n M e n t o r N o t a s .
S i q u i e r e s a c t i v a r l a s i g u e e s t e e n l a c e :
{ { u r l ( " j a m n _ a c t i v a r _ c u e n t a " , { " t o k e n " : u s u a r i o . t o k e n R e g i s t r o } ) } }
U n s a l u d o !
Es por eso que el servicio de registro depende del servicio de templating.
Ahora, para que el contenedor de dependencias sepa instanciar el servicio que acabamos de
crear, hay que registrarlo en el bundle indicando sus dependencias:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
p a r a m e t e r s :
j a m _ n o t a s _ f r o n t e n d . r e g i s t r o . c l a s s : J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ S e r v i c e s \ R e g i s t r o
s e r v i c e s :
j a m _ n o t a s _ f r o n t e n d . r e g i s t r o :
c l a s s : % j a m _ n o t a s _ f r o n t e n d . r e g i s t r o . c l a s s %
a r g u m e n t s :
- " @ d o c t r i n e "
El proceso de registro
211
- " @ m a i l e r "
- " @ t e m p l a t i n g "
- " @ s e c u r i t y . e n c o d e r _ f a c t o r y "
Ya slo nos queda crear las plantillas r e g i s t r o . h t m l . t w i g y
r e g i s t r o _ s u c c e s s . h t m l . t w i g usadas en el controlador
J A M N o t a s F r o n t e n d B u n d l e : L o g i n : r e g i s t r o .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / L o g i n / r e g i s t r o . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4 < d i v c l a s s = " m e n t o r n o t a s - c o n t e n t - l o g i n " >
5 < h 1 > R e g s t r a t e < / h 1 >
6 < d i v >
7 < f o r m n o v a l i d a t e = " t r u e " a c t i o n = " { { p a t h ( " j a m n _ r e g i s t r o " ) } } " m e t h o d = " p o s t " i d = " l o g i n " >
8
9 { % i f f o r m _ e r r o r s ( f o r m ) % }
1 0 < d i v c l a s s = " e r r o r " > { { f o r m _ e r r o r s ( f o r m ) } } < / d i v >
1 1 { % e n d i f % }
1 2
1 3 < d i v >
1 4 { % i f f o r m _ e r r o r s ( f o r m . n o m b r e ) % }
1 5 < d i v c l a s s = " e r r o r " > { { f o r m _ e r r o r s ( f o r m . n o m b r e ) } } < / d i v >
1 6 { % e n d i f % }
1 7 < l a b e l f o r = " n o m b r e " > T u n o m b r e : < / l a b e l >
1 8 { { f o r m _ w i d g e t ( f o r m . n o m b r e ) } }
1 9 < / d i v >
2 0
2 1 < d i v >
2 2 { % i f f o r m _ e r r o r s ( f o r m . a p e l l i d o s ) % }
2 3 < d i v c l a s s = " e r r o r " > { { f o r m _ e r r o r s ( f o r m . a p e l l i d o s ) } } < / d i v >
2 4 { % e n d i f % }
2 5 < l a b e l f o r = " a p e l l i d o s " > A p e l l i d o s : < / l a b e l >
2 6 { { f o r m _ w i d g e t ( f o r m . a p e l l i d o s ) } }
2 7 < / d i v >
2 8
2 9 < d i v >
3 0 { % i f f o r m _ e r r o r s ( f o r m . u s e r n a m e ) % }
3 1 < d i v c l a s s = " e r r o r " > { { f o r m _ e r r o r s ( f o r m . u s e r n a m e ) } } < / d i v >
3 2 { % e n d i f % }
3 3 < l a b e l f o r = " u s e r n a m e " > U s e r n a m e : < / l a b e l >
3 4 { { f o r m _ w i d g e t ( f o r m . u s e r n a m e ) } }
3 5 < / d i v >
3 6
3 7 < d i v >
3 8 { % i f f o r m _ e r r o r s ( f o r m . e m a i l ) % }
3 9 < d i v c l a s s = " e r r o r " > { { f o r m _ e r r o r s ( f o r m . e m a i l ) } } < / d i v >
4 0 { % e n d i f % }
4 1 < l a b e l f o r = " e m a i l " > E m a i l : < / l a b e l >
4 2 { { f o r m _ w i d g e t ( f o r m . e m a i l ) } }
4 3
4 4 < / d i v >
4 5
4 6 < d i v >
4 7 { % i f f o r m _ e r r o r s ( f o r m . p a s s w o r d ) % }
4 8 < d i v c l a s s = " e r r o r " > { { f o r m _ e r r o r s ( f o r m . p a s s w o r d ) } } < / d i v >
4 9 { % e n d i f % }
5 0 < l a b e l f o r = " p a s s w o r d " > P a s s w o r d : < / l a b e l >
5 1 { { f o r m _ w i d g e t ( f o r m . p a s s w o r d ) } }
El proceso de registro
212
5 2
5 3 < / d i v >
5 4
5 5 < d i v >
5 6 { % i f f o r m _ e r r o r s ( f o r m . p a s s w o r d _ a g a i n ) % }
5 7 < d i v c l a s s = " e r r o r " > { { f o r m _ e r r o r s ( f o r m . p a s s w o r d _ a g a i n ) } } < / d i v >
5 8 { % e n d i f % }
5 9 < l a b e l f o r = " p a s s w o r d _ a g a i n " > P a s s w o r d ( o t r a v e z ) : < / l a b e l >
6 0 { { f o r m _ w i d g e t ( f o r m . p a s s w o r d _ a g a i n ) } }
6 1
6 2 < / d i v >
6 3
6 4 { { f o r m _ r e s t ( f o r m ) } }
6 5 < i n p u t t y p e = " s u b m i t " c l a s s = " m e n t o r n o t a s - b u t t o n - g r e y " v a l u e = " A d e l a n t e " / >
6 6 < / f o r m >
6 7 < / d i v >
6 8 < / d i v >
6 9 { % e n d b l o c k % }
Esta plantilla pinta el formulario de registro, y la que transcribimos a continuacin informa
de que el proceso de registro se ha llevado a cabo con xito.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / L o g i n / r e g i s t r o _ s u c c e s s . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4 < d i v c l a s s = " m e n t o r n o t a s - c o n t e n t - l o g i n " >
5
6 < h 1 > H o l a { { u s u a r i o . n o m b r e } } < / h 1 >
7
8 < p > G r a c i a s p o r r e g i s t r a r t e e n < i > M e n t o r N o t a s < / i > < / p >
9
1 0 < p > E n b r e v e r e c i b i r s u n c o r r e o e l e c t r n i c o c o n u n e n l a c e p a r a q u e d e f i n a s t u
1 1 p a s s w o r d y a c t i v e s t u c u e n t a . < / p >
1 2
1 3 < p > < a h r e f = " { { p a t h ( ' j a m n _ h o m e p a g e ' ) } } " > I r a p g i n a d e l o g i n < / a > < / p >
1 4
1 5 < / d i v >
1 6 { % e n d b l o c k % }
Y ya tenemos lista una parte del proceso de registro. Tan solo nos falta la activacin de la
cuenta cuando el usuario pique en el enlace que se le enva por e-mail. Para que funcione el
envo de e-mails, asegrate de que tienes adecuadamente configurado el servicio mailing
(en la unidad 4 se cuenta como hacer esto).
Recuerda que la ruta para la activacin de la cuenta tambin la tenemos ya registrada, se
llama j a m n _ a c t i v a r _ c u e n t a . La accin mapeada en dicha ruta se llama
J A M N o t a s F r o n t e n d B u n d l e : L o g i n : a c t i v a r , y lo que debe hacer es comprobar que el token
que le llega por GET se corresponde con alguno de los que tenemos en la base de datos y,
en su caso, activar el usuario asociado a dicho token. Y eso es exactamente lo que hace el
siguiente cdigo:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r / L o g i n C o n t r o l l e r . p h p
1 < ? p h p
2 . . .
3
4 p u b l i c f u n c t i o n a c t i v a r A c t i o n ( )
5 {
El proceso de registro
213
6 $ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ;
7
8 $ e m = $ t h i s - > g e t ( ' d o c t r i n e ' ) - > g e t E n t i t y M a n a g e r ( ) ;
9
1 0 $ u s u a r i o = $ e m - > g e t R e p o s i t o r y ( ' J A M N o t a s F r o n t e n d B u n d l e : U s u a r i o ' )
1 1 - > f i n d O n e B y T o k e n R e g i s t r o ( $ r e q u e s t - > g e t ( ' t o k e n ' ) ) ;
1 2
1 3 i f ( ! $ u s u a r i o ) {
1 4 t h r o w $ t h i s - > c r e a t e N o t F o u n d E x c e p t i o n ( ' U s u a r i o n o r e g i s t r a d o ' ) ;
1 5 }
1 6
1 7 $ u s u a r i o - > s e t I s A c t i v e ( t r u e ) ;
1 8 $ e m - > p e r s i s t ( $ u s u a r i o ) ;
1 9
2 0 $ e m - > f l u s h ( ) ;
2 1
2 2 r e t u r n $ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : L o g i n : a c t i v a r _ s u c c e s s . h t m l . t w i g ' , a r r a y ( ' u s u a r i o ' = > $ u s u a r i o ) ) ;
2 3 }
Para completar la funcionalidad debemos implementar la plantilla
J A M N o t a s F r o n t e n d B u n d l e : L o g i n : a c t i v a r _ s u c c e s s . h t m l . t w i g .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / L o g i n / a c t i v a r _ s u c c e s s . h t m l . t w i g
1 { % e x t e n d s ' : : b a s e . h t m l . t w i g ' % }
2
3 { % b l o c k b o d y % }
4 < d i v c l a s s = " m e n t o r n o t a s - c o n t e n t - l o g i n " >
5
6 < h 1 > H o l a { { u s u a r i o . n o m b r e } } < / h 1 >
7
8 < p >
9 T u c u e n t a h a s i d o a c t i v a d a . Y a p u e d e s e n t r a r e n l a a p l i c a c i n a t r a v s d e
1 0 e s t e e n l a c e :
1 1 < / p >
1 2
1 3 < p > < a h r e f = " { { p a t h ( ' j a m n _ h o m e p a g e ' ) } } " > I r a p g i n a d e l o g i n < / a > < / p >
1 4
1 5 < / d i v >
1 6 { % e n d b l o c k % }
Y ahora ya tenemos completo el proceso de registro.
El resto de la aplicacin.
Aunque lo ms importante de la aplicacin ya est terminado, an nos faltan algunas cosas
por hacer:
Hay que modificar las plantillas para adaptarlas a la interfaz de usuario propuesta en la
unidad 5.
Tambin hay que implementar la contratacin de cuentas premium.
Y hay que desarrollar la parte de administracin.
Pero esta unidad finaliza aqu. Los dos primeros puntos los acabars t en los ejercicios, y el
tercero ser el tema que trataremos en la siguiente unidad.
Autor del cdigo: Juan David Rodrguez Garca <juandavid.rodriguez@ite.educacion.es>
El resto de la aplicacin.
214
Unidad 11: Desarrollo de la aplicacin MentorNotas
(VII). Desarrollo del backend
Llegamos al final del curso. Con lo que has estudiado hasta ahora ya dispones de suficientes
conocimientos para desarrollar lo que vamos a explicar en esta unidad: la parte de
administracin, conocida universalmente como backend de la aplicacin.
Prcticamente cualquier aplicacin web precisa de cierto trabajo de administracin para que
funcione adecuadamente. Por ejemplo, la gestin de usuarios es una tarea de administracin
que suele ser comn a todas ellas. Por lo general, la administracin de cualquier aplicacin
consiste en la gestin de cada una de las entidades que la componen, es decir en crear
(Create), recuperar (Retrieve), actualizar (Update) y borrar (Delete) elementos de dichas
entidades. En el argot de las aplicaciones web: en desarrollar mdulos CRUD sobre las
entidades. Une las primeras letras de las operaciones que hemos enumerado antes (en
ingls) y te dars cuenta de lo que significa mdulo CRUD.
Por ello una herramienta verstil y absolutamente eficaz para la administracin es un cliente
del tipo phpMyAdmin (http://www.phpmyadmin.net). A falta de una herramienta ms precisa,
este tipo de software puede servirnos. El problema que presentan es que es demasiado
genrico y da demasiado poder al administrador de la aplicacin, de manera que un fallo en
la administracin podra dejar en la estacada a la aplicacin completa. Adems, en ocasiones
existen importantes relaciones entre las entidades que deberan ser controladas por la
aplicacin para que funcione correctamente . Por eso, aunque estas herramientas genricas
puedan servirnos en una primera etapa, si queremos que nuestra aplicacin quede
completa, debemos construir una herramienta de gestin a medida. Y eso es lo que haremos
en esta unidad.
Estrategias de desarrollo para la parte de administracin
Proponemos tres estrategias para el desarrollo de la administracin de la aplicacin:
1. Completamente a medida,
2. Con ayuda del generador de mdulos CRUD de Symfony2
3. Utilizando algn bundle de terceros para el desarrollo de backends.
La primera de ellas es la ms obvia y directa; ya sabes programar con Symfony2, as que
puedes crear para cada entidad que debas administrar controladores (acciones) y plantillas
mediante las que se muestren listados y formularios para crear, actualizar y borrar
elementos.
Si decides seguir esta estrategia, te dars cuenta de que el cdigo que has picado para cada
entidad es prcticamente idntico, y que la diferencia esencial es que cambia el nombre de
la entidad. Por ello, Symfony2 incorpora una herramienta (asistente) que genera
automticamente el cdigo mnimo para realizar las operaciones CRUD. Posteriormente, si la
aplicacin lo requiere tendrs que tunear ese cdigo para adaptarlo a las necesidades
concretas.
El generador de mdulos de administracin de Symfony2 est bien, pero en honor a la
verdad, el de symfony 1.4 est mucho mejor. En realidad symfony 1.4 proporciona dos
herramientas para generar mdulos de administracin. Una ms sencilla, que es similar a la
de Symfony2, mediante la que se genera un cdigo mnimo, que incluye:
listado de objetos,
formularios para crear y editar objetos,
y otra mucho ms elaborada mediante la que se generan mdulos de administracin con
listado de objetos con paginado,
Unidad 11: Desarrollo de la aplicacin MentorNotas (VII). Desarrollo del backend
215
filtro de bsqueda de objetos,
formularios para crear y editar objetos,
CSS's poco intrusivas que pueden combinarse fcilmente en el diseo de cualquier
aplicacin,
configuracin de las funcionalidades mediante un fichero yaml,
cdigo y plantillas fcilmente modificables y adaptables mediante el mecanismo de
extensin (herencia).
Symfony2, por lo pronto, y no creo que vaya a cambiar la situacin, proporciona un
generador de mdulos sencillo al estilo del primero que hemos descrito. No obstante existen
varios bundles de terceros con los que se construyen aplicaciones de administracin que
superan en funcionalidades y estilismo al generador de administracin de symfony 1.4. Eso
s, no son oficiales de Symfony2.
En esta unidad mostraremos las dos ltimas estrategias: el uso del generador de mdulos
CRUD de Symfony2 y el del SonataAdminBundle, uno de los bundles ms populares para el
desarrollo de mdulos de administracin.
El generador de mdulos CRUD de Symfony2
Generar un mdulo de administracin (CRUD) con el generador de mdulos CRUD de
Symfony2 es extremadamente sencillo. Vamos a comprobarlo generando un mdulo para la
administracin de la entidad Tarifa.
a p p / c o n s o l e g e n e r a t e : d o c t r i n e : c r u d
Como ves se trata de un asistente interactivo, al estilo del generador de bundles, as que
toca a responder preguntas. En primer lugar nos pide el nombre corto de la entidad que
deseamos administrar. Introduce J A M N o t a s F r o n t e n d B u n d l e : T a r i f a . A continuacin dile que
si quieres generar la opcin de escribir, y despus usa yaml como formato de configuracin.
Esto ltimo se utiliza para las rutas, y como en nuestro bundle estamos usando ficheros yaml
para su configuracin, debemos continuar usando este formato. Puedes mantener como
prefijo de la ruta el que te propone. Finalmente contesta que si a la generacin de cdigo y a
la importacin de las rutas. Si, al finalizar el proceso, te indica que hagas manualmente
algn cambio, hazle caso.
Ahora puedes probar lo que acabamos de hacer a travs de la URL
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / n o t a s / t a r i f a
Vers una rudimentaria pantalla con un listado de tarifas. Puedes editarlas, borrarlas y crear
tarifas nuevas. Prubalo. A nivel de cdigo a ocurrido lo siguiente:
En el directorio s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / C o n t r o l l e r se ha
generado un controlador denominado T a r i f a C o n t r o l l e r con el cdigo que
implementa las operaciones de listar, editar, borrar y crear tarifas.
Se ha generado el directorio
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / v i e w s / T a r i f a con las
plantillas twig utilizadas en el mdulo.
Se ha generado el archivo
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g / t a r i f a . y m l
con las rutas del mdulo.
El generador de mdulos CRUD de Symfony2
216
Examina todo este cdigo y procura entender todo lo que hace. No te debe resultar
complicado con lo que has estudiado hasta el momento. Lo mismo que acabamos de hacer
con la entidad Tarifa podemos hacer con el resto de las entidade que requieran
administracin. Una vez generados todos los mdulos es el momento del tunning, es decir,
de modificar el cdigo para adaptarlo a nuestras necesidades. Por ejemplo:
Podemos cambiar las plantillas para que utilicen las CSS's de nuestra aplicacin.
Podemos aadir un paginado a los listados. Puedes currartelo t, o mejor, puedes
utilizar bundles de terceros que realizan esta tarea.
Podemos aadir un filtro de bsqueda.
Y todo lo que se te ocurra.
A partir de aqu la cosa va de indagar, probar, investigar, equivocarse, aprender de los
errores, asimilar y registrar todo aquello que consigamos hacer funcionar y que creamos til
para otros desarrollo. En fin, literalmente romperse la cabeza, que es de lo que va esto del
desarrollo.
El generador de mdulos CRUD SonataAdminBundle
Sonata Project es un proyecto de una empresa francesa (Ekino) que pretende desarrollar una
solucin de comercio electrnico sobre Symfony2. Una de las partes de este proyecto es el
desarrollo de una herramienta para generar aplicaciones de administracin (backends). Esta
parte, denominada SonataAdminBundle, est en un estado de desarrollo muy avanzado y es
completamente funcional, aunque la documentacin no est, en mi humilde opinin,
completamente afinada y hay que partirse la cabeza (de nuevo), un poco ms de lo normal
para ponerlo en funcionamiento. En lo que queda de tema, y de curso :-), daremos las
instrucciones para instalar el bundle y ponerlo a funcionar. Exploraremos sus caractersticas
bsicas, pero ten en cuenta que con este bundle se puede hacer mucho ms de lo que aqu
explicaremos. Como siempre, se trata de explorar la documentacin y "jugar" con el bundle
hasta exprimirlo.
Instalacin del SonataAdminBundle
El SonataAdminBundle depende de otros bundles de Symfony2, concretamente de:
SonataCacheBundle
SonataBlockBundle
SonatajQueryBundle
KnpMenuBundle (Version 1.1.*)
Exporter
Para instalar todo este cdigo debes aadir a tu fichero d e p s las siguientes lneas:
. . .
[ S o n a t a A d m i n B u n d l e ]
g i t = h t t p : / / g i t h u b . c o m / s o n a t a - p r o j e c t / S o n a t a A d m i n B u n d l e . g i t
t a r g e t = / b u n d l e s / S o n a t a / A d m i n B u n d l e
v e r s i o n = o r i g i n / 2 . 0
[ S o n a t a B l o c k B u n d l e ]
g i t = h t t p : / / g i t h u b . c o m / s o n a t a - p r o j e c t / S o n a t a B l o c k B u n d l e . g i t
t a r g e t = / b u n d l e s / S o n a t a / B l o c k B u n d l e
v e r s i o n = o r i g i n / 2 . 0
El generador de mdulos CRUD SonataAdminBundle
217
[ S o n a t a C a c h e B u n d l e ]
g i t = h t t p : / / g i t h u b . c o m / s o n a t a - p r o j e c t / S o n a t a C a c h e B u n d l e . g i t
t a r g e t = / b u n d l e s / S o n a t a / C a c h e B u n d l e
v e r s i o n = o r i g i n / 2 . 0
[ S o n a t a j Q u e r y B u n d l e ]
g i t = h t t p : / / g i t h u b . c o m / s o n a t a - p r o j e c t / S o n a t a j Q u e r y B u n d l e . g i t
t a r g e t = / b u n d l e s / S o n a t a / j Q u e r y B u n d l e
[ S o n a t a D o c t r i n e O R M A d m i n B u n d l e ]
g i t = h t t p : / / g i t h u b . c o m / s o n a t a - p r o j e c t / S o n a t a D o c t r i n e O R M A d m i n B u n d l e . g i t
t a r g e t = / b u n d l e s / S o n a t a / D o c t r i n e O R M A d m i n B u n d l e
v e r s i o n = o r i g i n / 2 . 0
[ K n p M e n u B u n d l e ]
g i t = h t t p : / / g i t h u b . c o m / K n p L a b s / K n p M e n u B u n d l e . g i t
t a r g e t = / b u n d l e s / K n p / B u n d l e / M e n u B u n d l e
v e r s i o n = o r i g i n / 1 . 1 . 0
[ K n p M e n u ]
g i t = h t t p : / / g i t h u b . c o m / K n p L a b s / K n p M e n u . g i t
t a r g e t = / k n p / m e n u
[ E x p o r t e r ]
g i t = h t t p : / / g i t h u b . c o m / s o n a t a - p r o j e c t / e x p o r t e r . g i t
t a r g e t = / e x p o r t e r
Antes de proseguir borra la cache:
s u d o r m - r f a p p / c a c h e / *
s u d o c h m o d - R 7 7 7 a p p / c a c h e
o
s u d o p h p a p p / c o n s o l e c a c h e : c l e a r
Nota
En linux puedes que tengas problemas con los permisos. Asegurate de hacer:
s u d o c h m o d - R 7 7 7 a p p / c a c h e
Ahora para descargar y aadir todo este cdigo al proyecto, debes ejecutar por lnea de
comando la instruccin:
. / b i n / v e n d o r s i n s t a l l
El generador de mdulos CRUD SonataAdminBundle
218
Si te bajaste la distribucin estndard de Symfony2 con vendors, tendrs que reinstalarlo
todo:
. / b i n / v e n d o r s i n s t a l l - - r e i n s t a l l
A continuacin debemos registrar en el a u t o l o a d . p h p los espacios de nombres de los
bundles que acabamos de instalar:
a p p / a u t o l o a d . p h p
< ? p h p
$ l o a d e r - > r e g i s t e r N a m e s p a c e s ( a r r a y (
/ / . . .
' S o n a t a ' = > _ _ D I R _ _ . ' / . . / v e n d o r / b u n d l e s ' ,
' E x p o r t e r ' = > _ _ D I R _ _ . ' / . . / v e n d o r / e x p o r t e r / l i b ' ,
' K n p \ B u n d l e ' = > _ _ D I R _ _ . ' / . . / v e n d o r / b u n d l e s ' ,
' K n p \ M e n u ' = > _ _ D I R _ _ . ' / . . / v e n d o r / k n p / m e n u / s r c ' ,
/ / . . .
) ) ;
Y a continuacin registramos los bundles:
a p p / A p p K e r n e l . p h p
< ? p h p
. . .
p u b l i c f u n c t i o n r e g i s t e r B u n d l e s ( ) {
$ b u n d l e s = a r r a y (
. . .
n e w S o n a t a \ A d m i n B u n d l e \ S o n a t a A d m i n B u n d l e ( ) ,
n e w S o n a t a \ B l o c k B u n d l e \ S o n a t a B l o c k B u n d l e ( ) ,
n e w S o n a t a \ C a c h e B u n d l e \ S o n a t a C a c h e B u n d l e ( ) ,
n e w S o n a t a \ j Q u e r y B u n d l e \ S o n a t a j Q u e r y B u n d l e ( ) ,
n e w K n p \ B u n d l e \ M e n u B u n d l e \ K n p M e n u B u n d l e ( ) ,
n e w S o n a t a \ D o c t r i n e O R M A d m i n B u n d l e \ S o n a t a D o c t r i n e O R M A d m i n B u n d l e ( ) ,
Ahora instalamos los a s s e t s que vienen con los bundles:
p h p a p p / c o n s o l e a s s e t s : i n s t a l l - - s y m l i n k w e b
Nota
En windows no se puede utilizar la opcin - - s y m l i n k
Y ahora configuramos los bundles. Para ello aadimos al archivo
a p p / c o n f i g / c o n f i g . y m l
. . .
s o n a t a _ a d m i n :
El generador de mdulos CRUD SonataAdminBundle
219
t i t l e : A d m i n i s t r a c i n d e l a a p l i c a c i n M e n t o r N o t a s
t i t l e _ l o g o : b u n d l e s / s o n a t a a d m i n / l o g o _ t i t l e . p n g
t e m p l a t e s :
# d e f a u l t g l o b a l t e m p l a t e s
l a y o u t : S o n a t a A d m i n B u n d l e : : s t a n d a r d _ l a y o u t . h t m l . t w i g
a j a x : S o n a t a A d m i n B u n d l e : : a j a x _ l a y o u t . h t m l . t w i g
d a s h b o a r d : S o n a t a A d m i n B u n d l e : C o r e : d a s h b o a r d . h t m l . t w i g
# d e f a u l t a c t i o n s t e m p l a t e s , s h o u l d e x t e n d a g l o b a l t e m p l a t e s
l i s t : S o n a t a A d m i n B u n d l e : C R U D : l i s t . h t m l . t w i g
s h o w : S o n a t a A d m i n B u n d l e : C R U D : s h o w . h t m l . t w i g
e d i t : S o n a t a A d m i n B u n d l e : C R U D : e d i t . h t m l . t w i g
d a s h b o a r d :
b l o c k s :
# d i s p l a y a d a s h b o a r d b l o c k
- { p o s i t i o n : l e f t , t y p e : s o n a t a . a d m i n . b l o c k . a d m i n _ l i s t }
s o n a t a _ b l o c k :
d e f a u l t _ c o n t e x t s : [ c m s ]
b l o c k s :
s o n a t a . a d m i n . b l o c k . a d m i n _ l i s t :
c o n t e x t s : [ a d m i n ]
Asegurate de que en este mismo fichero (a p p / c o n f i g / c o n f i g . y m l ) la opcin t r a n s l a t o r
est activada.
. . .
t r a n s l a t o r : { f a l l b a c k : % l o c a l e % }
. . .
Vamos a por las rutas. Aade al fichero
a p p / c o n f i g / r o u t i n g . y m l
. . .
a d m i n :
r e s o u r c e : ' @ S o n a t a A d m i n B u n d l e / R e s o u r c e s / c o n f i g / r o u t i n g / s o n a t a _ a d m i n . x m l '
p r e f i x : / a d m i n
_ s o n a t a _ a d m i n :
r e s o u r c e : .
t y p e : s o n a t a _ a d m i n
p r e f i x : / a d m i n
Con esto ya deberas poder ver algo. Borra la cache y prueba la siguiente ruta:
h t t p : / / l o c a l h o s t / S y m f o n y / w e b / a p p _ d e v . p h p / a d m i n / d a s h b o a r d
Si todo ha ido bien deberas ver el panel de control (dashboard) vaco, nicamente con el
ttulo.
Ahora ya estamos en condiciones de aadir mdulos de administracin a nuestro proyecto a
travs del SonataAdminBundle.
El generador de mdulos CRUD SonataAdminBundle
220
Inyeccin de los mdulos de administracin
Antes de hacer nada vamos a describir la filosofa de uso de este bundle. El bundle
proporciona un controlador que implementa todas las operaciones propias de un mdulo
CRUD:
crear nuevos objetos
editar objetos existentes
listar los objetos existentes
filtrar (buscar) objetos
borrar objetos
y algunas ms. chale un vistazo al archivo
v e n d o r / b u n d l e s / S o n a t a / A d m i n B u n d l e / C o n t r o l l e r / C R U D C o n t r o l l e r , y podrs ver todas
las acciones que incorpora el controlador base del bundle. La idea es que cada entidad que
vaya a ser administrada con este bundle, utilice como controlador una clase que derive de
este que acabamos de ver, y si las circunstancias lo requieren, se realicen las modificaciones
que sean necesaria usando el mecanismo de herencia. En muchos casos nos servir (y nos
sobrar) el controlador base tal y como est.
Por otro lado, cada entidad administrada tendr asociada una clase cuya finalidad es
configurar el aspecto que presentarn los formularios asociados (campos que se mostrarn
en el listado, campos que se mostrarn en los filtros, campos que se mostrarn en el
formulario de edicin/creacin, validadores extras, y algunas cosas ms). Esta clase debe
extender a S o n a t a \ A d m i n B u n d l e \ A d m i n \ A d m i n , y representa el vnculo con el
S o n a t a A d m i n B u n d l e .
Y ahora viene la gracia del bundle. Esta ltima clase asociada a la entidad que define como
deben ser los formularios de administracin, es registrada como un servicio de Symfony2 en
el archivo R e s o u r c e s / c o n f i g / s e r v i c e s . y m l del bundle donde estemos construyendo la
parte de administracin, en nuestro caso en el N o t a s B a c k e n d B u n d l e . Los argumentos que se
le inyectan como dependencias de este nuevo servicio son la entidad que va a ser
administrada y el controlador que se utilizar para administrarla. Y ya est, el servicio que
acabamos de crear, que conoce al S o n a t a A d m i n B u n d l e , pues deriva de una de sus clases, se
encarga de aadir al d a s h b o a r d la administracin de la entidad en cuestin.
Como ves, el SonataAdminBundle utiliza el mecanismo de inyeccin de dependencias para
su funcionamiento. Por eso hemos llamado a este apartado inyeccin de los mdulos de
administracin.
Aunque todo esto te puede resultar un poco abstracto hasta que no lo veas en
funcionamiento, chale un par de lecturas intensas antes de continuar con los siguientes
prrafos.
Y ahora que ya has leido varias veces la filosofa de uso del SonataAdminBundlde, vamos a
ponerlo en marcha. Comenzaremos por aadir un modulo de administracin para la entidad
Tarifa.
Lo primero que haremos ser crear en el NotasBackendBundle (que en su momento creamos
pensando en la parte de administracin), una clase, que denominaremos
A d m i n C o n t r o l l e r (el nombre puede ser cualquiera), que derive de
C R U D C o n t r o l l e r .
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / C o n t r o l l e r / A d m i n C o n t r o l l e r
Inyeccin de los mdulos de administracin
221
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ C o n t r o l l e r ;
u s e S o n a t a \ A d m i n B u n d l e \ C o n t r o l l e r \ C R U D C o n t r o l l e r a s C o n t r o l l e r ;
c l a s s A d m i n C o n t r o l l e r e x t e n d s C o n t r o l l e r
{
}
En principio, est clase servir para todas las entidades que vayamos a administrar, ya que
contiene la implementacin de las acciones propias y comunes de cualquier mdulo CRUD.
Ahora vamos a crear una clase de administracin asociada a la entidad Tarifa, donde
definiremos el aspecto de los formularios CRUD.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / A d m i n / T a r i f a A d m i n . p h p
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ A d m i n \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ L i s t M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ D a t a g r i d M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ V a l i d a t o r \ E r r o r E l e m e n t ;
u s e S o n a t a \ A d m i n B u n d l e \ F o r m \ F o r m M a p p e r ;
c l a s s T a r i f a A d m i n e x t e n d s A d m i n {
p r o t e c t e d f u n c t i o n c o n f i g u r e F o r m F i e l d s ( F o r m M a p p e r $ f o r m M a p p e r ) {
$ f o r m M a p p e r
- > a d d ( ' n o m b r e ' )
- > a d d ( ' p e r i o d o ' )
- > a d d ( ' p r e c i o ' )
- > a d d ( ' v a l i d o D e s d e ' )
- > a d d ( ' v a l i d o H a s t a ' )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e D a t a g r i d F i l t e r s ( D a t a g r i d M a p p e r $ d a t a g r i d M a p p e r ) {
$ d a t a g r i d M a p p e r
- > a d d ( ' n o m b r e ' )
- > a d d ( ' p e r i o d o ' )
- > a d d ( ' p r e c i o ' )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e L i s t F i e l d s ( L i s t M a p p e r $ l i s t M a p p e r ) {
$ l i s t M a p p e r
- > a d d I d e n t i f i e r ( ' n o m b r e ' )
- > a d d ( ' p e r i o d o ' )
- > a d d ( ' p r e c i o ' )
Inyeccin de los mdulos de administracin
222
- > a d d ( ' v a l i d o D e s d e ' )
- > a d d ( ' v a l i d o H a s t a ' )
;
}
p u b l i c f u n c t i o n v a l i d a t e ( E r r o r E l e m e n t $ e r r o r E l e m e n t , $ o b j e c t ) {
}
}
En el mtodo c o n f i g u r e F o r m F i e l d s , se definen los campos que tendrn los formularios de
edicin y creacin de objetos T a r i f a . En el mtodo c o n f i g u r e D a t a g r i d F i l t e r s se definen
los campos que incluir el formulario usado para filtrar registros. En el mtodo
c o n f i g u r e L i s t F i e l d s , se definen los campos que aparecern en el listado de registros. Por
ltimo, en el mtodo v a l i d a t e , se aaden validadores extras que puedan ser necesarios.
Por lo general, las reglas de validacin que hemos definido en las entidades son suficientes.
As que ya tenemos la clase de administracin asociada a la entidad Tarifa, y un controlador
genrico. El prximo paso es inyectar esta clase como nuevo servicio de nuestro proyecto.
Ello, como ya sabes, se hace en el archivo s e r v i c e s . y m l del bundle donde estamos
llevando a cabo la implementacin.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
p a r a m e t e r s :
# j a m _ n o t a s _ b a c k e n d . e x a m p l e . c l a s s : J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ E x a m p l e
s e r v i c e s :
s o n a t a . n o t a s _ b a c k e n d . a d m i n . t a r i f a :
c l a s s : J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n \ T a r i f a A d m i n
t a g s :
- { n a m e : s o n a t a . a d m i n , m a n a g e r _ t y p e : o r m , g r o u p : ' C o n t r a t o s ' , l a b e l : ' T a r i f a s ' }
a r g u m e n t s :
- n u l l
- J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ T a r i f a
- J A M N o t a s B a c k e n d B u n d l e : A d m i n
Hemos llamado al servicio s o n a t a . n o t a s _ b a c k e n d . a d m i n . t a r i f a , la clase que define al
servicio es J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n \ T a r i f a A d m i n , y los
argumentos que se le pasan son:
J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ T a r i f a y
J A M N o t a s B a c k e n d B u n d l e : A d m i n ,
es decir; la entidad que queremos administrar y el controlador bsico que hemos creado al
principio.
En el apartado t a g s , se definen la etiqueta que se mostrar en el dashboard y el grupo al
que pertenecer el mdulo.
Y ya est, fjate que no tenemos que preocuparnos del routing, el SonataAdminBundle se
encarga de todo. Si vuelves a acceder al dashboard vers ahora que se ha aadido un item
de men y un bloque para el acceso a la administracin de la entidad Tarifa.
Y lo mismo podemos hacer con el resto de entidades adminstradas.
Entidad Contrato
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / A d m i n / C o n t r a t o A d m i n . p h p
Entidad Contrato
223
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ A d m i n \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ L i s t M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ D a t a g r i d M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ V a l i d a t o r \ E r r o r E l e m e n t ;
u s e S o n a t a \ A d m i n B u n d l e \ F o r m \ F o r m M a p p e r ;
c l a s s C o n t r a t o A d m i n e x t e n d s A d m i n {
p r o t e c t e d f u n c t i o n c o n f i g u r e F o r m F i e l d s ( F o r m M a p p e r $ f o r m M a p p e r ) {
$ f o r m M a p p e r
- > a d d ( ' r e f e r e n c i a ' )
- > a d d ( ' u s u a r i o ' )
- > a d d ( ' f e c h a ' )
- > a d d ( ' t a r i f a ' )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e D a t a g r i d F i l t e r s ( D a t a g r i d M a p p e r $ d a t a g r i d M a p p e r ) {
$ d a t a g r i d M a p p e r
- > a d d ( ' r e f e r e n c i a ' )
- > a d d ( ' t a r i f a ' )
- > a d d ( ' u s u a r i o ' )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e L i s t F i e l d s ( L i s t M a p p e r $ l i s t M a p p e r ) {
$ l i s t M a p p e r
- > a d d I d e n t i f i e r ( ' r e f e r e n c i a ' )
- > a d d ( ' f e c h a ' )
- > a d d ( ' t a r i f a ' )
- > a d d ( ' u s u a r i o ' )
- > a d d ( ' _ a c t i o n ' , ' a c t i o n s ' , a r r a y (
' a c t i o n s ' = > a r r a y (
' e d i t ' = > a r r a y ( ) ,
' d e l e t e ' = > a r r a y ( ) ,
) ) )
;
}
p u b l i c f u n c t i o n v a l i d a t e ( E r r o r E l e m e n t $ e r r o r E l e m e n t , $ o b j e c t ) {
}
}
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
s e r v i c e s . y m l :
. . .
s o n a t a . n o t a s _ b a c k e n d . a d m i n . c o n t r a t o s :
Entidad Contrato
224
c l a s s : J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n \ C o n t r a t o A d m i n
t a g s :
- { n a m e : s o n a t a . a d m i n , m a n a g e r _ t y p e : o r m , g r o u p : ' C o n t r a t o s ' , l a b e l : C o n t r a t o s }
a r g u m e n t s :
- n u l l
- J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ C o n t r a t o
- J A M N o t a s B a c k e n d B u n d l e : A d m i n
Entidad Grupo
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / A d m i n / G r u p o A d m i n . p h p
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ A d m i n \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ L i s t M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ D a t a g r i d M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ V a l i d a t o r \ E r r o r E l e m e n t ;
u s e S o n a t a \ A d m i n B u n d l e \ F o r m \ F o r m M a p p e r ;
c l a s s G r u p o A d m i n e x t e n d s A d m i n {
p r o t e c t e d f u n c t i o n c o n f i g u r e F o r m F i e l d s ( F o r m M a p p e r $ f o r m M a p p e r ) {
$ f o r m M a p p e r
- > a d d ( ' n o m b r e ' )
- > a d d ( ' r o l ' )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e D a t a g r i d F i l t e r s ( D a t a g r i d M a p p e r $ d a t a g r i d M a p p e r ) {
$ d a t a g r i d M a p p e r
- > a d d ( ' n o m b r e ' )
- > a d d ( ' r o l ' )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e L i s t F i e l d s ( L i s t M a p p e r $ l i s t M a p p e r ) {
$ l i s t M a p p e r
- > a d d I d e n t i f i e r ( ' n o m b r e ' )
- > a d d ( ' r o l ' )
;
}
p u b l i c f u n c t i o n v a l i d a t e ( E r r o r E l e m e n t $ e r r o r E l e m e n t , $ o b j e c t ) {
}
}
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
s e r v i c e s :
. . .
s o n a t a . n o t a s _ b a c k e n d . a d m i n . g r u p o :
Entidad Grupo
225
c l a s s : J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n \ G r u p o A d m i n
t a g s :
- { n a m e : s o n a t a . a d m i n , m a n a g e r _ t y p e : o r m , g r o u p : ' A d m i n i s t r a c i n ' , l a b e l : G r u p o s }
a r g u m e n t s :
- n u l l
- J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ G r u p o
- J A M N o t a s B a c k e n d B u n d l e : A d m i n
Entidad Publicidad
En la administracin de la entidad Publicidad tenemos que poder subir las imgenes de los
banners correspondientes. Estara bien, adems, que en el listado de los registros de
publicidad se visualizase una miniatura de la imagen. Esta caracterstica obliga a realizar
algunas cosillas extras. Veamos como podemos conseguir esta funcionalidad. Por lo pronto
procedemos como en las anteriores entidades: Creamos su clase de administracin y la
registramos como servicio que utiliza la clase J A M N o t a s B a c k e n d B u n d l e : A d m i n como
controlador:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / A d m i n / P u b l i c i d a d A d m i n . p h p
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ A d m i n \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ L i s t M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ D a t a g r i d M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ V a l i d a t o r \ E r r o r E l e m e n t ;
u s e S o n a t a \ A d m i n B u n d l e \ F o r m \ F o r m M a p p e r ;
c l a s s P u b l i c i d a d A d m i n e x t e n d s A d m i n {
p r o t e c t e d f u n c t i o n c o n f i g u r e F o r m F i e l d s ( F o r m M a p p e r $ f o r m M a p p e r ) {
$ f o r m M a p p e r
- > a d d ( ' n o m b r e ' )
- > a d d ( ' t e x t o ' )
- > a d d ( ' p a t h ' , n u l l , a r r a y ( " r e a d _ o n l y " = > t r u e ) )
- > a d d ( ' f i l e ' , ' f i l e ' , a r r a y ( ' r e q u i r e d ' = > f a l s e ) )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e D a t a g r i d F i l t e r s ( D a t a g r i d M a p p e r $ d a t a g r i d M a p p e r ) {
$ d a t a g r i d M a p p e r
- > a d d ( ' n o m b r e ' )
- > a d d ( ' t e x t o ' )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e L i s t F i e l d s ( L i s t M a p p e r $ l i s t M a p p e r ) {
$ l i s t M a p p e r
- > a d d ( ' i m a g e n ' , n u l l , a r r a y ( ' t e m p l a t e ' = > ' J A M N o t a s B a c k e n d B u n d l e : S o n a t a : i m a g e n . h t m l . t w i g ' ) )
- > a d d I d e n t i f i e r ( ' n o m b r e ' )
- > a d d ( ' t e x t o ' )
- > a d d ( ' p a t h ' )
;
}
p u b l i c f u n c t i o n v a l i d a t e ( E r r o r E l e m e n t $ e r r o r E l e m e n t , $ o b j e c t ) {
}
p u b l i c f u n c t i o n p r e P e r s i s t ( $ o b j e c t )
{
Entidad Publicidad
226
$ o b j e c t - > u p l o a d ( ) ;
}
p u b l i c f u n c t i o n p r e U p d a t e ( $ o b j e c t )
{
$ o b j e c t - > u p l o a d ( ) ;
}
p u b l i c f u n c t i o n p o s t R e m o v e ( $ o b j e c t )
{
$ o b j e c t - > r e m o v e U p l o a d ( ) ;
}
}
Fjate que en la configuracin de los campos del formulario de edicin/creacin
(c o n f i g u r e F o r m F i e l d s ), se ha aadido un campo de tipo f i l e , es decir un campo de
subidad de archivos. Para que todo vaya bien es necesario que la entidad P u b l i c i d a d
implemente los mtodos para la subida y gestin de archivos asociados que ya hemos
utilizado en unidades anteriore para la entidad N o t a . La entidad P u b l i c i d a d quedara as:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / E n t i t y / P u b l i c i d a d . p h p
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y ;
u s e D o c t r i n e \ O R M \ M a p p i n g a s O R M ;
u s e S y m f o n y \ C o m p o n e n t \ V a l i d a t o r \ C o n s t r a i n t s a s A s s e r t ;
/ * *
* J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ P u b l i c i d a d
*
* @ O R M \ T a b l e ( )
* @ O R M \ E n t i t y ( r e p o s i t o r y C l a s s = " J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ P u b l i c i d a d R e p o s i t o r y " )
* /
c l a s s P u b l i c i d a d {
/ * *
* @ v a r i n t e g e r $ i d
*
* @ O R M \ C o l u m n ( n a m e = " i d " , t y p e = " i n t e g e r " )
* @ O R M \ I d
* @ O R M \ G e n e r a t e d V a l u e ( s t r a t e g y = " A U T O " )
* /
p r i v a t e $ i d ;
/ * *
* @ v a r s t r i n g $ n o m b r e
*
* @ O R M \ C o l u m n ( n a m e = " n o m b r e " , t y p e = " s t r i n g " , l e n g t h = 2 5 5 )
* /
p r i v a t e $ n o m b r e ;
/ * *
* @ v a r s t r i n g $ t e x t o
*
* @ O R M \ C o l u m n ( n a m e = " t e x t o " , t y p e = " s t r i n g " , l e n g t h = 2 5 5 )
* /
p r i v a t e $ t e x t o ;
Entidad Publicidad
227
/ * *
* @ v a r s t r i n g $ p a t h
*
* @ O R M \ C o l u m n ( n a m e = " p a t h " , t y p e = " s t r i n g " , l e n g t h = 2 5 5 , n u l l a b l e = " t r u e " )
* /
p r i v a t e $ p a t h ;
/ * *
* @ A s s e r t \ F i l e ( m a x S i z e = " 6 0 0 0 0 0 0 " )
* /
p u b l i c $ f i l e ;
/ * *
* G e t i d
*
* @ r e t u r n i n t e g e r
* /
p u b l i c f u n c t i o n g e t I d ( ) {
r e t u r n $ t h i s - > i d ;
}
/ * *
* S e t n o m b r e
*
* @ p a r a m s t r i n g $ n o m b r e
* /
p u b l i c f u n c t i o n s e t N o m b r e ( $ n o m b r e ) {
$ t h i s - > n o m b r e = $ n o m b r e ;
}
/ * *
* G e t n o m b r e
*
* @ r e t u r n s t r i n g
* /
p u b l i c f u n c t i o n g e t N o m b r e ( ) {
r e t u r n $ t h i s - > n o m b r e ;
}
/ * *
* S e t t e x t o
*
* @ p a r a m s t r i n g $ t e x t o
* /
p u b l i c f u n c t i o n s e t T e x t o ( $ t e x t o ) {
$ t h i s - > t e x t o = $ t e x t o ;
}
/ * *
* G e t t e x t o
*
* @ r e t u r n s t r i n g
* /
Entidad Publicidad
228
p u b l i c f u n c t i o n g e t T e x t o ( ) {
r e t u r n $ t h i s - > t e x t o ;
}
/ * *
* S e t p a t h
*
* @ p a r a m s t r i n g $ p a t h
* /
p u b l i c f u n c t i o n s e t P a t h ( $ p a t h ) {
$ t h i s - > p a t h = $ p a t h ;
}
/ * *
* G e t p a t h
*
* @ r e t u r n s t r i n g
* /
p u b l i c f u n c t i o n g e t P a t h ( ) {
r e t u r n $ t h i s - > p a t h ;
}
p u b l i c f u n c t i o n g e t A b s o l u t e P a t h ( ) {
r e t u r n n u l l = = = $ t h i s - > p a t h ? n u l l : $ t h i s - > g e t U p l o a d R o o t D i r ( ) . ' / ' . $ t h i s - > p a t h ;
}
p u b l i c f u n c t i o n g e t W e b P a t h ( ) {
r e t u r n n u l l = = = $ t h i s - > p a t h ? n u l l : $ t h i s - > g e t U p l o a d D i r ( ) . ' / ' . $ t h i s - > p a t h ;
}
p r o t e c t e d f u n c t i o n g e t U p l o a d R o o t D i r ( ) {
/ / t h e a b s o l u t e d i r e c t o r y p a t h w h e r e u p l o a d e d d o c u m e n t s s h o u l d b e s a v e d
r e t u r n _ _ D I R _ _ . ' / . . / . . / . . / . . / . . / w e b / ' . $ t h i s - > g e t U p l o a d D i r ( ) ;
}
p r o t e c t e d f u n c t i o n g e t U p l o a d D i r ( ) {
/ / g e t r i d o f t h e _ _ D I R _ _ s o i t d o e s n ' t s c r e w w h e n d i s p l a y i n g u p l o a d e d d o c / i m a g e i n t h e v i e w .
r e t u r n ' u p l o a d s / p u b l i c i d a d ' ;
}
p u b l i c f u n c t i o n u p l o a d ( ) {
/ / t h e f i l e p r o p e r t y c a n b e e m p t y i f t h e f i e l d i s n o t r e q u i r e d
i f ( n u l l = = = $ t h i s - > f i l e ) {
r e t u r n ;
}
/ / w e u s e t h e o r i g i n a l f i l e n a m e h e r e b u t y o u s h o u l d
/ / s a n i t i z e i t a t l e a s t t o a v o i d a n y s e c u r i t y i s s u e s
/ / m o v e t a k e s t h e t a r g e t d i r e c t o r y a n d t h e n t h e t a r g e t f i l e n a m e t o m o v e t o
$ t h i s - > f i l e - > m o v e ( $ t h i s - > g e t U p l o a d R o o t D i r ( ) , $ t h i s - > f i l e - > g e t C l i e n t O r i g i n a l N a m e ( ) ) ;
/ / s e t t h e p a t h p r o p e r t y t o t h e f i l e n a m e w h e r e y o u ' v e d s a v e d t h e f i l e
$ t h i s - > p a t h = $ t h i s - > f i l e - > g e t C l i e n t O r i g i n a l N a m e ( ) ;
/ / c l e a n u p t h e f i l e p r o p e r t y a s y o u w o n ' t n e e d i t a n y m o r e
$ t h i s - > f i l e = n u l l ;
}
p u b l i c f u n c t i o n _ _ t o S t r i n g ( )
{
r e t u r n $ t h i s - > g e t N o m b r e ( ) ;
}
} / / *
Y registramos el servicio:
``src/Jazzyweb/AulasMentor/NotasBackendBundle/Resources/config/services.yml`
Entidad Publicidad
229
s e r v i c e s :
. . .
s o n a t a . n o t a s _ b a c k e n d . a d m i n . p u b l i c i d a d :
c l a s s : J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n \ P u b l i c i d a d A d m i n
t a g s :
- { n a m e : s o n a t a . a d m i n , m a n a g e r _ t y p e : o r m , g r o u p : ' P u b l i c i d a d ' , l a b e l : P u b l i c i d a d }
a r g u m e n t s :
- n u l l
- J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ P u b l i c i d a d
- J A M N o t a s B a c k e n d B u n d l e : A d m i n
Con esto ya podemos aadir registros de publicidad con su imagen. Fjate que en realidad no
hemos hecho ms que adaptar la entidad P u b l i c i d a d para que sepa tratar la subidad de
ficheros, y aadir en la configuracin del formulario de administracin un campo del tipo
f i l e . Ahora vamos a ver como aadir una miniatura de las imgenes que subimos en el
listado de registros de publicidad.
Fjate que en el mtodo c o n f i g u r e L i s t F i e l d s de la clase de configuracin de la entidad
P u b l i c i d a d (P u b l i c i d a d A d m i n ), hemos aadido un campo denominado i m a g e n . Este campo
lleva como listado de opciones una plantilla que indica como debe pintarse el campo. Basta
con definir esta plantilla en el lugar que le corresponde en funcin de su nombre:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / R e s o u r c e s / v i e w s / S o n a t a / i m a g e n . h t m l . t w i g
{ % e x t e n d s ' S o n a t a A d m i n B u n d l e : C R U D : b a s e _ l i s t _ f i e l d . h t m l . t w i g ' % }
{ % b l o c k f i e l d % }
< i m g h e i g h t = " 4 2 " s r c = " { { a s s e t ( o b j e c t . g e t W e b P a t h ) } } " / >
{ % e n d b l o c k % }
Y ahora ya tenemos tuneado y listo nuestro mdulo de administracin de la entidad
P u b l i c i d a d .
Entidad U s u a r i o
Y vamos a por la ltima entidad que vamos a administrar. En esta ocasin tendremos que
hacer un cambio en el controlador, ya que cuando se aade un usuario no solo hay que
grabar sus datos en la base de datos, sino que adems hay que encriptar el password y
enviar un mail al usuario para avisarle de que se le ha abierto una cuenta en el sistema. Es
decir, lo mismo que se hizo en el proceso de registro.
As que lo primero que hacemos es crear la clase asociada a la entidad que define sus
formularios.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / A d m i n / U s u a r i o A d m i n . p h p
< ? p h p
n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ A d m i n \ A d m i n ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ L i s t M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ D a t a g r i d \ D a t a g r i d M a p p e r ;
u s e S o n a t a \ A d m i n B u n d l e \ V a l i d a t o r \ E r r o r E l e m e n t ;
u s e S o n a t a \ A d m i n B u n d l e \ F o r m \ F o r m M a p p e r ;
c l a s s U s u a r i o A d m i n e x t e n d s A d m i n {
Entidad Usuario
230
p r o t e c t e d f u n c t i o n c o n f i g u r e F o r m F i e l d s ( F o r m M a p p e r $ f o r m M a p p e r ) {
$ f o r m M a p p e r
- > a d d ( ' n o m b r e ' )
- > a d d ( ' a p e l l i d o s ' )
- > a d d ( ' u s e r n a m e ' )
- > a d d ( ' e m a i l ' )
- > a d d ( ' p a s s w o r d ' , ' p a s s w o r d ' )
- > a d d ( ' p a s s w o r d _ a g a i n ' , ' p a s s w o r d ' )
- > a d d ( ' g r u p o s ' , n u l l , a r r a y ( ' r e q u i r e d ' = > f a l s e ) )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e D a t a g r i d F i l t e r s ( D a t a g r i d M a p p e r $ d a t a g r i d M a p p e r ) {
$ d a t a g r i d M a p p e r
- > a d d ( ' n o m b r e ' )
- > a d d ( ' a p e l l i d o s ' )
- > a d d ( ' u s e r n a m e ' )
;
}
p r o t e c t e d f u n c t i o n c o n f i g u r e L i s t F i e l d s ( L i s t M a p p e r $ l i s t M a p p e r ) {
$ l i s t M a p p e r
- > a d d I d e n t i f i e r ( ' u s e r n a m e ' )
- > a d d ( ' n o m b r e ' )
- > a d d ( ' a p e l l i d o s ' )
- > a d d ( ' i s A c t i v e ' , n u l l , a r r a y ( ' e d i t a b l e ' = > t r u e , ' l a b e l ' = > ' A c t i v o ' ) )
;
}
p u b l i c f u n c t i o n v a l i d a t e ( E r r o r E l e m e n t $ e r r o r E l e m e n t , $ o b j e c t ) {
}
}
Fjate en el campo i s A c t i v e que se aade en el mtodo c o n f i g u r e L i s t F i e l d s , se le ha
aadido la opcin e d i t a b l e . Esto significa que en el listado de usuarios, se mostrar el
campo i s A c t i v e como un c h e c k B u t t o n , y se podr cambiar desde el propio listado. Esta es
una de las virgueras de este bundle, y tiene muchas ms. Explorarlo a fondo, aunque en un
principio puede ser duro, te dar muchas sorpresas. Adems aprenders a utilizar una
herramienta potentsima para la creacin de aplicaciones de administracin.
Ahora vamos a crear un controlador especfico para esta entidad. Le llamaremos
U s u a r i o A d m i n C o n t r o l l e r , y tiene esta pinta:
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / C o n t r o l l e r / U s u a r i o A d m i n C o n t r o l l e r . p h p
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ C o n t r o l l e r ;
4
5 u s e S o n a t a \ A d m i n B u n d l e \ C o n t r o l l e r \ C R U D C o n t r o l l e r a s C o n t r o l l e r ;
6
7 c l a s s U s u a r i o A d m i n C o n t r o l l e r e x t e n d s C o n t r o l l e r
8 {
Entidad Usuario
231
9
1 0 p u b l i c f u n c t i o n c r e a t e A c t i o n ( )
1 1 {
1 2 i f ( f a l s e = = = $ t h i s - > a d m i n - > i s G r a n t e d ( ' C R E A T E ' ) ) {
1 3 t h r o w n e w A c c e s s D e n i e d E x c e p t i o n ( ) ;
1 4 }
1 5
1 6 $ o b j e c t = $ t h i s - > a d m i n - > g e t N e w I n s t a n c e ( ) ;
1 7
1 8 $ t h i s - > a d m i n - > s e t S u b j e c t ( $ o b j e c t ) ;
1 9
2 0 $ f o r m = $ t h i s - > a d m i n - > g e t F o r m ( ) ;
2 1 $ f o r m - > s e t D a t a ( $ o b j e c t ) ;
2 2
2 3 i f ( $ t h i s - > g e t ( ' r e q u e s t ' ) - > g e t M e t h o d ( ) = = ' P O S T ' ) {
2 4 $ f o r m - > b i n d R e q u e s t ( $ t h i s - > g e t ( ' r e q u e s t ' ) ) ;
2 5
2 6 i f ( $ f o r m - > i s V a l i d ( ) ) {
2 7
2 8 $ s e r v i c e R e g i s t r o = $ t h i s - > g e t ( ' j a m _ n o t a s _ f r o n t e n d . r e g i s t r o ' ) ;
2 9 $ s e r v i c e R e g i s t r o - > r e g i s t r a ( $ o b j e c t , $ f o r m - > g e t ( ' p a s s w o r d ' ) - > g e t D a t a ( ) ) ;
3 0 $ o b j e c t - > s e t I s A c t i v e ( t r u e ) ;
3 1
3 2 $ t h i s - > a d m i n - > c r e a t e ( $ o b j e c t ) ;
3 3
3 4 i f ( $ t h i s - > i s X m l H t t p R e q u e s t ( ) ) {
3 5 r e t u r n $ t h i s - > r e n d e r J s o n ( a r r a y (
3 6 ' r e s u l t ' = > ' o k ' ,
3 7 ' o b j e c t I d ' = > $ t h i s - > a d m i n - > g e t N o r m a l i z e d I d e n t i f i e r ( $ o b j e c t )
3 8 ) ) ;
3 9 }
4 0
4 1 $ t h i s - > g e t ( ' s e s s i o n ' ) - > s e t F l a s h ( ' s o n a t a _ f l a s h _ s u c c e s s ' , ' f l a s h _ c r e a t e _ s u c c e s s ' ) ;
4 2 / / r e d i r e c t t o e d i t m o d e
4 3 r e t u r n $ t h i s - > r e d i r e c t T o ( $ o b j e c t ) ;
4 4 }
4 5
4 6 $ t h i s - > g e t ( ' s e s s i o n ' ) - > s e t F l a s h ( ' s o n a t a _ f l a s h _ e r r o r ' , ' f l a s h _ c r e a t e _ e r r o r ' ) ;
4 7 }
4 8
4 9 $ v i e w = $ f o r m - > c r e a t e V i e w ( ) ;
5 0
5 1 / / s e t t h e t h e m e f o r t h e c u r r e n t A d m i n F o r m
5 2 $ t h i s - > g e t ( ' t w i g ' ) - > g e t E x t e n s i o n ( ' f o r m ' ) - > s e t T h e m e ( $ v i e w , $ t h i s - > a d m i n - > g e t F o r m T h e m e ( ) ) ;
5 3
5 4 r e t u r n $ t h i s - > r e n d e r ( $ t h i s - > a d m i n - > g e t E d i t T e m p l a t e ( ) , a r r a y (
5 5 ' a c t i o n ' = > ' c r e a t e ' ,
5 6 ' f o r m ' = > $ v i e w ,
5 7 ' o b j e c t ' = > $ o b j e c t ,
5 8 ) ) ;
5 9 }
6 0
6 1 }
Qu es lo que hemos hecho aqu?. Igual que hicimos con la clase A d m i n C o n t r o l l e r , hemos
extendido la clase U s u a r i o A d m i n C o n t r o l l e r de
S o n a t a \ A d m i n B u n d l e \ C o n t r o l l e r \ C R U D C o n t r o l l e r . A continuacin hemos copiado el
cdigo de la accin c r e a t e A c t i o n ( ) de la clase padre
S o n a t a \ A d m i n B u n d l e \ C o n t r o l l e r \ C R U D C o n t r o l l e r en la clase U s u a r i o A d m i n C o n t r o l l e r , y
lo hemos modificado para que el proceso de creacin de un usuario incluya la encriptacin
del password y el envo del mail de bienvenida. Fjate que esto se hace en las lneas 28-30,
donde se instancia el servicio j a m _ n o t a s _ f r o n t e n d . r e g i s t r o que hemos construido en la
unidad 10. Es decir, siempre se trata de modificar aquello que el bundle no te proporciona.
Obviamente, el SonataAdminBundle no puede saber que la creacin de tu entidad U s u a r i o
requiere el envo de un email y la encriptacin de un password, pero te ofrece la oportunidad
Entidad Usuario
232
de que cambies el cdigo de las acciones para adaptarlo a tu modelo de negocio.
Ahora registramos la clase U s u a r i o A d m i n como servicio, con la precaucin de pasar como
controlador la clase U s u a r i o A d m i n C o n t r o l l e r que acabamos de crear, en lugar de la clase
A d m i n C o n t r o l l e r que hemos estado utilizando hasta ahora.
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s B a c k e n d B u n d l e / R e s o u r c e s / c o n f i g / s e r v i c e s . y m l
s e r v i c e s :
. . .
s o n a t a . n o t a s _ b a c k e n d . a d m i n . u s u a r i o :
c l a s s : J a z z y w e b \ A u l a s M e n t o r \ N o t a s B a c k e n d B u n d l e \ A d m i n \ U s u a r i o A d m i n
t a g s :
- { n a m e : s o n a t a . a d m i n , m a n a g e r _ t y p e : o r m , g r o u p : ' A d m i n i s t r a c i n ' , l a b e l : U s u a r i o s }
a r g u m e n t s :
- n u l l
- J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o
- J A M N o t a s B a c k e n d B u n d l e : U s u a r i o A d m i n
Y ya est!, ya tenemos una completa aplicacin de administracin para nuestro proyecto.
En realidad es bastante mejorable. Por ejemplo, encontrars que la administracin de la
entidad U s u a r i o la hemos esbozado y encaminado pero presenta algunos fallos. Por
ejemplo: el password no debera ser introducido por el administrador, si no que en el proceso
de activacin de la cuenta que se inicia cuando el usuario pica en el enlace enviado en el
email, la aplicacin debera presentarle al usuario un formulario para que definiese su
password.
Por otro lado, hemos mostrado una pequea parte de las capacidades de este magnfico
bundle. Aunque no es obligatorio en el curso, es un buen ejercicio para consolidar lo
aprendido, estudiar con detalle la documentacin del bundle y comprobar la cantidad de
cosas que se pueden hacer con l. La documentacin la puedes consultar en
http://sonata-project.org/bundles/admin.
8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 8 b 8 8 8
8 8 8 8 8 8 8 8 8 8 b 8 8 8
8 8 8 8 8 8 8 8 8 8 8 b 8 8 8
8 8 8 8 8 8 8 8 8 8 8 8 8 Y 8 8 b 8 8 8
8 8 8 8 8 8 8 8 8 Y 8 8 b 8 8 8
8 8 8 8 8 8 8 8 8 Y 8 8 8 8 8
8 8 8 8 8 8 8 8 8 Y 8 8 8 8
8 8 8 8 8 8 8 8 8 8 8 8 8 Y 8 8 8
o o o o $ $ $ $ $ $ $ $ $ $ $ $ o o o o
o o $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o
o o $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o o $ $ $ o $
o $ o o o $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o $ $ $ $ $ $ o $
o o $ $ " $ o $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o $ $ $ o $ $ o $
" $ $ $ $ $ $ o $ o $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o $ $ $ $ $ $ $ $
$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $
$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ " " " $ $ $
" $ $ $ " " " " $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ " $ $ $
$ $ $ o $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ " $ $ $ o
o $ $ " $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o
$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ " " $ $ $ $ $ $ o o o o o $ $ $ $ o
o $ $ $ o o o o $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ o $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $
Entidad Usuario
233
$ $ $ $ $ $ $ $ " $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ " " " " " " " "
" " " " $ $ $ $ " $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ " o $ $ $
" $ $ $ o " " " $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ " $ $ " $ $ $
$ $ $ o " $ $ " " $ $ $ $ $ $ " " " " o $ $ $
$ $ $ $ o o $ $ $ "
" $ $ $ $ o o $ $ $ $ $ $ o " $ $ $ $ o o $ $ $ $
" $ $ $ $ $ o o " " $ $ $ $ o $ $ $ $ $ o o $ $ $ $ " "
" " $ $ $ $ $ o o o o " $ $ $ o $ $ $ $ $ $ $ $ $ " " "
" " $ $ $ $ $ $ $ o o $ $ $ $ $ $ $ $ $ $
" " " " $ $ $ $ $ $ $ $ $ $ $
$ $ $ $ $ $ $ $ $ $ $ $
$ $ $ $ $ $ $ $ $ $ "
" $ $ $ " " " "
Entidad Usuario
234
Ejercicios del Curso: Desarrollo de Aplicaciones Web
con Symfony2
Contenidos:
Ejercicios de la unidad 2
Nota
Enlace a la Unidad 2: Desarrollo de una aplicacin web siguiendo el patrn MVC
Ejercicio 1
Cules son las razones que argumenta Fabien Potencier para decir que Symfony2 no es un
framework MVC?
Ejercicio 2
Explica al menos dos razones por las que es importante separar el cdigo de la aplicacin en
una parte accesible directamente al servidor web y otra no accesible.
Ejercicio 3
Cul es la funcin principal del controlador frontal en una aplicacin web?
Ejercicio 4
Qu debes tener en cuenta cuando utilizas PHP como lenguaje de plantillas? Qu ventajas
encuentras al utilizar PHP de esta manera?
Ejercicio 5
Observa que las acciones del men b u s c a r p o r e n e r g a y b s q u e d a c o m b i n a d a arrojan
un error 404, ya que no estn implementadas. Corrige este error implementndolas.
Ejercicio 6
Fjate en las plantillas b u s c a r P o r N o m b r e . p h p y m o s t r a r A l i m e n t o s . p h p . En las dos aparece
el mismo cdigo para mostrar en una tabla un array de alimentos. Esto, desde el punto de
vista de las buenas prcticas, es algo incorrecto. Por qu crees que no est bien esta
solucin? Propn una solucin ms en la lnea de las buenas prcticas de programacin.
Sugerencia
La cosa tiene que ver con el principio DRY (Don't Repeat Yourself)
Ejercicios de la unidad 3
Ejercicios del Curso: Desarrollo de Aplicaciones Web con Symfony2
235
Nota
Enlace a la Unidad 3: Symfony2 a vista de pjaro
Ejercicio 1
Crea un nuevo entorno de ejecucin que se comporte exactamente igual que el entorno de
desarrollo (d e v ), pero que no muestre la barra de depuracin. Llama a este nuevo entorno
p r u . Describe los pasos que has dado para resolver el problema.
Sugerencia
Debes crear un nuevo controlador frontal para indicarle al kernel que quieres cargar la
configuracin p r u .
Sugerencia
Observa el apartado w e b _ p r o f i l e r del archivo c o n f i g _ d e v . y m l . Ah se indica si se
quiere mostrar o no la barra de depuracin.
Sugerencia
En el fichero A p p K e r n e l . p h p se carga el bundle A c m e D e m o B u n d l e ( ) de forma
selectiva en funcin del entorno de ejecucin que se est utilizando.
Ejercicio 2
Crea un nuevo bundle (llmalo como quieras) con el generador automtico de bundles.
Colcalo en el directorio f u e n t e s (no te asustes, este ltimo es nuevo, no viene en la
distribucin estndar), en lugar de en s r c y define el prefijo f u e n t e s para todas las rutas
del bundle. Describe los pasos que has dado para que todo funcione.
Ejercicio 3
Construye una nueva accin que devuelva este sencillo documento HTML:
< h t m l > < b o d y > < h 1 > H o l a c a r a c o l a ! < / h 1 > < / b o d y > < / h t m l >
sin utilizar el mtodo r e n d e r y, por lo tanto, sin utilizar ninguna plantilla.
Ejercicio 1
236
Sugerencia
Recuerda que las acciones deben devolver un objeto R e s p o n s e .
Describe los pasos que has dado para resolver el problema. Transcribe el cdigo que has
aadido al proyecto.
Ejercicio 4
Construye el resto de los enlaces del men. transcribe el cdigo que has aadido al
proyecto.
Ejercicio 5
Aade las funcionalidades buscar por energa y bsqueda combinada. Describe los pasos
que has dado y transcribe el cdigo que has aadido al proyecto.
Ejercicios de la unidad 4
Nota
Enlace a la Unidad 4: Inyeccin de Dependencias
Ejercicio 1
Desarrolla e incorpora al bundle J a z z y w e b A l u l a s M e n t o r A l i m e n t o s B u n d l e un servicio
mediante el que se pueda obtener informacin de los alimentos mediante consulta a la
wikipedia. Describe como has resuelto el problema y transcribe el cdigo que has tenido que
aadir.
Sugerencia
En la wikipedia, la URL que le correponde a un artculo es del tipo:
h t t p : / / e s . w i k i p e d i a . o r g / w i k i / N o m b r e _ A r t i c u l o
Ejercicio 2
Aade una funcionalidad a la aplicacin de gestin de alimentos que permita mostrar la
informacin que la wikipedia tiene de los alimentos del sistema. Describe como has resuelto
el problema y transcribe el cdigo que has tenido que aadir.
Ejercicio 4
237
Sugerencia
Puedes aadir a la plantilla v e r A l i m e n t o . p h p la informacin obtenida desde la
wikipedia.
Ejercicios de la unidad 5
Nota
Enlace a la Unidad 5: Desarrollo de la aplicacin MentorNotas (I). Anlisis
Como has podido comprobar, esta unidad no tiene mucho que ver con el framework
Symfony2. Es necesaria en tanto que para desarrollar una aplicacin con cierto "cuerpo", es
necesario disponer de un anlisis previo que nos ponga en situacin y nos sirva como
referencia y "planos" de nuestra construccin software.
As que nos vamos a olvidar por lo pronto de Symfony2 y vamos a dedicar los ejercicios de
esta unidad a explorar un poco ms a fondo como esta organizado el cdigo HTML + CSS +
javascript que utilizaremos como interfaz grfica de usuario de nuestra aplicacin.
Ejercicio 1
Comencemos por el layout. Hemos utilizado un plugin de JQuery llamado jquery ui layout
(http://layout.jquery-dev.net/). Por ello hemos de utilizar en todas las pginas la librera
javascript JQuery (http://jquery.com/). Tambin hemos utilizado, para enriquecer el aspecto
de los botones y otros elementos, la librera jquery-ui (http://jqueryui.com/).
Localiza en el archivo i n i c i o . h t m l donde se cargan dichas libreras.
Ejercicio 2
La librera jquery ui layout utiliza unas CSS's para dar estilos a los layout que con ella se
crean. Tambin la librera jquery-ui utiliza sus CSS's. Localzalas en el cdigo del archivo
i n i c i o . h t m l .
Ejercicio 3
Fjate en el aspecto de la pgina i n i c i o . h t m l . Esta organizada en cinco zonas claramente
identificables: Un encabezado arriba, un pie de pgina abajo, y tres zonas en el centro; una
para las etiquetas (izquierda), otra para el listado de notas (centro) y la ltima para el detalle
de la nota (derecha). Es la librera jquery ui layout la encargada de enriquecer el cdigo
HTML de la pgina. Pero para que pueda hacer esto, el HTML debe tener una estructura
determinada que entiende jquery ui layout para construir el layout, es decir, para colocar
cada una de las zonas que hemos mencionado antes en su sitio.
Identifica en el cdigo del archivo i n i c i o . h t m l cuales son las capas que "informan" a
jquery ui layout sobre las zonas que debe crear.
Ejercicio 4
Ejercicios de la unidad 5
238
Ahora fijate en la pgina i n i c i o - e d i t a r _ n o t a . h t m l . Hemos colocado el formulario de
edicin en el lugar que estaba el detalle de la nota. Este formulario de edicin tiene un editor
muy majo en el que puedes dar formato al texto que escribes. Tambin tiene un
"etiquetador" enriquecido con el que puedes aadir/borrar etiquetas sin ms que escribirlas
y confirmarlas con la tecla e n t e r . Prubalo. En realidad, el editor de texto no es ms que un
t e x t a r e a de HTML enriquecido por un plugin de JQuery, y el "etiquetador" es una lista HTML
enriquecida por otro plugin de JQuery.
Localiza el cdio HTML correspondientes a estos dos elementos, las libreras javascript
(plugins de JQuery) y las llamadas a las funciones de dichas libreras que provocan el
enriquecimiento de los elementos HTML. Localiza tambin las CSS's que se utilizan.
Ejercicios de la unidad 6
Nota
Enlace a la Unidad 6: Desarrollo de la aplicacin MentorNotas (II). Rutas y
Controladores
Ejercicio 1
Explora la informacin que contiene el objeto Request haciendo un volcado del mismo
mediante el siguiente cdigo:
< ? p h p
. . .
$ r e q u e s t = $ t h i s - > g e t R e q u e s t ( ) ; / / e q u i v a l e n t e a $ t h i s - > g e t ( ' r e q u e s t ' ) ;
e c h o ' < p r e > ' ;
p r i n t _ r ( $ r e q u e s t ) ;
e c h o ' < / p r e > ' ;
e x i t ;
Examnala detenidamente. Cul es el parmetro mediante el que se mantiene la sesin?
Prueba a realizar el volcado de la Request utilizando el mtodo _ _ t o S t r i n g ( ) de la misma.
Observa que ofrece una versin simplificada con los datos ms relevantes.
Utiliza la documentacin de la API de Symfonuy sobre el objeto Request para averiguar:
El nombre del HOST
El mtodo utilizado para realizar la peticin
El puerto donde se ejecuta el servidor
Transcribe el cdigo que has escrito para realizar el ejercicio.
Ejercicio 2
En este ejercicio vamos a explorar el uso del parmetro _ f o r m a t de la Request. En muchas
aplicaciones web, especialmente en los servicios web, el servidor debe devolver una
respuesta que no es un fichero HTML, sino un JSON, un XML, etctera. A veces puede ser
necesario ofrecer la misma informacin en distintos formatos, para que el cliente la trate
como mejor le venga. Con el parmetro _ f o r m a t del objeto Request es precisamente lo que
Ejercicios de la unidad 6
239
conseguimos.
En este ejercicio vas a implementar una funcionalidad que consiste en devolver el listado de
notas de un usuario en un archivo XML y/o en un archivo JSON, segn lo que se pida en la
request.
El XML tendr el siguiente aspecto:
< n o t a s >
< n o t a >
< i d > 1 < / i d >
< t i t u l o > e l t i t u l o 1 < / t i t u l o >
< t e x t o > e l t e x t o 1 < / t e x t o >
< / n o t a >
< n o t a >
< i d > 2 < / i d >
< t i t u l o > e l t i t u l o 2 < / t i t u l o >
< t e x t o > e l t e x t o 2 < / t e x t o >
< / n o t a >
< / n o t a s >
Y el JSON:
{ " n o t a s " : [
{
" i d " : 1 ,
" t i t u l o " : " e l t i t u l o 1 " ,
" t e x t o " : " e l t e x t o 1 "
} ,
{
" i d " : 2 ,
" t i t u l o " : " e l t i t u l o 2 " ,
" t e x t o " : " e l t e x t o 2 "
}
]
}
Como, por lo pronto no tenemos las notas almacenadas en ningn sitio, puedes
inventrtelas. De hecho puedes utilizar exactamente los mismos ejemplos anteriores para
implementar las plantillas.
Comienza por crear una ruta nueva para la accin que vas a crear. Esta ruta debe incluir en
su patrn (pattern) el atributo _ f o r m a t . Haz que el _ f o r m a t por defecto sea j s o n . Adems
establece como requisito de la ruta que el atributo _ f o r m a t deba ser j s o n o x m l .
Sugerencia
Posibles patrones:
/ d a m e N o t a s . { _ f o r m a t } , d a m e N o t a s / { _ f o r m a t } , d a m e N o t a s E n F o r m a t o { _ f o r m a t } .
Ejercicios de la unidad 6
240
Crea la accin correspondiente. Debes recoger el parmetro _ f o r m a t de la Request y
renderizarla con la plantilla que le corresponda (JSON o XML).
Sugerencia
Esta lnea de cdigo te puede ayudar:
r e t u r n
$ t h i s - > r e n d e r ( ' J A M N o t a s F r o n t e n d B u n d l e : N o t a s : d a m e N o t a s . ' . $ f o r m a t . ' . t w i g ' ) ;
Crea las dos plantillas correspondientes, una para el JSON y otra para el XML.
Transcribe el cdigo que has escrito para resolver el ejercicio.
Ejercicio 3
En este ejercicio vamos a realizar un estudio prctico de la sesin de Symfony2. Para ello
haremos uso de la tcnica de depuracin que hemos empleado ms arriba:
< ? p h p
e c h o ' < p r e > ' ;
p r i n t _ r ( $ O b j e t o A I n s p e c c i o n a r ) ;
e c h o ' < / p r e > ' ;
e x i t ;
Nota
Debes enviar como respuesta a los ejercicios de esta unidad, pantallazos de los
resultados que vas obteniendo. Puedes volcar en un pdf todas las imgenes
correspondientes a los pantallazos o directamente comprimir dichas imgenes en un
zip.
1. Primero vamos a comprobar la creacin de la sesin en el servidor. Busca el directorio
donde tu servidor web crea los archivos de sesiones y, si en dicho directorio hubiera
algn o algunos archivos, elimnalos. Entonces ejecuta tu navegador (si lo tienes
abierto, cirralo previamente) y realiza una peticin a la aplicacin. Observa como
inmediatamente se ha creado un archivo en el directorio del servidor donde php guarda
las sesiones.
2. Comprueba que en tu navegador existe una cookie asociada al servidor (localhost en
nuestro caso ya que el servidor de desarrollo se encuentra en la misma mquina que el
navegador web) denominada P H P S E S S I D y cuyo valor coincide con el sufijo del archivo
de sesin del punto anterior. Ya tienes la relacin entre el cliente web y el servidor. Abre
dicho fichero y obsrvalo con detalle.
3. Ahora vamos a ver la representacin que PHP hace de dicho fichero. Para ello crea una
accin nueva y realiza una inspeccin a la variable $ _ S E S S I O N de PHP mediante la
tcnica de depuracin que hemos indicado al principio del ejercicio. Ahora puedes
confrontar el contenido del fichero de sesin con los datos de este array asociativo que
es la forma nativa que tienen PHP para representar la sesin.
Ejercicio 3
241
4. A continuacin realiza una inspeccin del objeto con el que Symfony2 representa la
sesin (``$this->get('session')``). Compralo con el resultado del punto anterior. Te
dars cuenta de que la representacin de la sesin de *Symfony2 es mucho ms
completa que la que hace PHP de forma nativa.
Ejercicios de la unidad 7
Nota
Enlace a la Unidad 7: Desarrollo de la aplicacin MentorNotas (III). El modelo y la
persistencia de datos.
Ejercicio 1. Fixtures
Cuando las aplicacin que estamos desarrollando lleva asociada una base de datos, lo cual
es muy frecuente, resulta til contar con una serie de datos mnimos en la base de datos. En
ocasiones, incluso se requieren ciertos datos iniciales para que la aplicacin funcione. Por
ello es preciso distribuir junto con la aplicacin un script SQL con el que se pueda cargar la
base de datos con dicha serie de datos mnimos. Por otro lado, en el proceso de construccin
de la aplicacin vamos contaminando la base de datos con datos inservibles a medida que la
probamos. De tanto en tanto, especialmente a la hora de ejecutar pruebas funcionales,
tenemos que limpiar la base de datos borrando todos los datos y volviendo a ejecutar el
script.
Pues bien, aunque la estrategia del script SQL es totalmente vlida para resolver los
problemas que acabamos de plantear, el uso de fixtures proporciona una manera mucho
ms cmoda de poblar e inicializar con datos de prueba la base de datos. Los fixtures son
datos de pruebas que son cargados en la base de datos utilizando un comando de Symfony2
a travs de la terminal de comandos. Las ventajas que presenta esta estrategia son:
Al ser cargados directamente por Symfony2 (ms concretamente por Doctrine2) el
script es agnstico a la base de datos, y por tanto puede poblar cualquier tipo de base
de datos compatible con Doctrine2 (en la solucin propuesta al principio debemos crear
un script por cada tipo de base de datos para asegurarnos que la carga se har
correctamente).
No tenemos que "salirnos" del entorno de Symfony2 para cargar los datos cada vez que
deseemos reinicializar la base de datos. Es decir no tenemos que cambiar al cliente SQL
(*phpMyAdmin, mysql client, etctera) y cargar el script correspondiente. Basta con
ejecutar el comando en cuestin. Esto agiliza mucho el proceso de reinicio de la base de
datos, y en la fase de desarrollo se agradece muchsimo.
En realidad ni Symfony2 ni el bundle de Doctrine2 que viene de serie con la distribucin
stndard de Symfony2 incorporan ninguna utilidad (comando) para la carga automtica de
fixtures. Pero existe un bundle que s lo hace. En este ejercicio aprenderemos a:
incorporar un bundle externo a nuestro proyecto
utilizar el sistema de fixtures para agilizar la reinicializacin de la base de datos de
nuestro proyecto con datos de pruebas.
Instalacin del bundle DoctrineFixturesBundle
En primer lugar actualizamos el fichero de dependencias d e p s con las referencias a los
repositorios donde se encuentra el cdigo que vamos a aadir al proyecto. Aade a este
Ejercicios de la unidad 7
242
fichero las siguientes lneas:
. . .
[ d o c t r i n e - f i x t u r e s ]
g i t = h t t p : / / g i t h u b . c o m / d o c t r i n e / d a t a - f i x t u r e s . g i t
[ D o c t r i n e F i x t u r e s B u n d l e ]
g i t = h t t p : / / g i t h u b . c o m / s y m f o n y / D o c t r i n e F i x t u r e s B u n d l e . g i t
t a r g e t = / b u n d l e s / S y m f o n y / B u n d l e / D o c t r i n e F i x t u r e s B u n d l e
chale un vistazo a este fichero. Observa que en l se encuentra, entre otras dependencias,
el ncleo de Symfony2.
Qu lneas referencian a dicho ncleo?
Ahora vamos a bajar el cdigo cuyas referencias acabamos de aadir. Esto se hace con la
herramienta b i n / v e n d o r s :
Si hubieramos partido de una distribucin estndard de Symfony2 sin vendors utilizariamos
la herramienta de la siguiente forma:
. / b i n / v e n d o r s u p d a t e
Pero como nuestra distribucin ya vena con los vendors instalados, debemos volver a
instalarlos para que se sincronicen con los respectivos repositorios remotos:
. / b i n / v e n d o r s i n s t a l l - - r e i n s t a l l
Nota
Obviamente, para instalar los vendors hay que disponer de conexin a internet. Tarda
un ratito, as que paciencia. Si tienes problemas al realizar la operacin lee el apartado
Instalacin manual del bundle (sin utilizar bin/vendor). Puedo asegurarte que es ms
rpido y menos propenso a fallos la instalacin manual que describimos al final de
esta actividad. Pero hay que decir que la manera oficial de instalar libreras de
terceros (vendors) es la que estamos contando aqu.
Esta operacin borra las libreras del directorio v e n d o r s , y vuelve a colocarlas
descargndolas de los repositorios y sincronizndolas con ellos.
Una vez finalizada la operacin puedes comprobar que la librera d o c t r i n e - f i x t u r e s ha
sido instalada en el directorio v e n d o r / d o c t r i n e - f i x t u r e s , y el bundle
D o c t r i n e F i x t u r e s B u n d l e se ha instalado en el directorio
v e n d o r / b u n d l e s / D o c t r i n e / B u n d l e / F i x t u r e s B u n d l e .
Por qu crees que se han instalado en dichos directorios?
Ahora tenemos que registrar manualmente el bundle DoctrineFixturesBundle en el fichero
A p p K e r n e l . p h p :
Ejercicios de la unidad 7
243
< ? p h p
p u b l i c f u n c t i o n r e g i s t e r B u n d l e s ( )
{
$ b u n d l e s = a r r a y (
/ / . . .
n e w S y m f o n y \ B u n d l e \ D o c t r i n e F i x t u r e s B u n d l e \ D o c t r i n e F i x t u r e s B u n d l e ( ) ,
/ / . . .
) ;
/ / . . .
}
Y registrar el espacio de nombres de las clases del bundle en el fichero a u t o l o a d . p h p :
< ? p h p
. . .
$ l o a d e r - > r e g i s t e r N a m e s p a c e s ( a r r a y (
/ / . . .
' D o c t r i n e \ \ C o m m o n \ \ D a t a F i x t u r e s ' = > _ _ D I R _ _ . ' / . . / v e n d o r / d o c t r i n e - f i x t u r e s / l i b ' ,
' D o c t r i n e \ \ C o m m o n ' = > _ _ D I R _ _ . ' / . . / v e n d o r / d o c t r i n e - c o m m o n / l i b ' ,
/ / . . .
) ) ;
Importante
Asegurate de que registras estos nombres de espacio antes de ' D o c t r i n e \ \ C o m m o n .
Y ya podemos aadir nuestros fixtures.
Creacin de los fixtures
Los fixtures son clases PHP que definen los datos de prueba que deseamos incorporar
automticamente a la base de datos. Deben ubicarse en el directorio F i x t u r e s / O R M de
algn bundle. En nuestro caso las colocaremos en el J A M N o t a s F r o n t e n d B u n d l e , es decir en
el directorio s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / F i x t u r e s / O R M .
Comenzamos por aadir algunos usuarios de prueba. Creamos la clase L o a d G r u p o D a t a en el
fichero
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / F i x t u r e s / O R M / L o a d G r u p o D a t a . p h p .
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F i x t u r e s \ O R M ;
4
5 u s e D o c t r i n e \ C o m m o n \ P e r s i s t e n c e \ O b j e c t M a n a g e r ;
6 u s e D o c t r i n e \ C o m m o n \ D a t a F i x t u r e s \ A b s t r a c t F i x t u r e ;
7 u s e D o c t r i n e \ C o m m o n \ D a t a F i x t u r e s \ O r d e r e d F i x t u r e I n t e r f a c e ;
8 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ G r u p o ;
9
1 0 c l a s s L o a d G r u p o D a t a e x t e n d s A b s t r a c t F i x t u r e i m p l e m e n t s O r d e r e d F i x t u r e I n t e r f a c e
Creacin de los fixtures
244
1 1 {
1 2 p u b l i c f u n c t i o n l o a d ( O b j e c t M a n a g e r $ m a n a g e r )
1 3 {
1 4 / / G r u p o s
1 5 $ g r u p o 1 = n e w G r u p o ( ) ;
1 6 $ g r u p o 1 - > s e t N o m b r e ( ' r e g i s t r a d o ' ) ;
1 7 $ g r u p o 1 - > s e t R o l ( ' R O L E _ R E G I S T R A D O ' ) ;
1 8 $ t h i s - > a d d R e f e r e n c e ( ' g r u p o - r e g i s t r a d o ' , $ g r u p o 1 ) ;
1 9 $ m a n a g e r - > p e r s i s t ( $ g r u p o 1 ) ;
2 0
2 1 $ g r u p o 2 = n e w G r u p o ( ) ;
2 2 $ g r u p o 2 - > s e t N o m b r e ( ' p r e m i u m ' ) ;
2 3 $ g r u p o 2 - > s e t R o l ( ' R O L E _ P R E M I U M ' ) ;
2 4 $ t h i s - > a d d R e f e r e n c e ( ' g r u p o - p r e m i u m ' , $ g r u p o 2 ) ;
2 5 $ m a n a g e r - > p e r s i s t ( $ g r u p o 2 ) ;
2 6
2 7 $ g r u p o 3 = n e w G r u p o ( ) ;
2 8 $ g r u p o 3 - > s e t N o m b r e ( ' a d m i n i s t r a d o r ' ) ;
2 9 $ g r u p o 3 - > s e t R o l ( ' R O L E _ A D M I N ' ) ;
3 0 $ t h i s - > a d d R e f e r e n c e ( ' g r u p o - a d m i n ' , $ g r u p o 3 ) ;
3 1 $ m a n a g e r - > p e r s i s t ( $ g r u p o 3 ) ;
3 2
3 3
3 4 $ m a n a g e r - > f l u s h ( ) ;
3 5 }
3 6
3 7 p u b l i c f u n c t i o n g e t O r d e r ( )
3 8 {
3 9 r e t u r n 1 ;
4 0 }
4 1 }
Los fixtures son clases que extienden de
D o c t r i n e \ C o m m o n \ D a t a F i x t u r e s \ A b s t r a c t F i x t u r e e implementan la interfaz
D o c t r i n e \ C o m m o n \ D a t a F i x t u r e s \ O r d e r e d F i x t u r e I n t e r f a c e . Deben implementar una
funcin l o a d ( ) , donde se crean los objetos que se desean persisitir en la base de datos. La
manera de hacer esto es como ya hemos estudiado en la unidad 7: se crea el objeto, se
definen sus atributos y se inyecta en el servicio de persistencia mediante el mtodo
p e r s i s t ( ) . Finalmente, para hacer la escritura en la base de datos se usa el mtodo
f l u s h ( ) .
La novedad en el cdigo anterior es el uso del mtodo a d d R e f e r e n c e ( ) de la clase
L o a d G r u p o D a t a . Dicho mtodo crea una referencia al objeto indicado en su segundo
argumento con el nombre indicado en el primer argumento. Esta referencia podr ser
utilizada en otras clases fixtures para realizar asociaciones entre objetos. Por ejemplo,
podemos crear una clase L o a d U s u a r i o D a t a y en ella podemos crear y persistir objetos
U s u a r i o 's asociados a los grupos que hemos creados en L o a d G r u p o D a t a a travs de sus
referencias.
Por ltimo, cuando existen asociaciones entre objetos, el orden de creacin de los mismos
puede ser importante. Por ejemplo, para aadir una N o t a a un U s u a r i o , hay que crear
primero la N o t a , despus el U s u a r i o y por ltimo podemos aadir la N o t a al U s u a r i o . Por
ello la ejecucin de las clases fixtures no siempre puede llevarse a cabo en cualquier orden.
El mtodo g e t O r d e r ( ) de las mismas, indica al framework en qu orden debe ejecutarlas.
Creacin de los fixtures
245
Una vez que tenemos alguna clase fixture ya podemos ejecutar el comando que cargar la
base de datos con los datos especificados en ellas:
p h p a p p / c o n s o l e d o c t r i n e : f i x t u r e s : l o a d - - f i x t u r e s = s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / F i x t u r e s
Comprueba que se hayan cargado los grupos en la base de datos.
Ahora vamos a crear unos cuantos de usuarios y le asociaremos algunos de los grupos que
hemos creado (y referenciado) en la clase L o a d G r u p o D a t a . Creamos la clase
L o a d U s u a r i o D a t a :
s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / F i x t u r e s / O R M / L o a d U s u a r i o D a t a . p h p .
1 < ? p h p
2
3 n a m e s p a c e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ F i x t u r e s \ O R M ;
4
5 u s e D o c t r i n e \ C o m m o n \ P e r s i s t e n c e \ O b j e c t M a n a g e r ;
6 u s e D o c t r i n e \ C o m m o n \ D a t a F i x t u r e s \ A b s t r a c t F i x t u r e ;
7 u s e D o c t r i n e \ C o m m o n \ D a t a F i x t u r e s \ O r d e r e d F i x t u r e I n t e r f a c e ;
8 u s e J a z z y w e b \ A u l a s M e n t o r \ N o t a s F r o n t e n d B u n d l e \ E n t i t y \ U s u a r i o ;
9
1 0 c l a s s L o a d U s u a r i o D a t a e x t e n d s A b s t r a c t F i x t u r e i m p l e m e n t s O r d e r e d F i x t u r e I n t e r f a c e
1 1 {
1 2 p u b l i c f u n c t i o n l o a d ( O b j e c t M a n a g e r $ m a n a g e r )
1 3 {
1 4 / / U s u a r i o s
1 5 $ u s u a r i o 1 = n e w U s u a r i o ( ) ;
1 6 $ u s u a r i o 1 - > s e t N o m b r e ( ' A l b e r t o ' ) ;
1 7 $ u s u a r i o 1 - > s e t A p e l l i d o s ( ' E i n s t e i n ' ) ;
1 8 $ u s u a r i o 1 - > s e t U s e r n a m e ( ' a l b e r t o ' ) ;
1 9 $ u s u a r i o 1 - > s e t P a s s w o r d ( ' 6 2 0 a 7 d e 8 2 7 6 3 5 2 7 4 0 6 a 4 1 3 c a 7 e e 2 6 7 8 1 6 d 3 3 2 8 1 1 ' ) ;
2 0 $ u s u a r i o 1 - > s e t E m a i l ( ' a l b e r t o @ m e n t o r n o t a s . e s ' ) ;
2 1 $ u s u a r i o 1 - > s e t S a l t ( ' ' ) ;
2 2 $ u s u a r i o 1 - > s e t I s A c t i v e ( 1 ) ;
2 3 $ u s u a r i o 1 - > s e t T o k e n R e g i s t r o ( ' ' ) ;
2 4
2 5 $ u s u a r i o 1 - > a d d G r u p o ( $ t h i s - > g e t R e f e r e n c e ( ' g r u p o - r e g i s t r a d o ' ) ) ;
2 6
2 7 $ m a n a g e r - > p e r s i s t ( $ u s u a r i o 1 ) ;
2 8 $ t h i s - > a d d R e f e r e n c e ( ' a l b e r t o ' , $ u s u a r i o 1 ) ;
2 9
3 0 $ u s u a r i o 2 = n e w U s u a r i o ( ) ;
3 1 $ u s u a r i o 2 - > s e t N o m b r e ( ' M x i m o ' ) ;
3 2 $ u s u a r i o 2 - > s e t A p e l l i d o s ( ' P l a n c k ' ) ;
3 3 $ u s u a r i o 2 - > s e t U s e r n a m e ( ' m a x i m o ' ) ;
3 4 $ u s u a r i o 2 - > s e t P a s s w o r d ( ' 6 2 0 a 7 d e 8 2 7 6 3 5 2 7 4 0 6 a 4 1 3 c a 7 e e 2 6 7 8 1 6 d 3 3 2 8 1 1 ' ) ;
3 5 $ u s u a r i o 2 - > s e t E m a i l ( ' m a x i m o @ m e n t o r n o t a s . e s ' ) ;
3 6 $ u s u a r i o 2 - > s e t S a l t ( ' ' ) ;
3 7 $ u s u a r i o 2 - > s e t I s A c t i v e ( 1 ) ;
3 8 $ u s u a r i o 2 - > s e t T o k e n R e g i s t r o ( ' ' ) ;
3 9
4 0 $ u s u a r i o 2 - > a d d G r u p o ( $ t h i s - > g e t R e f e r e n c e ( ' g r u p o - p r e m i u m ' ) ) ;
4 1
4 2 $ m a n a g e r - > p e r s i s t ( $ u s u a r i o 2 ) ;
4 3 $ t h i s - > a d d R e f e r e n c e ( ' m a x i m o ' , $ u s u a r i o 2 ) ;
4 4
Creacin de los fixtures
246
4 5 $ u s u a r i o 3 = n e w U s u a r i o ( ) ;
4 6 $ u s u a r i o 3 - > s e t N o m b r e ( ' M a r a ' ) ;
4 7 $ u s u a r i o 3 - > s e t A p e l l i d o s ( ' C u r i e ' ) ;
4 8 $ u s u a r i o 3 - > s e t U s e r n a m e ( ' m a r i a ' ) ;
4 9 $ u s u a r i o 3 - > s e t P a s s w o r d ( ' 6 2 0 a 7 d e 8 2 7 6 3 5 2 7 4 0 6 a 4 1 3 c a 7 e e 2 6 7 8 1 6 d 3 3 2 8 1 1 ' ) ;
5 0 $ u s u a r i o 3 - > s e t E m a i l ( ' m a r i a @ m e n t o r n o t a s . e s ' ) ;
5 1 $ u s u a r i o 3 - > s e t S a l t ( ' ' ) ;
5 2 $ u s u a r i o 3 - > s e t I s A c t i v e ( 1 ) ;
5 3 $ u s u a r i o 3 - > s e t T o k e n R e g i s t r o ( ' ' ) ;
5 4
5 5 $ u s u a r i o 3 - > a d d G r u p o ( $ t h i s - > g e t R e f e r e n c e ( ' g r u p o - a d m i n ' ) ) ;
5 6
5 7 $ m a n a g e r - > p e r s i s t ( $ u s u a r i o 3 ) ;
5 8 $ t h i s - > a d d R e f e r e n c e ( ' m a r i a ' , $ u s u a r i o 3 ) ;
5 9
6 0 $ u s u a r i o 4 = n e w U s u a r i o ( ) ;
6 1 $ u s u a r i o 4 - > s e t N o m b r e ( ' I s a a c ' ) ;
6 2 $ u s u a r i o 4 - > s e t A p e l l i d o s ( ' N e w t o n ' ) ;
6 3 $ u s u a r i o 4 - > s e t U s e r n a m e ( ' i s a a c ' ) ;
6 4 $ u s u a r i o 4 - > s e t P a s s w o r d ( ' 6 2 0 a 7 d e 8 2 7 6 3 5 2 7 4 0 6 a 4 1 3 c a 7 e e 2 6 7 8 1 6 d 3 3 2 8 1 1 ' ) ;
6 5 $ u s u a r i o 4 - > s e t E m a i l ( ' i s a a c @ k k . e s ' ) ;
6 6 $ u s u a r i o 4 - > s e t S a l t ( ' ' ) ;
6 7 $ u s u a r i o 4 - > s e t I s A c t i v e ( 1 ) ;
6 8 $ u s u a r i o 4 - > s e t T o k e n R e g i s t r o ( ' ' ) ;
6 9
7 0 $ u s u a r i o 4 - > a d d G r u p o ( $ t h i s - > g e t R e f e r e n c e ( ' g r u p o - p r e m i u m ' ) ) ;
7 1 $ u s u a r i o 4 - > a d d G r u p o ( $ t h i s - > g e t R e f e r e n c e ( ' g r u p o - a d m i n ' ) ) ;
7 2
7 3 $ m a n a g e r - > p e r s i s t ( $ u s u a r i o 4 ) ;
7 4 $ t h i s - > a d d R e f e r e n c e ( ' i s a a c ' , $ u s u a r i o 4 ) ;
7 5
7 6 $ m a n a g e r - > f l u s h ( ) ;
7 7 }
7 8
7 9 p u b l i c f u n c t i o n g e t O r d e r ( )
8 0 {
8 1 r e t u r n 2 ;
8 2 }
8 3 }
Observa como podemos obtener los objetos G r u p o que hemos creado en la clase
L o a d G r u p o D a t a mediante el mtodo g e t R e f e r e n c e ( ' n o m b r e _ d e _ l a _ r e f e r e n c i a ' ) . Una vez
recuperado dichos objetos podemos asociarlos a los U s u a r i o 's sin ms que utilizar el mtodo
a d d G r u p o ( ) de estos ltimos. Fjate tambin que hemos creado referencias de los objetos
U s u a r i o 's para poder utilizarlas en el resto de las fixtures de las entidades que an tenemos
que implementar.
Vuelve a ejecutar el comando:
p h p a p p / c o n s o l e d o c t r i n e : f i x t u r e s : l o a d - - f i x t u r e s = s r c / J a z z y w e b / A u l a s M e n t o r / N o t a s F r o n t e n d B u n d l e / F i x t u r e s
Se reinicializar la base de datos con los datos especificados en las fixtures, es decir con los
grupos de antes y los usuarios que acabamos de especificar.
Y eso es todo. Ahora te toca a ti seguir el ejercicio. Implementa clases fixtures para rellenar
las tablas que quedan. Introduce al menos tres notas y y tres etiquetas por cada usuario, y
haz que haya usuarios en todos los grupos para poder probar bien la aplicacin en las
Creacin de los fixtures
247
siguientes unidades.
Transcribe el cdigo que has aadido para completar el ejercicio.
Instalacin manual del bundle (sin utilizar b i n / v e n d o r )
La reinstalacin de todos los vendors puede llevar bastante tiempo y est expuesta a
posibles fallos en la red. Adems, como se bajan las ltimas versiones de las libreras, podra
ocurrir que algo dejase de funcionar. Si has tenido alguno de estos problemas te indicamos
aqu como instalar nicamente las libreras necesarias para las fixtures sobre el cdigo que
ya tienes de los vendors. Es decir, sin reinstalarlos todos de nuevo.
Ejecuta los siguientes comandos:
g i t c l o n e h t t p : / / g i t h u b . c o m / d o c t r i n e / d a t a - f i x t u r e s . g i t v e n d o r / d o c t r i n e - f i x t u r e s
g i t c l o n e - b 2 . 0 h t t p : / / g i t h u b . c o m / d o c t r i n e / D o c t r i n e F i x t u r e s B u n d l e . g i t v e n d o r / b u n d l e s / S y m f o n y / B u n d l e / D o c t r i n e F i x t u r e s B u n d l e
Y ya est. El registro del bundle y los espacios de nombres en el a u t o l o a d . p h p se hacen de
la misma forma.
Ejercicio 2
Cambia la base de datos MySQL por una base de datos sqlite. Estas ltimas son muy
sencillas y son muy apropiadas cuando no tenemos necesidades muy exigentes con el
almancenamiento de datos, ya que consisten en un simple fichero, es decir, no se necesita
un servidor para la base de datos.
Sugerencia
Los parmetros de conexin necesarios para este tipo de base de datos son: d r i v e r
(p d o _ s q l i t e ), d b n a m e y p a t h . Una vez que tengas correctamente definidos dichos
parmetros, debes volver a crear la base de datos y el esquema (conjunto de tablas)
con los comandos pertinentes.
Ejercicio 3
Modifica el bundle Jazzyweb/AulasMentor/AlimentosBundle para que se utilice el servicio de
persistencia Doctrine2 en lugar del modelo que se propuso en la unidad 3. Explica
claramente los pasos que has dado.
Ejercicios de la unidad 9
Nota
Enlace a la Unidad 9: Desarrollo de la aplicacin MentorNotas (V). Seguridad -
Autentificacin y Autorizacin
Instalacin manual del bundle (sin utilizar bin/vendor)
248
Nota
Cuando se trabaja con el sistema de seguridad de Symfony hay que tener siempre
presente si el navegador mantiene la cookie o el token de autentificacin para estar
seguro de que lo que estamos probando se comporta realmente como pensamos. Por
ello, en ocasiones, tendrs que borrar la cach del navegador o simplemente
reiniciarlo. Tambin te servir de ayuda mirar la barra de depuracin del entorno de
desarrollo, pues ah se muestra claramente si existe un usuario autentificado o no.
Ejercicio 1
Cambia el cortafuegos j a m n _ a r e a _ p r o t e g i d a para que utilice autentificacin basic HTTP y
el proveedor de usuarios por base de datos. Prueba a cerrar la sesin mediante el enlace
c e r r a r s e s i n . Se ha cerrado realmente la sesin? Por qu ocurre esto?
Ejercicio 2
Aade al layout de la aplicacin una capa que muestre el nombre del usuario y el perfil con
el que ha iniciado la sesin.
Ejercicio 3
El sistema de seguridad de Symfony2 permite crear cortafuegos que pueden ser
"atravesados" por lo que se denomina un usuario annimo, es decir, un usuario que no est
registrado en ningun sistema de informacin pero al que se le asigna un token de
autentificacin y, por tanto, tiene un objeto U s e r asociado. Para activar dicho usuario
annimo basta con aadir la directiva a n o n y n o u s : ~ al cortafuegos que queramos. Aade
dicha directiva al cortafuegos j a m n _ a r e a _ p u b l i c a y elimina de dicho cortafuegos la
directiva s e c u r i t y : f a l s e . Prueba ahora la aplicacin. En que ha cambiado el
comportamiento? Le encuentras alguna ventaja? Describe la/s ventajas que has
encontrado.
Nota
Los usuarios annimos son tcnicamente usuarios autenticados. Lo que quiere decir
que el mtodo i s A u t h e n t i c a t e d ( ) de un usuario annimo devolver un valor t r u e .
Para chequear si el usuario est realmente autenticado, hay que comporbar si tiene el
rol I S _ A U T H E N T I C A T E D _ F U L L Y
Ejercicio 4
Como se trata de un ejercicio vamos a ser un poquito caprichosos y bloquearemos las
cuentas de aquellos usuarios cuyo apellido comience con la letra E o cuyo nombre termina
con la letra A. Cmo implementaras esta funcionalidad?
Ejercicios de la unidad 10
Ejercicio 1
249
Nota
Enlace a la Unidad 10: Desarrollo de la aplicacin MentorNotas (VI). Esamblando todo
el frontend
Nota
En la entrega de estos ejercicios, adems de las habituales explicaciones, debes
enviar la versin final de tu JAMNotasFrontendBundle, con estos ejercicios realizados
(se entiende)
Ejercicio 1
Adapta las plantillas de la pantalla de login y del proceso de registro al diseo de lainterfaz
de usuario propuesto en la unidad 5 y que hemos comenzado a aplicar en esta unidad.
Ejercicio 2
Implementa el proceso de contratacin de una cuenta premium. Describe como lo has
hecho.
Nota
En el proceso de contratacin debera haber un momento en el que se hiciese uso de
algn servicio de pago electrnico, lo que se conoce como pasarela del pagos, que
normalmente es la de un banco determinado o la de paypal. En la implementacin de
esta funcionalidad no tienes por qu disponer de un servicio de pago electrnico, sera
demasiado jaleo (aunque paypal pone a disposicin de los desarrolladores un entorno
y una API para simular transferencias que es muy interesante y sencillo de usar). Lo
que debes hacer aqu es recurrir a la idea de servicio mock, es decir de simular el
servicio, haciendo que las transferencias que se hagan sean, por ejemplo, siempre
vlidas.
Ejercicio 3
Implementa alguna funcionalidad nueva y til que le de ms valor a la aplicacin.
Sugerencias:
posibilidad de intercambiar notas entre usuarios
posibilidad de enviar notas por correo electrnico
posibilidad de compartir las notas por twitter, facebook u otras redes sociales
lo que se te ocurra.
Ejercicio 1
250
Indices y tablas
genindex
search
Indices y tablas
251
Indices y tablas
genindex
search
1 "Patrones de Diseo" de los autores Erich Gamma, Richard Helm, Ralph
Johnson y John Vlissides (conocidos como The Gun of Four) es un clsico en
la literatura sobre este tema.
2 http://en.wikipedia.org/wiki/Separation_of_concerns
3 En el caso de Apache con PHP, que es el que nos interesa en este curso, el
servidor debe estar configurado adecuadamente para que se puedan
incluir archivos PHP que estn fuera del Document root*. Esto se hace con
la directiva o p e n _ b a s e d i r .
4 http://es.wikipedia.org/wiki/Decorator_%28patr%C3%B3n_de_dise%C3%B1o%29
5 http://twig.sensiolabs.org/
6 Drupal 8, phpBB4, Silex, son el nombre de algunos de los proyectos que
han optado por utilizar los componentes de Symfony2.
7 Algunos de los componentes ya cuentan con documentacin:
http://symfony.com/doc/current/components
8 https://github.com/symfony
9 Atencin, activar los mensajes de depuracin no significa mostrar la barra
de depuracin. Se trata de arrojar al fichero de registro (directorio
a p p / l o g ) mensajes que ayudan a depurar la aplicacin.
10 http://martinfowler.com/
11 http://www.evernote.com
12 http://jquery.com
13 http://layout.jquery-dev.net
14 http://es.wikipedia.org/wiki/Expresi%C3%B3n_regular
15 Si no tienes muy claro en que consiste el mecanismo de sesin de PHP
puedes mirar http://www.php.net/manual/es/features.sessions.php. En el
captulo 6 del curso Desarrollo de aplicaciones web con symfony de Aulas
Mentor tambin se explica con detalle el mecanismo de sesin. Puedes
verlo en http://el.curso.de.symfony14
16 http://en.wikipedia.org/wiki/PHPDoc#DocBlock
17 Propel es otro gran ORM que se integra fcilmente con Symfony2
18 Hibernate Query Language (HQL) y Java Persistence Query Language
(JPQL) son lenguajes del mismo tipo.
19 Ms adelante veremos que tambin se pueden crear formularios que no
estn asociados a ningn objeto.
Indices y tablas
252

Vous aimerez peut-être aussi