doctrine-messenger is required as of Symfony 5+, since the transports were moved into their own packages;
symfony/doctrine-messenger will not be a hard dependency of symfony/messenger as of Symfony 6+.
Uncomment the Doctrine transport line in the .env file:
1
2
3
4
5
6
###> symfony/messenger ###
# Choose one of the transports below
# MESSENGER_TRANSPORT_DSN=amqp://guest:guest@localhost:5672/%2f/messages
MESSENGER_TRANSPORT_DSN=doctrine://default
# MESSENGER_TRANSPORT_DSN=redis://localhost:6379/messages
###< symfony/messenger ###
<?phpnamespaceApp\Infra\Bus;useSymfony\Component\Messenger\Envelope;useSymfony\Component\Messenger\MessageBusInterface;useSymfony\Component\Messenger\Stamp\DispatchAfterCurrentBusStamp;classEventBus{/** @var MessageBusInterface */private$eventBus;publicfunction__construct(MessageBusInterface$eventBus){$this->eventBus=$eventBus;}/** * Dispatches an event in a new transaction (after any other message being dispatched beforehand was handled successfully). * It's not mandatory but most events are handled asynchronously. * * @see https://symfony.com/doc/current/messenger/message-recorder.html about transactional dispatch * * @param bool $transactional Set it to false if you're not invoking the dispatch method from a handler */publicfunctiondispatch($event,bool$transactional=true):void{if($transactional){$envelope=Envelope::wrap($event,[// See https://symfony.com/doc/current/messenger/message-recorder.htmlnewDispatchAfterCurrentBusStamp(),]);}$this->eventBus->dispatch($envelope??$event);}}
Note
As of Symfony 5.2+, the DispatchAfterCurrentBusStamp can be always added independently of the context (whether dispatching in a handler or not),
as no more logic exception is thrown.
See symfony/symfony/pull/37976
1
2
3
4
5
6
7
# config/services.yamlservices:event_bus:class:App\Infra\Bus\EventBusarguments:['@messenger.bus.events']# Alias for event bus:App\Infra\Bus\EventBus:'@event_bus'
# config/packages/messenger.yamlframework:messenger:# Uncomment this (and the failed transport below) to send failed messages to this transport for later handling.failure_transport:failed_events# https://symfony.com/doc/current/messenger.html#transport-configurationtransports:events:dsn:'%env(MESSENGER_TRANSPORT_DSN)%'options:auto_setup:false# Do not automatically create messenger DB tablesqueue_name:eventtable_name:messenger_messagesretry_strategy:max_retries:3# milliseconds delay - 10 secondsdelay:10_000# causes the delay to be higher before each retry# e.g. 10 second delay, 20 seconds, 40 seconds, etc.multiplier:2max_delay:0failed_events:dsn:'%env(MESSENGER_TRANSPORT_DSN)%'options:auto_setup:falsequeue_name:failed_eventtable_name:messenger_messages
<?phpnamespaceApp\Application\MyNamespace\Event;classMyEvent{/** * @see https://symfony.com/doc/current/messenger.html#doctrine-entities-in-messages Rather pass identifiers in the event, not the full object * @var string */private$objectUuid;publicfunction__construct(string$objectUuid){$this->objectUuid=$objectUuid;}publicfunctiongetObjectUuid():string{return$this->objectUuid;}}
<?phpnamespaceApp\Application\MyNamespace\Handler;useApp\Application\MyNamespace\Event\MyEvent;classMyEventHandler{publicfunction__invoke(MyEvent$event):void{// do something with $event->getObjectUuid();}}
# config/packages/messenger.yamlframework:messenger:transports:events:# ...failed_events:# ... routing:# Route your messages to the 'events' transport:'App\Application\MyNamespace\Event\MyEvent':events
<?phpuseApp\Infra\Bus\EventBus;classMyService{/** EventBus */private$eventBus;publicfunction__construct(EventBus$eventBus):void{$objectUuid='my-uuid';// This will trigger asynchronous handling by MyEventHandler:// Nota: always set the second argument to false if you do not dispatch the event in a handler$this->eventBus->dispatch(newMyEvent($objectUuid),false);}}
Create an interface for your async events (Optional)🔗
Routing all your events to your events transport can be tedious. You can then declare an interface that all of your events implement and make routing easier.
The async event interface:
1
2
3
4
5
6
7
8
9
10
<?phpnamespaceApp\Application\Common\Event;/** * Marker for messages to dispatch as events (async handling). */interfaceAsyncEventInterface{}
Update your event accordingly (to implement this new interface):
And finally route the interface (not every event type) to your transport:
1
2
3
4
5
6
7
# config/packages/messenger.yamlframework:messenger:routing:# Remove the following line and replace it with the following:# 'App\Application\MyNamespace\Event\MyEvent': events'App\Application\Common\Event\AsyncEventInterface':events
From now on, every event which implements AsyncEventInterface will systematically be routed to the events transport.
Is supervisor running ? In your VM: sudo service supervisor status
Then visit the URL of your VM on port 9001: http://<my-app>.vm:9001 to access the Supervisor administration board, stop your workers, restart them, view their logs, etc.
You should also add a line to your .manala.yaml to restart Supervisor after deployments:
1
2
3
4
5
6
7
8
# .manala.yamlreleases:-&release_production:# …deploy_post_tasks:# …-shell:sudo /usr/bin/supervisorctl restart all
Common mistakes
If you use the startsecs option in your supervisor configuration, you will need to make sure that the limit option of the symfony command is high enough to not stop before the time limit