Відправлення листів за допомогою Mailer
Дата оновлення перекладу 2024-06-09
Відправлення листів за допомогою Mailer
Установка
Компоненти Symfony Mailer та Mime формують потужну систему для створення та відправлення електронних листів - з підтримкою складових повідомлень, інтеграцією Twig, вбудовуванням CSS, прикріпленням файлів та багато чим іншим. Встановіть їх за допомогою:
1
$ composer require symfony/mailer
Налаштування транспорту
Листи доставляються за допомогою "транспорту". Одразу після установки, ви
можете відправляти листи через SMTP, сконфігурувавши DSN у вашому файлі
.env
(параметри user
, pass
та port
не обов'язкові):
1 2
# .env
MAILER_DSN=smtp://user:pass@smtp.example.com:port
1 2 3 4
# config/packages/mailer.yaml
framework:
mailer:
dsn: '%env(MAILER_DSN)%'
Caution
Якщо ім'я користувача, пароль або хостинг містять в URI будь-який символ, який вважається
особливим (такий як : / ? # [ ] @ ! $ & ' ( ) * + , ; =
), ви маєте закодувати
їх. Див. RFC 3986. щоб побачити повний список зарезервованих символів або використайте
функцію urlencode, щоб закодувати їх.
Використання вбудованого транспорту
DSN-???????? | ??????? | ???? |
---|---|---|
smtp | smtp://user:pass@smtp.example.com:25 |
Mailer ???????????? SMTP-?????? ??? ???????????? ?????? |
sendmail | sendmail://default |
Mailer ???????????? ???????? ?????????? sendmail ??? ???????????? ?????? |
native | native://default |
Mailer ???????????? ?????????? sendmail ?? ?????, ?????????????? ?
???????????? sendmail_path ??? php.ini . ?? ????????? Windows,
Mailer ???????? ???????????? ???????????? smtp ? smtp_port ???
php.ini , ???? sendmail_path ?? ??????????????. |
Caution
При використанні native://default
, якщо php.ini
використовує команду
sendmail -t
, у вас не буде звіту про помилки і заголовки Bcc
не будуть
видалені. Дуже рекомендовано НЕ використовувати native://default
, так як
ви не можете контролювати, як буде сконфігуровано sendmail (краще використайте
sendmail://default
, якщо це можливо).
Використання стороннього транспорту
Замість використання вашого власного SMTP-серверу або бінарності binary, ви можете відправляти листи через стороннього постачальника. Mailer підтримує декілька - встановіть той, який захочете:
?????? | ????????? ?? ????????? | ????????? ???-???? |
---|---|---|
Amazon SES | composer require symfony/amazon-mailer |
|
Azure | composer require symfony/azure-mailer |
|
Brevo | composer require symfony/brevo-mailer |
??? |
Infobip | composer require symfony/infobip-mailer |
|
Mailgun | composer require symfony/mailgun-mailer |
??? |
Mailjet | composer require symfony/mailjet-mailer |
??? |
MailPace | composer require symfony/mail-pace-mailer |
|
MailerSend | composer require symfony/mailer-send-mailer |
|
Mandrill | composer require symfony/mailchimp-mailer |
|
Postmark | composer require symfony/postmark-mailer |
??? |
Resend | composer require symfony/resend-mailer |
??? |
Scaleway | composer require symfony/scaleway-mailer |
|
SendGrid | composer require symfony/sendgrid-mailer |
??? |
7.1
Інтеграції Azure та Resend були представлені в Symfony 7.1.
Note
Задля зручності Symfony також надає підтримку для Gmail (composer
require symfony/google-mailer
), але це не можна використовувати у
виробництві. У розробці, вам, ймовірно, треба використати натомість
email catcher . Відмітьте, що більшість підтримуваних
провайдерів також пропонує безкоштовний рівень.
Кожна бібліотека містить рецепт Symfony Flex , який буде
додавати приклад конфігурації у ваш файл .env
. Наприклад, уявіть, що ви
хочете використати SendGrid. Спочатку встановіть його:
1
$ composer require symfony/sendgrid-mailer
Тепер у вас буде новий рядок у вашому файлі .env
, який ви можете розкоментувати:
1 2
# .env
MAILER_DSN=sendgrid://KEY@default
MAILER_DSN
- це не справжня адреса: це зручний формат, який віддає більшу
частину роботи конфігурації поштовій програмі. Схема sendgrid
активує постачальника
SendGrid, який ви щойно встановили, який знає все про те, як доставляти повідомлення
через SendGrid. Єдине, що вам необхідно змінити - заповнювач KEY
.
Кожний постачальник має різні змінні середовища, які Mailer використовує для
конфігурації справжнього протоколу, адреси та аутентифікації для відправки.
Деякі також мають опції, які можна сконфігурувати з параметрами запиту в кінці
MAILER_DSN
- як, наприклад, ?region=
для Amazon SES або Mailgun. Деякі
постачальники підтримують відправлення через http
, api
или smtp
.
Symfony обирає найкращий доступний транспорт, але ви можете форсувати використання
одного з них:
1 2 3
# .env
# форсувати використання SMTP замість HTTP (який стоїть за замовчуванням)
MAILER_DSN=sendgrid+smtp://$SENDGRID_KEY@default
Ця таблиця демонструє повний список доступних форматів DSN для кожного стороннього постачальника:
???????????? | ??????? |
---|---|
Amazon SES |
|
Azure |
|
Brevo |
|
Google Gmail |
|
Infobip |
|
Mandrill |
|
MailerSend |
|
Mailgun |
|
Mailjet |
|
MailPace |
|
Postmark |
|
Resend |
|
Scaleway |
|
Sendgrid |
|
Caution
Якщо ваші дані безпеки містять спеціальні символи, ви маєте URL-закодувати їх.
Наприклад, DSN ses+smtp://ABC1234:abc+12/345@default
має бути сконфігурована
як ses+smtp://ABC1234:abc%2B12%2F345@default
Caution
Якщо ви хочете використати транспорт ses+smtp
разом з Messenger
для фонового відправлення повідомлень , вам треба
додати параметр ping_threshold
до вашого= MAILER_DSN
зі значенням меншим, ніж
10
: ses+smtp://USERNAME:PASSWORD@default?ping_threshold=9
Caution
Якщо ви надсилаєте користувацькі заголовки при використанні транспорту
Amazon SES (щоб отримати їх пізніше через веб-хук), переконайтеся,
що ви використовуєте постачальника ses+https
, оскільки тільки він
їх підтримує.
Note
При використанні SMTP, тайм-аут за замовчуванням для відправлення повідомлення до виклику виключення - це значення, визначене в опції PHP.ini default_socket_timeout.
Note
Окрім SMTP, багато сторонніх транспортів пропонують веб-API для надсилання електронних
листів. Для цього вам потрібно встановити (додатково до мосту) компонент HttpClient
через composer require symfony/http-client
.
Note
Щоб користуватися Google Gmail, ви повинні мати обліковий запис Google з увімкненою 2-етапною верифікацією (2FA)
і ви повинні використовувати App Password для аутентифікації. Також зауважте, що Google
відкликає ваші App Password, коли ви змінюєте пароль до свого облікового запису Google, і тоді
вам потрібно згенерувати новий.
Використання інших методів (наприклад, XOAUTH2
або Gmail API
) наразі не підтримується.
Ви повинні використовувати Gmail тільки в тестових цілях і використовувати реального провайдера у виробництві.
Tip
Якщо ви хочете перевизначити хост провайдера за замовчуванням (щоб налагодити
проблему, використовуючи сервіс на кшлталт requestbin.com
), змініть default
у вашому хості:
1 2
# .env
MAILER_DSN=mailgun+https://KEY:DOMAIN@requestbin.com
Відмітьте, що протокол завжди буде HTTPs, і не може бути змінений.
Note
Певні види транспорту, наприклад, mailgun+smtp
, призначено для роботи без ручної конфігурації.
Зміна порту шляхом додавання його до вашого DSN не підтримується для жодного з цих транспортів <provider>+smtp
.
Якщо вам потрібно змінити порт, скористайтеся транспортом smtp
, наприклад, так:
1 2
# .env
MAILER_DSN=smtp://KEY:DOMAIN@smtp.eu.mailgun.org.com:25
Tip
Деякі сторонні поштові сервіси при використанні API підтримують зворотні виклики статусу за допомогою веб-хуків. Дивіться Документацію про веб-хуки для отримання додаткової інформації.
Висока доступність
Mailer Symfony підтримує високу доступність через техніку під назвою "відмовостійкість", щоб гарантувати, що листи будуть відправлені, навіть якщо один сервер зазнає невдачі.
Транспорт failover сконфігуровано з двома або більше транспортами та
ключовим словом failover
:
1
MAILER_DSN="failover(postmark+api://ID@default sendgrid+smtp://KEY@default)"
Транспорт відмовостійкості починає з використання першого транспорту, і якщо він зазнає невдачі, він повторно пробує відправлення з наступним транспортом, поки один з них не призведе до успіху (або поки вони всі не зазнають невдачі).
Балансування навантаження
Mailer Symfony підтримує балансування навантаження через техніку під назвою "round-robin" для розподілу робочого навантаження відправлення листів на декілька транспортів.
Транспорт round-robin сконфігуровано з двома або більше транспортами та
ключовим словом roundrobin
:
1
MAILER_DSN="roundrobin(postmark+api://ID@default sendgrid+smtp://KEY@default)"
Транспорт round-robin починає з рандомно обраного транспорту, а потім переключається на наступний доступний трансфер для кожного наступного листа.
Як і з транспортом failover, round-robin повторно намагається зробити відправлення, поки транспорт не досягне успіху (або поки всі не зазнають невдачі). На відміну від транспорту failover, він розповсюджує навантаження по всім своїм транспортам.
Верифікація точок TLS
За замовчуванням, транспорт SMTP виконує верифікацію точок TLS. Ця поведінка
конфігурується опцією verify_peer
. Хоча і не рекомендується відключати
верифікацію з міркувань безпеки, це може бути корисним при розробці додатку
або при використанні самозавіреного сертифікату:
1
$dsn = 'smtp://user:pass@smtp.example.com?verify_peer=0';
Верифікація відбитку пальця TLS Peer
Додаткову верифікацію відбитків пальців можна ввімкнути за допомогою опції peer_fingerprint
.
Це особливо корисно, коли використовується самопідписаний сертифікат і
вимкнення verify_peer
є необхідним, але безпека все одно бажана. Відбиток пальця
можна вказати у вигляді хешу SHA1 або MD:
1
$dsn = 'smtp://user:pass@smtp.example.com?peer_fingerprint=6A1CF3B08D175A284C30BC10DE19162307C7286E';
Disabling Automatic TLS
7.1
Опція для відключення автоматичного TLS була представлена в Symfony 7.1.
За замовчуванням компонент Mailer використовуватиме шифрування, якщо розширення
OpenSSL увімкнено, а SMTP-сервер підтримує STARTTLS
.Таку поведінку можна вимкнути
за допомогою виклику setAutoTls(false)
в екземплярі EsmtpTransport
або
встановлення опції auto_tls
у значення false
у DSN:
1
$dsn = 'smtp://user:pass@10.0.0.25?auto_tls=false';
Caution
Не рекомендується вимикати TLS під час підключення до SMTP-сервера через Інтернет, але це може бути корисно, коли і додаток, і сервер SMTP знаходяться в захищеній мережі, де немає необхідності в додатковому шифруванні.
Note
Це налаштування працює тільки за умови використання протоколу smtp://
.
Перевизначення аутентифікаторів SMTP за замовчуванням
За замовчуванням, SMTP-транспорт намагатиметься увійти в систему, використовуючи всі методи аутентифікації, доступні на SMTP-сервері, один за одним. У деяких випадках може бути корисно перевизначити підтримувані методи аутентифікації, щоб гарантувати, що бажаний метод буде використано в першу чергу.
Це можна зробити за допомогою конструктора EsmtpTransport
або за допомогою методу
setAuthenticators()
:
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\Mailer\Transport\Smtp\Auth\XOAuth2Authenticator;
use Symfony\Component\Mailer\Transport\Smtp\EsmtpTransport;
// Обрати одну з цих 2 опцій:
// Опція 1: передати аутентифікатори конструктору
$transport = new EsmtpTransport(
host: 'oauth-smtp.domain.tld',
authenticators: [new XOAuth2Authenticator()]
);
// Опція 2: викликати метод для перевизначення аутентифікаторів
$transport->setAuthenticators([new XOAuth2Authenticator()]);
Інші опції
command
-
Команда для виконання транспортом
sendmail
:1
$dsn = 'sendmail://default?command=/usr/sbin/sendmail%20-oi%20-t'
local_domain
-
Ім'я домену для використання в команді
HELO
:1
$dsn = 'smtps://smtp.example.com?local_domain=example.org'
restart_threshold
-
Максимальна кількість повідомлень для відправки до перезавантаження транспорту. Може бути використана разом з
restart_threshold_sleep
:1
$dsn = 'smtps://smtp.example.com?restart_threshold=10&restart_threshold_sleep=1'
restart_threshold_sleep
-
Кількість секунд сну між зупинкою та перезавантаженням транспорту. Часто поєднується з
restart_threshold
:1
$dsn = 'smtps://smtp.example.com?restart_threshold=10&restart_threshold_sleep=1'
ping_threshold
-
Мінімальна кількість секунд між двома повідомленнями, необхідна для пінгу серверу:
1
$dsn = 'smtps://smtp.example.com?ping_threshold=200'
max_per_second
-
- Кількість повідомлень, які відправляються за секунду (0, щоб відключити це обмеження)
-
number of messages to send per second (0 to disable this limitation):
1
$dsn = 'smtps://smtp.example.com?max_per_second=2'
Користувацькі фабрики транспорту
Якщо ви хочете підтримувати свою власну DSN (acme://...
), ви можете створити користувацьку фабрику
транспорту. Для цього створіть клас, який реалізує
TransportFactoryInterface або, якщо
ви бажаєте, розширте клас AbstractTransportFactory,
щоб зберегти частину шаблонного коду:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
// src/Mailer/AcmeTransportFactory.php
final class AcmeTransportFactory extends AbstractTransportFactory
{
public function create(Dsn $dsn): TransportInterface
{
// проаналізувати задану DSN, вилучити дані/повноваження з неї,
// а потім створити та повернути транспорт
}
protected function getSupportedSchemes(): array
{
// це підтримує DSN, починаючи з `acme://`
return ['acme'];
}
}
Після створення користувацького класу транспорту, зареєструйте його як сервіс у
вашому додатку і позначте його тегом
mailer.transport_factory
.
Створення та відправлення повідомлень
Для відправлення листа, отримайте екземпляр Mailer, використовуючи підказку MailerInterface, та створіть об'єкт Email:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// src/Controller/MailerController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;
use Symfony\Component\Routing\Attribute\Route;
class MailerController extends AbstractController
{
#[Route('/email')]
public function sendEmail(MailerInterface $mailer): Response
{
$email = (new Email())
->from('hello@example.com')
->to('you@example.com')
//->cc('cc@example.com')
//->bcc('bcc@example.com')
//->replyTo('fabien@example.com')
//->priority(Email::PRIORITY_HIGH)
->subject('Time for Symfony Mailer!')
->text('Sending emails is fun again!')
->html('<p>See Twig integration for better HTML integration!</p>');
$mailer->send($email);
// ...
}
}
Це все! Повідомлення буде негайно надіслано за допомогою налаштованого вами транспорту. Якщо ви бажаєте надсилати листи асинхронно для підвищення продуктивності, прочитайте розділ Відправлення повідомлень асинхронно . Також, якщо у вашому додатку встановлено компонент Messenger, всі листи за замовчуванням будуть надсилатися асинхронно (але ви можете змінити це ).
Адреси електронної пошти
Всі методи, які потребують адрес електронної пошти (from()
, to()
, та ін.),
приймають як рядки, так і об'єкти адрес:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
// ...
use Symfony\Component\Mime\Address;
$email = (new Email())
// адреса пошти як звичайний рядок
->from('fabien@example.com')
// адреса пошти як об'єкт
->from(new Address('fabien@example.com'))
// визначення адреси та імені пошти як об'єкту
// (поштові клієнти відобразять ім'я)
->from(new Address('fabien@example.com', 'Fabien'))
// визначення адреси та імені пошти як рядку
// (формат має відповідати: 'Ім'я <email@example.com>')
->from(Address::create('Fabien Potencier <fabien@example.com>'))
// ...
;
Tip
Замість виклику ->from()
кожний раз при створенні листа, ви можете
сконфігурувати листи глобально ,
щоб встановити одну й ту саму From
листа у всіх повідомленнях.
Note
Локальная частина адреси (те, що перед @
) може включати в себе символи UTF-8,
окрім адреси відправника (щоб уникнути помилок з поверненими листами). Наприклад:
föóbàr@example.com
, 用户@example.com
, θσερ@example.com
, і т.д.
Використовуйте методи addTo()
, addCc()
, або addBcc()
, щоб додати більше адрес:
1 2 3 4 5 6 7 8
$email = (new Email())
->to('foo@example.com')
->addTo('bar@example.com')
->cc('cc@example.com')
->addCc('cc2@example.com')
// ...
;
Як варіант, ви можете передати декілька адрес кожному методу:
1 2 3 4 5 6 7 8
$toAddresses = ['foo@example.com', new Address('bar@example.com')];
$email = (new Email())
->to(...$toAddresses)
->cc('cc1@example.com', 'cc2@example.com')
// ...
;
Заголовки повідомлень
Повідомлення містять деяку кількість полів заголовків, щоб описати їх зміст. Symfony встановлює всі заголовки автоматично, але ви також можете встановити власні заголовки. Існують різні типи заголовків (заголовок Id, заголовок Mailbox, заголовок Date і т.д.), але у більшості випадків ви будете встановлювати текстові заголовки:
1 2 3 4 5 6 7 8 9 10 11 12
$email = (new Email())
->getHeaders()
// цей заголовок повідомляє авто-відповідачам ("режим відпустки електронної пошти")
// не відповідати на це повідомлення, так як це автоматизований лист
->addTextHeader('X-Auto-Response-Suppress', 'OOF, DR, RN, NRN, AutoReply');
// використати масив, якщо ви хочете додати заголовок з багатьма значеннями
// (for example in the "References" or "In-Reply-To" header)
->addIdHeader('References', ['123@example.com', '456@example.com'])
// ...
;
Tip
Замість виклику ->addTextHeader()
кожний раз при створенні листа, ви можете
сконфігурувати листи глобально ,
щоб встановити одні й ті самі заголовки у всіх відправлених листах.
Зміст повідомлення
Текстовий та HTML-зміст повідомлень може бути рядками (зазвичай у результаті відображення якогось шаблону) або PHP-джерелами:
1 2 3 4 5 6 7 8 9 10
$email = (new Email())
// ...
// простий зміст, визначений як рядок
->text('Lorem ipsum...')
->html('<p>Lorem ipsum...</p>')
// прикріпити потік файлів
->text(fopen('/path/to/emails/user_signup.txt', 'r'))
->html(fopen('/path/to/emails/user_signup.html', 'r'))
;
Tip
Ви також можете використовувати шаблони Twig, щоб відобразити зміст тексту та HTML. Прочитайте розділ Twig: HTML & CSS далі в цій статті, щоб дізнатися більше.
Прикріплення файлів
Використайте метод addPart()
з BodyFile
, щоб додати файли, які існують у вашій
файловій системі:
1 2 3 4 5 6 7 8 9 10 11 12
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\File;
// ...
$email = (new Email())
// ...
->addPart(new DataPart(new File('/path/to/documents/terms-of-use.pdf')))
// опціонально ви можете вказати email-клієнтам відображати користувацьке імʼя для цього файлу
->addPart(new DataPart(new File('/path/to/documents/privacy.pdf'), 'Privacy Policy'))
// опціонально ви можете надати чіткий MIME-тип (інакше його не буде вгадано)
->addPart(new DataPart(new File('/path/to/documents/contract.doc'), 'Contract', 'application/msword'))
;
Як варіант, ви можете прикріпляти зміст з потоку шляхом передачі його напряму DataPart
:
1 2 3 4
$email = (new Email())
// ...
->addPart(new DataPart(fopen('/path/to/documents/contract.doc', 'r')))
;
Вбудовування зображень
Якщо ви хочете відобразити зображення всередині вашого листа, ви маєте їх вбудувати, а не додавати як вкладення. При використанні Twig для відображення змісту листа, як пояснюється далі в цій статті , зображення вбудовуються автоматично. В іншому випадку, вам потрібно буде вбудувати їх вручну.
Спочатку, використайте метод addPart()
, щоб додати зображення з файлу або потоку:
1 2 3 4 5 6 7
$email = (new Email())
// ...
// отримати зміст збораження з PHP-джерела
->addPart((new DataPart(fopen('/path/to/images/logo.png', 'r'), 'logo', 'image/png'))->asInline())
// отримати зміст зображення з існуючого файлу
->addPart((new DataPart(new File('/path/to/images/signature.gif'), 'footer-signature', 'image/gif'))->asInline())
;
Використайте метод asInline()
, щоб вбудувати зміст замість того, щоб прикріплювати його.
Другий необов'язковий аргумент обох методів - це ім'я зображення ("Content-ID" за стандартом MIME). Його значення - це довільний рядок, який використовується для того, щоб посилатися на зображення всередині HTML змісту:
1 2 3 4 5 6 7 8 9 10 11
$email = (new Email())
// ...
->addPart((new DataPart(fopen('/path/to/images/logo.png', 'r'), 'logo', 'image/png'))->asInline())
->addPart((new DataPart(new File('/path/to/images/signature.gif'), 'footer-signature', 'image/gif'))->asInline())
// посилайтеся на зображення, використовуючи синтаксис 'cid:' + "image embed name"
->html('<img src="cid:logo"> ... <img src="cid:footer-signature"> ...')
// використати такий самий синтаксис для зображень, доданих як фонові зображення HTML
->html('... <div background="cid:footer-signature"> ... </div> ...')
;
- Ви також можете використовувати метод DataPart::setContentId()
-
для визначення користувацького Content-ID для зображення і використання його як посилання на
cid
:1 2 3 4 5 6 7 8
$part = new DataPart(new File('/path/to/images/signature.gif')); $part->setContentId('footer-signature'); $email = (new Email()) // ... ->addPart($part->asInline()) ->html('... <img src="cid:footer-signature"> ...') ;
Конфігурація листів глобально
Замість виклику ->from()
по кожному листу, який ви створюєте, ви можете
сокнфігурувати це значення глобально, щоб воно було встановлене у всіх
відправлених листах. Те ж саме вірно і для ->to()
, і для заголовків.
1 2 3 4 5 6 7 8 9 10
# config/packages/mailer.yaml
framework:
mailer:
envelope:
sender: 'fabien@example.com'
recipients: ['foo@example.com', 'bar@example.com']
headers:
From: 'Fabien <fabien@example.com>'
Bcc: 'baz@example.com'
X-Custom-Header: 'foobar'
Caution
Деякі сторонні постачальники не підтримують використання ключових слів типу
from
у headers
. Перегляньте документацію вашого провайдера до встановлення
будь-якого глобального заголовку.
Обробка помилок відправлення
Symfony Mailer вважає відправку успішною, коли ваш транспорт (SMTP-сервер або сторонній постачальник) приймає лист для подальшої доставки. Повідомлення може бути втрачене або не доставлене пізніше через проблеми у вашому постачальнику, але це виходить за межі можливостей вашого додатку Symfony.
Якщо при передачі листа вашому транспорту сталася помилка, Symfony викликає TransportExceptionInterface. Виявіть виключення, щоб відновитися після помилки або для відображення якогось повідомлення:
1 2 3 4 5 6 7 8 9 10
use Symfony\Component\Mailer\Exception\TransportExceptionInterface;
$email = new Email();
// ...
try {
$mailer->send($email);
} catch (TransportExceptionInterface $e) {
// якась помилка завадила відправленню листа; відобразити повідомлення
// про помилку або спробувати відправити повідомлення знову
}
Налагодження листів
Об'єкт SentMessage, повернений методом send()
класу TransportInterface, надає доступ
до початкового повідомлення (getOriginalMessage()
) і до деякої інформації налагодження
(getDebug()
) на кшталт HTTP-викликів, зроблених HTTP-транспортом, що корисно для
налагодження помилок.
Note
Якщо у вашому коді використовувався MailerInterface, вам
потрібно замінити його на TransportInterface,
щоб отримати об'єкт SentMessage
.
Note
Деякі постачальники поштових програм змінюють Message-Id
при відправленні листа.
Метод getMessageId()
з SentMessage
завжди повертає визначений ID повідомлення
(той самий рандомний ID, згенерований Symfony, або новий ID, згенерований постачальником
поштової програми).
Виключення, пов'язані з транспортом поштової програми (які реалізують
TransportException) також
надають цю інформацію налагодження через метод getDebug()
.
Twig: HTML і CSS
Компонент Mime інтегрується з шаблонізатором Twig , щоб надати просунуті функції на кшталт вбудовування CSS-стилів та підтримки для фреймворків HTML/CSS, щоб створювати складні повідомлення HTML-листів. Спочатку переконайтеся, що Twig встановлено:
1 2 3 4
$ composer require symfony/twig-bundle
# або, якщо ви використовуєте компонент у додатку не на Symfony:
# composer require symfony/twig-bridge
HTML-зміст
Щоб визначити зміст вашого листа з Twig, використайте клас TemplatedEmail. Цей клас розширює нормальний клас Email, але додає деякі нові методі для шаблонів Twig:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
use Symfony\Bridge\Twig\Mime\TemplatedEmail;
$email = (new TemplatedEmail())
->from('fabien@example.com')
->to(new Address('ryan@example.com'))
->subject('Thanks for signing up!')
// шлях шаблону Twig для відображення
->htmlTemplate('emails/signup.html.twig')
// передайте змінні (ім'я => значення) шаблону
->context([
'expiration_date' => new \DateTime('+7 days'),
'username' => 'foo',
])
;
Потім створіть шаблон:
1 2 3 4 5 6 7 8 9 10 11 12
{# templates/emails/signup.html.twig #}
<h1>Welcome {{ email.toName }}!</h1>
<p>
You signed up as {{ username }} the following email:
</p>
<p><code>{{ email.to[0].address }}</code></p>
<p>
<a href="#">Click here to activate your account</a>
(this link is valid until {{ expiration_date|date('F jS') }})
</p>
Шаблон Twig має доступ до будь-яких параметрів, які передано в метод context()
класу TemplatedEmail
, а також спеціальної змінної під назвою email
, яка є
екземпляром WrappedTemplatedEmail.
Текстовий зміст
Коли текстовий зміст TemplatedEmail
не визначено чітко, поштова програма
автоматично згенерує його, перетворивши HTML-зміст у текст.
Symfony використовує наступну стратегію коли генерує текстову версію листа:
- Якщо було сконфігуровано чіткий перетворювач з HTML на текст (див. twig.mailer.html_to_text_converter ), вона викликає його;
- Якщо ні, і у вас встановлено league/html-to-markdown у вашому додатку, вона використовує його задля перетворення HTML на розмітку (щоб текстовий електронний лист був візуально привабливим);
- В інших випадках, вона застосовує PHP-функцію strip_tags до оригінального змісту HTML.
Якщо ви хочете визначити текстовий зміст самостійно, використайте метод
text()
, пояснений у попередніх розділах, або метод textTemplate()
,
наданий класом TemplatedEmail
:
1 2 3 4 5 6 7 8 9
+ use Symfony\Bridge\Twig\Mime\TemplatedEmail;
$email = (new TemplatedEmail())
// ...
->htmlTemplate('emails/signup.html.twig')
+ ->textTemplate('emails/signup.txt.twig')
// ...
;
Вбудовування зображень
Замість того, щоб розбиратися з синтаксисом <img src="cid: ...">
, який пояснювався у
попердніх розділах, при використанні Twig для відображення змісту листа, ви можете послатися
на файли зображень, як звичайно. Для початку, щоб все спростити, визначіть простір іменґ
Twig під назвою images
, який вказує на той каталог, де зберігаються ваші зображення:
1 2 3 4 5 6 7
# config/packages/twig.yaml
twig:
# ...
paths:
# вкажіть туди, де живуть ваші зображення
'%kernel.project_dir%/assets/images': images
Тепер використайте спеціального помічника Twig email.image()
, щоб вбудувати
зображення у зміст листа:
1 2 3 4 5
{# '@images/' відноситься до простору імен Twig, визначеного раніше #}
<img src="{{ email.image('@images/logo.png') }}" alt="Logo">
<h1>Welcome {{ email.toName }}!</h1>
{# ... #}
Вбудовування CSS-стилів
Дизайн HTML-змісту листа дуже відрізняється від дизайну звичайної HTML-сторінки. Перш
за все, більшість поштових клієнтів підтримують лише частину всіх CSS-функцій. Більш
того, популярні поштові клієнти, на кшталт Gmail, не підтримують визначальні стилі
всередині розділів <style> ... </style>
, і ви маєте вбудувати всі CSS-стилі.
Вбудовування CSS означає, що кожний HTML-тег має визначати атрибут style
, з усіма
його CSS-стилями. Це може сильно заплутати впорядкування вашого CSS. Тому Twig надає
CssInlinerExtension
, який автоматизує все для вас. Встановіть його:
1
$ composer require twig/extra-bundle twig/cssinliner-extra
Розширення вмикається автоматично. Щоб використати його, огорніть весь шаблон у
фільтр inline_css
:
1 2 3 4 5 6 7 8 9 10 11
{% apply inline_css %}
<style>
{# тут визначте ваші СSS-стилі, як звичайно #}
h1 {
color: #333;
}
</style>
<h1>Welcome {{ email.toName }}!</h1>
{# ... #}
{% endapply %}
Використання зовнішніх СSS-файлів
Ви також можете визначати СSS-стилі у зовнішніх файлах та передавати їх як аргументи фільтру:
1 2 3 4
{% apply inline_css(source('@styles/email.css')) %}
<h1>Welcome {{ username }}!</h1>
{# ... #}
{% endapply %}
Ви можете передати необмежену кількість аргументів до inline_css()
для
завантаження декількох CSS-файлів. Для того, щоб цей приклад працював, вам також
треба визначити новий простів імен Twig під назвою styles
, який вказує на
каталог, де живе email.css
:
1 2 3 4 5 6 7
# config/packages/twig.yaml
twig:
# ...
paths:
# вкажіть туди, де живуть ваші css-файли
'%kernel.project_dir%/assets/styles': styles
Відображення змісту з розміткою
Twig надає інше розширення під назвою MarkdownExtension
, яке дозволяє вам
визначати зміст листів з використанням синтаксису Markdown. Щоб використати
його, встановіть розширення та бібліотеку конверсій Markdown (розширення сумісне
з декількома популярними бібліотеками):
1 2
# замість league/commonmark, ви також можете використати erusev/parsedown або michelf/php-markdown
$ composer require twig/extra-bundle twig/markdown-extra league/commonmark
Розширення додає фільтр markdown_to_html
, який ви можете використати, щоб
перетворити частини або весь змісти листа з Markdown в HTML:
1 2 3 4 5 6 7 8 9
{% apply markdown_to_html %}
Welcome {{ email.toName }}!
===========================
Ви підписалися на наш сайт, використовуючи наступну адресу електронної пошти:
`{{ email.to[0].address }}`
[Натисніть тут, щоб активувати ваш акаунт]({{ url('...') }})
{% endapply %}
Мова шаблонізації листів Inky
Створення листів з чудовим дизайном, які працюють у кожному поштовому клієнті, настільки складне, що існують цілі фреймворки HTML/CSS, присвячені цьому. Один з найпопулярніших фреймворків називається Inky. Він визначає синтаксис, засновуючись на тегах типу HTML, які пізніше перетворюються у справжній HTML-код та відправляються користувачас:
1 2 3 4 5 6
<!-- спрощений приклад синтаксису Inky -->
<container>
<row>
<columns>This is a column.</columns>
</row>
</container>
Twig надає інтеграцію з Inky через InkyExtension
. Спочатку, встановіть
розширення у вашому додатку:
1
$ composer require twig/extra-bundle twig/inky-extra
Розширення додає фільтр inky_to_html
, який може бути використаний
для перетворення частин або всього змісту листа з Inky в HTML:
1 2 3 4 5 6 7 8 9 10 11 12
{% apply inky_to_html %}
<container>
<row class="header">
<columns>
<spacer size="16"></spacer>
<h1 class="text-center">Welcome {{ email.toName }}!</h1>
</columns>
{# ... #}
</row>
</container>
{% endapply %}
Ви можете скомбінувати всі фільтри, щоб створювати складні повідомлення листів:
1 2 3
{% apply inky_to_html|inline_css(source('@styles/foundation-emails.css')) %}
{# ... #}
{% endapply %}
Це використовує простір імен стилів Twig , який ми
створили раніше. Ви могли б, наприклад, завантажити файл foundation-emails.css
прямо з GitHub і зберегти його в assets/styles
.
Цифровий підпис та кодування повідомлень
Існує можливість цифрового підписання та/або кодування повідомлень електронних листів для посилення їхньої цілісності/безпеки. Обидві опції можна об'єднати для кодування повідомлення з електронним підписом та/або цифрофого підписання закодованого повідомлення.
Перед тим, як підписувати/закодовувати повідомлення, переконайтеся, що у вас є:
- Правильно встановлене та сконфігуроване розширення OpenSSL PHP ;
- Валідний сертифікат безпеки S/MIME.
Tip
При використанні OpenSSL для генерування сертифікатів, не забудьте додати
опцію команди -addtrust emailProtection
.
Caution
Підписання та шифрування повідомлень вимагає повного відображення їх змісту. Наприклад, зміст шаблонізованих листів обробляється за допомогою MessageListener. Отже, якщо ви хочете підписати та/або зашифрувати таке повідомлення, вам потрібно зробити це в слухачі MessageEvent, запущеному після нього (вам потрібно встановити від'ємний пріоритет для вашого слухача).
Цифровий підпис повідомлень
При додаванні цифрового підпису до повідомлення, генерується криптографічний хеш для всього змісту повідомелння (включно зі вкладеннями). Цей хеш додається як вкладення, щоб отримувач міг валідувати цілісність отриманого повідомлення. Однак, зміст початкового повідомлення залишається читаним для поштових агентів, які не підтримують підписані повідомлення, тому ви також маєте закодувати повідомлення, якщо ви хочете приховати його зміст.
Ви можете підписувати повідомлення використовуючи S/MIME
або DKIM
. В обох
випадках, сертифікат та приватний ключ мають бути PEM-закодовані, і можут бути
або створені з використанням, наприклад, OpenSSL, або отримані в офіційной
Сертифікаційної компанії (CA).
Отримувач листа повинен мати сертифікат CA у списку довірених осіб, щоб верифікувати
підпис.
Caution
Якщо ви використовуєте підпис повідомлення, надсилання до Bcc
буде вилучено з
повідомлення. Якщо вам потрібно надіслати повідомлення кільком одержувачам, вам потрібно
обчислити новий підпис для кожного одержувача.
Підпис S/MIME
S/MIME стандартна для кодування публічних ключів та підписання даних MIME. Вона вимагає використання як сертифікату, так і приватного ключа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14
use Symfony\Component\Mime\Crypto\SMimeSigner;
use Symfony\Component\Mime\Email;
$email = (new Email())
->from('hello@example.com')
// ...
->html('...');
$signer = new SMimeSigner('/path/to/certificate.crt', '/path/to/certificate-private-key.key');
// якщо приватний ключ має кодову фразу, передайте її як третій аргумент
// new SMimeSigner('/path/to/certificate.crt', '/path/to/certificate-private-key.key', 'the-passphrase');
$signedEmail = $signer->sign($email);
// тепер використайте компонент Mailer, щоб відправити цей $signedEmail замість початкового листа
Tip
Клас SMimeSigner
визначає інші необов'язкові аргументи для передачі
проміжкових сертифікатів та для конфігурації процесу підписання, використовуючи
побітні опції оператору для PHP-функції openssl_pkcs7_sign.
Підпис DKIM
DKIM - це метод аутентифікації листа, який додає цифровий підпис, що посилається на основний домен, до кожного вихідного листа. Він вимагає приватний ключ, але не сертифікат:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
use Symfony\Component\Mime\Crypto\DkimSigner;
use Symfony\Component\Mime\Email;
$email = (new Email())
->from('hello@example.com')
// ...
->html('...');
// перший аргумент: такий же, як openssl_pkey_get_private(), або рядок зі змістом
// приватного ключа, або абсолютний шлях до нього (з префіксом 'file://')
// другий і третій аргументи: ім'я домену та "селектор", використовувания для виконання пошуку DNS
// (селектор - це рядок, використовуваний для вказання на конкретний запис публічного ключа DKIM у вашій DNS)
$signer = new DkimSigner('file:///path/to/private-key.key', 'example.com', 'sf');
// якщо приватний ключ має кодову фразу, передайте її як п'ятий аргумент
// new DkimSigner('file:///path/to/private-key.key', 'example.com', 'sf', [], 'the-passphrase');
$signedEmail = $signer->sign($email);
// тепер використайте компонент Mailer, щоб відправити цей $signedEmail замість початкового листа
// підпис DKIM надає багато опцій конфігурації та об'єкт помічника для їхнього конфігурування
use Symfony\Component\Mime\Crypto\DkimOptions;
$signedEmail = $signer->sign($email, (new DkimOptions())
->bodyCanon('relaxed')
->headerCanon('relaxed')
->headersToIgnore(['Message-ID'])
->toArray()
);
Кодування повідомлень
При кодуванні повідомлення, все повідомлення (включно із вкладеннями) кодується з використанням сертифікату. Отже, тільки отримувачі, які мають відповідний приватний ключ, можуть прочитати зміст початкового повідомлення:
1 2 3 4 5 6 7 8 9 10 11
use Symfony\Component\Mime\Crypto\SMimeEncrypter;
use Symfony\Component\Mime\Email;
$email = (new Email())
->from('hello@example.com')
// ...
->html('...');
$encrypter = new SMimeEncrypter('/path/to/certificate.crt');
$encryptedEmail = $encrypter->encrypt($email);
// тепер використайте компонент Mailer, щоб відправити цей $encryptedEmail замість початкового листа
Ви можете передати більше одного сертифікату конструктору SMimeEncrypter
і
він обере відповідний сертифікат, в залежності від опції To
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
$firstEmail = (new Email())
// ...
->to('jane@example.com');
$secondEmail = (new Email())
// ...
->to('john@example.com');
// другий необов'язковий аргумент SMimeEncrypter визначає, який алгоритм кодування використовується
// (має бути однією з цих констант: https://www.php.net/manual/en/openssl.ciphers.php)
$encrypter = new SMimeEncrypter([
// ключ = отримувач листа; значення = шлях до файлу сертифікату
'jane@example.com' => '/path/to/first-certificate.crt',
'john@example.com' => '/path/to/second-certificate.crt',
]);
$firstEncryptedEmail = $encrypter->encrypt($firstEmail);
$secondEncryptedEmail = $encrypter->encrypt($secondEmail);
Декілька транспортів листів
Ви можете захотіти використати більше одного транспорту поштової програми для
доставки ваших повідомлень. Це можна сконфігурувати, замінивши запис конфігурації
dsn
на запис transports
, наступним чином:
1 2 3 4 5 6
# config/packages/mailer.yaml
framework:
mailer:
transports:
main: '%env(MAILER_DSN)%'
alternative: '%env(MAILER_DSN_IMPORTANT)%'
За замовчуванням використовується перший транспорт. Інші транспорти можуть бути використані
шляхом додавання до листа текстового заголовку X-Transport
:
1 2 3 4 5 6
// Відправити, використовуючи перший "головний" транспорт ...
$mailer->send($email);
// ... або використати "альтернативний"
$email->getHeaders()->addTextHeader('X-Transport', 'alternative');
$mailer->send($email);
Асинхронне відправлення повідомлень
Коли ви викликаєте $mailer->send($email)
, лист одразу ж відправляється транспорту. Для
покращення продуктивності, ви можете використати переваги Месенджеру,
щоб відправляти повідомлення пізніше через транспорт Месенджера.
Почніть, слідуючи документації Месенджеру та сконфігурувавши
транспорт. Коли все буде налаштовано, при виклику $mailer->send()
, повідомлення
SendEmailMessage буде запущене через
автобус повідомлень за замовчуванням (messenger.default_bus
). Якщо припустити, що
у вас є транспорт під назвою async
, ви можете маршрутизувати повідомлення у нього:
1 2 3 4 5 6 7 8
# config/packages/messenger.yaml
framework:
messenger:
transports:
async: "%env(MESSENGER_TRANSPORT_DSN)%"
routing:
'Symfony\Component\Mailer\Messenger\SendEmailMessage': async
Завдяки цьому, замість негайної доставки, повідомлення будуть відправлені транспорту для обробки пізніше (див. ). Відмітьте, що "відображення" листа (обчислені заголовки, відображення тіла, ...) також відкладено і відбудеться прямо перед відпарвкою листа обробником Messenger.
При відправленні листа асинхронно, його езкемпляр повинен бути серіалізовуваним.
Це завжди так для екземплярів Email, але
при відправці TemplatedEmail, ви повинні
гарантувати, що context
серіалізовуваний. Якщо у вас є несеріалізовувані змінні,
типу сутностей Doctrine, або замініть їх на більш конкретні змінні, або відобразіть
лист перед викликом $mailer->send($email)
:
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\BodyRendererInterface;
public function action(MailerInterface $mailer, BodyRendererInterface $bodyRenderer): void
{
$email = (new TemplatedEmail())
->htmlTemplate($template)
->context($context)
;
$bodyRenderer->render($email);
$mailer->send($email);
}
Ви можете сконфігурувати, який автобус використовується для запуску повідомлення,
використовуючи опцію message_bus
. Ви також можете встановити її як false
,
щоб викликати транспорт Mailer напряму та відключити асинхронну доставку.
1 2 3 4
# config/packages/mailer.yaml
framework:
mailer:
message_bus: app.another_bus
Note
У випадку довготривалих скриптів і коли Mailer використовує
SmtpTransport, ви
можете відключитися від SMTP-сервера, щоб уникнути відкритого зʼєднання
з SMTP-сервером між відправкою листів. Ви можете зробити це, використавши
метод stop()
.
Ви також можете обрати транспоррт, додавши заголовок X-Bus-Transport
(який
буде автоматично видалено з фінального повідомлення):
1 2 3
// Використати автобус транспорту "app.another_bus":
$email->getHeaders()->addTextHeader('X-Bus-Transport', 'app.another_bus');
$mailer->send($email);
Додавання тегів та метаданих до листів
Певні сторонні транспорти підтримують теги та метадані листів, що може бути використано для групування, відслідковування і робочих процесів. Ви можете додати їх, використовуючи класи TagHeader та MetadataHeader. Якщо ваш транспорт підтримує заголовки, він перетворить їх у відповідний формат:
1 2 3 4 5 6
use Symfony\Component\Mailer\Header\MetadataHeader;
use Symfony\Component\Mailer\Header\TagHeader;
$email->getHeaders()->add(new TagHeader('password-reset'));
$email->getHeaders()->add(new MetadataHeader('Color', 'blue'));
$email->getHeaders()->add(new MetadataHeader('Client-ID', '12345'));
Якщо ваш транспорт не підтримує теги та метадані, вони будуть додані у вигляді користувацьких заголовків:
1 2 3
X-Tag: password-reset
X-Metadata-Color: blue
X-Metadata-Client-ID: 12345
На даний момент, наступний транспорт підтримує теги та метадані:
- MailChimp
- Mailgun
- Postmark
- Sendgrid
- Sendinblue
Наступний транспорт підтримує лише теги:
- MailPace
Наступний трнаспорт підтримує лише метадані:
- Amazon SES (відмітьте, що Amazon називає цю функцію "тегами", але Symfony називає це "метаданими" через те, що вони містять ключ та значення)
Чернетки електронних листів
DraftEmail - це сеціальний екземпляр
Email. Його ціль полягає в тому, щоб побудувати
електронний лист (з тілом, вкладеннями та ін.) та зробити його доступним для
завантаження як .eml
із заголовком X-Unsent
. Багато email-клієнтів можуть
відкривати ці файли та взаємодіяти з ними, як з "чернетками електронного листа". Ви
можете використати їх, щоб створювати просунуті посилання mailto:
.
Here's an example of making one available to download:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
// src/Controller/DownloadEmailController.php
namespace App\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\HttpFoundation\ResponseHeaderBag;
use Symfony\Component\Mime\DraftEmail;
use Symfony\Component\Routing\Attribute\Route;
class DownloadEmailController extends AbstractController
{
#[Route('/download-email')]
public function __invoke(): Response
{
$message = (new DraftEmail())
->html($this->renderView(/* ... */))
->addPart(/* ... */)
;
$response = new Response($message->toString());
$contentDisposition = $response->headers->makeDisposition(
ResponseHeaderBag::DISPOSITION_ATTACHMENT,
'download.eml'
);
$response->headers->set('Content-Type', 'message/rfc822');
$response->headers->set('Content-Disposition', $contentDisposition);
return $response;
}
}
Note
Так як DraftEmail можуть бути створені без Кому/Від, вони не можуть бути відправлені без Mailer.
Події Mailer
MessageEvent
Клас події: MessageEvent
MessageEvent
дозволяє змінювати повідомлення Mailer та конверт перед відправкою
листа:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\Event\MessageEvent;
use Symfony\Component\Mime\Email;
public function onMessage(MessageEvent $event): void
{
$message = $event->getMessage();
if (!$message instanceof Email) {
return;
}
// зробити щось з повідомленням (логування, ...)
// та/або додати якісь штампи Мессенджеру
$event->addStamp(new SomeMessengerStamp());
}
Якщо ви хочете запобігти відправці повідомлення, викличте reject()
(це
також зупинить розповсюдження події):
1 2 3 4 5 6
use Symfony\Component\Mailer\Event\MessageEvent;
public function onMessage(MessageEvent $event): void
{
$event->reject();
}
Виконайте цю команду, щоб дізнатися, які слухачі зареєстровані для цієї події, та їх пріоритети:
1
$ php bin/console debug:event-dispatcher "Symfony\Component\Mailer\Event\MessageEvent"
SentMessageEvent
Клас події: SentMessageEvent
SentMessageEvent
дозволяє вам діяти у класі SentMessage,
щоб отримати доступ до початкового повідомлення (getOriginalMessage()
) та деякої
інформації налагодження (getDebug()
), такої як HTTP-виклики, зроблені транспортом
HTTP, що корисно для налагодження помилок:
1 2 3 4 5 6 7 8 9 10 11 12 13
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\Event\SentMessageEvent;
use Symfony\Component\Mailer\SentMessage;
public function onMessage(SentMessageEvent $event): void
{
$message = $event->getMessage();
if (!$message instanceof SentMessage) {
return;
}
// зробити щось з повідомленням
}
Виконайте цю команду, щоб дізнатися, які слухачі зареєстровані для цієї події, та їх пріоритети:
1
$ php bin/console debug:event-dispatcher "Symfony\Component\Mailer\Event\SentMessageEvent"
FailedMessageEvent
Клас події: FailedMessageEvent
FailedMessageEvent
дозволяє діяти у початковому повідомленні у випадку невдачі:
1 2 3 4 5 6 7 8 9 10
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\Event\FailedMessageEvent;
public function onMessage(FailedMessageEvent $event): void
{
// наприклад, ви можете отримати більше інформації про цю помилку при відправці листа
$event->getError();
// зробити щось з повідомленням
}
Виконайте цю команду, щоб дізнатися, які слухачі зареєстровані для цієї події, та їх пріоритети:
1
$ php bin/console debug:event-dispatcher "Symfony\Component\Mailer\Event\FailedMessageEvent"
Розробка та налагодження
Підключення Email Catcher
При локальній розробці рекомендовано використовувати email catcher. Якщо ви включили підтримку Docker через рецепти Symfony, email catcher конфігурується автоматично. На додаток, якщо ви використовуєте локальний веб-сервер Symfony, mailer DSN автоматично розкриваєтьсся через бінарну інтеграцію symfony з Docker .
Відправка тестових електронних листів
Symfony надає команду для відправки листів, яка корисна під час розробки, щоб тестувати, чи правильно працює відправка електронних листів:
1 2 3
# єдиний обовʼязковий аргумент - це адреса отримувача
# (перегляньте допомогу команди, щоб дізнатися про її опції)
$ php bin/console mailer:test someone@example.com
Ця команда обходить автобус Messenger, якщо його сконфігуровано, щоб полегшити тестування листів, навіть якщо споживач Messenger не працює.
Відключення доставки
Під час розробки (або тестування), ви можете захотіти відключити доставку повідомлень
повністю. Ви можете зробити це, використовуючи null://null
як DSN поштової програми,
або у ваших файлах конфігурації .env , або у
файлі конфігурації поштової програми (наприклад, у середовищах dev
або test
):
1 2 3 4 5
# config/packages/mailer.yaml
when@dev:
framework:
mailer:
dsn: 'null://null'
Note
Якщо ви використовуєете Месенджер та маршрутизуєте його до транспорту, повідомлення всеодно буде відправлене цьому транспорту.
Постійна відправка листа за однією адресою
Замість повного відключення доставки, ви можете захотіти завжди відправляти листи за однією конкретною адресою, замість справжньої адреси:
1 2 3 4 5 6
# config/packages/mailer.yaml
when@dev:
framework:
mailer:
envelope:
recipients: ['youremail@example.com']
Використовуйте опцію allowed_recipients
, щоб вказати винятки з поведінки, визначеної
в опції recipients
; дозволяючи листам, спрямованим до цих конкретних одержувачів,
зберігати свою початкову адресу:
1 2 3 4 5 6 7 8 9 10
# config/packages/mailer.yaml
when@dev:
framework:
mailer:
envelope:
recipients: ['youremail@example.com']
allowed_recipients:
- 'internal@example.com'
# ви також можете використати регулярний вираз для визначення дозволених отримувачів
- 'internal-.*@example.(com|fr)'
З цією конфігурацією всі листи будуть надсилатися на youremail@example.com
,
окрім тих, що надсилаються на internal@example.com
, internal-monitoring@example.fr
і т.д., які будуть отримувати листи як зазвичай.
7.1
Опція allowed_recipients
була представлена в Symfony 7.1.
Напишіть функціональний тест
Щоб функціонально протестувати відправку електронного листа, і навіть ствердити зміст або заголовки листа, ви можете використати вбудовані ствердження:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
// tests/Controller/MailControllerTest.php
namespace App\Tests\Controller;
use Symfony\Bundle\FrameworkBundle\Test\WebTestCase;
class MailControllerTest extends WebTestCase
{
public function testMailIsSentAndContentIsOk(): void
{
$client = static::createClient();
$client->request('GET', '/mail/send');
$this->assertResponseIsSuccessful();
$this->assertEmailCount(1); // use assertQueuedEmailCount() when using Messenger
$email = $this->getMailerMessage();
$this->assertEmailHtmlBodyContains($email, 'Welcome');
$this->assertEmailTextBodyContains($email, 'Welcome');
}
}
Tip
Якщо ваш контролер повертає відповідь перенаправлення після відправлення листа, переконайтеся, що ваш клієнт не слідує за перенаправленнями. Ядро перезавантажиться після перенаправлення, і повідомлення буде втрачено з обробника події.