Aller au contenu principal

Souscrire en temps réel

Créer la page client qui reçoit les mises à jour Mercure et met à jour l'interface automatiquement sans rechargement

Notions théoriques

La fonction Twig mercure()

Le bundle Mercure ajoute une fonction Twig mercure() qui génère l'URL complète d'abonnement au Hub pour un topic donné. Elle intègre automatiquement les paramètres nécessaires (topic, JWT si besoin).

{% set url = mercure('https://symfolivraison.local/livraison/' ~ livraison.id) %}

La fonction génère une URL du type :

https://localhost/.well-known/mercure?topic=https%3A%2F%2Fsymfolivraison.local%2Flivraison%2F1

Cette URL est passée à EventSource pour ouvrir la connexion SSE.

attention

Dans un contexte JavaScript inline (attribut <script>), utilisez toujours le filtre |escape('js') pour protéger contre les injections XSS :

"{{ mercure(...)|escape('js') }}"

L'API EventSource JavaScript

EventSource est une API JavaScript native qui établit et maintient une connexion SSE avec le Hub Mercure. Elle est supportée par tous les navigateurs modernes sans bibliothèque externe.

const eventSource = new EventSource(url);

eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
// data = { id: 1, statut: "Expédiée" }
};
ÉvénementDéclenchement
onopenConnexion établie avec le Hub
onmessageUne mise à jour a été reçue
onerrorErreur de connexion (reconnexion automatique en cours)
astuce

EventSource se reconnecte automatiquement si la connexion est interrompue — c'est un comportement natif du protocole SSE. Contrairement aux WebSockets, aucun code de gestion de reconnexion n'est nécessaire.

Mise à jour du DOM en JavaScript

Quand une mise à jour est reçue, il suffit de modifier le DOM pour refléter le nouveau statut :

eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
document.getElementById('statut').textContent = data.statut;
};
info

JSON.parse(event.data) désérialise la chaîne JSON envoyée par Symfony en objet JavaScript. Les clés correspondent aux clés du tableau PHP passé à json_encode() lors de la publication.

Exemple de mise en application

Le contrôleur de suivi (page client)

<?php
// src/Controller/LivraisonController.php
namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Attribute\Route;

class LivraisonController extends AbstractController
{
#[Route('/livraison/{id}', name: 'livraison_suivi')]
public function suivi(int $id): Response
{
// En pratique, charger depuis Doctrine ; ici simulé pour simplifier
$livraison = ['id' => $id, 'client' => 'Alice', 'statut' => 'En préparation'];

return $this->render('livraison/suivi.html.twig', [
'livraison' => $livraison,
]);
}
}

Le template Twig avec souscription Mercure

{# templates/livraison/suivi.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}Suivi de votre livraison #{{ livraison.id }}{% endblock %}

{% block body %}
<h1>Suivi de votre commande #{{ livraison.id }}</h1>

<div id="statut-container">
<p>Bonjour {{ livraison.client }}, statut actuel :</p>
<p id="statut" style="font-size: 1.5rem; font-weight: bold;">
{{ livraison.statut }}
</p>
</div>

<script>
const mercureUrl = "{{ mercure('https://symfolivraison.local/livraison/' ~ livraison.id)|escape('js') }}";
const eventSource = new EventSource(mercureUrl);

eventSource.onmessage = function(event) {
const data = JSON.parse(event.data);
document.getElementById('statut').textContent = data.statut;
};

eventSource.onerror = function() {
console.error('Connexion Mercure perdue, reconnexion automatique...');
};
</script>
{% endblock %}

Test du flux complet

Ouvrez deux onglets dans votre navigateur :

  1. Onglet 1https://localhost/livraison/1 (page de suivi client)
  2. Onglet 2 — Appelez l'URL admin pour changer le statut : https://localhost/admin/livraison/1/statut/Expédiée

Le statut dans l'onglet 1 se met à jour instantanément sans rechargement de page.

Test de mémorisation/compréhension


Quelle API JavaScript native est utilisée pour s'abonner aux événements SSE de Mercure ?


Que fait la fonction Twig mercure() ?


Quel événement JavaScript est déclenché à chaque réception d'une mise à jour Mercure ?


Que se passe-t-il si la connexion SSE est interrompue avec EventSource ?


Sous quel format arrivent les données dans event.data lors d'un onmessage ?


Pourquoi faut-il utiliser le filtre |escape('js') sur l'URL générée par mercure() dans un <script> ?


TP pour réfléchir et résoudre des problèmes

Étape 1 — Créer le contrôleur de suivi client

Dans src/Controller/, créez le fichier LivraisonController.php avec la route /livraison/{id} :

Bonne pratique - Séparer le contrôleur admin du contrôleur client

Maintenez deux contrôleurs distincts : Admin\LivraisonController pour les routes d'administration (protégées), et LivraisonController pour les routes publiques. Cela simplifie l'application des règles de sécurité dans security.yaml.

Étape 2 — Créer le template Twig et générer l'URL Mercure

Dans templates/livraison/, créez suivi.html.twig. Commencez par la souscription au Hub :

Bonne pratique - Toujours échapper les variables Twig dans JavaScript

Utilisez systématiquement |escape('js') quand vous insérez une variable Twig dans un contexte JavaScript. Sans cet échappement, un topic contenant des guillemets ou des backslashes peut briser le script ou créer une faille XSS.

Étape 3 — Mettre à jour l'affichage à la réception d'une mise à jour

Bonne pratique - Préférer textContent à innerHTML

Utilisez textContent plutôt qu'innerHTML pour insérer du texte reçu d'un serveur. innerHTML interprète le HTML et peut introduire une faille XSS si les données ne sont pas assainies. textContent insère toujours du texte brut.

Étape 4 — Tester le flux complet

Ouvrez deux onglets dans votre navigateur et testez :

  1. Onglet 1https://localhost/livraison/1
  2. Onglet 2https://localhost/admin/livraison/1/statut/Expédiée

Vérifiez que le statut dans l'onglet 1 change sans rechargement après l'appel de l'onglet 2.

📌 Une solution