A guide on how to create a custom middleware for Symfony Messenger to discard(not send) or not handle certain messages.
Table of contents
Open Table of contents
Introduction
Recently, I’ve been working on a project where I needed to implement a logic where certain messages from Symfony Messenger would not get sent or handled if they met certain criteria. It was pretty hard to find a working solution and Symfony documentation didn’t provide a clear answer, so after I found a solution, I decided to write this article to help others who might encounter the same issue.
How Symfony Messenger works
When you dispatch a message in Symfony Messenger, it goes through a couple of predefined middlewares. The order is the following:
add_bus_name_stamp_middleware
dispatch_after_current_bus
failed_message_processing_middleware
- YOUR CUSTOM MIDDLEWARES
send_message
handle_message
Creating a custom message interface
Here is a basic example of how you can create a custom message interface that will work with your custom middleware.
<?php
namespace App\Messenger;
interface CustomMessageInterface
{
public function shouldSendEmail(): bool;
}
Now, let’s create a message that implements this interface.
<?php
namespace App\Messenger;
class SendEmailMessage implements CustomMessageInterface
{
public function __construct(private readonly string $email)
{
}
//If the email ends with @test.com, we should notb send the email
public function shouldSendEmail(): bool
{
return false === str_ends_with($this->getEmail(), '@test.com');
}
public function getEmail(): ?string
{
return $this->email;
}
}
Creating the custom middleware
Now, let’s create the custom middleware that will check if the message should be sent or not.
<?php
namespace App\Messenger\Middleware;
use App\Messenger\CustomMessageInterface;
use Symfony\Component\Messenger\Envelope;
use Symfony\Component\Messenger\Middleware\MiddlewareInterface;
use Symfony\Component\Messenger\Middleware\StackInterface;
class SendEmailMiddleware implements MiddlewareInterface
{
public function handle(Envelope $envelope, StackInterface $stack): Envelope
{
$message = $envelope->getMessage();
if ($message instanceof CustomMessageInterface && false === $message->shouldSendEmail()) {
$envelope = $envelope->with(new ReceivedStamp(__CLASS__));
}
return $this->next->handle($envelope, $stack);
}
}
Registering the custom middleware
To register the custom middleware, you need to add it to the messenger.yaml
file.
framework:
messenger:
buses:
messenger.bus.default:
middleware:
- App\Messenger\Middleware\SendEmailMiddleware
Sending but not handling the message
If you want to send the message but not handle it, you can use the HandledStamp
class instead.
$envelope = $envelope->with(new HandledStamp('Handled from email middleware',__CLASS__));
Conclusion
This is very useful when you need to implement a custom logic for lots of messages, and you don’t want to clutter your code with if statements in each handler or dispatcher. If you only have one message that needs this logic, you can implement it directly in the handler or right before the dispatch.
Note: This is also very useful when you don’t have any handlers configured for the messages, but instead sending messages async to a queueing system, like RabbitMQ. In this case, the handler can be another application which you don’t have access to