Symfony 1.4, la guía definitiva

16.4. Cargando datos en una base de datos

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.

Ejemplo de modelo relacional de una base de datos

Figura 16.8 Ejemplo de modelo relacional de una base de datos

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