Optimisez vos imports de données avec Migrate
Qui suis-je ?           Matthieu Guillermin             •    Consultant chez Clever Age             •    Java, PHP, Symfon...
Agenda        •     Contexte        •     Solutions d’import de données dans Drupal        •     Anatomie d’une tâche Migr...
Imports de donnéesDrupal Camp Lyon - Optimisez vos imports de données avec Migrate   4Matthieu Guillermin - Mai 2012
Imports de données        •     Problème récurrent             •    Récupération de contenu initial             •    Affich...
Imports de données        •     Problématiques variées             •    Sources : BDD, fichiers, flux,...             •    S...
Solutions d’import de             données dans DrupalDrupal Camp Lyon - Optimisez vos imports de données avec Migrate   7M...
« A la main »        •     Transfert direct vers la base             •    Via des scripts             •    En utilisant un...
« A la main »        •     En utilisant l’API Drupal pour enregistrer les              données             •    Développem...
« A la main »        •     Les +             •    Souplesse totale sur la source et les                  traitements      ...
Feeds        •     Module « historique » pour l’import de              données        •     Au départ, plutôt dédié aux flu...
Feeds           •     Les +               •     Rapide à mettre en place               •     Configuration via l’interface ...
Migrate        •     Module dédié à l’import de données        •     Interface graphique minimale        •     Nécessite d...
Migrate        •     Les +             •    Connecteurs fournis             •    Performances sur de gros volumes         ...
En résumé        •     Feeds             •    Si vous voulez une interface simple             •    Si il suffit à votre bes...
Anatomie d’une tâche                  MigrateDrupal Camp Lyon - Optimisez vos imports de données avec Migrate   16Matthieu...
Source        •     Gère le requêtage des données             •    SQL : MySQL, Oracle, MSSQL,..             •    Fichiers...
Source   $query = Database::getConnection(myDb, myDbKey)       ->select(article, a);   $query->innerJoin(‘article_category...
Source   $this->source = new MigrateSourceCSV($source_file,     $this->csvcolumns(), array(header_rows => 1),     array())...
Source   $items_url = $xml_folder . positions.xml;   $item_xpath = /producers/producer;   $item_ID_xpath = sourceid;   $th...
Destination        •     Gère l’enregistrement des données             •    Types fournis par Migrate :                 • ...
Destination   // Node   $this->destination = new MigrateDestinationNode(       article, array(text_format => full_html)); ...
Mapping        •     Associations champs             •    Source => Destination        •     Peuvent être simples ou + com...
Mapping           SRC                                                      DEST        SRC_ID                             ...
Mapping        •     Transformations de données             •    Chaîne ➞ tableau (explode)             •    Appel de « Ca...
Mapping   // Default value   $this->addFieldMapping(language, lang)     ->defaultValue(fr);   // Multiple   $this->addFiel...
Mapping        •     « Sous le capot »             •    les FieldHandlers s’occupent de                  convertir les val...
FieldHandlers   // Before   string(2) "C1"   // After   ["field_numero"]=>     array(1) {       ["und"]=>       array(1) {...
Mapping   // Arguments Date   $date_args =     DateMigrateFieldHandler::arguments(Europe/Paris);   $this->addFieldMapping(...
Map        •     Correspondance entre ID source et ID              destination             •    Stockée dans une table déd...
Map                                                       MAP                                             ID Src 1        ...
Map   $this->map = new MigrateSQLMap($this->machineName,      array(         id => array(           type => int,          ...
Anatomie d’une tâche        •     En résumé :             •    Source             •    Destination             •    Mappin...
Migration        •     1 Migration = 1 Classe PHP             •    Doit étendre Migration        •     Configuration de    ...
Migration        •     Offre des points d’entrée pour             •    Altérer les données en lecture                 • pr...
Lancement des tâches        •     Pour lancer les tâches             •    Ligne de commande                 • Drush       ...
Lancement des tâches        •     Drush             •    Différentes commandes : mi, ms, mr, mrs             •    Paramètr...
Lancement tâches   $ drush ms    Name                         Total    Imported      Unimported   Status   Last imported  ...
Lancement tâchesDrupal Camp Lyon - Optimisez vos imports de données avec Migrate   39Matthieu Guillermin - Mai 2012
En pratique...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate   40Matthieu Guillermin - Mai 2012
Gestion des références             entre contenusDrupal Camp Lyon - Optimisez vos imports de données avec Migrate   41Matt...
Références entre contenus        •     Prérequis             •    Mécanisme de dépendances entre tâches        •     Si B ...
Références entre contenus        •     Tables ‘Map’             •    Correspondances entre ID src. & dest.             •  ...
Références entre contenus        •     Lors de l’import de ce champ dans B :             •    L’Id source va être « conver...
Références entre contenus        •     Import de A        SRC_A                                      MAP_A                ...
Références entre contenus        •     Import de B        SRC_B                                          MAP_B            ...
En pratique...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate   47Matthieu Guillermin - Mai 2012
Stratégies                             d’optimisationDrupal Camp Lyon - Optimisez vos imports de données avec Migrate   48...
Requêtage        •     Comme dans tout import, le requêtage est              essentiel             •    Limiter le nombre ...
Modules « gourmands »        •     Désactivation de traitements lors de l’import             •    Pathauto : il faut mieux...
Highwater Field        •     Pour les imports réguliers             •    N’importer que les données mises à jour          ...
Highwater Field        •     Avec le Highwater Field             •    Migrate ajoutera automatiquement une                ...
preImport()        •     En cas de structure de base trop « tordue »             •    On peut être amené à faire une premi...
// Called once during the migration processpublic function preImport(){  parent::preImport();  $query = Database::getConne...
// Called for each source rowpublic function prepareRow($row){  if (isset($this->metas[$row->id_doc][titre])) {    $row->t...
En cas de besoin        •     Attention à la mémoire             •    On peut être amené à ajuster la                  mem...
Des questions ?Drupal Camp Lyon - Optimisez vos imports de données avec Migrate   57Matthieu Guillermin - Mai 2012
Merci pour votre attention !      @mguillermin
Upcoming SlideShare
Loading in...5
×

Optimisez vos imports de données avec Migrate

2,079

Published on

Published in: Technology
0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total Views
2,079
On Slideshare
0
From Embeds
0
Number of Embeds
2
Actions
Shares
0
Downloads
18
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Optimisez vos imports de données avec Migrate

  1. 1. Optimisez vos imports de données avec Migrate
  2. 2. Qui suis-je ? Matthieu Guillermin • Consultant chez Clever Age • Java, PHP, Symfony, Play!,... et ... Drupal @mguillerminDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 2Matthieu Guillermin - Mai 2012
  3. 3. Agenda • Contexte • Solutions d’import de données dans Drupal • Anatomie d’une tâche Migrate • Gestion des références entre contenus • Stratégies d’optimisationDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 3Matthieu Guillermin - Mai 2012
  4. 4. Imports de donnéesDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 4Matthieu Guillermin - Mai 2012
  5. 5. Imports de données • Problème récurrent • Récupération de contenu initial • Affichage de données depuis sources externes • Agrégation de données • ...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 5Matthieu Guillermin - Mai 2012
  6. 6. Imports de données • Problématiques variées • Sources : BDD, fichiers, flux,... • Structures de donnée : plat, relationnel,... • Fréquences d’imports : one-shot, quotidient,... • Cibles : Nodes, Files, Users, Taxonomy,...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 6Matthieu Guillermin - Mai 2012
  7. 7. Solutions d’import de données dans DrupalDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 7Matthieu Guillermin - Mai 2012
  8. 8. « A la main » • Transfert direct vers la base • Via des scripts • En utilisant un ETL • Schéma cible difficile à appréhender • Encore + depuis Drupal 7 • Risque d’erreurs importantDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 8Matthieu Guillermin - Mai 2012
  9. 9. « A la main » • En utilisant l’API Drupal pour enregistrer les données • Développement d’un module • Implémentation de la lecture des données • Construction de la structure du node • Utilisation de node_save() pour l’enregistrementDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 9Matthieu Guillermin - Mai 2012
  10. 10. « A la main » • Les + • Souplesse totale sur la source et les traitements • Les - • Beaucoup de travail • On va « réinventer la roue » • Difficile de construire la structure « node »Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 10Matthieu Guillermin - Mai 2012
  11. 11. Feeds • Module « historique » pour l’import de données • Au départ, plutôt dédié aux flux XML (RSS) • Configuration UI + API • Connecteurs disponibles : XML, CSV, LDAP,...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 11Matthieu Guillermin - Mai 2012
  12. 12. Feeds • Les + • Rapide à mettre en place • Configuration via l’interface • Connecteurs disponibles • Les - • Peu adapté aux gros volumes de données • Difficile de gérer les cas « particuliers » • Références entre les contenusDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 12Matthieu Guillermin - Mai 2012
  13. 13. Migrate • Module dédié à l’import de données • Interface graphique minimale • Nécessite de « coder » les imports • Fournit des outils clés en main (lancement de tâches, ...) • Peut faire peur de prime abordDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 13Matthieu Guillermin - Mai 2012
  14. 14. Migrate • Les + • Connecteurs fournis • Performances sur de gros volumes • Gestion des références entre contenus • Les - • Nécessite d’écrire du code • ça pourrait être un + !Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 14Matthieu Guillermin - Mai 2012
  15. 15. En résumé • Feeds • Si vous voulez une interface simple • Si il suffit à votre besoin • Et si vous aimez cliquer • Sinon, Migrate • Pas si compliqué que çaDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 15Matthieu Guillermin - Mai 2012
  16. 16. Anatomie d’une tâche MigrateDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 16Matthieu Guillermin - Mai 2012
  17. 17. Source • Gère le requêtage des données • SQL : MySQL, Oracle, MSSQL,.. • Fichiers : CSV, XML, ... • Fournit une série d’enregistrements • Avec les valeurs des champs • S’occupe du parcours des données • Extensible en cas de besoinDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 17Matthieu Guillermin - Mai 2012
  18. 18. Source $query = Database::getConnection(myDb, myDbKey) ->select(article, a); $query->innerJoin(‘article_category’, ‘ac’, ‘a.category_id = ac.id’); $query->fields(‘a’, ‘id’); $query->fields(‘a’, ‘title’); $query->fields(‘a’, ‘content’); $query->fields(‘ac’, ‘name’, ‘cat_name’); $this->source = new MigrateSourceSQL($query, array(), NULL, array(map_joinable => FALSE));Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 18Matthieu Guillermin - Mai 2012
  19. 19. Source $this->source = new MigrateSourceCSV($source_file, $this->csvcolumns(), array(header_rows => 1), array()); function csvcolumns() { $columns[0] = array(title, Title); $columns[2] = array(desc, Description); return $columns; }Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 19Matthieu Guillermin - Mai 2012
  20. 20. Source $items_url = $xml_folder . positions.xml; $item_xpath = /producers/producer; $item_ID_xpath = sourceid; $this->source = new MigrateSourceXML($items_url, $item_xpath, $item_ID_xpath, $fields);Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 20Matthieu Guillermin - Mai 2012
  21. 21. Destination • Gère l’enregistrement des données • Types fournis par Migrate : • User, Role, Node, Comment, Term, File,... • Utilise l’API pour enregistrer : node_save, user_save(),... • Extensible • on peut créer ses propres types de destinationsDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 21Matthieu Guillermin - Mai 2012
  22. 22. Destination // Node $this->destination = new MigrateDestinationNode( article, array(text_format => full_html)); // Taxonomy $this->destination = new MigrateDestinationTerm( ‘voc_name’); // User $this->destination = new MigrateDestinationUser();Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 22Matthieu Guillermin - Mai 2012
  23. 23. Mapping • Associations champs • Source => Destination • Peuvent être simples ou + complexes • Transformation de données • Conversion de formats • ...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 23Matthieu Guillermin - Mai 2012
  24. 24. Mapping SRC DEST SRC_ID DEST_ID SRC_1 Transfo. DEST_1 SRC_2 DEST_2 SRC_3 Transfo. DEST_3Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 24Matthieu Guillermin - Mai 2012
  25. 25. Mapping • Transformations de données • Chaîne ➞ tableau (explode) • Appel de « Callbacks » • Arguments • A utiliser suivant les types de champs • cf les « Migrate*FieldHandler »Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 25Matthieu Guillermin - Mai 2012
  26. 26. Mapping // Default value $this->addFieldMapping(language, lang) ->defaultValue(fr); // Multiple $this->addFieldMapping(field_auteur, auteurs) ->separator(,); // Callbacks $this->addFieldMapping(title, titre) ->callbacks( strip_tags, array($this, trimTitle), array($this, convertToUtf8) );Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 26Matthieu Guillermin - Mai 2012
  27. 27. Mapping • « Sous le capot » • les FieldHandlers s’occupent de convertir les valeurs • Construction des structures en fonction des types de champs • Utilise les arguments pour savoir comment les générerDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 27Matthieu Guillermin - Mai 2012
  28. 28. FieldHandlers // Before string(2) "C1" // After ["field_numero"]=> array(1) { ["und"]=> array(1) { [0]=> array(3) { ["value_format"]=> string(10) "plain_text" ["format"]=> string(10) "plain_text" ["value"]=> string(2) "C1" } } }Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 28Matthieu Guillermin - Mai 2012
  29. 29. Mapping // Arguments Date $date_args = DateMigrateFieldHandler::arguments(Europe/Paris); $this->addFieldMapping(field_pub_date, pub) ->arguments($date_args); // Arguments Term Reference $this->addFieldMapping(field_categorie, categorie) ->arguments(array( source_type => tname, create_term => TRUE ));Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 29Matthieu Guillermin - Mai 2012
  30. 30. Map • Correspondance entre ID source et ID destination • Stockée dans une table dédiée • Permet de relancer une tâche en « update » • Permet les « rollbacks » • Permet les références entre contenusDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 30Matthieu Guillermin - Mai 2012
  31. 31. Map MAP ID Src 1 ID Dest 1 ID Src 2 ID Dest 2 SRC DEST SRC_ID DEST_ID SRC_1 Transfo. DEST_1 SRC_2 DEST_2 SRC_3 Transfo. DEST_3Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 31Matthieu Guillermin - Mai 2012
  32. 32. Map $this->map = new MigrateSQLMap($this->machineName, array( id => array( type => int, unsigned => TRUE, description => Content ID, ) ), MigrateDestinationNode::getKeySchema() );Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 32Matthieu Guillermin - Mai 2012
  33. 33. Anatomie d’une tâche • En résumé : • Source • Destination • Mapping • MapDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 33Matthieu Guillermin - Mai 2012
  34. 34. Migration • 1 Migration = 1 Classe PHP • Doit étendre Migration • Configuration de • Source, Destination, Field Mapping, Map • Effectuée dans __construct()Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 34Matthieu Guillermin - Mai 2012
  35. 35. Migration • Offre des points d’entrée pour • Altérer les données en lecture • prepareRow($row) • Altérer les entités en écriture • prepare($entity, $row) • Possibilité d’héritage • Mutualisation de configurationDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 35Matthieu Guillermin - Mai 2012
  36. 36. Lancement des tâches • Pour lancer les tâches • Ligne de commande • Drush • Interface graphique • Disponible dans le BackOfficeDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 36Matthieu Guillermin - Mai 2012
  37. 37. Lancement des tâches • Drush • Différentes commandes : mi, ms, mr, mrs • Paramètres intéressants : • «limit» : permet de limiter le nombre d’éléments à importer (ou le temps d’import) • «feedback» : notifications de l’avancementDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 37Matthieu Guillermin - Mai 2012
  38. 38. Lancement tâches $ drush ms Name Total Imported Unimported Status Last imported LyonMetroLigneTypes 4 4 0 Idle 2012-05-23 13:49:35 LyonMetroLignes 154 154 0 Idle 2012-05-23 14:08:29 LyonMetroArrets 4036 4036 0 Idle 2012-05-23 13:58:16 $ drush mi LyonMetroLignes --update --feedback="50 items" Processed 50 (0 created, 50 updated, 0 failed, 0 ignored) in 1.4 sec (2156/min) - continuing with LyonMetroLignes Processed 50 (0 created, 50 updated, 0 failed, 0 ignored) in 1.9 sec (1566/min) - continuing with LyonMetroLignes Processed 50 (0 created, 50 updated, 0 failed, 0 ignored) in 2.2 sec (1377/min) - continuing with LyonMetroLignes Processed 4 (0 created, 4 updated, 0 failed, 0 ignored) in 0.3 sec (899/min) - done with LyonMetroLignesDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 38Matthieu Guillermin - Mai 2012
  39. 39. Lancement tâchesDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 39Matthieu Guillermin - Mai 2012
  40. 40. En pratique...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 40Matthieu Guillermin - Mai 2012
  41. 41. Gestion des références entre contenusDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 41Matthieu Guillermin - Mai 2012
  42. 42. Références entre contenus • Prérequis • Mécanisme de dépendances entre tâches • Si B est dépendant de A • La tâche A devra être lancée avant la tâche B $this->dependencies = array(MigrationA);Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 42Matthieu Guillermin - Mai 2012
  43. 43. Références entre contenus • Tables ‘Map’ • Correspondances entre ID src. & dest. • Permet à Migrate de gérer les références • Dans le mapping : $this->addFieldMapping(field_a_dest, field_a_src) ->sourceMigration(MigrationA);Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 43Matthieu Guillermin - Mai 2012
  44. 44. Références entre contenus • Lors de l’import de ce champ dans B : • L’Id source va être « converti » en Id Destination en utilisant la table Map de la Migration A SELECT destid1 /* nid/tid/uid/fid */ FROM migrate_map_A /* Map table for ‘A‘ */ WHERE sourceid1 = :id_src /* id in ‘A’ src table */ • C’est l’Id « converti » qui sera enregistré dans le contenuDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 44Matthieu Guillermin - Mai 2012
  45. 45. Références entre contenus • Import de A SRC_A MAP_A NODE 1023 378 SRC_ID NID 1029 379 ... ... ... ...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 45Matthieu Guillermin - Mai 2012
  46. 46. Références entre contenus • Import de B SRC_B MAP_B NODE_B 4204 455 SRC_ID 4605 456 NID ... ... A_ID A_REF MAP_A 1023 378 1029 379 ... ...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 46Matthieu Guillermin - Mai 2012
  47. 47. En pratique...Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 47Matthieu Guillermin - Mai 2012
  48. 48. Stratégies d’optimisationDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 48Matthieu Guillermin - Mai 2012
  49. 49. Requêtage • Comme dans tout import, le requêtage est essentiel • Limiter le nombre de requêtes • Eviter de faire une requête pour chaque ligne source • INNER JOIN / LEFT JOIN /... • Optimiser la source (si possible) • ajout d’indexes • configuration DB (innodb_buffer_pool_size,...)Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 49Matthieu Guillermin - Mai 2012
  50. 50. Modules « gourmands » • Désactivation de traitements lors de l’import • Pathauto : il faut mieux construire l’url « à la main » • ‘migrate_extras’ fournit un moyen de le désactiver pour une migration donnéeDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 50Matthieu Guillermin - Mai 2012
  51. 51. Highwater Field • Pour les imports réguliers • N’importer que les données mises à jour depuis le dernier import $this->highwaterField = array( name => last_changed, alias => w, type => int, ); $query->orderBy(last_changed);Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 51Matthieu Guillermin - Mai 2012
  52. 52. Highwater Field • Avec le Highwater Field • Migrate ajoutera automatiquement une condition sur la requête source • Restriction des enregistrements sources à traiter • Ne fonctionne automatiquement que pour les sources : SQL, Oracle & MSSQLDrupal Camp Lyon - Optimisez vos imports de données avec Migrate 52Matthieu Guillermin - Mai 2012
  53. 53. preImport() • En cas de structure de base trop « tordue » • On peut être amené à faire une première passe pour charger des éléments en mémoire • Ces éléments seront réinjectés dans chaque contenu cible • Pour cela on peut utilise la méthode preImport()Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 53Matthieu Guillermin - Mai 2012
  54. 54. // Called once during the migration processpublic function preImport(){ parent::preImport(); $query = Database::getConnection(myDB) ->select(metas, m); $query->fields(m, array( id_doc, id_meta, valeur)); /* ... */ $result = $query->execute(); while ($row = $result->fetchObject()) { if ($row->id_meta == self::ID_META_TITRE) { $this->metas[$row->id_doc][titre] = $row->valeur; } if ($row->id_meta == self::ID_META_DATE) { $this->metas[$row->id_doc][date] = $row->valeur; } }}Drupal Camp Lyon - Optimisez vos imports de données avec MigrateMatthieu Guillermin - Mai 2012
  55. 55. // Called for each source rowpublic function prepareRow($row){ if (isset($this->metas[$row->id_doc][titre])) { $row->titre = $this->metas[$row->id_doc][titre]; } if (isset($this->metas[$row->id_doc][date])) { $row->titre = $this->metas[$row->id_doc][date]; } return parent::prepareRow($row);}Drupal Camp Lyon - Optimisez vos imports de données avec MigrateMatthieu Guillermin - Mai 2012
  56. 56. En cas de besoin • Attention à la mémoire • On peut être amené à ajuster la memory_limit • Séparation en 2 migrations • Utilisation de «system of record» • Seuls les champs «mappés» seront écrasés $this->systemOfRecord = Migration::DESTINATION;Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 56Matthieu Guillermin - Mai 2012
  57. 57. Des questions ?Drupal Camp Lyon - Optimisez vos imports de données avec Migrate 57Matthieu Guillermin - Mai 2012
  58. 58. Merci pour votre attention ! @mguillermin
  1. Gostou de algum slide específico?

    Recortar slides é uma maneira fácil de colecionar informações para acessar mais tarde.

×