How to send notification asynchronously using Symfony & RabbitMQ

Sd404
7 min readSep 28, 2021

--

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.

  1. Fill email on the checkout form
  2. Submit
  3. 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

RabbitMQ manager dashboard

And by checking the mailhog, the mail was delivered:

MailHog

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 😉

--

--

Sd404

Hey there! I'm a passionate web developer, serial reader and tech enthusiast. Follow me on Medium to stay updated with my latest articles. Happy coding! 🚀