Drupal: Importar y sincronizar desde un CSV con Migrate
Hoy nos toca importar información desde archivos CSV. Como la implementacion de las clases no varian mucho con respecto a la de BD y XML, voy a obviar la explicacion de algunas partes y me voy a centrar en el “source” de la clase.
Los archivos CSV de ejemplo están al final del post.
Para mostrar las posibles configuraciones que lleva un CSV, he creado 3 ejemplos, y en cada uno atendemos algún posible caso a la hora de importar CSV’s:
El ejemplo más básico: un CSV sin headers y de múltiples líneas por fila
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
< ? class WPMigrationFromCSV extends Migration { public function __construct($arguments) { parent::__construct($arguments); $module_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'mimodulo'); $csv_path = $module_path . 'wp_posts-normal.csv'; $columns = array( 0 => array('id', 'Post ID'), 2 => array('post_date', 'Post date'), 4 => array('post_content', 'Post content'), 5 => array('post_title', 'Post title'), ); $this->source = new MigrateSourceCSV($csv_path, $columns, array('embedded_newlines' => TRUE)); $this->destination = new MigrateDestinationNode('page'); $this->map = new MigrateSQLMap($this->machineName, array( 'id' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ), ), MigrateDestinationNode::getKeySchema() ); $this->addFieldMapping('uid')->defaultValue(1); $this->addFieldMapping('created', 'post_date'); $this->addFieldMapping('title', 'post_title'); $this->addFieldMapping('body', 'post_content'); } } |
Resumiendo el ejemplo anterior:
- La importación de CSV extienden siempre de Migration().
- $this->source hace uso de la clase MigrateSourceCSV() que se nutre de:
- $csv_path que es el path hasta el archivo.
- $columns que es un array que indica en qué posición de la fila se encuentra el item, el nombre maquina que queremos usar para ese item, y un label (Util para UI’s). Noten que el key de cada elemento del array no es secuencial. Esto es porque en el CSV no se encuentran uno detrás del otro los elementos que queremos importar. vean el CSV de ejemplo si no le ven sentido 🙂
- El tercer parámetro de la clase es el array de opciones. en nuestro ejemplo usamos “embedded_newlines” ya que el CSV de ejemplo tiene items que estan repartidos en varias líneas. Si van a importar un CSV cuyas filas no tienen varias líneas, pueden obviar este parámetro.
- El resto de la clase mantiene los mismos principios explicados en el articulo de introduccion
CSV con headers
Cuando el CSV a importar trae headers podemos usarlo directamente en lugar de mapear los campos.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
< ? class WPMigrationFromCSVconHeaders extends Migration { public function __construct($arguments) { parent::__construct($arguments); $module_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'mimodulo'); $csv_path = $module_path . 'wp_posts-con-cabeceras.csv'; $this->source = new MigrateSourceCSV($csv_path, array(), array('embedded_newlines' => TRUE, 'header_rows' => 1)); $this->destination = new MigrateDestinationNode('page'); $this->map = new MigrateSQLMap($this->machineName, array( 'ID' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ), ), MigrateDestinationNode::getKeySchema() ); $this->addFieldMapping('uid')->defaultValue(1); $this->addFieldMapping('created', 'post_date'); $this->addFieldMapping('title', 'post_title'); $this->addFieldMapping('body', 'post_content'); } } |
Fijense que en lugar de hacer un array mapeando los campos con su respectiva posición en el CSV, hemos pasado un array vacío, y en las opciones de MigrateSourceCSV() le hemos pasado “header_rows” para indicarle que la cabecera del CSV es hasta la línea 1 (lo que haría que lea solo la primer línea como header).
En este ejemplo, resulta que casi todas las columnas del CSV se llaman igual que el mapeo del ejemplo anterior, salvo el “id” que en el CSV viene representado como “ID”.
CSV con delimitadores cambiados
Hay veces que el CSV tiene delimitadores distintos a los normales. Por ejemplo, para separar los item de una fila en lugar de usar , (coma), puede haber un ; (punto y coma) o para encerrar el contenido de un ítem en lugar de ” (comillas dobles) se usa ‘ (comillas simples).
para ese tipo de casos podemos especificar a MigrateSourceCSV() estos separadores:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
< ? class WPMigrationFromCSVdistintosDelimiters extends Migration { public function __construct($arguments) { parent::__construct($arguments); $module_path = DRUPAL_ROOT . '/' . drupal_get_path('module', 'mimodulo'); $csv_path = $module_path . 'wp_posts-con-delimitadores.csv'; $columns = array( 0 => array('id', 'Post ID'), 2 => array('post_date', 'Post date'), 4 => array('post_content', 'Post content'), 5 => array('post_title', 'Post title'), ); $this->source = new MigrateSourceCSV($csv_path, $columns, array( 'embedded_newlines' => TRUE, 'length' => NULL, 'delimiter' => ';', 'enclosure' => "'", 'escape' => '|', )); $this->destination = new MigrateDestinationNode('page'); $this->map = new MigrateSQLMap($this->machineName, array( 'id' => array( 'type' => 'int', 'unsigned' => TRUE, 'not null' => TRUE, ), ), MigrateDestinationNode::getKeySchema() ); $this->addFieldMapping('uid')->defaultValue(1); $this->addFieldMapping('created', 'post_date'); $this->addFieldMapping('title', 'post_title'); $this->addFieldMapping('body', 'post_content'); } } |
‘length’, ‘delimiter’, ‘enclosure’, y ‘escape’ van directamente a parar a fgetcsv.
Archivos de ejemplo:
wp_posts-con-cabeceras.csv
Pingback: Combo: Importar y sincronizar productos con Migrate + Drupal commerce + product display desde CSV/XML/JSON | Capy