Comment envoyer des mails avec Symfony

5 min

Pour envoyer des mails avec Symfony, on utilise le composant Mailer : il construit le message, le transmet à un serveur SMTP ou à l’API d’un service d’envoi, et peut passer par une file d’attente pour ne pas ralentir vos pages. Voici la configuration complète, de l’installation aux templates Twig, avec la gestion des erreurs et les points de délivrabilité à ne pas oublier.

Installer et configurer le transport

  1. Installer le composant. Le composant Mime, qui sert à construire les messages, est installé avec lui :
    composer require symfony/mailer
  2. Définir le transport dans la variable MAILER_DSN, dans .env.local en développement et dans les variables d’environnement du serveur en production, jamais dans un fichier versionné avec un mot de passe :
    # Serveur SMTP (caractères spéciaux du mot de passe encodés en URL)
    MAILER_DSN=smtp://utilisateur:motdepasse@smtp.example.com:587
    
    # Développement : n'envoie rien
    MAILER_DSN=null://null
  3. Pour un service d’envoi (Brevo, Mailgun, Amazon SES, Postmark…), installez le bridge correspondant, qui permet d’utiliser leur API plutôt que le SMTP. Exemple avec Brevo :
    composer require symfony/brevo-mailer
    # puis dans .env.local :
    # MAILER_DSN=brevo+api://VOTRE_CLE_API@default

En développement, le plus pratique est un faux serveur SMTP comme Mailpit, qui capture tous les messages dans une interface web : rien ne part chez un vrai destinataire. La barre de débogage de Symfony affiche aussi les e-mails envoyés pendant une requête.

Envoyer un premier e-mail

  1. Construire le message et l’envoyer avec le service MailerInterface, injecté dans un contrôleur ou un service :
    <?php
    
    namespace App\Controller;
    
    use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
    use Symfony\Component\HttpFoundation\Response;
    use Symfony\Component\Mailer\MailerInterface;
    use Symfony\Component\Mime\Address;
    use Symfony\Component\Mime\Email;
    use Symfony\Component\Routing\Attribute\Route;
    
    class ContactController extends AbstractController
    {
        #[Route('/contact/test', name: 'contact_test')]
        public function test(MailerInterface $mailer): Response
        {
            $email = (new Email())
                ->from(new Address('contact@example.com', 'Mon application'))
                ->to('client@example.com')
                ->replyTo('support@example.com')
                ->subject('Votre demande a bien été reçue')
                ->text('Nous revenons vers vous rapidement.')
                ->html('<p>Nous revenons vers vous rapidement.</p>');
    
            $mailer->send($email);
    
            return new Response('E-mail envoyé');
        }
    }

Renseignez toujours une version texte en plus du HTML : certains clients de messagerie l’affichent, et son absence pèse dans l’évaluation anti-spam. Pour joindre un fichier, la méthode attachFromPath() prend un chemin de fichier.

Des e-mails HTML avec Twig

  1. Utiliser TemplatedEmail, qui rend un template Twig avec des variables (le bundle Twig doit être installé) :
    use Symfony\Bridge\Twig\Mime\TemplatedEmail;
    
    $email = (new TemplatedEmail())
        ->from(new Address('contact@example.com', 'Mon application'))
        ->to($utilisateur->getEmail())
        ->subject('Confirmez votre adresse e-mail')
        ->htmlTemplate('emails/confirmation.html.twig')
        ->textTemplate('emails/confirmation.txt.twig')
        ->context([
            'prenom' => $utilisateur->getPrenom(),
            'lien' => $lienDeConfirmation,
        ]);
    
    $mailer->send($email);
  2. Écrire le template : les variables passées dans context() y sont disponibles, ainsi qu’une variable email qui donne accès au message :
    {# templates/emails/confirmation.html.twig #}
    <p>Bonjour {{ prenom }},</p>
    <p>Pour activer votre compte, confirmez votre adresse :</p>
    <p><a href="{{ lien }}">Confirmer mon adresse</a></p>

Les clients de messagerie gèrent mal les feuilles de style externes : le filtre inline_css (paquet twig/cssinliner-extra) recopie les styles directement dans les balises.

Gérer les erreurs et envoyer en arrière-plan

  1. Attraper les erreurs d’envoi : un serveur SMTP indisponible ou une clé d’API invalide lèvent une exception qui, non attrapée, renvoie une erreur 500 à l’utilisateur :
    use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
    
    try {
        $mailer->send($email);
    } catch (TransportExceptionInterface $e) {
        $this->logger->error('Échec d\'envoi', ['erreur' => $e->getMessage()]);
        // informer l'utilisateur ou réessayer plus tard
    }
  2. Envoyer de façon asynchrone avec Messenger : la page répond immédiatement et l’e-mail part dans un processus séparé, avec de nouvelles tentatives automatiques en cas d’échec. Une fois Messenger installé et un transport async configuré :
    # config/packages/messenger.yaml
    framework:
        messenger:
            routing:
                'Symfony\Component\Mailer\Messenger\SendEmailMessage': async

En mode asynchrone, $mailer->send() ne lève plus d’erreur de transport : les échecs se suivent dans les logs du worker (php bin/console messenger:consume async), qui doit tourner en permanence sur le serveur.

Délivrabilité : ce qui se joue hors du code

L’envoi d’e-mails fait partie de presque toutes les applications que nous développons en Symfony : inscription, notifications, factures. Pour un projet, voyez notre agence Symfony à Lille ou notre page développement d’application web sur mesure.

Comment pouvons nous vous aider ?

Contactez-nous dès maintenant pour discuter de vos besoins spécifiques et obtenir un devis personnalisé.

Faisons de
grandes choses Contactez-nous
ensemble