MidПрактика4 min

Mailer и Mime

Mailer component, Mime component, Email class, Twig integration, async with Messenger

Mailer Component

Symfony 8.0: Компоненты Mailer и Mime явно указаны в темах экзамена.

Mailer -- компонент для отправки email. Поддерживает различные транспорты (SMTP, Sendmail, API-провайдеры).

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

# config/packages/mailer.yaml
framework:
    mailer:
        dsn: '%env(MAILER_DSN)%'

# .env
MAILER_DSN=smtp://user:[email protected]:465

Основные DSN

Провайдер DSN
SMTP smtp://user:[email protected]:587
Gmail gmail+smtp://user:pass@default
Sendmail sendmail://default
Null (тесты) null://null

Mime Component и Email

Mime component создаёт email-сообщения.

Простой email

<?php

declare(strict_types=1);

namespace App\Service;

use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Email;

final class NotificationService
{
    public function __construct(
        private readonly MailerInterface $mailer,
    ) {
    }

    public function sendWelcome(string $to, string $name): void
    {
        $email = (new Email())
            ->from('[email protected]')
            ->to($to)
            ->subject('Welcome to Our Platform!')
            ->text("Hello $name, welcome to our platform!")
            ->html("<h1>Hello $name!</h1><p>Welcome to our platform!</p>");

        $this->mailer->send($email);
    }
}

Email с вложениями

<?php

declare(strict_types=1);

namespace App\Service;

use Symfony\Component\Mime\Email;
use Symfony\Component\Mime\Part\DataPart;
use Symfony\Component\Mime\Part\File;

final class InvoiceMailer
{
    public function __construct(
        private readonly MailerInterface $mailer,
    ) {
    }

    public function sendInvoice(
        string $to,
        string $invoiceNumber,
        string $pdfPath,
    ): void {
        $email = (new Email())
            ->from('[email protected]')
            ->to($to)
            ->cc('[email protected]')
            ->bcc('[email protected]')
            ->replyTo('[email protected]')
            ->priority(Email::PRIORITY_HIGH)
            ->subject("Invoice #{$invoiceNumber}")
            ->text("Please find your invoice attached.")
            ->html('<p>Please find your invoice attached.</p>')

            // File attachment
            ->attachFromPath($pdfPath, "invoice-{$invoiceNumber}.pdf")

            // Inline attachment (embed in HTML)
            ->embedFromPath('/path/to/logo.png', 'logo')
            ->html('<img src="cid:logo"> <p>Your invoice is attached.</p>')

            // Data attachment (from string)
            ->attach(
                $csvContent,
                "invoice-{$invoiceNumber}.csv",
                'text/csv',
            );

        $this->mailer->send($email);
    }
}

Email с адресами

<?php

declare(strict_types=1);

use Symfony\Component\Mime\Address;
use Symfony\Component\Mime\Email;

$email = (new Email())
    // Simple address
    ->from('[email protected]')

    // Address with name
    ->from(new Address('[email protected]', 'John Doe'))

    // Multiple recipients
    ->to('[email protected]', '[email protected]')
    ->to(new Address('[email protected]', 'User Three'))

    ->cc('[email protected]')
    ->bcc('[email protected]')
    ->replyTo('[email protected]')
    ->subject('Test Email');

Twig Integration (TemplatedEmail)

<?php

declare(strict_types=1);

namespace App\Service;

use Symfony\Bridge\Twig\Mime\TemplatedEmail;
use Symfony\Component\Mailer\MailerInterface;
use Symfony\Component\Mime\Address;

final class OrderMailer
{
    public function __construct(
        private readonly MailerInterface $mailer,
    ) {
    }

    public function sendOrderConfirmation(Order $order): void
    {
        $email = (new TemplatedEmail())
            ->from(new Address('[email protected]', 'MyShop'))
            ->to($order->getCustomerEmail())
            ->subject("Order #{$order->getNumber()} Confirmation")

            // Twig template for HTML
            ->htmlTemplate('emails/order_confirmation.html.twig')

            // Variables passed to template
            ->context([
                'order' => $order,
                'items' => $order->getItems(),
                'customer_name' => $order->getCustomerName(),
            ]);

        $this->mailer->send($email);
    }
}

Twig-шаблон email

{# templates/emails/order_confirmation.html.twig #}
{% extends 'emails/base.html.twig' %}

{% block subject %}Order #{{ order.number }} Confirmation{% endblock %}

{% block body %}
<h1>Thank you, {{ customer_name }}!</h1>

<p>Your order #{{ order.number }} has been confirmed.</p>

<table>
    <thead>
        <tr>
            <th>Product</th>
            <th>Qty</th>
            <th>Price</th>
        </tr>
    </thead>
    <tbody>
        {% for item in items %}
        <tr>
            <td>{{ item.name }}</td>
            <td>{{ item.quantity }}</td>
            <td>{{ item.price|format_currency('USD') }}</td>
        </tr>
        {% endfor %}
    </tbody>
    <tfoot>
        <tr>
            <td colspan="2"><strong>Total:</strong></td>
            <td><strong>{{ order.total|format_currency('USD') }}</strong></td>
        </tr>
    </tfoot>
</table>
{% endblock %}

Async Email с Messenger

Отправка email может быть медленной. Messenger позволяет отправлять email асинхронно.

# config/packages/messenger.yaml
framework:
    messenger:
        transports:
            async: '%env(MESSENGER_TRANSPORT_DSN)%'

        routing:
            # Route email messages to async transport
            'Symfony\Component\Mailer\Messenger\SendEmailMessage': async

Теперь все вызовы $mailer->send() автоматически отправляют сообщение в очередь. Worker обработает его асинхронно.

<?php

declare(strict_types=1);

// This is automatic! Just route SendEmailMessage to async transport.
// No changes to your code needed:
$this->mailer->send($email);
// Email goes to Messenger queue, not sent immediately

Email Events

<?php

declare(strict_types=1);

namespace App\EventSubscriber;

use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Mailer\Event\MessageEvent;

final class EmailSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            MessageEvent::class => 'onMessage',
        ];
    }

    public function onMessage(MessageEvent $event): void
    {
        $message = $event->getMessage();

        // Add custom header to every email
        $message->getHeaders()->addTextHeader(
            'X-Application',
            'MyApp',
        );
    }
}

Итоги

  • Mailer отправляет email через настроенный транспорт (SMTP, API)
  • Email -- простой email, TemplatedEmail -- с Twig-шаблоном
  • Вложения: attachFromPath(), attach(), embedFromPath()
  • Address -- адрес с именем
  • Async через Messenger: роутинг SendEmailMessage в async transport
  • MessageEvent -- событие для модификации всех исходящих email