- Введение
- Генерация Mailables
- Создание Mailables
- Markdown Mailables
- Отправка почты
- Рендеринг Mailables
- Локализация Mailables
- Тестирование
- Почта и локальная разработка
- События
- Пользовательские транспорты
#Введение
Отправка электронной почты не должна быть сложной. Laravel предоставляет чистый и простой API для работы с почтой на базе популярного компонента Symfony Mailer. Laravel и Symfony Mailer поддерживают драйверы для отправки почты через SMTP, Mailgun, Postmark, Amazon SES и sendmail, что позволяет быстро начать отправлять почту через локальный или облачный сервис по вашему выбору.
#Настройка
Почтовые сервисы Laravel настраиваются через конфигурационный файл вашего приложения config/mail.php. Каждый настроенный в этом файле mailer может иметь свою уникальную конфигурацию и даже собственный "транспорт", что позволяет вашему приложению использовать разные почтовые сервисы для отправки различных сообщений. Например, ваше приложение может использовать Postmark для отправки транзакционных писем и Amazon SES для массовой рассылки.
В конфигурационном файле mail вы найдете массив mailers. Этот массив содержит пример конфигурации для каждого из основных драйверов / транспортов, поддерживаемых Laravel, а значение default определяет, какой mailer будет использоваться по умолчанию при отправке почты вашим приложением.
#Требования к драйверам / транспортам
Драйверы на базе API, такие как Mailgun, Postmark и MailerSend, часто проще и быстрее, чем отправка почты через SMTP-серверы. По возможности мы рекомендуем использовать один из этих драйверов.
#Драйвер Mailgun
Для использования драйвера Mailgun установите транспорт Mailgun Mailer Symfony через Composer:
composer require symfony/mailgun-mailer symfony/http-client
Затем установите опцию default в конфигурационном файле вашего приложения config/mail.php в значение mailgun. После настройки mailer по умолчанию убедитесь, что в вашем файле config/services.php присутствуют следующие параметры:
'mailgun' => [
'transport' => 'mailgun',
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
],
Если вы не используете регион Mailgun в Соединенных Штатах Mailgun region, вы можете определить конечную точку вашего региона в конфигурационном файле services:
'mailgun' => [
'domain' => env('MAILGUN_DOMAIN'),
'secret' => env('MAILGUN_SECRET'),
'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
],
#Драйвер Postmark
Для использования драйвера Postmark установите транспорт Postmark Mailer Symfony через Composer:
composer require symfony/postmark-mailer symfony/http-client
Затем установите опцию default в конфигурационном файле вашего приложения config/mail.php в значение postmark. После настройки mailer по умолчанию убедитесь, что в вашем файле config/services.php присутствуют следующие параметры:
'postmark' => [
'token' => env('POSTMARK_TOKEN'),
],
Если вы хотите указать поток сообщений Postmark, который должен использоваться конкретным mailer, вы можете добавить параметр конфигурации message_stream_id в массив конфигурации mailer. Этот массив находится в конфигурационном файле вашего приложения config/mail.php:
'postmark' => [
'transport' => 'postmark',
'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
],
Таким образом, вы можете настроить несколько mailer Postmark с разными потоками сообщений.
#Драйвер SES
Для использования драйвера Amazon SES необходимо сначала установить Amazon AWS SDK для PHP. Вы можете установить эту библиотеку через менеджер пакетов Composer:
composer require aws/aws-sdk-php
Затем установите опцию default в конфигурационном файле config/mail.php в значение ses и убедитесь, что в вашем файле config/services.php присутствуют следующие параметры:
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
],
Для использования временных учетных данных AWS через токен сессии вы можете добавить ключ token в конфигурацию SES вашего приложения:
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'token' => env('AWS_SESSION_TOKEN'),
],
Если вы хотите определить дополнительные параметры, которые Laravel должен передавать методу SendEmail AWS SDK при отправке почты, вы можете определить массив options в конфигурации ses:
'ses' => [
'key' => env('AWS_ACCESS_KEY_ID'),
'secret' => env('AWS_SECRET_ACCESS_KEY'),
'region' => env('AWS_DEFAULT_REGION', 'us-east-1'),
'options' => [
'ConfigurationSetName' => 'MyConfigurationSet',
'EmailTags' => [
['Name' => 'foo', 'Value' => 'bar'],
],
],
],
#Драйвер MailerSend
MailerSend — сервис для транзакционной почты и SMS, поддерживает собственный API-драйвер для Laravel. Пакет с драйвером можно установить через менеджер пакетов Composer:
composer require mailersend/laravel-driver
После установки пакета добавьте переменную окружения MAILERSEND_API_KEY в файл .env вашего приложения. Кроме того, переменная окружения MAIL_MAILER должна быть установлена в значение mailersend:
MAIL_MAILER=mailersend
MAIL_FROM_ADDRESS=app@yourdomain.com
MAIL_FROM_NAME="App Name"
MAILERSEND_API_KEY=your-api-key
Чтобы узнать больше о MailerSend, включая использование хостинг-шаблонов, ознакомьтесь с документацией драйвера MailerSend.
#Настройка резервного варианта
Иногда внешний сервис, который вы настроили для отправки почты вашего приложения, может быть недоступен. В таких случаях полезно определить один или несколько резервных вариантов доставки почты, которые будут использоваться, если основной драйвер доставки недоступен.
Для этого следует определить mailer в конфигурационном файле mail вашего приложения, который использует транспорт failover. Массив конфигурации для mailer failover должен содержать массив mailers, указывающий порядок выбора настроенных mailer для доставки:
'mailers' => [
'failover' => [
'transport' => 'failover',
'mailers' => [
'postmark',
'mailgun',
'sendmail',
],
],
// ...
],
После определения mailer failover, установите его как mailer по умолчанию, указав его имя в значении ключа default в конфигурационном файле mail вашего приложения:
'default' => env('MAIL_MAILER', 'failover'),
#Настройка Round Robin
Транспорт roundrobin позволяет распределять нагрузку по отправке почты между несколькими mailer. Для начала определите mailer в конфигурационном файле mail вашего приложения, который использует транспорт roundrobin. Массив конфигурации для mailer roundrobin должен содержать массив mailers, указывающий, какие настроенные mailer будут использоваться для доставки:
'mailers' => [
'roundrobin' => [
'transport' => 'roundrobin',
'mailers' => [
'ses',
'postmark',
],
],
// ...
],
После определения mailer roundrobin, установите его как mailer по умолчанию, указав его имя в значении ключа default в конфигурационном файле mail вашего приложения:
'default' => env('MAIL_MAILER', 'roundrobin'),
Транспорт round robin выбирает случайный mailer из списка настроенных mailer, а затем переключается на следующий доступный mailer для каждой последующей почты. В отличие от транспорта failover, который обеспечивает высокую доступность, транспорт roundrobin обеспечивает балансировку нагрузки.
#Генерация Mailables
При разработке приложений на Laravel каждый тип отправляемой почты представлен классом "mailable". Эти классы хранятся в директории app/Mail. Не беспокойтесь, если вы не видите эту директорию в вашем приложении — она будет создана автоматически при создании первого mailable класса с помощью Artisan-команды make:mail:
php artisan make:mail OrderShipped
#Создание Mailables
После генерации класса mailable откройте его, чтобы изучить содержимое. Конфигурация mailable класса выполняется в нескольких методах, включая envelope, content и attachments.
Метод envelope возвращает объект Illuminate\Mail\Mailables\Envelope, который определяет тему и, иногда, получателей сообщения. Метод content возвращает объект Illuminate\Mail\Mailables\Content, который определяет Blade шаблон, используемый для генерации содержимого сообщения.
#Настройка отправителя
#Использование Envelope
Сначала рассмотрим настройку отправителя письма, то есть адреса, от которого будет отправлено письмо. Существует два способа настройки отправителя. Во-первых, вы можете указать адрес "from" в конверте сообщения:
use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;
/**
* Получить конверт сообщения.
*/
public function envelope(): Envelope
{
return new Envelope(
from: new Address('jeffrey@example.com', 'Jeffrey Way'),
subject: 'Order Shipped',
);
}
Если хотите, вы также можете указать адрес replyTo:
return new Envelope(
from: new Address('jeffrey@example.com', 'Jeffrey Way'),
replyTo: [
new Address('taylor@example.com', 'Taylor Otwell'),
],
subject: 'Order Shipped',
);
#Использование глобального адреса from
Если ваше приложение использует один и тот же адрес "from" для всех писем, неудобно указывать его в каждом mailable классе. Вместо этого вы можете задать глобальный адрес "from" в конфигурационном файле config/mail.php. Этот адрес будет использоваться, если в mailable классе не указан другой адрес "from":
'from' => [
'address' => env('MAIL_FROM_ADDRESS', 'hello@example.com'),
'name' => env('MAIL_FROM_NAME', 'Example'),
],
Кроме того, вы можете определить глобальный адрес "reply_to" в конфигурационном файле config/mail.php:
'reply_to' => ['address' => 'example@example.com', 'name' => 'App Name'],
#Настройка представления
В методе content класса mailable вы можете определить view — шаблон, который будет использоваться для рендеринга содержимого письма. Поскольку каждое письмо обычно использует Blade шаблон для генерации содержимого, вы получаете всю мощь и удобство Blade при создании HTML письма:
/**
* Получить определение содержимого сообщения.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
Вы можете создать директорию resources/views/emails для хранения всех шаблонов писем; однако вы свободны размещать их в любом месте внутри директории resources/views.
#Текстовые письма
Если вы хотите определить текстовую версию письма, вы можете указать шаблон для plain-text при создании определения Content сообщения. Как и параметр view, параметр text должен содержать имя шаблона, который будет использоваться для рендеринга текстового содержимого письма. Вы можете определить как HTML, так и текстовую версии письма:
/**
* Получить определение содержимого сообщения.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
text: 'mail.orders.shipped-text'
);
}
Для ясности, параметр html может использоваться как псевдоним параметра view:
return new Content(
html: 'mail.orders.shipped',
text: 'mail.orders.shipped-text'
);
#Данные для представления
#Через публичные свойства
Обычно вы захотите передать данные в представление, чтобы использовать их при рендеринге HTML письма. Есть два способа сделать данные доступными в представлении. Во-первых, любые публичные свойства, определённые в вашем mailable классе, автоматически становятся доступными в представлении. Например, вы можете передать данные в конструктор mailable класса и присвоить их публичным свойствам класса:
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* Создать новый экземпляр сообщения.
*/
public function __construct(
public Order $order,
) {}
/**
* Получить определение содержимого сообщения.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
);
}
}
После присвоения данных публичному свойству, они автоматически становятся доступны в вашем представлении, и вы можете обращаться к ним так же, как к любым другим данным в ваших Blade шаблонах:
<div>
Цена: {{ $order->price }}
</div>
#Через параметр with:
Если вы хотите изменить формат данных письма перед передачей в шаблон, вы можете вручную передать данные в представление через параметр with определения Content. Обычно вы всё равно передаете данные через конструктор mailable класса, но в этом случае следует сделать свойства protected или private, чтобы данные не были автоматически доступны в шаблоне:
<?php
namespace App\Mail;
use App\Models\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Mail\Mailable;
use Illuminate\Mail\Mailables\Content;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable
{
use Queueable, SerializesModels;
/**
* Создать новый экземпляр сообщения.
*/
public function __construct(
protected Order $order,
) {}
/**
* Получить определение содержимого сообщения.
*/
public function content(): Content
{
return new Content(
view: 'mail.orders.shipped',
with: [
'orderName' => $this->order->name,
'orderPrice' => $this->order->price,
],
);
}
}
После передачи данных через метод with, они автоматически становятся доступны в вашем представлении, и вы можете обращаться к ним так же, как к любым другим данным в ваших Blade шаблонах:
<div>
Цена: {{ $orderPrice }}
</div>
#Вложения
Чтобы добавить вложения к письму, добавьте их в массив, возвращаемый методом attachments сообщения. Во-первых, вы можете добавить вложение, указав путь к файлу с помощью метода fromPath класса Attachment:
use Illuminate\Mail\Mailables\Attachment;
/**
* Получить вложения для сообщения.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromPath('/path/to/file'),
];
}
При добавлении файлов к сообщению вы также можете указать отображаемое имя и/или MIME-тип вложения с помощью методов as и withMime:
/**
* Получить вложения для сообщения.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromPath('/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
#Вложение файлов с диска
Если файл сохранён на одном из ваших дисков файловой системы, вы можете прикрепить его к письму с помощью метода вложения fromStorage:
/**
* Получить вложения для сообщения.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorage('/path/to/file'),
];
}
Конечно, вы также можете указать имя вложения и MIME-тип:
/**
* Получить вложения для сообщения.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorage('/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
Метод fromStorageDisk можно использовать, если нужно указать диск хранения, отличный от диска по умолчанию:
/**
* Получить вложения для сообщения.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromStorageDisk('s3', '/path/to/file')
->as('name.pdf')
->withMime('application/pdf'),
];
}
#Вложения из необработанных данных
Метод вложения fromData позволяет прикрепить необработанную строку байтов в качестве вложения. Например, вы можете использовать этот метод, если сгенерировали PDF в памяти и хотите прикрепить его к письму без записи на диск. Метод fromData принимает замыкание, которое возвращает необработанные данные и имя, которое будет присвоено вложению:
/**
* Получить вложения для сообщения.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [
Attachment::fromData(fn () => $this->pdf, 'Report.pdf')
->withMime('application/pdf'),
];
}
#Встроенные вложения
Встраивание встроенных изображений в письма обычно неудобно; однако Laravel предоставляет удобный способ прикреплять изображения к письмам. Чтобы встроить изображение, используйте метод embed на переменной $message внутри шаблона письма. Laravel автоматически делает переменную $message доступной во всех шаблонах писем, поэтому вам не нужно передавать её вручную:
<body>
Here is an image:
<img src="{{ $message->embed($pathToImage) }}">
</body>
Переменная $message недоступна в шаблонах текстовых сообщений, так как plain-text сообщения не используют встроенные вложения.
#Встраивание вложений из необработанных данных
Если у вас уже есть строка с необработанными данными изображения, которую вы хотите встроить в шаблон письма, вы можете вызвать метод embedData на переменной $message. При вызове embedData необходимо указать имя файла, которое будет присвоено встроенному изображению:
<body>
Here is an image from raw data:
<img src="{{ $message->embedData($data, 'example-image.jpg') }}">
</body>
#Объекты для вложения
Хотя прикрепление файлов через простые строковые пути часто достаточно, во многих случаях объекты для вложения в вашем приложении представлены классами. Например, если ваше приложение прикрепляет фотографию к сообщению, у вас может быть модель Photo, представляющая эту фотографию. В таком случае было бы удобно просто передать объект Photo в метод attach. Объекты для вложения позволяют сделать именно это.
Для начала реализуйте интерфейс Illuminate\Contracts\Mail\Attachable в классе объекта, который будет прикрепляться к сообщениям. Этот интерфейс требует, чтобы ваш класс определял метод toMailAttachment, возвращающий экземпляр Illuminate\Mail\Attachment:
<?php
namespace App\Models;
use Illuminate\Contracts\Mail\Attachable;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Mail\Attachment;
class Photo extends Model implements Attachable
{
/**
* Получить представление объекта для вложения в почту.
*/
public function toMailAttachment(): Attachment
{
return Attachment::fromPath('/path/to/file');
}
}
После определения объекта для вложения вы можете возвращать экземпляр этого объекта из метода attachments при формировании сообщения:
/**
* Получить вложения для сообщения.
*
* @return array<int, \Illuminate\Mail\Mailables\Attachment>
*/
public function attachments(): array
{
return [$this->photo];
}
Конечно, данные вложений могут храниться на удалённом файловом сервисе, например Amazon S3. Laravel также позволяет создавать экземпляры вложений из данных, хранящихся на одном из дисков файловой системы вашего приложения:
// Создать вложение из файла на диске по умолчанию...
return Attachment::fromStorage($this->path);
// Создать вложение из файла на конкретном диске...
return Attachment::fromStorageDisk('backblaze', $this->path);
Кроме того, вы можете создавать экземпляры вложений из данных, которые есть в памяти. Для этого передайте замыкание в метод fromData. Замыкание должно возвращать необработанные данные вложения:
return Attachment::fromData(fn () => $this->content, 'Photo Name');
Laravel также предоставляет дополнительные методы для настройки вложений. Например, вы можете использовать методы as и withMime для настройки имени файла и MIME-типа:
return Attachment::fromPath('/path/to/file')
->as('Photo Name')
->withMime('image/jpeg');
#Заголовки
Иногда необходимо добавить дополнительные заголовки к исходящему сообщению. Например, вы можете захотеть установить пользовательский Message-Id или другие произвольные текстовые заголовки.
Для этого определите метод headers в вашем mailable. Метод headers должен возвращать экземпляр Illuminate\Mail\Mailables\Headers. Этот класс принимает параметры messageId, references и text. Разумеется, вы можете указать только те параметры, которые нужны для конкретного сообщения:
use Illuminate\Mail\Mailables\Headers;
/**
* Получить заголовки сообщения.
*/
public function headers(): Headers
{
return new Headers(
messageId: 'custom-message-id@example.com',
references: ['previous-message@example.com'],
text: [
'X-Custom-Header' => 'Custom Value',
],
);
}
#Теги и метаданные
Некоторые сторонние почтовые провайдеры, такие как Mailgun и Postmark, поддерживают "теги" и "метаданные" сообщений, которые можно использовать для группировки и отслеживания писем, отправленных вашим приложением. Вы можете добавить теги и метаданные к письму через определение вашего Envelope:
use Illuminate\Mail\Mailables\Envelope;
/**
* Получить конверт сообщения.
*
* @return \Illuminate\Mail\Mailables\Envelope
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Order Shipped',
tags: ['shipment'],
metadata: [
'order_id' => $this->order->id,
],
);
}
Если ваше приложение использует драйвер Mailgun, вы можете ознакомиться с документацией Mailgun для получения дополнительной информации о тегах и метаданных. Аналогично, документация Postmark содержит информацию о поддержке тегов и метаданных.
Если ваше приложение использует Amazon SES для отправки писем, следует использовать метод metadata для прикрепления тегов SES к сообщению.
#Кастомизация Symfony Message
Почтовые возможности Laravel основаны на Symfony Mailer. Laravel позволяет регистрировать пользовательские callback-функции, которые будут вызваны с экземпляром Symfony Message перед отправкой сообщения. Это даёт возможность глубоко настроить сообщение перед отправкой. Для этого определите параметр using в вашем определении Envelope:
use Illuminate\Mail\Mailables\Envelope;
use Symfony\Component\Mime\Email;
/**
* Получить конверт сообщения.
*/
public function envelope(): Envelope
{
return new Envelope(
subject: 'Order Shipped',
using: [
function (Email $message) {
// ...
},
]
);
}
#Markdown Mailables
Markdown-сообщения позволяют использовать готовые шаблоны и компоненты почтовых уведомлений в ваших mailables. Поскольку сообщения написаны на Markdown, Laravel может сгенерировать красивые, адаптивные HTML-шаблоны, а также автоматически создать их текстовую версию.
#Генерация Markdown Mailables
Чтобы создать mailable с соответствующим Markdown-шаблоном, используйте опцию --markdown команды make:mail Artisan:
php artisan make:mail OrderShipped --markdown=mail.orders.shipped
Затем при настройке определения Content в методе content используйте параметр markdown вместо view:
use Illuminate\Mail\Mailables\Content;
/**
* Получить определение содержимого сообщения.
*/
public function content(): Content
{
return new Content(
markdown: 'mail.orders.shipped',
with: [
'url' => $this->orderUrl,
],
);
}
#Написание Markdown сообщений
Markdown mailables используют комбинацию Blade-компонентов и синтаксиса Markdown, что позволяет легко создавать почтовые сообщения с использованием готовых UI-компонентов Laravel:
<x-mail::message>
# Order Shipped
Your order has been shipped!
<x-mail::button :url="$url">
View Order
</x-mail::button>
Thanks,<br>
{{ config('app.name') }}
</x-mail::message>
Не используйте избыточные отступы при написании Markdown-писем. Согласно стандартам Markdown, парсеры будут интерпретировать отступы как блоки кода.
#Компонент кнопки
Компонент кнопки отображает центрированную кнопку-ссылку. Компонент принимает два аргумента: url и необязательный color. Поддерживаемые цвета: primary, success и error. Вы можете добавить в сообщение любое количество кнопок:
<x-mail::button :url="$url" color="success">
View Order
</x-mail::button>
#Компонент панели
Компонент панели отображает блок текста на фоне с немного отличающимся цветом, чтобы выделить данный блок текста:
<x-mail::panel>
This is the panel content.
</x-mail::panel>
#Компонент таблицы
Компонент таблицы преобразует Markdown-таблицу в HTML-таблицу. Компонент принимает Markdown-таблицу как содержимое. Поддерживается выравнивание столбцов с помощью стандартного синтаксиса Markdown:
<x-mail::table>
| Laravel | Table | Example |
| ------------- |:-------------:| --------:|
| Col 2 is | Centered | $10 |
| Col 3 is | Right-Aligned | $20 |
</x-mail::table>
#Кастомизация компонентов
Вы можете экспортировать все Markdown-компоненты почты в своё приложение для кастомизации. Для этого используйте команду Artisan vendor:publish для публикации тега ресурсов laravel-mail:
php artisan vendor:publish --tag=laravel-mail
Эта команда опубликует компоненты Markdown-почты в директорию resources/views/vendor/mail. В папке mail будут директории html и text, содержащие соответствующие представления каждого компонента. Вы можете свободно изменять эти компоненты по своему усмотрению.
#Кастомизация CSS
После экспорта компонентов в директории resources/views/vendor/mail/html/themes появится файл default.css. Вы можете изменить CSS в этом файле, и ваши стили автоматически будут преобразованы в inline-стили в HTML-представлениях Markdown-писем.
Если вы хотите создать полностью новую тему для Markdown-компонентов Laravel, поместите CSS-файл в директорию html/themes. После сохранения файла обновите опцию theme в конфигурационном файле config/mail.php, указав имя вашей новой темы.
Чтобы настроить тему для отдельного mailable, установите свойство $theme в классе mailable с именем темы, которая должна использоваться при отправке этого письма.
#Отправка почты
Для отправки сообщения используйте метод to фасада Mail facade. Метод to принимает email-адрес, экземпляр пользователя или коллекцию пользователей. Если передать объект или коллекцию, mailer автоматически возьмёт их свойства email и name для определения получателей. После указания получателей передайте экземпляр вашего mailable в метод send:
<?php
namespace App\Http\Controllers;
use App\Http\Controllers\Controller;
use App\Mail\OrderShipped;
use App\Models\Order;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Mail;
class OrderShipmentController extends Controller
{
/**
* Отправить заказ.
*/
public function store(Request $request): RedirectResponse
{
$order = Order::findOrFail($request->order_id);
// Отправить заказ...
Mail::to($request->user())->send(new OrderShipped($order));
return redirect('/orders');
}
}
Вы не ограничены только указанием получателей "to" при отправке сообщения. Вы можете указать получателей "to", "cc" и "bcc", вызывая соответствующие методы цепочкой:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->send(new OrderShipped($order));
#Отправка по списку получателей
Иногда нужно отправить mailable списку получателей, перебирая массив адресов. Поскольку метод to добавляет адреса к списку получателей, при каждой итерации письма будут отправляться всем предыдущим адресатам. Поэтому всегда создавайте новый экземпляр mailable для каждого получателя:
foreach (['taylor@example.com', 'dries@example.com'] as $recipient) {
Mail::to($recipient)->send(new OrderShipped($order));
}
#Отправка через конкретный mailer
По умолчанию Laravel отправляет почту через mailer, указанный как default в конфигурации mail. Однако вы можете использовать метод mailer для отправки через конкретный mailer:
Mail::mailer('postmark')
->to($request->user())
->send(new OrderShipped($order));
#Очередь отправки почты
#Помещение письма в очередь
Поскольку отправка писем может замедлять отклик приложения, многие разработчики помещают письма в очередь для фоновой отправки. Laravel упрощает это с помощью встроенного унифицированного API очередей. Чтобы поставить письмо в очередь, используйте метод queue фасада Mail после указания получателей:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue(new OrderShipped($order));
Этот метод автоматически создаст задачу в очереди для фоновой отправки. Перед использованием настройте ваши очереди согласно документации.
#Отложенная отправка письма
Если вы хотите отложить доставку поставленного в очередь сообщения электронной почты, вы можете использовать метод later. В качестве первого аргумента метод later принимает экземпляр DateTime, указывающий, когда сообщение должно быть отправлено:
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->later(now()->addMinutes(10), new OrderShipped($order));
#Отправка в конкретные очереди
Все классы mailable, созданные через make:mail, используют трейт Illuminate\Bus\Queueable. Вы можете вызвать методы onQueue и onConnection для указания подключения и имени очереди:
$message = (new OrderShipped($order))
->onConnection('sqs')
->onQueue('emails');
Mail::to($request->user())
->cc($moreUsers)
->bcc($evenMoreUsers)
->queue($message);
#Очередь по умолчанию
Если вы хотите, чтобы определённые mailables всегда ставились в очередь, реализуйте интерфейс ShouldQueue в классе. Тогда даже при вызове send письмо будет помещено в очередь:
use Illuminate\Contracts\Queue\ShouldQueue;
class OrderShipped extends Mailable implements ShouldQueue
{
// ...
}
#Очередь mailables и транзакции базы данных
Если mailables ставятся в очередь внутри транзакций базы данных, они могут быть обработаны до фиксации транзакции. В этом случае изменения моделей или записей, сделанные в транзакции, могут ещё не быть в базе. Также созданные в транзакции модели могут отсутствовать в базе. Если mailable зависит от этих моделей, при обработке задачи могут возникнуть ошибки.
Если в конфигурации очереди параметр after_commit установлен в false, вы можете указать, что конкретный mailable должен отправляться после фиксации всех открытых транзакций, вызвав метод afterCommit при отправке:
Mail::to($request->user())->send(
(new OrderShipped($order))->afterCommit()
);
Или вызовите метод afterCommit в конструкторе вашего mailable:
<?php
namespace App\Mail;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
class OrderShipped extends Mailable implements ShouldQueue
{
use Queueable, SerializesModels;
/**
* Создать новый экземпляр сообщения.
*/
public function __construct()
{
$this->afterCommit();
}
}
Чтобы узнать больше о решении этих проблем, ознакомьтесь с документацией по очередям и транзакциям базы данных.
#Рендеринг Mailables
Иногда нужно получить HTML-содержимое mailable без отправки. Для этого вызовите метод render у mailable. Он вернёт сгенерированный HTML в виде строки:
use App\Mail\InvoicePaid;
use App\Models\Invoice;
$invoice = Invoice::find(1);
return (new InvoicePaid($invoice))->render();
#Предпросмотр Mailables в браузере
При разработке шаблона mailable удобно быстро просмотреть его в браузере, как обычный Blade-шаблон. Для этого Laravel позволяет возвращать mailable напрямую из замыкания маршрута или контроллера. При возврате mailable он будет отрендерен и показан в браузере, что позволяет быстро проверить дизайн без отправки на email:
Route::get('/mailable', function () {
$invoice = App\Models\Invoice::find(1);
return new App\Mail\InvoicePaid($invoice);
});
#Локализация Mailables
Laravel позволяет отправлять mailables на языке, отличном от текущей локали запроса, и даже запоминает эту локаль, если письмо ставится в очередь.
Для этого фасад Mail предоставляет метод locale для установки нужного языка. Приложение переключится на эту локаль при генерации шаблона mailable и вернётся к предыдущей после завершения:
Mail::to($request->user())->locale('es')->send(
new OrderShipped($order)
);
#Предпочитаемые локали пользователя
Иногда приложения хранят предпочитаемую локаль каждого пользователя. Реализовав интерфейс HasLocalePreference в одной или нескольких моделях, вы можете указать Laravel использовать эту локаль при отправке почты:
use Illuminate\Contracts\Translation\HasLocalePreference;
class User extends Model implements HasLocalePreference
{
/**
* Получить предпочитаемую локаль пользователя.
*/
public function preferredLocale(): string
{
return $this->locale;
}
}
После реализации интерфейса Laravel автоматически будет использовать предпочитаемую локаль при отправке mailables и уведомлений модели. Поэтому вызов метода locale не обязателен:
Mail::to($request->user())->send(new OrderShipped($order));
#Тестирование
#Тестирование содержимого Mailable
Laravel предоставляет множество методов для проверки структуры вашего mailable. Кроме того, есть удобные методы для тестирования, что mailable содержит ожидаемый контент. Эти методы: assertSeeInHtml, assertDontSeeInHtml, assertSeeInOrderInHtml, assertSeeInText, assertDontSeeInText, assertSeeInOrderInText, assertHasAttachment, assertHasAttachedData, assertHasAttachmentFromStorage и assertHasAttachmentFromStorageDisk.
Как и ожидалось, "HTML" утверждения проверяют наличие строки в HTML-версии письма, а "text" — в текстовой версии:
use App\Mail\InvoicePaid;
use App\Models\User;
public function test_mailable_content(): void
{
$user = User::factory()->create();
$mailable = new InvoicePaid($user);
$mailable->assertFrom('jeffrey@example.com');
$mailable->assertTo('taylor@example.com');
$mailable->assertHasCc('abigail@example.com');
$mailable->assertHasBcc('victoria@example.com');
$mailable->assertHasReplyTo('tyler@example.com');
$mailable->assertHasSubject('Invoice Paid');
$mailable->assertHasTag('example-tag');
$mailable->assertHasMetadata('key', 'value');
$mailable->assertSeeInHtml($user->email);
$mailable->assertSeeInHtml('Invoice Paid');
$mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);
$mailable->assertSeeInText($user->email);
$mailable->assertSeeInOrderInText(['Invoice Paid', 'Thanks']);
$mailable->assertHasAttachment('/path/to/file');
$mailable->assertHasAttachment(Attachment::fromPath('/path/to/file'));
$mailable->assertHasAttachedData($pdfData, 'name.pdf', ['mime' => 'application/pdf']);
$mailable->assertHasAttachmentFromStorage('/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
$mailable->assertHasAttachmentFromStorageDisk('s3', '/path/to/file', 'name.pdf', ['mime' => 'application/pdf']);
}
#Тестирование отправки Mailable
Рекомендуется тестировать содержимое mailables отдельно от тестов, проверяющих факт отправки mailables конкретному пользователю. Обычно содержимое mailables не влияет на тестируемый код, достаточно проверить, что Laravel был вызван для отправки mailables.
Вы можете использовать метод fake фасада Mail, чтобы предотвратить отправку писем. После вызова метода fake фасада Mail вы можете проверить, что mailable-объекты были назначены для отправки пользователям, и даже просмотреть данные, которые получили эти mailable-объекты:
<?php
namespace Tests\Feature;
use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;
use Tests\TestCase;
class ExampleTest extends TestCase
{
public function test_orders_can_be_shipped(): void
{
Mail::fake();
// Выполнить отправку заказа...
// Проверить, что письма не отправлялись...
Mail::assertNothingSent();
// Проверить, что письмо было отправлено...
Mail::assertSent(OrderShipped::class);
// Проверить, что письмо было отправлено дважды...
Mail::assertSent(OrderShipped::class, 2);
// Проверить, что письмо не было отправлено...
Mail::assertNotSent(AnotherMailable::class);
// Проверить, что всего отправлено 3 письма...
Mail::assertSentCount(3);
}
}
Если вы ставите mailables в очередь для фоновой отправки, используйте метод assertQueued вместо assertSent:
Mail::assertQueued(OrderShipped::class);
Mail::assertNotQueued(OrderShipped::class);
Mail::assertNothingQueued();
Mail::assertQueuedCount(3);
Вы можете передать замыкание в методы assertSent, assertNotSent, assertQueued или assertNotQueued, чтобы проверить, что было отправлено письмо, удовлетворяющее определённому условию. Если хотя бы одно письмо прошло проверку, утверждение считается успешным:
Mail::assertSent(function (OrderShipped $mail) use ($order) {
return $mail->order->id === $order->id;
});
При вызове методов утверждения фасада Mail экземпляр mailable, передаваемый в замыкание, предоставляет полезные методы для проверки mailables:
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) {
return $mail->hasTo($user->email) &&
$mail->hasCc('...') &&
$mail->hasBcc('...') &&
$mail->hasReplyTo('...') &&
$mail->hasFrom('...') &&
$mail->hasSubject('...');
});
Экземпляр mailable также содержит методы для проверки вложений:
use Illuminate\Mail\Mailables\Attachment;
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) {
return $mail->hasAttachment(
Attachment::fromPath('/path/to/file')
->as('name.pdf')
->withMime('application/pdf')
);
});
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) {
return $mail->hasAttachment(
Attachment::fromStorageDisk('s3', '/path/to/file')
);
});
Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($pdfData) {
return $mail->hasAttachment(
Attachment::fromData(fn () => $pdfData, 'name.pdf')
);
});
Вы могли заметить, что есть два метода для проверки, что почта не была отправлена: assertNotSent и assertNotQueued. Иногда нужно проверить, что почта не была ни отправлена, ни поставлена в очередь. Для этого используйте методы assertNothingOutgoing и assertNotOutgoing:
Mail::assertNothingOutgoing();
Mail::assertNotOutgoing(function (OrderShipped $mail) use ($order) {
return $mail->order->id === $order->id;
});
#Почта и локальная разработка
При разработке приложения, отправляющего почту, вы, вероятно, не хотите отправлять письма на реальные адреса. Laravel предоставляет несколько способов "отключить" реальную отправку почты в локальной среде.
#Драйвер логирования
Вместо отправки писем драйвер log записывает все сообщения в лог-файлы для проверки. Обычно этот драйвер используется только в локальной разработке. Подробнее о настройке приложения по окружениям смотрите в документации по конфигурации.
#HELO / Mailtrap / Mailpit
В качестве альтернативы можно использовать сервисы вроде HELO или Mailtrap с драйвером smtp, чтобы отправлять письма в "тестовый" почтовый ящик, где их можно просмотреть в настоящем почтовом клиенте. Это удобно для проверки итоговых писем в просмотрщике Mailtrap.
Если вы используете Laravel Sail, вы можете просматривать сообщения через Mailpit. При запущенном Sail интерфейс Mailpit доступен по адресу: http://localhost:8025.
#Использование глобального адреса to
Наконец, вы можете указать глобальный адрес "to", вызвав метод alwaysTo фасада Mail. Обычно этот метод вызывается в методе boot одного из сервис-провайдеров приложения:
use Illuminate\Support\Facades\Mail;
/**
* Инициализация сервисов приложения.
*/
public function boot(): void
{
if ($this->app->environment('local')) {
Mail::alwaysTo('taylor@example.com');
}
}
#События
Laravel генерирует два события в процессе отправки почты. Событие MessageSending вызывается перед отправкой сообщения, а MessageSent — после отправки. Помните, что эти события срабатывают при отправке, а не при постановке в очередь. Вы можете зарегистрировать слушатели этих событий в вашем сервис-провайдере App\Providers\EventServiceProvider:
use App\Listeners\LogSendingMessage;
use App\Listeners\LogSentMessage;
use Illuminate\Mail\Events\MessageSending;
use Illuminate\Mail\Events\MessageSent;
/**
* Сопоставления слушателей событий для приложения.
*
* @var array
*/
protected $listen = [
MessageSending::class => [
LogSendingMessage::class,
],
MessageSent::class => [
LogSentMessage::class,
],
];
#Пользовательские транспорты
Laravel включает несколько почтовых транспортов, однако вы можете написать собственные транспорты для доставки почты через сервисы, которые Laravel не поддерживает из коробки. Для начала определите класс, расширяющий Symfony\Component\Mailer\Transport\AbstractTransport. Затем реализуйте методы doSend и __toString() в вашем транспорте:
use MailchimpTransactional\ApiClient;
use Symfony\Component\Mailer\SentMessage;
use Symfony\Component\Mailer\Transport\AbstractTransport;
use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\MessageConverter;
class MailchimpTransport extends AbstractTransport
{
/**
* Создаёт новый экземпляр транспорта Mailchimp.
*/
public function __construct(
protected ApiClient $client,
) {
parent::__construct();
}
/**
* {@inheritDoc}
*/
protected function doSend(SentMessage $message): void
{
$email = MessageConverter::toEmail($message->getOriginalMessage());
$this->client->messages->send(['message' => [
'from_email' => $email->getFrom(),
'to' => collect($email->getTo())->map(function (Address $email) {
return ['email' => $email->getAddress(), 'type' => 'to'];
})->all(),
'subject' => $email->getSubject(),
'text' => $email->getTextBody(),
]]);
}
/**
* Получить строковое представление транспорта.
*/
public function __toString(): string
{
return 'mailchimp';
}
}
После того как вы определили свой кастомный транспорт, вы можете зарегистрировать его через метод extend, предоставляемый фасадом Mail. Обычно это делается в методе boot сервис-провайдера AppServiceProvider вашего приложения. В замыкание, передаваемое в метод extend, будет передан аргумент $config. Этот аргумент содержит массив конфигурации, определённый для mailer в файле конфигурации config/mail.php вашего приложения:
use App\Mail\MailchimpTransport;
use Illuminate\Support\Facades\Mail;
/**
* Инициализация сервисов приложения.
*/
public function boot(): void
{
Mail::extend('mailchimp', function (array $config = []) {
return new MailchimpTransport(/* ... */);
});
}
После того как ваш кастомный транспорт определён и зарегистрирован, вы можете создать определение mailer в файле конфигурации config/mail.php вашего приложения, которое будет использовать новый транспорт:
'mailchimp' => [
'transport' => 'mailchimp',
// ...
],
#Дополнительные транспорты Symfony
Laravel поддерживает некоторые существующие транспорты почты, поддерживаемые Symfony, такие как Mailgun и Postmark. Однако вы можете расширить Laravel, добавив поддержку дополнительных транспортов Symfony. Для этого необходимо установить нужный Symfony mailer через Composer и зарегистрировать транспорт в Laravel. Например, вы можете установить и зарегистрировать Symfony mailer "Brevo" (ранее "Sendinblue"):
composer require symfony/brevo-mailer symfony/http-client
После установки пакета mailer Brevo вы можете добавить запись с вашими API-учётными данными Brevo в файл конфигурации services вашего приложения:
'brevo' => [
'key' => 'your-api-key',
],
Далее вы можете использовать метод extend фасада Mail для регистрации транспорта в Laravel. Обычно это делается в методе boot сервис-провайдера:
use Illuminate\Support\Facades\Mail;
use Symfony\Component\Mailer\Bridge\Brevo\Transport\BrevoTransportFactory;
use Symfony\Component\Mailer\Transport\Dsn;
/**
* Инициализация сервисов приложения.
*/
public function boot(): void
{
Mail::extend('brevo', function () {
return (new BrevoTransportFactory)->create(
new Dsn(
'brevo+api',
'default',
config('services.brevo.key')
)
);
});
}
После регистрации транспорта вы можете создать определение mailer в конфигурационном файле вашего приложения config/mail.php, которое будет использовать новый транспорт:
'brevo' => [
'transport' => 'brevo',
// ...
],