Aller au contenu principal
Retour au blog

Symfony Notifier en 2026 : les bridges Prelude et WhatsApp (Meta)

Flavien Métivier26 septembre 20269 min

Le composant Notifier est resté longtemps le parent pauvre de l'écosystème Symfony — un aiguilleur pratique entre Chatter et Texter, utile pour brancher Slack en développement et un fournisseur SMS en production, mais rarement au centre de l'actualité du framework. Les deux dernières semaines de juillet 2026 ont changé la donne : A Week of Symfony #1019 documente un correctif sur le bridge Prelude, disponible depuis Symfony 8.1, et #1020 référence une pull request ouverte qui ajoute un bridge WhatsApp connecté directement à l'API Cloud de Meta — un chantier encore en revue pour la branche 8.2. L'occasion de faire un point complet sur Notifier : son fonctionnement, ce que ces deux bridges changent concrètement, et comment les brancher à Messenger pour des notifications transactionnelles multi-canal robustes.

Le composant Notifier : une seule abstraction pour tous les canaux transactionnels

Notifier reprend un principe hérité de Mailer : un Transport configuré via un DSN, une interface d'envoi unique, et un mécanisme de repli déclaratif. Contrairement à Mailer qui ne gère que l'email, Notifier orchestre des canaux hétérogènes derrière deux abstractions — Texter pour le SMS, Chatter pour la messagerie instantanée — pilotées par NotifierInterface. Celle-ci accepte une Notification, une liste de canaux, et délègue l'envoi à un Recipient porteur d'un email et d'un numéro de téléphone. Chaque fournisseur enregistre un schéma d'URI dans le composant — twilio://, slack://, expo:// — et c'est cette registry qui permet à Symfony de savoir quel bridge charger, et surtout de produire un message d'erreur exploitable quand le package composer correspondant n'est pas installé.

  • SMS (Texter) — plus de 60 fournisseurs référencés (Twilio, Vonage, AWS SNS, Infobip, Brevo…), chacun avec son DSN et ses options propres
  • Chat (Chatter) — Slack, Telegram, Discord, Microsoft Teams, Matrix, Mattermost, Google Chat, et désormais WhatsApp en connexion directe
  • Push — Expo, OneSignal, Pushover, Firebase, pour les notifications mobiles natives
  • Navigateur et email — canal browser pour les flash messages, canal email délégué au composant Mailer

Le channel_policy ajoute une couche d'arbitrage : une notification porte une importance (urgent, high, medium, low), et la configuration décide quels canaux s'activent pour chaque niveau. C'est ce mécanisme qui rend crédible l'idée de notifications transactionnelles multi-canal — email par défaut, SMS ou WhatsApp en escalade si l'importance grimpe — sans dupliquer la logique métier dans chaque contrôleur. Les deux bridges arrivés en juillet 2026 s'inscrivent dans ce cadre existant : ils ajoutent des canaux, pas un nouveau paradigme.

Prelude : SMS et WhatsApp unifiés, disponible depuis Symfony 8.1

Prelude est un fournisseur spécialisé dans l'OTP, le SMS transactionnel et la messagerie WhatsApp, avec un routage automatique entre les deux canaux selon la fiabilité de livraison constatée pour chaque numéro. Le bridge symfony/prelude-notifier a été fusionné dans la branche 8.1 en tant que nouvelle fonctionnalité. Ce que documente A Week of Symfony #1019, c'est un correctif ultérieur — une pull request également mergée dans 8.1 — qui répare un oubli : le schéma prelude n'était pas déclaré dans UnsupportedSchemeException. Sans cette déclaration, un développeur oubliant composer require symfony/prelude-notifier obtenait une erreur générique au lieu du message habituel indiquant le package exact à installer. Un détail, mais qui révèle un point d'architecture utile à connaître : chaque bridge Notifier doit s'enregistrer à plusieurs endroits — la liste des factories dans Transport.php, la table schéma→package dans UnsupportedSchemeException.php, et la définition de service dans la configuration du FrameworkBundle. Un bridge qui n'apparaît que dans Transport.php fonctionne très bien à l'exécution, mais dégrade l'expérience développeur au premier oubli de dépendance — exactement ce que corrige ce patch.

# Installation du bridge Prelude
composer require symfony/prelude-notifier

# .env — clé API obligatoire, sender ID optionnel
PRELUDE_DSN=prelude://API_KEY@default?sender=SENDER_ID

Côté envoi, l'API Prelude impose un template_id pré-approuvé : impossible d'envoyer un SMS en texte libre avec ce fournisseur. La classe PreludeOptions reflète cette contrainte — templateId() est obligatoire, tandis que les compléments restent optionnels : variables() pour interpoler le contenu du template, locale() au format BCP-47, expiresAt() et scheduleAt() en RFC3339, callbackUrl() pour recevoir les événements de livraison, et surtout preferredChannel() qui bascule l'envoi entre 'sms' et 'whatsapp' selon la préférence connue du destinataire. Le message manipulé reste un SmsMessage classique : c'est PreludeOptions, pas le type de message, qui pilote le comportement réel.

<?php
use Symfony\Component\Notifier\Bridge\Prelude\PreludeOptions;
use Symfony\Component\Notifier\Message\SmsMessage;

$options = (new PreludeOptions())
    ->templateId('template_01k8xxxxxxxxxxxxx')
    ->variables([
        'order_id' => '48213',
        'amount' => '129,90 €',
    ])
    ->locale('fr-FR')
    ->callbackUrl('https://app.example.com/webhooks/prelude')
    ->preferredChannel('whatsapp');

$message = (new SmsMessage('+33612345678', 'Confirmation de commande'))
    ->options($options);

$notifier->send($message);

Le paramètre callbackUrl() mérite un traitement aussi soigné qu'un webhook entrant classique : signature à vérifier, idempotence sur l'identifiant d'événement, et réponse HTTP rapide pour ne pas bloquer le fournisseur. Si tu n'as pas encore d'endpoint standardisé pour ce genre d'intégration, le composant Webhook de Symfony couvre exactement ce besoin — la même mécanique de vérification de signature s'applique, que la source soit Stripe, GitHub ou un fournisseur Notifier comme Prelude.

WhatsApp (ex-MetaWhatsApp) : connexion directe à l'API Cloud de Meta, en revue pour 8.2

Besoin d'un expert Symfony ?

Réserver un appel →

Le second chantier, référencé par A Week of Symfony #1020, est plus jeune : une pull request intitulée à l'origine « Add the MetaWhatsApp bridge », toujours ouverte au moment d'écrire ces lignes et ciblant la branche 8.2 — donc pas encore mergée, à ne pas installer en production sans relire l'état exact de la revue. Fait notable, le nom du bridge a été ajusté en cours de discussion : la classe et le package composer retenus sont WhatsApp et symfony/whats-app-notifier, même si le titre de la pull request garde encore la mention MetaWhatsApp. Ce qui distingue ce bridge des intégrations WhatsApp déjà disponibles chez Twilio ou Vonage : il ne passe par aucun Business Solution Provider tiers et dialogue en direct avec l'API Graph de Meta, sur l'endpoint POST /{phone_number_id}/messages.

# .env — jeton d'accès permanent (system user token recommandé)
WHATSAPP_DSN=whatsapp://TOKEN@default?phone_number_id=PHONE_NUMBER_ID&api_version=v21.0

Le fonctionnement du canal reprend les règles imposées par Meta pour la Cloud API : hors de la fenêtre de service client de 24 heures qui suit un message entrant du destinataire, seuls des templates pré-approuvés — ce que Meta appelle des HSM, Highly Structured Messages — peuvent être envoyés à l'initiative de l'entreprise. Dans cette fenêtre de 24 heures, en revanche, un message texte libre est autorisé. La classe WhatsAppOptions matérialise cette distinction : recipientPhoneNumber() cible le destinataire, et template() — nom du template, code langue, paramètres positionnels du corps — bascule l'envoi en mode structuré. L'absence d'appel à template() envoie simplement le sujet du ChatMessage comme texte libre.

<?php
use Symfony\Component\Notifier\Bridge\WhatsApp\WhatsAppOptions;
use Symfony\Component\Notifier\Message\ChatMessage;

// Message avec template pré-approuvé (hors fenêtre de 24h)
$options = (new WhatsAppOptions())
    ->recipientPhoneNumber('33612345678')
    ->template('confirmation_commande', 'fr', ['48213', '129,90 €']);

$chatMessage = new ChatMessage('Confirmation de commande', $options);
$chatter->send($chatMessage);

// Message libre, uniquement dans la fenêtre de 24h suivant un message entrant
$freeform = (new WhatsAppOptions())->recipientPhoneNumber('33612345678');
$chatter->send(new ChatMessage('Votre colis est en cours de livraison.', $freeform));

Un point de vigilance mérite d'être répété : ce bridge n'est pas encore fusionné dans le tronc commun de Symfony au moment de la rédaction. Le suivre demande de surveiller l'avancement de la revue plutôt que de considérer le DSN ci-dessus comme figé — le nom du schéma, les options de la factory ou le nom du package pourraient encore bouger avant merge. C'est une réalité fréquente avec les bridges Notifier communautaires : ils naissent souvent d'une contribution externe, passent par plusieurs itérations de revue avant merge, puis reçoivent leurs premiers correctifs de robustesse — exactement le chemin qu'a suivi Prelude quelques mois plus tôt.

Notifications transactionnelles multi-canal avec Messenger

Le cas d'usage qui justifie ces deux bridges est presque toujours le même : une confirmation de commande envoyée par email, avec un canal de secours plus intrusif — SMS ou WhatsApp — pour les événements réellement critiques comme une rupture de stock après paiement ou un incident de livraison. Le channel_policy couplé à l'importance de la notification permet d'exprimer cette escalade sans introduire de conditionnelles dans le code métier, et la configuration des transports supporte nativement le repli déclaratif entre deux DSN.

# config/packages/notifier.yaml
framework:
    notifier:
        texter_transports:
            prelude: '%env(PRELUDE_DSN)%'
        chatter_transports:
            whatsapp: '%env(WHATSAPP_DSN)%'
            # secours : si l'API Graph de Meta répond en erreur, repli sur Slack pour l'équipe support
            alerte: '%env(WHATSAPP_DSN)% || %env(SLACK_DSN)%'
        channel_policy:
            urgent: ['sms', 'chat/whatsapp', 'email']
            high: ['chat/whatsapp', 'email']
            medium: ['email']
            low: ['browser']

Quand Messenger est installé, Notifier route chaque canal indépendamment sur le bus par défaut : si le SMS échoue et qu'un transport d'échec est configuré, seul ce canal est rejoué — l'email et le WhatsApp déjà partis ne sont pas renvoyés en double. C'est le comportement recommandé pour ce genre de flux, et il évite l'écueil classique du retry naïf qui spamme un client sur trois canaux à la fois pour un seul incident transitoire. Pour affiner encore le déclenchement — par exemple ne router vers WhatsApp que si le destinataire a un numéro renseigné et a déjà interagi avec l'entreprise dans les 24 dernières heures — l'attribut #[AsMessage] et son option when: permettent d'exprimer cette condition côté handler plutôt que de la disperser dans les contrôleurs qui déclenchent la notification.

Reste la question de la supervision : un worker qui traite ces envois en tâche de fond doit être surveillé comme n'importe quel autre worker Messenger, avec des retries bornés et une alerte si la file stagne — un consumer WhatsApp bloqué pendant une heure un vendredi soir, c'est autant de confirmations de commande jamais livrées. Les bonnes pratiques de supervision des workers Messenger en production s'appliquent telles quelles ici : rien de spécifique à Notifier, mais rien à négliger non plus.

Prelude et WhatsApp illustrent bien où en est le composant Notifier en 2026 : une abstraction stable, un mécanisme d'enregistrement des bridges suffisamment mûr pour produire de bons messages d'erreur, et un flux régulier de nouveaux fournisseurs porté par la communauté plutôt que par l'équipe cœur. C'est aussi le genre de sujet où la friction se déplace vite du code vers l'architecture — quel canal pour quelle importance, quel transport de secours, quelle stratégie de retry côté Messenger — des décisions qui gagnent à être posées avant la première mise en production plutôt qu'après le premier incident client. Si ce genre d'arbitrage sur une stack Symfony te préoccupe en ce moment, c'est précisément le type de sujet qu'on traite lors d'un accompagnement technique ponctuel ou d'un audit d'architecture.

Cet article vous a plu ? Partagez-le !

Besoin d'un expert Symfony ?

20 ans d'expérience sur l'écosystème PHP/Symfony.