Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти
Інше
Події Про нас

Пошта

Вступ

Надсилання пошти не мусить бути складним. Laravel надає чистий і простий API для електронної пошти, побудований на популярному компоненті Symfony Mailer. Laravel і Symfony Mailer надають драйвери для надсилання пошти через SMTP, Cloudflare, Mailgun, Postmark, Resend, Amazon SES та sendmail, тож ви можете швидко почати надсилати пошту через локальний чи хмарний сервіс на свій вибір.

Конфігурація

Поштові сервіси Laravel налаштовуються у файлі config/mail.php вашого застосунку. Кожен налаштований у цьому файлі мейлер може мати власну унікальну конфігурацію й навіть власний унікальний «транспорт», що дозволяє застосунку надсилати різні повідомлення через різні поштові сервіси. Наприклад, ваш застосунок може надсилати транзакційні листи через Postmark, а масові розсилки - через Amazon SES.

У файлі конфігурації mail ви знайдете масив конфігурації mailers. Цей масив містить зразок конфігурації для кожного з основних поштових драйверів / транспортів, які підтримує Laravel, а значення конфігурації default визначає, який мейлер використовуватиметься за замовчуванням, коли вашому застосунку треба надіслати листа.

Передумови драйверів / транспортів

Драйвери на основі API - Mailgun, Postmark і Resend - часто простіші та швидші за надсилання пошти через SMTP-сервери. Коли це можливо, ми радимо користуватися одним із них.

Драйвер Cloudflare

Щоб скористатися драйвером Cloudflare, встановіть HTTP-клієнт Symfony через Composer:

composer require symfony/http-client

Далі вам потрібно внести дві зміни у файл конфігурації config/mail.php вашого застосунку. Спершу задайте мейлером за замовчуванням cloudflare:

'default' => env('MAIL_MAILER', 'cloudflare'),

По-друге, додайте такий масив конфігурації до вашого масиву mailers:

'cloudflare' => [
    'transport' => 'cloudflare',
],

Налаштувавши мейлер за замовчуванням, додайте такі опції до файлу конфігурації config/services.php:

'cloudflare' => [
    'account_id' => env('CLOUDFLARE_ACCOUNT_ID'),
    'key' => env('CLOUDFLARE_KEY'),
],

Драйвер Mailgun

Щоб скористатися драйвером Mailgun, встановіть транспорт Mailgun Mailer від Symfony через Composer:

composer require symfony/mailgun-mailer symfony/http-client

Далі вам потрібно внести дві зміни у файл конфігурації config/mail.php вашого застосунку. Спершу задайте мейлером за замовчуванням mailgun:

'default' => env('MAIL_MAILER', 'mailgun'),

По-друге, додайте такий масив конфігурації до вашого масиву mailers:

'mailgun' => [
    'transport' => 'mailgun',
    // 'client' => [
    //     'timeout' => 5,
    // ],
],

Налаштувавши мейлер за замовчуванням, додайте такі опції до файлу конфігурації config/services.php:

'mailgun' => [
    'domain' => env('MAILGUN_DOMAIN'),
    'secret' => env('MAILGUN_SECRET'),
    'endpoint' => env('MAILGUN_ENDPOINT', 'api.mailgun.net'),
    'scheme' => 'https',
],

Якщо ви не користуєтеся регіоном Mailgun для США, вкажіть точку свого регіону у файлі конфігурації services:

'mailgun' => [
    'domain' => env('MAILGUN_DOMAIN'),
    'secret' => env('MAILGUN_SECRET'),
    'endpoint' => env('MAILGUN_ENDPOINT', 'api.eu.mailgun.net'),
    'scheme' => 'https',
],

Драйвер Postmark

Щоб скористатися драйвером Postmark, встановіть транспорт Postmark Mailer від Symfony через Composer:

composer require symfony/postmark-mailer symfony/http-client

Далі задайте опції default у файлі config/mail.php вашого застосунку значення postmark. Налаштувавши мейлер за замовчуванням, переконайтеся, що файл конфігурації config/services.php містить такі опції:

'postmark' => [
    'key' => env('POSTMARK_API_KEY'),
],

Якщо ви хочете вказати потік повідомлень Postmark, який має використовувати конкретний мейлер, додайте до його масиву конфігурації опцію message_stream_id. Цей масив конфігурації лежить у файлі config/mail.php вашого застосунку:

'postmark' => [
    'transport' => 'postmark',
    'message_stream_id' => env('POSTMARK_MESSAGE_STREAM_ID'),
    // 'client' => [
    //     'timeout' => 5,
    // ],
],

Так ви можете налаштувати й кілька мейлерів Postmark з різними потоками повідомлень.

Драйвер Resend

Щоб скористатися драйвером Resend, встановіть PHP SDK від Resend через Composer:

composer require resend/resend-php

Далі задайте опції default у файлі config/mail.php вашого застосунку значення resend. Налаштувавши мейлер за замовчуванням, переконайтеся, що файл конфігурації config/services.php містить такі опції:

'resend' => [
    'key' => env('RESEND_API_KEY'),
],

Драйвер 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'),
],

Щоб працювати з можливостями керування підписками SES, поверніть заголовок X-Ses-List-Management-Options у масиві, який повертає метод headers поштового повідомлення:

/**
 * Get the message headers.
 */
public function headers(): Headers
{
    return new Headers(
        text: [
            'X-Ses-List-Management-Options' => 'contactListName=MyContactList;topicName=MyTopic',
        ],
    );
}

Якщо ви хочете описати додаткові опції, які 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'],
        ],
    ],
],

Конфігурація failover

Інколи зовнішній сервіс, який ви налаштували для надсилання пошти, може бути недоступним. У таких випадках корисно описати одну чи кілька резервних конфігурацій доставки, які використовуватимуться, якщо основний драйвер лежить.

Для цього опишіть у файлі конфігурації mail мейлер, який використовує транспорт failover. Масив конфігурації мейлера failover має містити масив mailers із порядком, у якому слід обирати налаштовані мейлери для доставки:

'mailers' => [
    'failover' => [
        'transport' => 'failover',
        'mailers' => [
            'postmark',
            'mailgun',
            'sendmail',
        ],
        'retry_after' => 60,
    ],

    // ...
],

Щойно ви налаштували мейлер із транспортом failover, вам потрібно зробити його мейлером за замовчуванням у файлі .env вашого застосунку, щоб скористатися цією можливістю:

MAIL_MAILER=failover

Конфігурація round robin

Транспорт roundrobin дозволяє розподілити навантаження з надсилання пошти між кількома мейлерами. Для початку опишіть у файлі конфігурації mail мейлер, який використовує транспорт roundrobin. Масив конфігурації мейлера roundrobin має містити масив mailers із переліком налаштованих мейлерів, які слід використовувати для доставки:

'mailers' => [
    'roundrobin' => [
        'transport' => 'roundrobin',
        'mailers' => [
            'ses',
            'postmark',
        ],
        'retry_after' => 60,
    ],

    // ...
],

Щойно ваш мейлер round robin описано, зробіть його мейлером за замовчуванням, вказавши його ім'я значенням ключа конфігурації default у файлі mail вашого застосунку:

'default' => env('MAIL_MAILER', 'roundrobin'),

Транспорт round robin обирає випадковий мейлер зі списку налаштованих, а для кожного наступного листа перемикається на наступний доступний. На відміну від транспорту failover, який допомагає досягти високої доступності, транспорт roundrobin забезпечує балансування навантаження.

Генерація mailable-класів

Створюючи застосунки Laravel, кожен тип листа, який надсилає ваш застосунок, представляють класом «mailable». Ці класи зберігаються в каталозі app/Mail. Не переймайтеся, якщо цього каталогу у вашому застосунку немає: його буде згенеровано, коли ви створите свій перший mailable-клас командою Artisan make:mail:

php artisan make:mail OrderShipped

Написання mailable-класів

Щойно ви згенерували mailable-клас, відкрийте його, щоб дослідити вміст. Конфігурація mailable-класу відбувається в кількох методах: envelope, content та attachments.

Метод envelope повертає об'єкт Illuminate\Mail\Mailables\Envelope, який описує тему і, часом, отримувачів повідомлення. Метод content повертає об'єкт Illuminate\Mail\Mailables\Content, який описує шаблон Blade, що згенерує вміст повідомлення.

Налаштування відправника

Через конверт

Спершу розгляньмо налаштування відправника листа. Іншими словами, від кого лист. Налаштувати відправника можна двома способами. По-перше, ви можете вказати адресу «from» у конверті повідомлення:

use Illuminate\Mail\Mailables\Address;
use Illuminate\Mail\Mailables\Envelope;

/**
 * Get the message 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, під час створення HTML вашого листа вам доступні вся потуга й зручність шаблонізатора Blade:

/**
 * Get the message content definition.
 */
public function content(): Content
{
    return new Content(
        view: 'mail.orders.shipped',
    );
}

Вам може захотітися створити каталог resources/views/mail, щоб зібрати там усі шаблони листів; утім, ви вільні класти їх будь-де в каталозі resources/views.

Листи у простому тексті

Якщо ви хочете описати текстову версію свого листа, вкажіть текстовий шаблон під час створення опису Content. Як і параметр view, параметр text має бути назвою шаблону, який відрендерить вміст листа. Ви вільні описати і HTML-, і текстову версію свого повідомлення:

/**
 * Get the message content definition.
 */
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;

    /**
     * Create a new message instance.
     */
    public function __construct(
        public Order $order,
    ) {}

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'mail.orders.shipped',
        );
    }
}

Щойно дані записано в публічну властивість, вони автоматично стануть доступні у вашому представленні, тож ви можете звертатися до них як до будь-яких інших даних у шаблонах Blade:

<div>
    Price: {{ $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;

    /**
     * Create a new message instance.
     */
    public function __construct(
        protected Order $order,
    ) {}

    /**
     * Get the message content definition.
     */
    public function content(): Content
    {
        return new Content(
            view: 'mail.orders.shipped',
            with: [
                'orderName' => $this->order->name,
                'orderPrice' => $this->order->price,
            ],
        );
    }
}

Щойно дані передано через параметр with, вони автоматично стануть доступні у вашому представленні, тож ви можете звертатися до них як до будь-яких інших даних у шаблонах Blade:

<div>
    Price: {{ $orderPrice }}
</div>

Вкладення

Щоб додати вкладення до листа, додайте їх до масиву, який повертає метод attachments повідомлення. По-перше, ви можете додати вкладення, передавши шлях до файлу методу fromPath класу Attachment:

use Illuminate\Mail\Mailables\Attachment;

/**
 * Get the attachments for the message.
 *
 * @return array<int, \Illuminate\Mail\Mailables\Attachment>
 */
public function attachments(): array
{
    return [
        Attachment::fromPath('/path/to/file'),
    ];
}

Вкладаючи файли до повідомлення, ви можете вказати відображувану назву та / або MIME-тип вкладення методами as і withMime:

/**
 * Get the attachments for the message.
 *
 * @return array<int, \Illuminate\Mail\Mailables\Attachment>
 */
public function attachments(): array
{
    return [
        Attachment::fromPath('/path/to/file')
            ->as('name.pdf')
            ->withMime('application/pdf'),
    ];
}

Вкладення файлів з диска

Якщо ви зберегли файл на одному з дисків файлової системи, вкласти його до листа можна методом fromStorage:

/**
 * Get the attachments for the message.
 *
 * @return array<int, \Illuminate\Mail\Mailables\Attachment>
 */
public function attachments(): array
{
    return [
        Attachment::fromStorage('/path/to/file'),
    ];
}

Звісно, ви також можете вказати назву та MIME-тип вкладення:

/**
 * Get the attachments for the message.
 *
 * @return array<int, \Illuminate\Mail\Mailables\Attachment>
 */
public function attachments(): array
{
    return [
        Attachment::fromStorage('/path/to/file')
            ->as('name.pdf')
            ->withMime('application/pdf'),
    ];
}

Метод fromStorageDisk дозволяє вказати диск, відмінний від диска за замовчуванням:

/**
 * Get the attachments for the message.
 *
 * @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 приймає замикання, яке повертає сирі байти даних, і назву, яку слід призначити вкладенню:

/**
 * Get the attachments for the message.
 *
 * @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 недоступна в шаблонах повідомлень у простому тексті, адже такі повідомлення не використовують вбудованих вкладень.

Вбудовування вкладень із сирих даних

Якщо ви вже маєте рядок сирих даних зображення, який хочете вбудувати в шаблон листа, викличте метод 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
{
    /**
     * Get the attachable representation of the model.
     */
    public function toMailAttachment(): Attachment
    {
        return Attachment::fromPath('/path/to/file');
    }
}

Щойно ви описали свій об'єкт для вкладення, ви можете повертати його екземпляр із методу attachments під час побудови листа:

/**
 * Get the attachments for the message.
 *
 * @return array<int, \Illuminate\Mail\Mailables\Attachment>
 */
public function attachments(): array
{
    return [$this->photo];
}

Звісно, дані вкладення можуть зберігатися у віддаленому файловому сховищі на кшталт Amazon S3. Тож Laravel також дозволяє генерувати екземпляри вкладень із даних, збережених на одному з дисків файлової системи вашого застосунку:

// Create an attachment from a file on your default disk...
return Attachment::fromStorage($this->path);

// Create an attachment from a file on a specific disk...
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 чи інші довільні текстові заголовки.

Для цього опишіть у своєму mailable-класі метод headers. Метод headers має повернути екземпляр Illuminate\Mail\Mailables\Headers. Цей клас приймає параметри messageId, references та text. Звісно, ви можете передати лише ті параметри, які потрібні для конкретного повідомлення:

use Illuminate\Mail\Mailables\Headers;

/**
 * Get the message 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;

/**
 * Get the message 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

Поштові можливості Laravel побудовані на Symfony Mailer. Laravel дозволяє реєструвати власні колбеки, які буде викликано з екземпляром Symfony Message перед надсиланням повідомлення. Це дає вам змогу глибоко налаштувати повідомлення перед відправкою. Для цього опишіть параметр using у своєму описі Envelope:

use Illuminate\Mail\Mailables\Envelope;
use Symfony\Component\Mime\Email;

/**
 * Get the message envelope.
 */
public function envelope(): Envelope
{
    return new Envelope(
        subject: 'Order Shipped',
        using: [
            function (Email $message) {
                // ...
            },
        ]
    );
}

Markdown-листи

Markdown-листи дозволяють скористатися готовими шаблонами й компонентами поштових сповіщень у ваших mailable-класах. Оскільки повідомлення пишуться в Markdown, Laravel може відрендерити для них гарні адаптивні HTML-шаблони, а заразом автоматично згенерувати текстовий відповідник.

Генерація Markdown-листів

Щоб згенерувати mailable-клас із відповідним Markdown-шаблоном, скористайтеся опцією --markdown команди Artisan make:mail:

php artisan make:mail OrderShipped --markdown=mail.orders.shipped

Далі, налаштовуючи опис Content у методі content, використовуйте параметр markdown замість view:

use Illuminate\Mail\Mailables\Content;

/**
 * Get the message content definition.
 */
public function content(): Content
{
    return new Content(
        markdown: 'mail.orders.shipped',
        with: [
            'url' => $this->orderUrl,
        ],
    );
}

Написання Markdown-повідомлень

Markdown-листи поєднують компоненти 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 парсери рендерять вміст із відступами як блоки коду.

Компонент button

Компонент button рендерить відцентроване посилання-кнопку. Компонент приймає два аргументи: url та необов'язковий color. Підтримувані кольори - primary, success та error. Ви можете додати до повідомлення скільки завгодно компонентів button:

<x-mail::button :url="$url" color="success">
View Order
</x-mail::button>

Компонент panel

Компонент panel рендерить заданий блок тексту в панелі, колір тла якої трохи відрізняється від решти повідомлення. Це дозволяє привернути увагу до певного блоку тексту:

<x-mail::panel>
This is the panel content.
</x-mail::panel>

Компонент table

Компонент table дозволяє перетворити таблицю 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 до власного застосунку, щоб їх налаштувати. Щоб експортувати компоненти, опублікуйте тег ресурсів laravel-mail командою Artisan vendor:publish:

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 у цьому файлі, і ваші стилі автоматично буде перетворено на вбудований CSS у HTML-представленнях ваших Markdown-листів.

Якщо ви хочете створити цілком нову тему для компонентів Markdown у Laravel, покладіть CSS-файл у каталог html/themes. Назвавши й зберігши свій CSS-файл, оновіть опцію theme у файлі конфігурації config/mail.php вашого застосунку відповідно до назви нової теми.

Щоб змінити тему для окремого mailable-класу, задайте властивості $theme цього класу назву теми, яку слід використати під час надсилання.

Надсилання пошти

Щоб надіслати повідомлення, скористайтеся методом to фасаду Mail. Метод to приймає адресу електронної пошти, екземпляр користувача або колекцію користувачів. Якщо ви передаєте об'єкт чи колекцію об'єктів, мейлер автоматично використає їхні властивості email і name для визначення отримувачів листа, тож переконайтеся, що ці атрибути є у ваших об'єктах. Щойно ви вказали отримувачів, передайте екземпляр свого mailable-класу методу send:

<?php

namespace App\Http\Controllers;

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
{
    /**
     * Ship the given order.
     */
    public function store(Request $request): RedirectResponse
    {
        $order = Order::findOrFail($request->order_id);

        // Ship the order...

        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));

Цикл по отримувачах

Інколи вам може знадобитися надіслати лист списку отримувачів, проходячи масив отримувачів / адрес. Проте, оскільки метод to додає адреси до списку отримувачів листа, кожна ітерація циклу надсилатиме ще один лист усім попереднім отримувачам. Тому завжди створюйте екземпляр листа заново для кожного отримувача:

foreach (['taylor@example.com', 'dries@example.com'] as $recipient) {
    Mail::to($recipient)->send(new OrderShipped($order));
}

Надсилання через конкретний мейлер

За замовчуванням Laravel надсилає пошту через мейлер, налаштований як default у файлі конфігурації mail вашого застосунку. Проте ви можете скористатися методом 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()->plus(minutes: 10), new OrderShipped($order));

Відправка в конкретні черги

Оскільки всі mailable-класи, згенеровані командою make:mail, використовують трейт Illuminate\Bus\Queueable, ви можете викликати методи onQueue та onConnection на будь-якому екземплярі mailable-класу, щоб вказати підключення та ім'я черги для повідомлення:

$message = (new OrderShipped($order))
    ->onConnection('sqs')
    ->onQueue('emails');

Mail::to($request->user())
    ->cc($moreUsers)
    ->bcc($evenMoreUsers)
    ->queue($message);

Як варіант, ви можете вказати підключення й чергу атрибутами Connection та Queue у mailable-класі:

use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Queue;

#[Connection('sqs')]
#[Queue('emails')]
class OrderShipped extends Mailable
{
    // ...
}

Постановка в чергу за замовчуванням

Якщо у вас є mailable-класи, які ви хочете завжди ставити в чергу, реалізуйте в них контракт ShouldQueue. Тепер, навіть якщо ви викличете метод send, лист усе одно потрапить у чергу, адже клас реалізує цей контракт:

use Illuminate\Contracts\Queue\ShouldQueue;

class OrderShipped extends Mailable implements ShouldQueue
{
    // ...
}

Листи в черзі та транзакції бази даних

Коли листи в черзі диспетчеризуються всередині транзакцій бази даних, черга може обробити їх ще до того, як транзакцію буде зафіксовано. Коли таке трапляється, будь-які зміни, які ви внесли до моделей чи записів у базі під час транзакції, ще можуть не бути в базі. Ба більше, будь-які моделі чи записи, створені всередині транзакції, можуть у базі не існувати. Якщо ваш лист залежить від цих моделей, під час обробки завдання, яке його надсилає, можуть виникнути несподівані помилки.

Якщо опція конфігурації after_commit вашого підключення черги має значення false, ви все одно можете вказати, що конкретний лист у черзі слід диспетчеризувати після фіксації всіх відкритих транзакцій, - викличте метод 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;

    /**
     * Create a new message instance.
     */
    public function __construct()
    {
        $this->afterCommit();
    }
}

Щоб дізнатися більше про обхід цих проблем, перегляньте документацію про завдання в черзі та транзакції бази даних.

Невдачі листів у черзі

Коли лист у черзі зазнає невдачі, у mailable-класі буде викликано метод failed, якщо його описано. До методу failed буде передано екземпляр Throwable, який спричинив невдачу:

<?php

namespace App\Mail;

use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Mail\Mailable;
use Illuminate\Queue\SerializesModels;
use Throwable;

class OrderDelayed extends Mailable implements ShouldQueue
{
    use SerializesModels;

    /**
     * Handle a queued email's failure.
     */
    public function failed(Throwable $exception): void
    {
        // ...
    }
}

Рендеринг mailable-класів

Інколи вам може захотітися отримати HTML-вміст листа, не надсилаючи його. Для цього викличте метод render на mailable-класі. Цей метод поверне обчислений HTML-вміст листа рядком:

use App\Mail\InvoicePaid;
use App\Models\Invoice;

$invoice = Invoice::find(1);

return (new InvoicePaid($invoice))->render();

Попередній перегляд листів у браузері

Розробляючи шаблон листа, зручно швидко переглядати відрендерений лист у браузері, як звичайний шаблон Blade. Тому Laravel дозволяє повертати будь-який mailable-клас напряму із замикання маршруту чи контролера. Коли повернуто mailable-клас, його буде відрендерено й показано в браузері, тож ви зможете швидко переглянути дизайн, не надсилаючи листа на справжню адресу:

Route::get('/mailable', function () {
    $invoice = App\Models\Invoice::find(1);

    return new App\Mail\InvoicePaid($invoice);
});

Локалізація листів

Laravel дозволяє надсилати листи в локалі, відмінній від поточної локалі запиту, і навіть запам'ятає цю локаль, якщо лист поставлено в чергу.

Для цього фасад Mail пропонує метод locale, щоб задати потрібну мову. Застосунок перейде в цю локаль під час обчислення шаблону листа, а після завершення повернеться до попередньої:

Mail::to($request->user())->locale('es')->send(
    new OrderShipped($order)
);

Бажані локалі користувачів

Інколи застосунки зберігають бажану локаль кожного користувача. Реалізувавши в одній чи кількох своїх моделях контракт HasLocalePreference, ви можете сказати Laravel використовувати цю збережену локаль під час надсилання пошти:

use Illuminate\Contracts\Translation\HasLocalePreference;

class User extends Model implements HasLocalePreference
{
    /**
     * Get the user's preferred locale.
     */
    public function preferredLocale(): string
    {
        return $this->locale;
    }
}

Щойно ви реалізували цей інтерфейс, Laravel автоматично використовуватиме бажану локаль, надсилаючи моделі листи та сповіщення. Тому викликати метод locale при використанні цього інтерфейсу не потрібно:

Mail::to($request->user())->send(new OrderShipped($order));

Тестування

Тестування вмісту листа

Laravel надає низку методів для огляду структури вашого листа. Крім того, Laravel надає кілька зручних методів, щоб перевірити, що ваш лист містить очікуваний вміст:

use App\Mail\InvoicePaid;
use App\Models\User;

test('mailable content', function () {
    $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->assertDontSeeInHtml('Invoice Not Paid');
    $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);

    $mailable->assertSeeInText($user->email);
    $mailable->assertDontSeeInText('Invoice Not Paid');
    $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']);
});
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->assertDontSeeInHtml('Invoice Not Paid');
    $mailable->assertSeeInOrderInHtml(['Invoice Paid', 'Thanks']);

    $mailable->assertSeeInText($user->email);
    $mailable->assertDontSeeInText('Invoice Not Paid');
    $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']);
}

Як ви й очікуєте, перевірки «HTML» стверджують, що HTML-версія вашого листа містить заданий рядок, а перевірки «text» - що заданий рядок містить текстова версія.

Тестування надсилання листа

Радимо тестувати вміст ваших листів окремо від тестів, які перевіряють, що конкретний лист було «надіслано» конкретному користувачеві. Зазвичай вміст листів не стосується коду, який ви тестуєте, і достатньо просто перевірити, що Laravel отримав вказівку надіслати заданий лист.

Метод fake фасаду Mail дозволяє завадити надсиланню пошти. Після виклику методу fake фасаду Mail ви можете перевіряти, що листи мали бути надіслані користувачам, і навіть оглядати дані, які вони отримали:

<?php

use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;

test('orders can be shipped', function () {
    Mail::fake();

    // Perform order shipping...

    // Assert that no mailables were sent...
    Mail::assertNothingSent();

    // Assert that a mailable was sent...
    Mail::assertSent(OrderShipped::class);

    // Assert a mailable was sent twice...
    Mail::assertSent(OrderShipped::class, 2);

    // Assert a mailable was sent to an email address...
    Mail::assertSent(OrderShipped::class, 'example@laravel.com');

    // Assert a mailable was sent to multiple email addresses...
    Mail::assertSent(OrderShipped::class, ['example@laravel.com', '...']);

    // Assert a mailable was not sent...
    Mail::assertNotSent(AnotherMailable::class);

    // Assert a mailable was sent twice...
    Mail::assertSentTimes(OrderShipped::class, 2);

    // Assert 3 total mailables were sent...
    Mail::assertSentCount(3);
});
<?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();

        // Perform order shipping...

        // Assert that no mailables were sent...
        Mail::assertNothingSent();

        // Assert that a mailable was sent...
        Mail::assertSent(OrderShipped::class);

        // Assert a mailable was sent twice...
        Mail::assertSent(OrderShipped::class, 2);

        // Assert a mailable was sent to an email address...
        Mail::assertSent(OrderShipped::class, 'example@laravel.com');

        // Assert a mailable was sent to multiple email addresses...
        Mail::assertSent(OrderShipped::class, ['example@laravel.com', '...']);

        // Assert a mailable was not sent...
        Mail::assertNotSent(AnotherMailable::class);

        // Assert a mailable was sent twice...
        Mail::assertSentTimes(OrderShipped::class, 2);

        // Assert 3 total mailables were sent...
        Mail::assertSentCount(3);
    }
}

Якщо ви ставите листи в чергу на фонову доставку, користуйтеся методом assertQueued замість assertSent:

Mail::assertQueued(OrderShipped::class);
Mail::assertNotQueued(OrderShipped::class);
Mail::assertNothingQueued();
Mail::assertQueuedCount(3);

Ви також можете перевірити загальну кількість надісланих або поставлених у чергу листів методом assertOutgoingCount:

Mail::assertOutgoingCount(3);

Ви можете передати замикання методам assertSent, assertNotSent, assertQueued чи assertNotQueued, щоб перевірити, що було надіслано лист, який проходить заданий тест. Якщо надіслано щонайменше один лист, який проходить цей тест, перевірка буде успішною:

Mail::assertSent(function (OrderShipped $mail) use ($order) {
    return $mail->order->id === $order->id;
});

Коли ви викликаєте методи перевірок фасаду Mail, екземпляр листа, який приймає передане замикання, надає корисні методи для його огляду:

Mail::assertSent(OrderShipped::class, function (OrderShipped $mail) use ($user) {
    return $mail->hasTo($user->email) &&
           $mail->hasCc('...') &&
           $mail->hasBcc('...') &&
           $mail->hasReplyTo('...') &&
           $mail->hasFrom('...') &&
           $mail->hasSubject('...') &&
           $mail->hasMetadata('order_id', $mail->order->id);
           $mail->usesMailer('ses');
});

Екземпляр листа також містить кілька корисних методів для огляду вкладень:

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

Замість надсилати листи, поштовий драйвер 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;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    if ($this->app->environment('local')) {
        Mail::alwaysTo('taylor@example.com');
    }
}

Коли ви користуєтеся методом alwaysTo, будь-які додаткові адреси «cc» чи «bcc» у листах буде вилучено.

Події

Надсилаючи листи, Laravel диспетчеризує дві події. Подія MessageSending диспетчеризується перед надсиланням повідомлення, а подія MessageSent - після. Пам'ятайте: ці події диспетчеризуються, коли пошту надсилають, а не коли її ставлять у чергу. Ви можете створити слухачів подій для цих подій у своєму застосунку:

use Illuminate\Mail\Events\MessageSending;
// use Illuminate\Mail\Events\MessageSent;

class LogMessage
{
    /**
     * Handle the event.
     */
    public function handle(MessageSending $event): void
    {
        // ...
    }
}

Власні транспорти

Laravel містить різноманітні поштові транспорти; проте вам може захотітися написати власний, щоб доставляти пошту через сервіси, які Laravel не підтримує «з коробки». Для початку опишіть клас, який розширює Symfony\Component\Mailer\Transport\AbstractTransport. Далі реалізуйте у своєму транспорті методи doSend та __toString:

<?php

namespace App\Mail;

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
{
    /**
     * Create a new Mailchimp transport instance.
     */
    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(),
        ]]);
    }

    /**
     * Get the string representation of the transport.
     */
    public function __toString(): string
    {
        return 'mailchimp';
    }
}

Щойно ви описали власний транспорт, зареєструйте його методом extend фасаду Mail. Зазвичай це роблять у методі boot вашого AppServiceProvider. До замикання, переданого методу extend, буде передано аргумент $config. Він міститиме масив конфігурації, описаний для мейлера у файлі config/mail.php вашого застосунку:

use App\Mail\MailchimpTransport;
use Illuminate\Support\Facades\Mail;
use MailchimpTransactional\ApiClient;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Mail::extend('mailchimp', function (array $config = []) {
        $client = new ApiClient;

        $client->setApiKey($config['key']);

        return new MailchimpTransport($client);
    });
}

Щойно ваш власний транспорт описано й зареєстровано, ви можете створити у файлі config/mail.php опис мейлера, який використовує новий транспорт:

'mailchimp' => [
    'transport' => 'mailchimp',
    'key' => env('MAILCHIMP_API_KEY'),
    // ...
],

Додаткові транспорти Symfony

Laravel підтримує деякі наявні поштові транспорти, які підтримує Symfony, - як-от Mailgun і Postmark. Проте вам може захотітися розширити Laravel підтримкою інших транспортів від Symfony. Це робиться підключенням потрібного мейлера Symfony через Composer і реєстрацією транспорту в Laravel. Наприклад, ви можете встановити й зареєструвати мейлер Symfony «Brevo» (раніше «Sendinblue»):

composer require symfony/brevo-mailer symfony/http-client

Щойно пакет мейлера Brevo встановлено, додайте запис із обліковими даними API Brevo до файлу конфігурації services вашого застосунку:

'brevo' => [
    'key' => env('BREVO_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;

/**
 * Bootstrap any application services.
 */
public function boot(): void
{
    Mail::extend('brevo', function () {
        return (new BrevoTransportFactory)->create(
            new Dsn(
                'brevo+api',
                'default',
                config('services.brevo.key')
            )
        );
    });
}

Щойно ваш транспорт зареєстровано, ви можете створити у файлі config/mail.php опис мейлера, який використовує новий транспорт:

'brevo' => [
    'transport' => 'brevo',
    // ...
],