Asynchronous email sending with Symfony

Asynchronous email sending with Symfony

Below article serves as a reminder for myself on how to quickly setup asynchronous email sending for Symfony.

We will use Symfony messenger with Doctrine as the queue mechanism.

The below instructions have been tested for Symfony version 6.4.

Install composer packages

Install the required composer packages to do the actual asynchronous sending:

composer require symfony/messenger;
composer require symfony/doctrine-messenger;

And for testing purposes:

composer require --dev zenstruck/messenger-test;

Configure messenger

Add the following YAML configuration file in /config/packages/messenger.yaml.

framework:
    messenger:
        # Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.
        failure_transport: failed

        transports:
            # https://symfony.com/doc/current/messenger.html#transport-configuration
            async:
                dsn: '%env(MESSENGER_TRANSPORT_DSN)%'
                retry_strategy:
                    max_retries: 10
                    delay: 10000 # milliseconds delay
                    # causes the delay to be higher before each retry
                    # e.g. 10 seconds delay, 20 seconds, 40 seconds
                    multiplier: 2
                    max_delay: 0
            failed: 'doctrine://default?queue_name=failed'
            # sync: 'sync://'

        routing:
            # Route your messages to the transports
            # 'App\Message\YourMessage': async
            'Symfony\Component\Mailer\Messenger\SendEmailMessage':  async

when@test:
    framework:
        messenger:
            transports:
                # replace with your transport name here (e.g., my_transport: 'in-memory://')
                # For more Messenger testing tools, see https://github.com/zenstruck/messenger-test
                async: 'test://'

when@dev:
    framework:
        messenger:
            transports:
                # replace with your transport name here (e.g., my_transport: 'in-memory://')
                # For more Messenger testing tools, see https://github.com/zenstruck/messenger-test
                async: 'sync://'

As you noticed, in test environment, we will be using the zenstruck/messenger-test library, and in dev environment we will send email still synchronously.

You may have also noticed that it uses an ENV variable MESSENGER_TRANSPORT_DSN. Please add a definition for it in your .env file:

MESSENGER_TRANSPORT_DSN=doctrine://default?auto_setup=1

Install supervisor

Install supervisor package, e.g.:

sudo apt-get install supervisor;

Add the below configuration in /etc/supervisor/conf.d/messenger-worker.conf.

[program:messenger-consume]
command=php /path/to/symfony/bin/console messenger:consume async --limit=10 -v
user=www-data
numprocs=1
startsecs=0
autostart=true
autorestart=true
startretries=10
process_name=%(program_name)s_%(process_num)02d

Update your tests

The zenstruck/messenger-test library provides many useful methods to assert email sending.

Ensure that your test class implements the following trait:

use Zenstruck\Messenger\Test\InteractsWithMessenger;
use Symfony\Component\Mailer\Messenger\SendEmailMessage;

// Ensure one email is sent
$this->transport()->queue()->assertNotEmpty();
$this->transport()->queue()->assertCount(1);
$this->transport()->queue()->assertContains(SendEmailMessage::class);

// Ensure email contains $string
$messages = $this->transport()->queue()->messages(SendEmailMessage::class);
/** @var Email $email */
$email = $messages[0]->getMessage();
/** @var TextPart $textPart */
$textPart = $email->getBody();
$emailBody = $textPart->getBody();
$this->assertStringContainsString($string, $emailBody);
comments powered by Disqus

Related Posts

How to naturally get pain relief from toothache

How to naturally get pain relief from toothache

Dealing with a toothache can be excruciating, disrupting your daily routine and making simple tasks seem unbearable.

Read More
Christmas 2023

Christmas 2023

Merry Christmas! It’s our first Christmas in Finland. Most of the below photos are from December, 2023.

Read More