Scroll down to the script below, click on any sentence (including terminal blocks!) to jump to that spot in the video!
Sending an email - like after we complete registration - takes a little bit of time because it involves making a network request to SendGrid. Yep, sending emails is always going to be a "heavy" operation. And whenever you're doing something heavy... it means your user is waiting for the response. That's... not the end of the world... but it's not ideal.
So... when a user registers, instead of sending the email immediately, could we send it... later and return the response faster? Of course! Thanks to Symfony's Messenger component, which has first-class integration with Mailer.
First: in our editor, open
.env.local and, for simplicity. let's change the
MAILER_DSN back to use Mailtrap. To install Messenger... you can kinda guess
the command. In your terminal, run:
composer require messenger
Messenger is super cool and we have an entire tutorial about it. But, it's also simple to get set up and running. Let's see how.
The recipe for Messenger just did a few things: it created a new
configuration file and also added a section in
.env. Let's go find that.
Here's the 30 second description of how to get Messenger set up. In order to do
some work "later" - like sending an email - you need to configure a "queueing"
system where details about that work - called "messages" - will be sent. Messenger
calls these transports. Because we're already using Doctrine, the easiest "queueing"
system is a database table. Uncomment that
MESSENGER_TRANSPORT_DSN to use it.
config/packages/messenger.yaml - that's the new config file - and
uncomment the transport called
Great. As soon as you install Messenger, when Mailer sends an email, internally,
it will automatically start doing that by dispatching a message through Messenger.
Hit Shift + Shift to open a class called
Specifically, Mailer will create this object, put our
Now, if we only installed messenger, the fact that this is being dispatched through the message bus would make... absolutely no difference. The emails would still be handled immediately - or synchronously.
But now we can tell Messenger to "send" instances of
async transport instead of "handling" them - meaning delivering the
email - right now. We do that via the
routing section. Go copy the namespace of
SendEmailMessage class and, under
routing, I'll clear out the comments and
Symfony\Component\Mailer\Messenger\, copy the class name, and paste:
SendEmailMessage. Set this to
Hey! We just made all emails async! Woo! Let's try it: find the registration
page.... register as "Fox", email
[email protected], any password,
agree to the terms and register!
You may not have noticed, but if you compared the response times of submitting the form before and after that change... this was way, way faster.
Over in Mailtrap... there are no new messages. I can refresh and... nothing. The email was not delivered. Yay! Where is it? Sitting & waiting inside our queue... which is a database table. You can see it by running:
php bin/console doctrine:query:sql 'SELECT * FROM messenger_messages'
That table was automatically created when we sent our first message. It has one row with our one Email inside. If you look closely... you can see the details: the subject, and the email template that will be rendered when it's delivered.
How do we actually send the email? In Messenger, you process any waiting messages in the queue by running:
php bin/console messenger:consume -vv
-vv adds extra debugging info... it's more fun. This process is called a
"worker" - and you'll have at least one of these commands running at all times
on production. Check out our Messenger tutorial for details about that.
Cool! The message was "received" from the queue and "handled"... which is a fancy way in this case to say that the email was actually delivered! Go check out Mailtrap! Ah! There it is! The full correct email... in all its glory.
By the way, in order for your emails to be rendered correctly when being sent via Messenger, you need to make sure that you have the route context parameters set up correctly. That's a topic we covered earlier in this tutorial.
So... congrats on your new shiny async emails! Next, let's make sure that the "author weekly report" email still works... because... honestly... there's going to be a gotcha. Also, how does sending to a transport affect our functional tests?