Simuler un loader pendant l'exécution d'une tâche

J’ai parlé il y a deux semaines de la méthode que l’on pouvait utiliser pour créer des tâches dans symfony et la façon de les alimenter dynamiquement : créer des tâches avec symfony.

Il serait maintenant intéressant de pouvoir l’utiliser dans une application et également de pouvoir suivre l’évolution de notre tâche.

Pour lancer la tâche nous avions dans un premier penser la lancer grâce à la fonction proc_open() de PHP qui inclut une gestion des logs assez intéressante. Nos premiers tests en local se sont déroulés sans problèmes mais une fois en production il semble que la configuration du serveur ne permette pas d’utiliser cette commande.
On s’est donc rabattus sur la façon qui semble la plus utilisée :


chdir(sfConfig::get('sf_root_dir')); 
$myTask = new importExecuteTask(sfContext::getInstance()->getEventDispatcher(), new sfFormatter());
$myTask->run(array(), array());

Il est important de ne pas oublier le chdir() pour permettre l’exécution de notre tâche.

La première étape est donc terminée, on a pu lancer notre tâche depuis notre application, on va maintenant s’intéresser à suivre l’évolution de celle-ci.
Pour cela je vais créer un fichier que j’appelerai lock.txt dans mon répertoire d’import et je vais même l’utiliser comme compteur. Pour des raisons que nous verrons un peu plus bas, je vais créer une constante dans la classe de ma tâche qui correspondra au nom du fichier.


const LOCK_FILE = 'lock.txt';

Au début de ma boucle je donc avoir le code suivant :


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

Bon ok on a notre fichier mais à quoi nous sert-il ?
On peut dès à présent l’utiliser pour faire un redirect dans notre action pour empêcher de lancer plusieurs tâches en même temps :


$this->redirectIf(is_file('chemin/de/mon/dossier/import'.'/'.importExecuteTask::LOCK_FILE), 'import/loading');

Ici je redirige vers une page “loading” qui permettra de faire “patienter” notre utilisateur.

On vient de voir la première utilisation de notre fichier lock on va maitenant l’utiliser pour suivre l’évolution de la tâche. Pour cela je vais utiliser un compteur dans la boucle de ma tâche et l’écrire dans mon fichier. Il me faut tout d’abord obtenir le nombre maximum de mon fichier CSV.


            $handle = fopen($arguments['import_dir'].$file, 'r');
            $contenu_fichier = fread($handle, filesize($arguments['import_dir'].$file));
            $max = substr_count($contenu_fichier, "\n");
            fclose($handle);

Dans ma boucle je vais maintenant incrémenter mon compteur et mettre tout ça dans notre fichier lock :


$count = 0;
while (($data = fgetcsv($handle, 5000, ";")) !== false)
{
//- Début de mon code

file_put_contents($arguments['import_dir'].self::LOCK_FILE, $count.'/'.$max);

$count++;
}

Maintenant que mon fichier contient les informations nécessaires je vais les utiliser dans l’action :


        $filename = sfConfig::get('app_chemin_import').'/'.importExecuteTask::LOCK_FILE;
        if (is_file($filename))
        {
            $handle = fopen($filename , 'r');

            $moyenne = fread($handle, filesize($filename));

            $a = explode("/", $moyenne);

            $this->content = floor(($a[0] * 100) / $a[1]);

            fclose($handle);

        }

Voilà maintenant lorsque l’utilisateur sera redirigé vers notre page d’attente. On pourra lui afficher le pourcentage de l’évolution de la tâche. Petit problème si l’on veut suivre en direct cette évolution on va recharger la page indéfiniment ? Non pour cela on va utiliser un Helper Javascript : periodically_call_remote()

On va donc créer une action page progess qui ne sera appeler que par le javacript et qui sera inclut dans notre page “loading” par l’appel d’un component ce qui nous donnera :

// actions.class.php

    public function executeProgress()
    {

        $filename = sfConfig::get('app_chemin_import').'/lock.txt';
        if (is_file($filename))
        {
            $handle = fopen($filename , 'r');

            $moyenne = fread($handle, filesize($filename));

            $a = explode("/", $moyenne);

            $this->content = floor(($a[0] * 100) / $a[1]);

            fclose($handle);

        }
}

Notre template :


%

Une div qui contiendra une image gif que l’on fera grandir au fur et à mesure tel une barre de progression.
Et pour terminer notre page d’attente :




'1', 'update' => 'progress', 'url' => 'import/progress' )) ?>

Tout les secondes la div va donc être rafraîchie en appelant notre page progress ce qui simulera l’évolution de notre tâche.

Pour terminer, il ne faudra surtout pas oublier de supprimer le fichier lock à l’aide d’un petit unlink().

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