v13.39.0. Pour l’envoi de base, consultez Envoi d’e-mails, et pour préparer le provider, consultez Développement de packages.
Distinguer le nom du Mailer du nom du Transport
Mail::mailer('receipts') lit mail.mailers.receipts et appelle la création correspondant à transport. MailManager::build() transmet ce Transport au Mailer de Laravel et, lors d’une résolution par nom classique, configure aussi l’expéditeur global, entre autres.
Les créations personnalisées sont choisies avant les Transports intégrés. N’écrasez pas smtp ou ses : utilisez un nom de Transport propre au package.
Construire le chemin d’enregistrement sans envoi externe
Commencez par vérifier la configuration et le chemin de résolution plutôt que l’API de destination. Danssrc/Transport/MemoryTransport.php du package Acme\Postbox, créez une classe qui hérite de l’ArrayTransport standard.
ArrayTransport::send() enregistre le SentMessage de Symfony dans une Collection interne. Vous pouvez le consulter avec messages() et effacer les enregistrements de cette instance avec flush(). Le label sert à vérifier que la configuration atteint bien la création.
Enregistrer sur un Manager chargé de manière différée
La documentation officielle appelleMail::extend() dans boot(). Dans un package, vous pouvez combiner cela avec les hooks après résolution de service afin d’éviter de créer inutilement le Manager dans les applications qui n’utilisent pas le mail.
Créez src/PostboxServiceProvider.php.
- Le
MailServiceProviderde Laravel est un provider différé qui enregistremail.manageren singleton. La façadeMailrésout également cette clé. callAfterResolving()attend la résolution ultérieure si le Manager n’est pas encore résolu, et applique immédiatement l’extension à un Manager déjà résolu. Il ne remplace pas les Mailers déjà générés.- Évitez de rendre simplement
DeferrableProvidervotre propre provider qui enregistre l’extension. Faites en sorte que le Transport personnalisé ne soit pas demandé avant l’enregistrement du hook. - Cet exemple enregistre le hook dans son propre
boot(). Il ne garantit pas un ordre de démarrage où un autre provider utiliserait le Mailer personnalisé plus tôt. Concevez l’ordre d’enregistrement et n’envoyez pas de mail dansregister().
Ne pas confondre les arguments d’extend avec ceux d’autres Managers
MailManager::createSymfonyTransport() appelle la création personnalisée avec un seul argument : le tableau de configuration. Ce ne sont pas les deux arguments application et configuration de Cache::extend(). La valeur de retour est un Transport implémentant TransportInterface, et non un Mailer de Laravel ni un résultat d’envoi.
De plus, MailManager::extend() se contente de conserver la closure et ne la relie pas au Manager. L’exemple utilise une closure statique et des arguments explicites afin de ne pas dépendre de l’interprétation de $this.
Si vous avez besoin d’un client pour une API externe, appliquez la configuration dans cette création et transmettez-le au Transport. N’envoyez rien au moment de l’enregistrement, et concevez le code de façon à ne pas mélanger configurations et destinations lorsque plusieurs Mailers partagent le même nom de Transport.
Ajouter les Mailers dans l’application cliente
Ajoutez les deux entrées suivantes au tableaumailers existant de config/mail.php. Le package ne doit pas écraser la valeur par défaut de l’utilisateur ni l’ensemble de mailers.
MAIL_MAILER le nom du Mailer receipts, et non le nom du Transport. En le désignant explicitement, vous pouvez l’utiliser sans modifier la valeur par défaut existante.
raw() est un exemple permettant de vérifier le chemin d’envoi synchrone sans préparer de vue. Lors d’un envoi classique avec un Mailable, le Mailer génère le contenu puis le transmet au même Transport.
Limites lors de la création d’un vrai Transport de livraison
Pour une implémentation qui livre vers un service externe, vous pouvez, conformément à la documentation officielle, hériter deSymfony\Component\Mailer\Transport\AbstractTransport et implémenter doSend(SentMessage $message): void et __toString(): string. La méthode d’enregistrement ci-dessus reste identique.
Mailer::sendSymfonyMessage() de Laravel appelle send() sur le Transport. Si un listener MessageSending annule l’envoi ou si le Transport retourne null, le send() de Laravel ne retourne pas non plus de résultat d’envoi. Ne vous fiez pas à la seule absence d’exception pour juger qu’une livraison a réussi. Même si un SentMessage est retourné, cela ne garantit pas que le mail a atteint la boîte du destinataire.
Distinguer changement de configuration et nouvelle résolution du Mailer
MailManager conserve le Mailer et son Transport par nom de Mailer. Un changement de configuration ou un nouvel enregistrement via extend() ne met pas à jour les objets déjà générés.
$same est le même Mailer que $before. Pour $after, obtenu après purge('receipts'), le Transport est créé à partir de la nouvelle configuration.
L’implémentation de
purge() examinée se contente de retirer la référence du tableau interne du Manager. L’ancien $before et les services dans lesquels ce Mailer a déjà été injecté ne sont pas remplacés. Ce n’est pas non plus une opération qui ferme explicitement les connexions externes.
Les Mailers parents comme failover sont aussi conservés séparément
failover et roundrobin lisent la configuration des noms de Mailers enfants, créent directement les Transports et les intègrent dans le Transport parent. Ils ne partagent pas les instances Mail::mailer() des enfants.
Par conséquent, même si vous modifiez la configuration d’un enfant et appelez purge('receipts'), le Transport interne d’un Mailer parent déjà résolu n’est pas mis à jour. Il faut aussi résoudre à nouveau le Mailer parent concerné. Même forgetMailers() ne peut pas remplacer les références au Mailer parent déjà conservées par le code appelant.
En production, intégrez la mise à jour du cache de configuration et le redémarrage des workers de longue durée dans votre procédure de déploiement habituelle. Ne considérez pas la nouvelle résolution comme une mise à jour globale de tous les processus, et consultez également le cycle de vie des services.
Tester le vrai chemin de création, pas seulement Mail::fake
Mail::fake() sert à enregistrer l’envoi et la mise en queue des Mailables. MailFake::mailer() ne résout pas de vrai Mailer : il enregistre le nom du Mailer et retourne le fake lui-même. raw() n’effectue pas non plus d’envoi. Les assertions d’envoi de Mailables peuvent donc réussir même sans Transport personnalisé enregistré.
Dans votre environnement de test de package, faites passer les appels par le vrai Manager et le vrai Transport, sans fake, comme ci-dessous. Le Transport de cet exemple n’envoie rien vers l’extérieur, ce qui permet de vérifier le chemin d’enregistrement sans risque de livraison réelle. Un provider auxiliaire est chargé en premier pour vérifier aussi le cas où le Manager est déjà résolu.
Sources primaires consultées
- Documentation officielle de Laravel : Transports personnalisés et tests des mails (au moment de la vérification)
- Documentation officielle de Laravel : Développement de packages
- MailManager : création, conservation des Mailers, purge et Transports composites
- MailServiceProvider : liaison du MailManager
- ServiceProvider : callAfterResolving
- Mailer : envoi vers le Transport et événements
- ArrayTransport : enregistrement des messages
- Façade Mail et MailFake
- MailFake : sélection du Mailer et enregistrement des Mailables
- Tests officiels de Laravel : vérification de l’envoi avec Mailer et ArrayTransport