Archives Wild Code School
Symfony

21 - Symfony : Envoi d’e-mail

Quête 24 sur 35 · 1 image archivée

Objectifs

  • ✅ Installer et configurer l'envoi d'email dans Symfony
  • ✅ Envoyer un mail de notification à l'administrateur.
  • ✅ Optimiser l'envoi des mails dans ton code.
  • ✅ Styliser tes mails

Pré-requis

​ Avoir terminé la quête

Quêtes liées
title-book
Introduction

Les pages d'une application Web représentent la majeure partie de notre interface utilisateur.

Il ne faut cependant pas négliger une autre fonctionnalité très importante de nos interactions avec l'utilisateur : l'envoi d’emails.

Les courriels envoyés par une application sont fréquents : newsletters, confirmations d'inscription, nouveaux messages, etc.

Dans cette quête, tu vas pouvoir créer un service de notifications afin d'informer tes utilisateurs qu'une nouvelle série vient d'être publiée. En effet, ce serait dommage de passer à côté du remake de Manimal !

Sommaire

Installation

Pour commencer, installe le composant Mailer recommandé par Symfony, (ce dernier n'est pas installé par défaut), via la commande :

bash
composer require symfony/mailer

Configurer l'envoi des mails

Le serveur SMTP

Avant de rentrer dans le vif du sujet, il faut tout d'abord permettre à ton application de communiquer avec le serveur qui fera transiter tes mails : le serveur SMTP.

Commence par regarder cette courte vidéo qui présente en 3 minutes ce qu'est un serveur SMTP

▶
Vidéo YouTube
https://www.youtube.com/watch?v=j7kMZD81hec

Pour commencer, il te faut choisir l'adresse mail (et donc le serveur SMTP) qui va envoyer les messages. Utiliser une adresse personnelle type Gmail est fortement déconseillé (car peu adapté à l'envoi d'un grand nombre d'email, risque de passer pour un spammeur). Il faut plutôt se tourner vers des fournisseurs spécialisés qui fournissent généralement des offres gratuites pour de faibles volumes d'envois.

Important

À propos de Gmail

De base, les adresses Gmail n’autorisent pas l'envoi des courriels en dehors de l’interface web de gmail.com. Il faut donc manuellement configurer les options de son compte Google et cocher le paramètre "Autoriser les applications moins sécurisées", puis générer un mot de passe spécifique à l'application, bref évite de l'utiliser..

Mailtrap

Pour la phase de développement, il est recommandé d'utiliser un fake mailer tel que mailtrap. Cet outil va agir comme un smtp. La différence est que, au lieu de distribuer réellement les mails aux destinataires, il va les garder et les déposer dans une boîte de réception à laquelle tu auras accès !

Les avantages sont nombreux : la configuration est très simple pour Symfony, il y a des outils d’analyse des mails envoyés et surtout, il n'est pas possible d'envoyer par erreur (en phase de développement) des mails à des personnes réelles !

Info

Pour la phase de production, voici quelques fournisseurs intéressants (liste non exhaustive) : MailChimp, Mailgun, Mailjet, SendGrid, Sendinblue. Ils possèdent généralement des offres gratuites pour les petits projets, et sont adaptés pour envoyer et suivre des campagnes de mailing massifs. Symfony possède des configurations pour ces différents services.

Pour en revenir à Mailtrap, suis ces étapes :

Tout d’abord, connecte-toi à mailtrap.io et crée un compte (tu peux utiliser ton compte Github par exemple). Tu arrives sur une boîte de réception avec un premier projet "Demo Inbox". Sur la ligne, tout à droite, clique sur la roulette “Settings”.

Sur cette page “Settings”, dans le menu déroulant “Integrations”, sélectionne Symfony .

Mailtrap te fournira la ligne à copier directement dans ton fichier .env.local. (enlève bien le # en début de ligne sinon la ligne sera considérée comme un commentaire) Ça ressemblera à ceci :

bash
MAILER_DSN=smtp://caaed6cc1d54d:86287f9d6ra7bb@smtp.mailtrap.io:2525?encryption=tls&auth_mode=login

C’est tout !

↗
Mailslurper

Un autre fake mailer, équivalent de Mailtrap.

mailslurper.com

Configurer le serveur SMTP dans Symfony

Si tu te rappelles tes premiers pas sur Symfony, tu dois te souvenir du fichier /.env.local. Celui-ci contenait des paramètres relatifs à la configuration de la base de données.

Ouvre ce fichier. Tu verras qu'il y a déjà des lignes (commentées) pour symfony/mailer. Décommente la ligne commençant par MAILER_DSN :

env
###> symfony/mailer ###
MAILER_DSN=smtp://localhost
###< symfony/mailer ###

La configuration du SMTP dans symfony se fait sous un format "DSN" comme pour la configuration de la base de données. Copie-colle ici le MAILER_DSN fourni par Mailtrap dans l'étape précédente.

Si tu utilises un autre fournisseur que Mailtrap à un moment donné, il est intéressant de comprendre un minimum comment est structuré cette ligne :

env
MAILER_DSN=smtp://user:pass@smtp.example.com
#            ^     ^     ^         ^
#            1     2     3         4
  1. Correspond au type d'envoi d'email possible (mailer_transport). Il faut savoir que la configuration de ce protocole SMTP change selon le système d'envoi de mails désiré.

  2. L'identifiant utilisé pour se connecter au serveur d'envoi de message. Cette information, contrairement aux précédentes, se retrouve dans beaucoup d'autres configurations, comme gmail.

  3. Le mot de passe associé à l'identifiant précédent.

  4. C'est l'adresse (IP ou domaine) du serveur qui va être utilisée pour l'envoi des mails (mailer_host). Ce paramètre dépend de ton hébergement (perso, OVH, localhost, etc.), ou du service de messagerie qui va te permettre d'envoyer les mails. Cela peut être ton compte Gmail ou Outlook personnel par exemple.

  5. Pour Mailtrap, à la fin de la chaîne de caractères, tu trouveras également d'autres informations, qui sont des options supplémentaires (numéro de port utilisé, type de chiffrement, etc.).

Info

Ce que tu viens de faire est lié à l'envoi de mail en général depuis ton application Symfony, ce n'est pas directement lié au composant Mailer. Si plus tard tu souhaites utiliser un autre service, comme PHPMailer par exemple, tu pourras te baser sur ce fichier .env.local pour le configurer à son tour.

Maintenant, tu peux jeter un oeil à la configuration propre à Mailer. Comme la majeure partie des autres librairies, celle-ci se trouve dans config/packages/mailer.yaml.

Tu retrouves juste une référence à la variable configurée dans le fichier précédent dsn: '%env(MAILER_DSN)%' et c'est tout à fait normal. En effet, mailer.yaml s'appuie sur les données renseignées dans env.local. Comme pour les identifiants de base de données, les identifiants de serveur de messagerie sont des données sensibles, qui ne doivent pas être partagées et donc versionnées. De plus, cela te permet de configurer différemment ton environnement de développement et de production, en leur attribuant des identifiants différents.

▶
Vidéo YouTube
https://youtu.be/8e6mWL56MRw

Une fois ton MAILER_DSN Mailtrap ajouté dans le fichier .env.local, il te reste une dernière étape de configuration. Par défaut, Symfony envoie les emails de manière asynchrone. Les messages sont mis dans une file d'attente, et il faut que tu gères toi même le moment où tu souhaites les envoyer. C'est très pratique quand tu as de gros flux de messages, mais nous allons partir sur un système plus simple, en synchrone. Rends toi à nouveau dans le fichier config/packages/mailer.yaml et dans la partie "mailer", ajoute message_bus: false. Ton fichier dois ressembler à ça maintenant :

yaml
framework:
    mailer:
        dsn: '%env(MAILER_DSN)%'
        message_bus: false

C'est tout, l'envoi se fait maintenant de manière synchrone.

Appeler Mailer depuis le contrôleur

Voilà, maintenant que ton application est configurée, tu vas passer aux choses sérieuses.

À quel moment dois-tu envoyer tes mails ? Dès qu'une série est publiée, par exemple. Mais d'un point de vue code ?

Tu dois le faire une fois que le formulaire est validé et que tu as ajouté en base ton instance de l'entité Program. Si tu le fais trop tôt, tu risques d'envoyer un mail avant que la série ne soit sauvegardée, ce qui pourrait poser problème.

Ce qui donne, dans la méthode associée à l'ajout d'une série, le code suivant :

php
public function new(Request $request, MailerInterface $mailer, EntityManagerInterface $entityManager): Response
{
    // [...]
    if ($form->isSubmitted() && $form->isValid()) {
        // [...]
        $entityManager->flush(); 

        $email = (new Email())
                ->from('your_email@example.com')
                ->to('your_email@example.com')
                ->subject('Une nouvelle série vient d\'être publiée !')
                ->html('<p>Une nouvelle série vient d\'être publiée sur Wild Séries !</p>');

        $mailer->send($email);
        // [...]
    }
}

Voici maintenant le détail de ce code :

  1. Tu passes en paramètre de ta méthode un MailerInterface $mailer et via l'injection de dépendances, tu récupères un objet $mailer utilisable.

  2. Tu crées un email via l'instanciation d'un object Email

  3. Cet objet Email possède des méthodes pour configurer le message, comme from() pour l'adresse de l'expéditeur, to() pour l'adresse du destinataire, subject() pour le sujet du mail, html() pour le corps du mail au format HTML, etc. Reporte toi à la documentation du composant pour connaître toutes les méthodes disponibles (elles sont nombreuses).

Attention

Les adresses email renseignées ici dans from() et to() sont fictives et saisies "en dur". Ce n'est pas une bonne pratique de saisir des adresses emails "en dur", car cela les rend difficilement modifiables. Ces adresses pourraient soit provenir d'un enregistrement en base de données, soit d'un paramètre défini dans tes variables d’environnement. Voyons comment améliorer cela.

Par exemple, tu peux déclarer dans ton fichier .env.local le paramètre suivant :

env
MAILER_FROM_ADDRESS=wilder@wildcodeschool.fr

puis dans le fichier services.yaml, sous la clé parameters, récupérer ta variable d’environnement ainsi :

yaml
parameters:
    mailer_from: '%env(resolve:MAILER_FROM_ADDRESS)%'

Enfin pour accéder à cette variable depuis un contrôleur :

php
$this->getParameter('mailer_from')

Maintenant, ajoute une nouvelle série pour tester ta méthode fraîchement modifiée. Lors de la création, un email est normalement expédié vers ton utilisateur destinataire. Consulte la boîte mail correspondante pour vérifier que tout a fonctionné.

Info

Attention, avec Mailtrap, l'email n'est pas réellement envoyé à l'adresse configurée dans le to(), mais sera disponible sur ton interface mailtrap ! Si tu ne reçois rien, c'est peut-être qu'il y a un problème de configuration ou que l'adresse du destinataire n'est pas bonne.

↗
Documentation officielle de Mailer

Documentation de Mailer embarquée dans Symfony.

symfony.com

Mise en forme des mails.

Le contenu du mail est une simple chaîne de caractères au format HTML. Si tu reprends l'exemple suivant (où a été ajouté l'utilisation du paramètre mailer_from), le HTML était directement saisi dans la méthode html()

php
$email = (new Email())
    ->from($this->getParameter('mailer_from'))
    ->to('your_email@example.com')
    ->subject('Une nouvelle série vient d\'être publiée !')
    ->html('<p>Une nouvelle série vient d\'être publiée sur Wild Séries !</p>');

Ce n'est pas une bonne pratique car cela va à l'encontre du MVC qui veut que la Vue soit indépendante du Contrôleur (oui, ici la vue n'est pas une page web en HTML mais c'est ton email !). Pour résoudre ce problème, tu vas simplement utiliser Twig. Pour le moment, tu as principalement utilisé $this->render('file.html.twig') dans tes contrôleurs. Cette méthode renvoie un objet Response dans lequel tu trouveras le rendu du fichier Twig. Mais tes contrôleurs ont également accès à une méthode $this->renderView('file.html.twig') qui permet de ne faire que le rendu du template Twig (et donc de renvoyer l'HTML sous forme d'une chaîne de caractères). Dans un premier temps, créé un fichier Twig Program/newProgramEmail.html.twig. Ce fichier sera une simple page HTML contenant les informations utiles pour générer le mail.

Info

Si tu as plusieurs emails différents à créer mais que tu veux reprendre une mise en page commune entre ces emails, il est intéressant de créer un layoutEmail.html.twig et d'utiliser {% extends %} de ce layout pour tes vues dédiées aux emails.

Ton fichier devrait ressembler à

html
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <title>New program</title>
</head>
<body>
    <h1>New program {{ program.title }} created</h1>
    <p>Hello, the new program {{ program.title }} has been created on Wild Series</p>
    <p>Synopsis : {{program.synopsis }}</p>
</body>
</html>

Côté contrôleur, il faut modifier la méthode html pour qu'elle rende le template Twig, auquel tu auras passé les informations de la série nouvellement crée.

php
$email = (new Email())
    ->from($this->getParameter('mailer_from'))
    ->to('your_email@example.com')
    ->subject('Une nouvelle série vient d\'être publiée !')
    ->html($this->renderView('Program/newProgramEmail.html.twig', ['program' => $program]));

Templating avancé

Pour ton information, la création de templates de mails peut s’avérer être un challenge plus compliqué qu'il n'y paraît. Obtenir un affichage satisfaisant et responsive pour tous les utilisateurs ne sera pas une mince affaire.

En effet, les mails n'ont pas forcément vocation à être affichés directement dans un navigateur. Bien souvent, ceux-ci sont lus directement à partir d'un client mail (Outlook de Microsoft, Mail d'Apple, Thunderbird de Mozilla, etc. ). Il faut malheureusement parfois renoncer aux bonnes pratiques du Web pour obtenir un affichage satisfaisant et compatible avec tous les supports. Fais en sorte, dans cette quête, que le rendu soit propre dans un navigateur, et ne perds pas de temps sur le rendu dans les logiciels de mails.

Il existe des outils de templating dédiés aux email, comme Inky, qui possède une intégration avancée dans Symfony. Le composant Mailer possède également un objet TemplatedEmail (au lieu de l'objet Email) qui permet de faciliter le travail de mise en forme des emails. Tu auras tout le loisir de te casser la tête sur le sujet dans ta future carrière professionnelle (pour l'instant, reste sur des emails basiques), mais si ta curiosité est forte et que tu tiens absolument à approfondir le sujet, tu peux déjà jeter un œil aux ressources ci-dessous.

↗
Inky

Si tu veux vraiment aller plus loin, voici un framework de templating d'email.

get.foundation
↗
Intégration d'Inky dans Symfony
symfony.com
↗
Utilisation de l'objet TemplatedEmail dans Symfony
symfony.com

💪 Challenge

Un nouvel épisode a été créé.

Un email est envoyé pour chaque nouvelle série ajoutée à Wild Series. Tu vas maintenant mettre en place un envoi d'email pour chaque nouvel épisode ajouté.

De plus, puisque tu as deux emails différents envoyés, tu vas créer un layout Twig dédié aux emails (comme évoqué précédemment dans la quête) et faire en sorte que les deux templates d'email étendent ce layout.

Critères de validation

  • Le mail de l'expéditeur (administrateur) est issu d'une variable d'environnement,
  • Le contenu des mails envoyés reprend l'apparence générale de l'application, à l'aide d'un layout de mail général et se trouve dans une vue Twig.
  • Le contenu du mail indique le nom/numéro du nouvel épisode, le nom de la série rattachée, ainsi qu'un lien vers la page épisodes de cette série.
  • Le code est disponible sur un repository GitHub, avec une branche correspondant à cette quête.

Ressources partagées sur cette quête