Symfony Mailer – wysyłanie maili w PHP

Wraz z Symfony 4.3 pojawił się nowy komponent do wysyłania maili – Mailer oraz uzupełniający go moduł Mime służący do tworzenia wiadomości. W założeniu te dwie nowości mają zastąpić Swift Mailer, który jest biblioteką mającą ponad kilkanaście lat i część rozwiązań z niego jest przestarzałych. Te dwa komponenty dają ogromne możliwości:

  • tworzenie wieloczęściowych wiadomości (multipart),
  • integracja z Twigiem,
  • inlinowanie CSSów,
  • dodawanie załączników i wiele innych.

Instalacja

Instalujemy komponent po przez uruchomienie następującej komendy:


composer require symfony/mailer

Konfiguracja transportu

Emaile są dostarczane poprzez transporty. Domyślnie możemy wysyłać emaile przez SMTP. Ustawiamy adres serwera w pliku .env


# .env
MAILER_DSN=smtp://user:pass@smtp.example.com

Możemy dodać inne serwisy poprzez dodanie transportów do composera.

TransportKomenda
Amazon SEScomposer require symfony/amazon-mailer
Gmailcomposer require symfony/google-mailer
MailChimpcomposer require symfony/mailchimp-mailer
Mailguncomposer require symfony/mailgun-mailer
Postmarkcomposer require symfony/postmark-mailer
SendGridcomposer require symfony/sendgrid-mailer

Jeśli przykładowo chcemy użyć Gmaila odpalamy:


composer require symfony/google-mailer

Każdy z serwisów ma własną receptę dla Flexa, także potrzebne ustawienia będą dostępne w plikach konfiguracyjnych. Dla Gmaila dostajemy w pliku .env


# .env
###> symfony/google-mailer ###
# Gmail SHOULD NOT be used on production, use it in development only.
GMAIL_USERNAME=email@gmail.com
GMAIL_PASSWORD=foobar
MAILER_DSN=smtp://$GMAIL_USERNAME:$GMAIL_PASSWORD@gmail
###< symfony/google-mailer ###

MAILER_DSN to nie jest prawdziwy adres SMTP, tylko konwencja konfiguracji. Zmienne GMAIL_USERNAME oraz GMAIL_PASSWORD możemy ustawić w pliku .env lub .env.local

Tworzenie wiadomości

Aby wysłać email potrzebujemy wstrzyknąć MailerInterface oraz utworzyć obiekt Email.


namespace App\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\NamedAddress;
use Symfony\Component\Routing\Annotation\Route;

class MailerController extends AbstractController
{
    /**
     * @Route("/mailer")
     */

    public function index(MailerInterface $mailer)
    {
        $email = (new Email())
            ->to('piotr@example.com')
            //->cc('user@example.com')
            //->bcc('bcc@example.com')
            //->replyTo('piotr@example.com')
            //->priority(Email::PRIORITY_HIGH)
            ->subject('Test Symfony Mailera')
            ->text('Zwykły tekst')
            ->html('<p>Email HTML</p>')
        ;

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

Adres email

Wszystkie metody, które wymagają adresu email (->from, ->to, ->cc, ->bcc) , przyjmują jako parametr string albo obiekty adresu.


use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\NamedAddress;

$email = (new Email())
    // email jako string
    ->to('piotr@example.com')

    // email address jako obiekt
    ->to(new Address('piotr@example.com'))

    // email address jako obiekt (widoczna będzie nazwa zamiast adresu)
    ->to(new NamedAddress('piotr@example.com', 'Piotr'))
;

Możemy dodać wiele adresów jako (to, cc, bcc)


$email = (new Email())
    ->bcc('test@example.com')
    ->addBcc('test2@example.com')
    ->addBcc('test3@example.com')
;

Ewentualnie jako parametry


$email = (new Email())
    ->cc('test2@example.com', 'test4@example.com')
    ->bcc(['test@example.com', 'test2@example.com'])
;

Treść wiadomości

Możemy dołączyć treść tekstową i HTML zarówno jako stringi, jak i resource (np. fopen, streams).


$email = (new Email())
    ->text('Test...')
    ->html('<p>Test...</p>')

    // strumienie
    ->text(fopen('/emaile/rejestracja.txt', 'r'))
    ->html(fopen('/emaile/rejestracja.html', 'r'))
;

Załączniki

Załączniki możemy dodawać na dwa sposoby. Po pierwsze przy użyciu attachFromPath i podaniu ścieżki


$email = (new Email())
    ->attachFromPath('/zalaczniki/regulamin.pdf')
    // opcjonalnie można podać inną nazwę dla pliku
    ->attachFromPath('/zalaczniki/regulamin-v3.pdf', 'Regulamin.pdf')
    // można również podać typ MIME, który nie jest wówczas zgadywany
    ->attachFromPath('/zalaczniki/regulamin-v3.pdf', 'Regulamin.pdf', 'application/pdf')
    // można podać absolutny url
    ->attachFromPath('http://example.com/zalaczniki/regulamin.pdf', 'Regulamin.pdf', 'application/pdf')
;

Można skorzystać również z metody attach i podać strumień:


$email = new Email();
$email->attach(fopen('/sciezka/pliku.pdf', 'r'));

Zagnieżdżanie obrazków

Kiedy chcesz wyświetlić obrazek w emailu, powinieneś najpierw go zagnieździć a później użyć w treści emaila. Kiedy używasz Twiga, nie musisz tego robić, Twig sam zagnieżdża obrazki.


$email = new Email();
$email->embed(fopen('/sciezka/do/obrazka.png', 'r'), 'logo');
$email->embedFromPath('/sciezka/do/podpis.png', 'podpis');

$email->html('<img src="cid:logo"> ... <img src="cid:podpis">');

Tak jak widzimy żeby użyć obrazka w treści emaila należy użyć skład “cid:” + “nazwa obrazka”, którą wcześniej zdefiniowaliśmy.

Globalne działanie przy wysyłce

Przy każdej wysyłce wiadomości jest tworzony event do którego możemy się podpiąć. Na przykład jeśli chcemy wysyłać do siebie kopie każdego maila, możemy dodać ukrytego odbiorce bcc:


<?php

namespace App\EventListener;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\NamedAddress;

class MailerBccCopyListener implements EventSubscriberInterface
{
    public function onMessageSend(MessageEvent $event)
    {
        $message = $event->getMessage();

        if (!$message instanceof Email) {
            return;
        }

        $message->bcc(new NamedAddress('piotr@example.com', 'Kopia maila'));
    }
   
    /**
     * {@inheritDoc}
     */

    public static function getSubscribedEvents()
    {
        return [MessageEvent::class => 'onMessageSend'];
    }
}

Twig

Dzięki integracji z Twigiem, możemy w łatwy sposób utrzymać porządek w szablonach, zagnieżdżać style, używać frameworków CSS. Żeby użyć Twiga, dodajmy go do composera.


composer require symfony/twig-bundle

Treść HTML

Żeby stworzyć email przy użyciu Twig, użyjemy klasy TemplatedEmail, która dziedziczy po klasie Email


$email = (new TemplatedEmail())
    ->to(new NamedAddress('piotr@example.com', 'Piotr Belina'))
    ->subject('Test Symfony Mailera')

    // tutaj podajemy ścieżkę do szablonu Twig
    ->htmlTemplate('emails/signup.html.twig')

    // zmienne kontekstowe dla szablonu
    ->context([
        'user' => 'Ania',
        'expiration_date' => new \DateTime('+7 days'),
    ])
;

Szablon dodajemy do templates/emails/signup.html.twig


<h1>Witaj {{ email.toName }}!</h1>

<p>
    Zarejestrowałeś się jako {{ user }} używając poniższego emailu:
</p>
<p><code>{{ email.to[0].address }}</code></p>

<p>
    <a href="#">Kliknij aby aktywować konto</a>
    (ten link jest ważny do {{ expiration_date|date('F jS') }})
</p>

W szablonie mamy dostęp do wszystkich zmiennych, które przekazaliśmy w kontekście oraz do specjalnego obiektu email, który jest instancją klasy WrappedTemplatedEmail.

Treść tekstowa

Jeśli nie dodamy treści tekstowej dla emaila, zostanie ona automatycznie wygenerowana. Można dodać własną treść w ten sposób.


$email = (new TemplatedEmail())
    //...

    // tutaj podajemy ścieżkę do szablonu Twig
    ->htmlTemplate('emails/signup.html.twig')
    ->textTemplate('emails/signup.txt.twig')

    // ...
;

Leave a comment

Your email address will not be published. Required fields are marked *