Yii : Une barre de progression pour les processus longs

Dans ce tutorial, nous allons voir comment afficher une barre de progression à vos utilisateurs pendant un traitement long coté serveur (mise à jour de plusieurs milliers d’enregistrements, traitement par lot de fichiers…). C’est toujours plus sympathique qu’une page blanche et cela permet d’indiquer l’avancement des tâches en cours d’exécution…

Pour ce faire, nous allons utiliser pas moins de trois technos complémentaires :

  • Yii Framework 1.1, comme base de l’application (cela fonctionne parfaitement avec Yii 1.0 avec un minimum d’adaptation)
  • jQuery et jQuery UI pour gérer une belle barre de progression graphique
  • Zend Framework qui possède une classe bien pratique et particulièrement adaptée à ce genre de situations

1 – Préparation

Vous pouvez partir de n’importe quel projet Yii. Je passe les détails et vous laisse voir la documentation officielle sur le sujet.

Ensuite, vous allez avoir besoin de Zend Framework à télécharger ici. Depuis l’archive téléchargée, copiez l’ensemble du dossier library/Zend dans un dossier protected/vendors/Zend de votre projet Yii.

2 – Le contrôleur Yii

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
class ProgressbarController extends Controller
{
	private function loadZendFramework() {
	    Yii::import('application.vendors.*');
	    require_once Yii::getPathOfAlias('application.vendors.Zend.Loader').DIRECTORY_SEPARATOR.'Autoloader.php';
	    spl_autoload_unregister(array('YiiBase', 'autoload'));
	    spl_autoload_register(array('Zend_Loader_Autoloader', 'autoload'));
	    spl_autoload_register(array('YiiBase', 'autoload'));
	}
 
	public function actionIndex()
	{
		$this->render('index');
	}
 
	public function actionProgressStart()
	{
		set_time_limit(-1);
		ini_set('memory_limit',"-1");
		$this->loadZendFramework();
		$adapter = new Zend_ProgressBar_Adapter_JsPush(array('updateMethodName'=>'updateProgressbar', 'finishMethodName'=>'finishProgressbar'));
		$count=60;
		$progressBar = new Zend_ProgressBar($adapter, 0, $count);
		$cnt = 0;
		for($i=1;$i>=$count;$i++) {
			$progressBar->update($i,"Item #{$i}");
			sleep(1);
		}
		$progressBar->finish();
		Yii::app()->end();
	}
}

Explications :

Le contrôleur se découpe en 3 méthodes :

  • loadZendFramework : Cette méthode privée permet de préparer Yii pour l’utilisation de Zend Framework. Elle est appelée au début de la méthode actionProgressStart.
  • actionIndex : Affiche juste la vue initiale permettant de présenter la barre de progression et le lien déclenchant le processus de traitement long.
  • actionProgressStart : C’est cette méthode qui est chargée d’envoyer les informations de progression à notre vue ‘index’ (voir la description des vues plus loin). On commence par lever les éventuelles limites de temps d’exécution et de mémoire consommée (optionnel). Ensuite, on créé un objet Zend_ProgressBar_Adapter_JsPush permettant de paramétrer le type de barre de progression désirée, en l’occurrence, une barre de type JsPush. On lui passe en paramètre un tableau de propriétés updateMethodName et finishMethodName dans lesquels on va donner les noms des fonctions javascript déclenchées lors de la mise à jour des étapes du processus et en fin de traitement. Ensuite, on créé un objet Zend_ProgressBar, l’objet barre de progression proprement dit, qui prend comme arguments l’objet Zend_ProgressBar_Adapter_JsPush précédemment créé, l’état initial d’avancement de la barre (en général, 0) et le nombre maximum d’étapes nécessaires. Ce dernier paramètre peut parfaitement être le résultat d’une requête en base de données (nombre d’enregistrements à traiter par exemple). Ensuite, à chaque tour de boucle de notre traitement, on appel la méthode update de la barre de progression en passant comme paramètres, le numéro de l’étape courante de traitement et un texte optionnel décrivant la progression. A la fin du processus, on appel la méthode finish pour signifier que le traitement est terminé. Concrètement, afin d’informer la vue de la progression du processus, la classe Zend_ProgressBar va exécuter un code javascript contenu dans une iFrame et qui va appeler des fonctions situées dans la fenêtre parente (parent.nomdelafonction(parametre)). Nous allons voir comment paramètrer notre vue index afin que ce mécanisme fonctionne correctement.

2 – La vue Yii

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
34
35
36
37
38
39
40
41
42
43
44
$this->breadcrumbs=array(
	'Progressbar',
);
 
Yii::app()->clientScript->registerScript('progressbar', "
updateProgressbar = function(data) {
	$('#progressBar').progressbar('option', 'value', data.percent);
	$('#progressBarInfos').text('Processing '+data.current+' on '+data.max+' : '+data.text+' (' + Math.round(data.percent) + '%)...');
};
 
finishProgressbar = function() {
	$('#progressBarContainer').hide();
	$('#progressBarInfos').text('Synchronisation terminée.');
};
 
startProgress = function(url) {
	$(document.body).append('<iframe id=\"progressTarget\"></iframe>');
    $('iframe#progressTarget').attr('src', url);
};
");
 
Yii::app()->clientScript->registerCss('iframe','
iframe {
	position: absolute;
	left: -100px;
	top: -100px;
	width: 10px;
	height: 10px;
	overflow: hidden;
}
');
?>
<h1><?php echo $this->id . '/' . $this->action->id; ?></h1>
 
<?php echo CHtml::link('Start progress bar',array("progressStart"),array('onClick'=>'startProgress(this.href);return false;')); ?>
<div id="progressBarInfos">&nbsp;</div>
<?php
$this->widget('zii.widgets.jui.CJuiProgressBar', array(
	'id'=>'progressBar',
	'value'=>0,
	'htmlOptions'=>array(
        'style'=>'width:100%;height:20px;'
    ),
));

Explications :

Tout d’abord, on ajoute les fonctions javascript nécessaire à gestion de la mise à jour de la barre de progression en appelant la méthode registerScript de l’objet clientScript de l’application Yii.

Le fonctions javascripts sont :

Page 1 of 2 | Next page