Durante el desarrollo de una aplicación, uno de los problemas recurrentes es el de la carga inicial de datos en la base de datos. Algunos sistemas de bases de datos disponen de soluciones específicas para esta tarea, pero ninguna se puede utilizar junto en el ORM de Symfony. Gracias al uso de YAML y al objeto sfPropelData
, Symfony puede transferir automáticamente los datos almacenados en un archivo de texto a una base de datos. Aunque puede parecer que crear el archivo de texto con los datos iniciales de la aplicación cuesta más tiempo que insertarlos directamente en la base de datos, a la larga se ahorra mucho tiempo. Se trata de una utilidad muy práctica para la carga automática de datos de prueba para la aplicación.
16.4.1. Sintaxis del archivo de datos
Symfony es capaz de procesar todos los archivos que siguen una sintaxis YAML definida muy simple y que se encuentren en el directorio data/fixtures/
. Los archivos de datos, también llamados "fixtures", se organizan por clases y cada sección de clase utiliza una cabecera con el valor del nombre de la clase. Para cada clase, las filas de datos disponen de una etiqueta que las identifica de forma única y una serie de pares nombre_campo: valor
. El listado 16-16 muestra un ejemplo de un archivo preparado para cargar sus datos en una base de datos.
Listado 16-16 - Archivo de datos de ejemplo, en data/fixtures/import_data.yml
Article: ## Crea filas de datos en la tabla blog_article
first_post: ## Etiqueta de la primera fila de datos
title: My first memories
content: |
For a long time I used to go to bed early. Sometimes, when I had put
out my candle, my eyes would close so quickly that I had not even time
to say "I am going to sleep."
second_post: ## Etiqueta de la segunda fila de datos
title: Things got worse
content: |
Sometimes he hoped that she would die, painlessly, in some accident,
she who was out of doors in the streets, crossing busy thoroughfares,
from morning to night.
Symfony transforma el nombre indicado para las columnas, en métodos setter utilizando la conversión de tipo camelCase (la columna title
se transforma en setTitle()
, la columna content
se transforma en setContent()
, etc.). La ventaja de esta transformación es que se puede definir, por ejemplo, una columna llamada password
para la que no existe una columna en la tabla de la base de datos; solamente es necesario definir un método llamado setPassword()
en el objeto User
y ya es posible asignar valores a otras columnas de datos en función de este dato, como por ejemplo una columna que guarde la contraseña encriptada.
No es necesario definir el valor de la columna de la clave primaria. Como es un campo cuyo valor se autoincrementa, la capa de base de datos es capaz de determinar su valor.
A las columnas created_at
tampoco es necesario asignarles un valor, ya que Symfony sabe que a las columnas que se llaman así, les debe asignar la fecha actual del sistema a la hora de crearlas.
16.4.2. Importando los datos
La tarea propel:data-load
importa los datos de los archivos YAML en una base de datos. Las opciones de conexión con la base de datos se obtienen del archivo de configuración databases.yml
, por lo que es necesario indicar a la tarea el nombre de una aplicación. Además, es posible indicar el nombre de un entorno de ejecución mediante la opción --env
(su valor por defecro es dev
).
> php symfony propel:data-load --env=prod frontend
Al ejecutar este comando, se leen todos los archivos de datos YAML del directorio data/fixtures
y se insertan las filas de datos en la base de datos. Por defecto, se reemplaza todo el contenido existente en la base de datos, aunque si se utiliza la opción llamada --append
, el comando no borra los datos existentes.
> php symfony propel:data-load --append frontend
También es posible especificar otro archivo de datos u otro directorio, indicando su valor como una ruta relativa respecto del directorio del proyecto.
> php symfony propel:data-load frontend --dir[]=data/misfixtures
16.4.3. Usando tablas relacionadas
Ahora ya es posible añadir filas de datos a una tabla, pero de esta forma no es posible añadir filas con claves externas que hacen relación a otra tabla. Como los archivos de datos no incluyen la clave primaria, se necesita un método alternativo para relacionar los diferentes registros de datos entre sí.
Volviendo al ejemplo del Capítulo 8, donde la tabla blog_article
está relacionada con la tabla blog_comment
, de la forma que se muestra en la figura 16-8.
En esta situación es en la que se utilizan las etiquetas únicas de cada fila de datos. Para añadir un campo de tipo Comment
al artículo llamado first_post
, simplemente es necesario añadir las siguientes líneas del listado 16-17 al archivo de datos import_data.yml
.
Listado 16-17 - Añadiendo un registro relacionado con otra tabla, en data/fixtures/import_data.yml
Comment:
first_comment:
article_id: first_post
author: Anonymous
content: Your prose is too verbose. Write shorter sentences.
La tarea propel:data-load
es capaz de reconocer la etiqueta que se asignó anteriormente al artículo en el archivo import_data.yml
y es capaz de obtener la clave primaria del registro de tipo Article
correspondiente en la base de datos, para asignar ese valor al campo article_id
. No es necesario trabajar con los valores de las columnas de tipo ID, solo es necesario enlazar las filas de datos mediante sus etiquetas, por lo que su funcionamiento es realmente simple.
La única restricción para las filas de datos enlazadas es que los objetos utilizados en una clave externa tienen que estar definidos anteriormente en el archivo; es decir, igual que si se tuvieran que definir uno a uno. Los archivos de datos se procesan desde el principio hasta el final y por tanto, el orden en el que se escriben las filas de datos es muy importante.
A partir de Symfony 1.1, este mecanismo también funciona para las relaciones muchos-a-muchos, en las que dos clases se relacionan a través de una tercera clase. Un ejemplo podría ser una clase Articulo
que puede tener varios Autores
y un Autor
puede tener varios Articulos
. Normalmente, estas relaciones se resuelven creando una clase ArticuloAutor
que se corresponde con una tabla llamada articulo_autor
que tiene las columnas articulo_id
y autor_id
. El listado 16-18 muestra un ejemplo de archivo de datos que define una relación de este tipo. Lo más importante es el uso del plural en el nombre de la tabla (articulo_autors
) que indica que existe una clase intermedia entre las dos clases.
Listado 16-18 - Añadiendo un registro con una relación de tipo muchos-a-muchos, en data/fixtures/import_data.yml
Autor:
primer_autor:
nombre: Juan Pérez
articulo_autors: [primer_articulo, segundo_articulo]
Un solo archivo de datos puede contener la declaración de varias clases diferentes. Sin embargo, si se necesitan insertar muchos datos en muchas tablas diferentes, es posible que el archivo de datos sea demasiado largo como para manejarlo fácilmente.
Como la tarea propel:data-load
procesa todos los archivos que encuentra en el directorio fixtures/
, es posible dividir el archivo de datos YAML en otros archivos más pequeños. Lo único que hay que tener en cuenta es que las claves externas obligan a definir un determinado orden al procesar los datos. Para asegurar que los archivos se procesan en el orden adecuado, se puede añadir un número como prefijo del nombre del archivo, de forma que se procesen en el orden establecido.
100_article_import_data.yml 200_comment_import_data.yml 300_rating_import_data.yml