Envoyer un e-mail avec Symfony 4

Nous avons sorti le formulaire de contact dans le code, et nous pouvons voir notre formulaire sur la page /contact . Lorsque nous soumettons notre formulaire de contact, nous pouvons voir les données soumises déposées sur la barre d'outils de débogage web.

Maintenant, transformons cela en un email, et affichons un message Flash pour que le visiteur sache que son message a été envoyé.

Envoyer un email avec Symfony 4

Il y a une bonne page dans la documentation qui décrit comment envoyer un email à la fois via Gmail et avec un "service cloud". Tout le monde aime un bon nuage, n'est-ce pas? Je sais que je le fais.

Envoyer un email avec Symfony 4 est vraiment très simple.

Et comme je le dis, il y a une bonne page dans les docs qui vous guide étape par étape.

Ce que nous allons faire c'est utiliser cela comme une opportunité pour aller un peu plus geekier. Et par geekier, je veux bien sûr dire un peu plus de monde réel. Pour la caverne ballot!

<?php

namespace App\Controller;

use App\Form\ContactType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;

class ContactController extends AbstractController
{
    /**
     * @Route("/contact", name="contact")
     */
-   public function index(Request $request)
+   public function index(Request $request, \Swift_Mailer $mailer)
    {
        $form = $this->createForm(ContactType::class);

        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {

            $contactFormData = $form->getData();

+            $message = (new \Swift_Message('You Got Mail!'))
+               ->setFrom($contactFormData['from'])
+               ->setTo('our.own.real@email.address')
+               ->setBody(
+                   $contactFormData['message'],
+                   'text/plain'
+               )
+           ;
+
+           $mailer->send($message);
+
+           return $this->redirectToRoute('contact');
        }

        return $this->render('contact/index.html.twig', [
            'our_form' => $form->createView(),
        ]);
    }
}

Une bonne quantité de changement.

Nous avons dû injecter le service \Swift_Mailer dans nos arguments de fonction d' index , car c'est le service que Symfony utilise (au départ, au moins) pour envoyer des emails.

Nous avons ensuite créé un nouvel objet $message en \Swift_Message une instance de \Swift_Message , rempli tous les champs appropriés et utilisé $mailer pour send notre $message .

Nous return $this->redirectToRoute('contact'); pour forcer une actualisation complète de la page, lors de l'envoi réussi d'un courrier. Cela efface notre formulaire en forçant un «rechargement dur» de la page.

Comme nous l'avons vu dans la tutoriel précédent, notre $contactFormData est un array contenant une / toutes les données de formulaire soumises. Étant donné que nous sommes dans le conditionnel $form->isSubmitted() , nous pouvons accéder aux clés / valeurs de ce tableau et les utiliser pour remplir notre courrier électronique.

Nous n'avons pas utilisé toutes les informations que nous avons soumises à partir du formulaire. Nous ferons cependant, ne vous inquiétez pas.

Nous sommes presque prêts à envoyer. Nous avons juste besoin de brancher nos informations d'identification Gmail . Dans votre fichier .env dans la racine de votre projet:

- MAILER_URL=null://localhost
+ MAILER_URL=gmail://username:password@localhost

En fonction de la configuration de Gmail pour vous, vous pouvez soit recevoir immédiatement l'e-mail que vous attendez, soit un avertissement indiquant que vous essayez de faire des «choses de mauvais gars». Je paraphrase, mais c'est l'essentiel. Si c'est le cas, suivez les conseils / précautions appropriés et contournez le problème. Encore une fois, les documents officiels vous guideront ici.

Obtenir Flashy

Bien que je ne sois pas encore satisfait de la mise en œuvre que nous venons d'ajouter, nous allons continuer à faire du camionnage et obtenir une autre victoire rapide.

Immédiatement après l'envoi d'un email je veux montrer au visiteur un bon message Flash pour dire: "hé, ça s'est bien passé".

Nous pouvons même utiliser un joli style Bootstrap pour ajouter des niveaux de style supplémentaires aux procédures. C'est gentil.

Un message flash est un message unique qui dure la durée de la demande en cours. Un exemple pourrait être d'afficher un message d'échec si la tentative de connexion précédente a échoué. Ou un message de réussite si l'utilisateur a correctement mis à jour son mot de passe. Ils ne devraient pas voir ce message chaque fois qu'ils naviguent sur une page, juste une fois quand ils ont tenté une tâche, et cela a réussi ou échoué.

J'ai déjà fait une tutoriel sur un Guide de débutant pour les messages instantanés de Symfony , donc j'irai vite ici. Cette tutoriel est pour Symfony 3, mais c'est en grande partie la même chose pour Symfony 4.

Nous allons créer un nouveau template pour stocker notre block affichage Flash Message.

Tout d'abord, je vais créer un nouveau fichier de template vide:

 cd {project_root} 
 touch templates/flash_messages.html.twig 

J'ai mis ce nouveau fichier de template vide à la racine du répertoire des templates car il n'y a pas de contrôleur dans notre projet qui utilise directement ce template. Vous pouvez, bien sûr, le stocker n'importe où. Vous pouvez même le fusionner avec votre base.html.twig existante, si vous le souhaitez.

Le contenu de templates/flash_messages.html.twig est le suivant:

{% block flash_messages %}
    {% for type, messages in app.session.flashbag.all() %}
        {% for message in messages %}
            <div class="alert alert-{{ type }} alert-dismissible" role="alert">
                <button type="button" class="close" data-dismiss="alert"><span aria-hidden="true">&times;</span><span class="sr-only">Close</span></button>
                {{ message | raw }}
            </div>
        {% endfor %}
    {% endfor %}
{% endblock %}

Cela peut être copié / collé directement, entre n'importe quel projet qui utilise Bootstrap.

La bonne chose à propos de ce template est qu'il fonctionne de manière générique pour tout «niveau» de message flash : success , warning, erreur », et ainsi de suite.

Ensuite, nous devons include ce template flash_messages.html.twig dans notre template base.html.twig . Je vais inclure ceci dans la balise main , mais au-dessus de la balise block body :

<main role="main" class="container main">
    {% include 'flash_messages.html.twig' %}

    {% block body %}{% endblock %}
</main>

La bonne partie à propos du template de messages flash est que si nous n'avons aucun message flash défini (et nous allons passer à la façon de le faire en une seconde), il n'y a pas de HTML supplémentaire ajouté à notre page. Ce n'est que si nous avons un ou plusieurs messages flash que nous voyons la sortie.

Dans cet esprit, ajoutons notre premier message flash dès maintenant.

Nous ajoutons généralement des messages flash à partir de nos actions de contrôleur. Tout comme nous avons utilisé $this->render(...) et $this->createForm(...) tout au long de cette série, nous pouvons aussi utiliser $this->addFlash(...) .

Un aperçu de la signature de la méthode pour addFlash :

**
     * Adds a flash message to the current session for type.
     *
     * @throws \LogicException
     *
     * @final since version 3.4
     */
    protected function addFlash(string $type, string $message)

Nous voyons que addFlash prend deux paramètres obligatoires.

Les $message $type et $message peuvent être n'importe quelle chaîne. Tout ce que tu aimes.

Cela dit, si vous voulez appliquer les styles Bootstrap, le $type doit être l'un des niveaux d'alerte disponibles :

  • primaire
  • Succès
  • Attention
  • foncé

Etc.

Ajoutons ceci dans notre méthode de contrôleur:

<?php

namespace App\Controller;

use App\Form\ContactType;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;

class ContactController extends AbstractController
{
    /**
     * @Route("/contact", name="contact")
     */
    public function index(Request $request, \Swift_Mailer $mailer)
    {
        $form = $this->createForm(ContactType::class);

        $form->handleRequest($request);

+       $this->addFlash('info', 'Some useful info');

        if ($form->isSubmitted() && $form->isValid()) {

            $contactFormData = $form->getData();

            $message = (new \Swift_Message('You Got Mail!'))
                ->setFrom($contactFormData['from'])
                ->setTo('christopherdmoss@gmail.com')
                ->setBody(
                    $contactFormData['message'],
                    'text/plain'
                )
            ;

            $mailer->send($message);

+           $this->addFlash('success', 'It sent!');

            return $this->redirectToRoute('contact');
        }

        return $this->render('contact/index.html.twig', [
            'our_form' => $form->createView(),
        ]);
    }
}

Nous avons ajouté deux messages flash.

On affichera toujours:

$this->addFlash('info', 'Some useful info');

L'autre ne s'affichera que si nous soumettons le formulaire avec succès:

$this->addFlash('success', 'It sent!');

Actualisez votre /contact page de /contact et essayez-le.

Notez que même si nous avons utilisé return $this->redirectToRoute('contact'); Pour forcer un rechargement de page complet après la soumission d'un formulaire, notre message flash a survécu et s'affiche comme prévu.

Vers une meilleure mise en œuvre

Même si ce que nous avons fonctionne, je pense déjà que la méthode du contrôleur d' index fait un peu trop.

Nous avons tous les formulaires de gestion, puis l'envoi et les messages flash. Et c'est avant que nous ayons peur d'afficher une page.

Il pourrait être intéressant d'extraire les détails de la façon dont nous créons cet e-mail spécifique, et aussi comment nous envoyons des courriels, dans un service distinct.

Savoir comment faire ceci est vraiment utile, et c'est quelque chose que vous ferez beaucoup quand vous travaillerez avec Symfony.

Nous commençons à nous égarer dans les eaux troubles. Les conseils donnés ici et dans les lignes directrices sur les meilleures pratiques sont (à mon avis) complètement dépendants de la taille et de l'envergure de votre projet. Si vous travaillez sur un petit site Web en grande partie statique de 5 pages, vos exigences seront sensiblement différentes de celles d'une API JSON de grande taille ou d'un système d'entreprise interne complexe.

Typiquement Symfony serait plus probablement utilisé dans des applications plus grandes et plus dynamiques, donc je vais me diriger vers le type d'approche que vous pouvez trouver sur un système plus grand. S'il vous plaît, comprenez que les changements que nous allons faire dans le prochaine tutoriel sont exagérés pour la taille réelle et la complexité de cette application  tutoriel.

Dans cet esprit, passons à cela dans le prochaine tutoriel de cette courte série.