Symfony et les tâches

Pour un projet récent j’ai du implémenter un script qui devait insérer des utilisateurs dans la base d’un projet symfony. Afin de conserver l’avantage des objets et de leur validator je n’ai pas effectué un script à base de ‘INSERT INTO’ mais j’ai bel et bien créé ma propre tâche.
Je ne vais pas dans cette article vous parler de la façon de créer les tâches pour cela je vous renvoie vers le cookbook aucune excuse pour ne pas le lire, il a été traduit en français mais je vais vous parler de la façon dont je l’ai implémenté dans mon application.

Il s’agit donc d’un projet très simple listant des profils d’utilisateur pour lesquels on spécifie des dates d’inscription à une option.

Voici donc le schéma que l’on obtient :

Utilisateur:
actAs:
Timestampable:  ~
columns:
civilite:         { type: string(5)}
nom:              { type: string(150)}
prenom:           { type: string(150)}
email:            { type: string(255), unique: true}
profession:       { type: string(255)}
adresse:          { type: string(255)}
code_postal:      { type: string(10)}
ville:            { type: string(100)}
pays:             { type: string(5)}
date_naissance:   { type: date}
telephone:        { type: string(30)}
site_inscription: { type: string(255)}
option1:         { type: Date}
option2:         { type: Date}
option3:         { type: Date}
option4:         { type: Date}

On a vu plus compliqué comme schéma. Le but de ma tâche est donc de récupérer des fichiers CSV et de les insérer dans ma base de données. On va commencer par créer notre tâche :

./symfony generate:task import:execute

On se retrouve donc avec notre class importExecuteTask dans le dossier : /lib/task
Dans la partie configure je vais d’abord définir l’option file qui va définir le fichier CSV que je veux importer :

new sfCommandOption('file', null, sfCommandOption::PARAMETER_REQUIRED, 'Nom du fichier a traiter', '')

Maintenant passons à la partir qui va se charger de l’import. Dans un premier temps création de ma boucle

$handle = fopen($options['file'], 'r');
while (($data = fgetcsv($handle, 5000, ";")) !== false)
{
 /* Mon code d'insertion */
}

Je veux que les utilisateurs déjà existant soit juste mis à jour pour les autres ils seront créé.

$user = Doctrine::getTable('Utilisateur')->createQuery('c')
->andWhere('c.email = ? ', $data[0])->fetchOne();

if (!$user)
{
  $user =new Utilisateur();
}
$user->setEmail($data[0]);
$user->setPrenom($data[1]);
 /* Etc pour chaque champs */

Je veux maintenant préciser les options et pouvoir en ajouter dans le futur assez facilement. Pour cela je vais mettre dans mon fichier apps/monApplication/modules/monModule/config/app.yml la liste de mes options :

all:
  option: [option1, option2, option3, option4]

Je vais ensuite définir un attribut dans ma tâche, n’oublions pas que c’est une classe et que symfony ou pas il n’en reste pas moins qu’on peut lui définir des attributs, j’insiste sur ce point car je pense que c’est une des difficultés quand on débute avec symfony, ne pas oublier que l’on est dans du PHP.

private static $app_yml = array();

Maitenant que l’attribut est crée on va lui attribuer la valeur contenu dans le fichier app.yml graĉe à la classe sfYaml()

    protected function configure()
    {
       self::$app_yml = sfYaml::load(sfConfig::get('sf_root_dir').'/apps/backend/config/app.yml');
// suite du code ...

Mais à quoi cela peut-il servir ? Et bien par exemple à créer des options à la volée :

protected function configure()
    {
       // Début du code
        foreach (self::$app_yml['all']['option'] as $option)
        {
            $this->addOption($option,null,sfCommandOption::PARAMETER_OPTIONAL, 'Valeur pour '.$option, false);
        }

     // Suite du code

Je me retrouve maintenant avec 4 options supplémentaires que l’on peut vérifier en tapant la commande

./symfony help import:execute

Maintenant que nous avons créé ces options autant les utiliser mais il faut que cela soit dynamique pour cela on va utiliser les méthodes magiques dans ma boucle qui insère les utilisateurs :

// debut du code
 foreach (self::$app_yml['all']['option'] as $option)
{
  if ($options[option])
  {
    $user->__get($option, date('Y-m-d'));
  }
}
// fin du code

Maintenant lorsque que je vais appeler ma tâche je peux préciser les options auxquelles correspond mon fichier CSV.

./symfony import:execute --option1="true"

Pour terminer voilà un petit conseil pour le traitement de masse et la création multiple d’objet. En fin de boucle une fois que votre objet ne sera plus utiliser ajouté :

// debut du code
$user->free();
unset($user);
// fin de ma boucle

Ceci évitera certains problèmes de mémoire.

En conclusion :

Dans le futur si des options supplémentaires sont nécessaires je pourrais simplement rajouter une colonne dans ma base de données et compléter mon fichier app.YML. Il est bien évident que l’on pourrait faire cette gestion dans la base de données directement avec une table lié et une relation 1-1. Mais ici le but été de voir également la possibilité de charger un fichier YML depuis un autre module voir même d’ailleurs.

Voir l’étude de cas
Lire l’article
Voir le témoignage
Fermer