Do you like movies? Yeah, me too. Do you love coding? Of course! And what about rabbits? Great!
Coding is like making movies, you are the director, you have a story to tell (The Goal), tools to use or work with (The actors), an environment (The scene) and last but not least The Scenario!
Are you ready to make your first movie with me? Okay! Action…🎬
The Goal
Once upon a time, there was Notification…
Notification is used everywhere, to keep users up to date, to confirm payment, to inform of some news, invite friends…
There are different ways to notify users: SMS, Web Notification, Mobile Notification, Emails…
In this tutorial (movie) we will focus on email.
Sending mail automatically can take a while and it can be a pain to the end user if it has to wait until the mail be sent.
A great way to avoid this pain of waiting, is to use queues. That gives you the ability to trigger the sending action on a queue to be handled later.
Let’s try to create a payment page and send mail confirmation asynchronously using queues!
The actors
Symfony
As described on Symfony’s website : “Symfony is a set of reusable PHP components … and a PHP framework”.
The great news is that if you don’t like to use Symfony like your principle framework that’s fine, you can use only needed components.
But to keep it simple, let’s admit that you rock with Symfony and PHP, in this (movie) we will use the Messenger component “The Messenger component helps applications send and receive messages to/from other applications or via message queues.”
RabbitMQ
Do you know Apache Kafka? Nope! Okay that’s fine RabbitMQ is an open source message broker. which manipulates messages, a producer sends a message to a queue and messages are received by one or many consumers.
The scene
Classic stage
To complete this tutorial, on your local development environment, you need :
- PHP 7.4 (extensions : curl, amqp, intl)
- Composer
- RabbitMQ manager (You can even install it locally or you can get a cloud instance for free using CloudAMQP)
- Symfony CLI
Opera stage
To be productive we will use docker and docker-compose.
The project will be structured like this :
The app directory will contain the Symfony project.
Utils directory will contain all ops folders Docker in our case.
You can find this structure here : https://github.com/sd-404/dev-stack
NB : Tell me below if you would like me to write a tutorial about this docker structure.
After cloning the repository we jump in:
cd utils/docker
You can adjust the .env file as you like:
then, you can even use the Makefile:
make build
Or use directly docker-compose commands:
docker-compose builddocker-compose up -d
You will get 6 containers up and running all we need is to start coding Yeah!
To stop containers just use :
docker-compose down — remove-orphans
And for running commands like composer or symfony we have to start a bash on the php container :
utils/docker > docker-compose run — rm php bash
That being said let’s get our hands dirty 😎
The scenario
Create the Symfony app
First of all, we need to create a Symfony app, to keep things simple we assume that you are using the dev stack mentioned before or every requirement was installed and is working correctly on your local environment.
The specification of Symfony, is that you can create, even a full web skeleton, in this case, you will have all components that you need like twig, doctrine, annotations … Or you can only get a console application with fewer components and you can add your needed ones.
Let’s take the second option and create a console app. We will add needed components next:
root@phpcontainer:/var/www# symfony new .
If you keep the .env file unchanged, you can go to http://localhos:8181, else you have to replace the port 8181 by your CUSTOM_WEBSERVER_PORT
Install packages
Great, we have our app, up and running, so let’s make a random checkout page. On Bootstrap there are many example pages, and you know what? Our checkout page is there!
But before that we have to install few packages :
The Symfony MakerBundle (dev requirement):
root@phpcontainer:/var/www# composer require — dev symfony/maker-bundle
Annotation, Twig, Asset and Validator:
root@phpcontainer:/var/www# composer require \symfony/twig-bundle \symfony/asset \doctrine/annotations \symfony/validator
The HomePage
We start by creating the HomeController
root@phpcontainer:/var/www# symfony console make:controller HomeController
Second, we add a custom route and methods :
Then, let’s create a template, on the templates directory, we create a home folder with the index.html.twig file:
we will not focus in this tutorial on the front-end, so let’s use Bootstrap examples template and guess what there’s a checkout template.
We can paste the HTML from the bootstrap checkout template.
Next, we will customize it by making all fields not required only the email one
And add the action with the method to the form tag.
That’s it now when you reload your http://localhost:8181
Asynchronously
How it works
First, the user will submit the form and get an immediate response.
In the background, a message will be sent to RabbitMQ that will be queued to a specific queue.
Then, we have to run a consumer to handle this message.
If we get many messages we can run multiple consumers to handle them which is very powerful.
Another great news, if for some reason our message can not be handled it will be placed automatically on the failed queue so nothing is lost.
Symfony messenger
Now we have to install and configure the messenger component:
root@phpcontainer:/var/www# composer require symfony/messenger
After installation the package will create messenger.yml and 2 folders (Message, MessageHandler)
Let’s start by configuring messenger.yml
async : is the name of transport
dsn: the rabbitmq server declared in .env file
On RabbitMQ, messages are not published directly to a queue but to an exchanger and using the routing key the exchanger can route the message to the correct queue.
NB: You can find more details about exchanger here.
When we run the consumer to handle messages, the name of the transport to handle in this example will be “async”.
On the routing part of messenger.yml, we map the message with the transport manager.
So let’s create the SendEmailNotification message and its handler:
root@phpcontainer:/var/www# symfony console make:message SendEmailNotification
To explain, each message has it’s handler which must implement the __invoke method.
Our __invoke method here will send the email using the symfony Mailer package.
Don’t forget to install it :
root@phpcontainer:/var/www# composer require symfony/mailer
Next, we have to adjust the HomeController to create a new message each time the user clicks the submit button.
We added some validation to the email address using the symfony Validator but it’s not the goal of this tutorial.
The result
We are good to go now, let’s try it.
On the dev stack you have mailhog installed, you can run the http://localhost:8025 to get the web interface.
- Fill email on the checkout form
- Submit
- Run
root@phpcontainer:/var/www# symfony console messenger:consume async -vv
On the RabbitMQ web interface (http://localhost:15672) you can find the queued message
And by checking the mailhog, the mail was delivered:
You can even see the debug mode on your console:
That works? Great you just made your first movie with me 😉
The End
In this movie we try to make an example using the Symfony messenger component to do tasks asynchronously like sending email with the help of message broker RabbitMQ.
You can find this tutorial here.
Hope you enjoy this introduction to the Symfony messenger.
Stay connected, stay informed, and stay inspired! Today, I invite you to take the next step and follow me. Thank you for being a loyal reader. I appreciate your support. Happy coding 😉