[Ajax + PHP] Modfication/affichage d'un message en temps réel lié aux traitements d'une page Ajax

Petit nouveau ! | 5 Messages

31 janv. 2022, 16:50

Bonjour,

Je poste ce message parce que j'ai beau chercher un peu partout sur le net, je ne trouve pas de réponse à cette question.

J'ai un gros traitement qui est lancé à partir d'un appel ajax (via jQuery) et je voudrais afficher en temps réel des messages d'avancement (ou mette à jour un message d'une fenêtre modale par exemple) au fur et à mesure que le traitement avance.

Je sais qu'il est possible de forcer le navigateur a affiché quelque chose même si la page n'a pas fini de charger avec les fonctions flush() ou ob_flush().

J'ai d'ailleurs ce bout de code qui fonctionne bien lorsque je l'appelle avec mon navigateur mais pas lorsque je l’appelle en ajax :

Code : Tout sélectionner

for ($i = 0; $i < 10; $i++) { ob_start(); ob_implicit_flush(true); echo 'Traitement '.$i.' : terminé<br>'; ob_end_flush(); ob_flush(); sleep(10); }
J'appelle ce code avec un appel de ce genre :

Code : Tout sélectionner

$.ajax({ type: 'POST', url: 'test.php', cache: true, error: function (xhr, ajaxOptions, thrownError) { alert(xhr.responseText); alert(thrownError); }, xhr: function (x) { console.log(x); var xhr = new window.XMLHttpRequest(); //Download progress xhr.addEventListener("progress", function (evt) { console.log(evt); $('#message').append(evt.target.response); }, false); return xhr; }, beforeSend: function () { console.log('beforeSend'); }, complete: function () { console.log('complete'); }, success: function (json) { console.log('fin'); } });
J'ai analysé le retour sur la console et en fait, je me rends compte que le message est concaténé :

1. Après le premier traitement j'ai : "Traitement 1 : terminé<br>".
2. Après le deuxième traitement j'ai : "Traitement 1 : terminé<br>Traitement 2 : terminé<br>". au lieu d'avoir que "Traitement 2 : terminé<br>".
3. Après le troisième traitement j'ai : "Traitement 1 : terminé<br>Traitement 2 : terminé<br>Traitement 3 : terminé<br>". au lieu d'avoir que "Traitement 3 : terminé<br>".

Avez-vous une idée de comment faire ?

Je vous remercie par avance pour votre aide.

Eléphant du PHP | 385 Messages

31 janv. 2022, 18:08

Salut,

Cela ne peut pas fonctionner en effet le php est appelé et retourne au navigateur du javascript html css, la page que voit l'utilisateur une page avec le résultat rien de dynamique.
Tu peux utiliser setInterval https://www.w3schools.com/jsref/met_win_setinterval.asp avec un call ajax ou tu récupère le statut. Il faut donc que la valeur de ton echo soit dans une session.

Je récapitule
- Créer un autre fichier php progress.p^hp par exemple
- mettre ta donnée en session sinon le script php ne pourra pas y accéder
// je démarre une session
session_start();
// besoind e savoir quand le traitement est terminé
$_SESSION['status'] = 'processing';
for ($i = 0; $i < 10; $i++)
{
// je store le message
$_SESSION['progress'][] =  'Traitement '.$i.' : terminé<br>';
sleep(10);
}
// terminé
$_SESSION['status'] = 'finish';
- un autre script qui utilise c'est sessions
<?php
// si j'ai bien ma data
if(false === empty($_SESSION['progress']) {
    // j'utilise json mais ça pourrait être un echo
     echo json_encode(['progress' => $_SESSION['progress'], 'status' => $_SESSION['status']);
     // si terminé
    if(false === empty($_SESSION['status']) && $_SESSION['progress'] === 'finish') {
       // netoyage
      unset($_SESSION['status'] );
      unset($_SESSION['progress'] );
   }
}
- côté javascript reste à call ce script avec setinterval et stoper le setinterval si rien

globalement c'est l'idée qui me viens à tester :)

Ou alors tu gardes ton fonctionnement actuel mais tu utilises les sessions

Petit nouveau ! | 5 Messages

01 févr. 2022, 11:13

Bonjour kevin254kl,

Tout d'abord merci d'avoir pris le temps de me répondre.

J'aime beaucoup ton idée, elle est propre :D Il ne me reste plus qu'à l'essayer.

Depuis quelques jours, je commençais à imaginer une solution du style renvoyer un chiffre avec un séparateur (exemple : "1#") avec toujours mon système de flush() pour forcer l'envoi et de l'autre côté je parse le retour avec le "#". Puis, en fonction du dernier chiffre, j'affiche le message correspondant .

Mais ça demande de préparer tous les messages côté javascript en amont et leur affecter un numéro et la même chose côté PHP au niveau de la page de traitement. Et je trouvais mon système pas "générique". Je voulais éviter...

Petit nouveau ! | 5 Messages

02 févr. 2022, 18:21

Bonjour kevin254kl,

J'ai essayé mais n'y suis pas arrivé.

On dirait que je ne peux pas lancer 2 appels ajax simultanés.

L'appel à la page "progress.php" est pris en compte quand la page "test.php" a fini son traitement.

Aurais-tu une idée ?

Eléphant du PHP | 385 Messages

03 févr. 2022, 12:56

Tu as bien utilisé le setInterval ?
https://www.educba.com/jquery-ajax-async/ tu dois faire de l'asynchrone pour ne pas attendre la réponse pour ton ajax
Tu fais un setinterval et à l'intérieur l'autre call ajax avec traitement de la réponse quand tu es en finish tu fais un clear interval

Petit nouveau ! | 5 Messages

03 févr. 2022, 13:09

Oui, j'ai bien utilisé le setInterval.

Je vais revoir mon code à nouveau et te le poster si je n'y arrive toujours pas.

Petit nouveau ! | 5 Messages

03 févr. 2022, 13:37

Page "test.php" :

Code : Tout sélectionner

session_start(); $_SESSION['status'] = 'processing'; for ($i = 0; $i < 3; $i++) { $_SESSION['progress'] = 'Traitement '.$i.' : terminé<br>'; sleep(10); } $_SESSION['status'] = 'finish';

Page "progresss.php" :

Code : Tout sélectionner

session_start(); if(isset($_SESSION['progress']) && isset($_SESSION['status'])) { $reponse = array('progress' => $_SESSION['progress'], 'status' => $_SESSION['status']); echo json_encode($reponse); if(isset($_SESSION['status']) && $_SESSION['status'] === 'finish') { unset($_SESSION['progress']); unset($_SESSION['status']); } }

Appels :

Code : Tout sélectionner

$.ajax({ type: 'POST', url: 'test.php', async: true, success: function (output){ alert('fin'); } }); var x = setInterval(function() { $.ajax({ type: 'GET', url: 'progress.php', dataType: 'JSON', async: true, success: function (datas){ if(datas.status=='finish') { clearInterval(x); } else { $('#message').html(datas.progress); } }); }, 10000);