Massive import via Symfony2 command depuis fichier CSV

La DATA c’est la clef

Hé oui c’est fini le temps ou les applications comptaient une centaine d’utilisateurs et 10 fiches de contenus.

Non, maintenant avec 3 milliards d’internautes,  certains devraient venir sur votre appli et il faut être prêt à ingurgiter  un gros paquet d’informations rapidement et de manière automatisée.

Alors aujourd’hui je vous propose une solution pour ingurgiter du gros, du gras, bien poilu,  fichier CSV (testé avec un fichier 200K + lignes) depuis une simple commande Symfony2 et en utilisant Doctrine !

Faut pas faire ça

Alors soyons clair dés le début, Doctrine n’est pas fait pour de l’import de masse. Concrètement dans les solutions préconisées on a :

– Envoyer directement un fichier SQL

– Envoyer un fichier CSV via la commande mysqlimport.

Seulement voilà, votre problème concerne plusieurs tables, vous avez besoin de vérifier toutes les données pour les ajouter / mettre à jour / supprimer mais seulement à certaines conditions et vous avez un bon paquet de relations qu’il faut mettre à jour avec les clefs étrangères et toute la clique. Du coup bosser avec Doctrine rendrait les choses beaucoup moins complexe.

Le problème avec doctrine c’est l’utilisation de la mémoire qui explose et votre processus qui s’arrête brutalement vous laissant seul et désemparé. Il existe cependant des astuces pour palier à ce problème.

Un exemple simple

Voilà la tête du dit fichier CSV qu’il vous faut importer dans votre application.


email firstname lastname
iron@man.com Tony Stark

+ 200 k lignes !


 

Alors imaginons hein ! Oui c’est le CSV pour les enfants mais à vous de le complexifier à volonté, ça changera rien au fonctionnement.

Bon, passons aux choses sérieuses, voilà du code bien des familles on en parle juste après :

Acme\AcmeBundle\Command\ImportCommand.php

J’ai essayé de commenter un peu partout, c’est important les commentaires, bon là il y en a carrément trop mais c’est pour être  le plus clair possible. Alors dans l’ordre on a fait quoi ?

  • On configure notre commande pour qu’au app/console elle apparaisse proprement
  • Ensuite on utilise notre service qui transforme notre CSV en tableau et le rendre exploitable facilement(voir le code plus bas)
  • Tout de suite derrière on prépare doctrine et on lui dit de ne pas mettre en log les requêtes SQL parce qu’on veut économiser notre mémoire
  • On compte combien d’utilisateurs vont être importés et on dit qu’on va les importer tous les 20 occurrences (libre à vous de changer par rapport à votre besoin)
  • On initialise ProgressBar. Vous connaissez pas ProgressBar ? Checkez donc, ça existe depuis Symfony 2.5 et ça envoie du steak.
  • Après on tourne sur notre gros tableau et on fait ce qu’on veut avec les données et surtout avec doctrine. Mettez à jour des relations, supprimer en d’autres, vous êtes libres !
  • Enfin à la fin d’un paquet de 20 occurrences on enregistre le tout et on passe au paquet de 20 suivants.

Le code correspondant au service est juste ici :

 Acme\AcmeBundle\Services\ConvertCsvToArray.php

 app\config\config.yml

Enfin dans votre console, dans le dossier de votre projet Symfony taper la commande pour lancer l’import :

Et voilà !

0wAZ8JJ

 

 Épilogue

J’ai testé ce script avec un CSV pour les grands et avec de fortes contraintes ( suppression, mise à jour de relations et autres ) et le résultat à été plus que satisfaisant.
Encore une fois cette solution n’est pas la bonne pour des problèmes de très très grosse ampleur. On atteint ici la limite de doctrine, un superbe outil, mais qui n’est pas destiné à tout faire. Pensez plus aux solutions MySQL évoquées plus haut pour les cas plus extrêmes !

Sinon je ne saurais conseiller de lancer ce script la nuit via un crontab et de s’envoyer le résultat par mail, pour l’admirer le lendemain plein de fierté.

Et sinon tu peut me follow sur twitter, m'insulter à cette e-mail ou le faire directement dans les commentaires juste en dessous.