|
| 1 | +.. index:: |
| 2 | + single: Messenger |
| 3 | + single: Components; Messenger |
| 4 | + |
| 5 | +The Messenger Component |
| 6 | +======================= |
| 7 | + |
| 8 | + The Messenger component helps applications send and receive messages to/from other applications or via message queues. |
| 9 | + |
| 10 | +Installation |
| 11 | +------------ |
| 12 | + |
| 13 | +.. code-block:: terminal |
| 14 | +
|
| 15 | + $ composer require symfony/messenger |
| 16 | +
|
| 17 | +Alternatively, you can clone the `<https://github.com/symfony/messenger>`_ repository. |
| 18 | + |
| 19 | +.. include:: /components/require_autoload.rst.inc |
| 20 | + |
| 21 | +Concepts |
| 22 | +-------- |
| 23 | + |
| 24 | +.. image:: /_images/components/messenger/overview.png |
| 25 | + |
| 26 | +**Sender**: |
| 27 | + Responsible for serializing and sending messages to _something_. This |
| 28 | + something can be a message broker or a third party API for example. |
| 29 | + |
| 30 | +**Receiver**: |
| 31 | + Responsible for deserializing and forwarding messages to handler(s). This |
| 32 | + can be a message queue puller or an API endpoint for example. |
| 33 | + |
| 34 | +**Handler**: |
| 35 | + Responsible for handling messages using the business logic applicable to the messages. |
| 36 | + |
| 37 | +Bus |
| 38 | +--- |
| 39 | + |
| 40 | +The bus is used to dispatch messages. The behaviour of the bus is in its ordered |
| 41 | +middleware stack. The component comes with a set of middleware that you can use. |
| 42 | + |
| 43 | +When using the message bus with Symfony's FrameworkBundle, the following middleware |
| 44 | +are configured for you: |
| 45 | + |
| 46 | +#. ``LoggingMiddleware`` (logs the processing of your messages) |
| 47 | +#. ``SendMessageMiddleware`` (enables asynchronous processing) |
| 48 | +#. ``HandleMessageMiddleware`` (calls the registered handle) |
| 49 | + |
| 50 | +Example:: |
| 51 | + |
| 52 | + use App\Message\MyMessage; |
| 53 | + use Symfony\Component\Messenger\MessageBus; |
| 54 | + use Symfony\Component\Messenger\HandlerLocator; |
| 55 | + use Symfony\Component\Messenger\Middleware\HandleMessageMiddleware; |
| 56 | + |
| 57 | + $bus = new MessageBus([ |
| 58 | + new HandleMessageMiddleware(new HandlerLocator([ |
| 59 | + MyMessage::class => $handler, |
| 60 | + ])), |
| 61 | + ]); |
| 62 | + |
| 63 | + $result = $bus->handle(new MyMessage(/* ... */)); |
| 64 | + |
| 65 | +.. note: |
| 66 | +
|
| 67 | + Every middleware needs to implement the ``MiddlewareInterface`` interface. |
| 68 | +
|
| 69 | +Handlers |
| 70 | +-------- |
| 71 | + |
| 72 | +Once dispatched to the bus, messages will be handled by a "message handler". A |
| 73 | +message handler is a PHP callable (i.e. a function or an instance of a class) |
| 74 | +that will do the required processing for your message:: |
| 75 | + |
| 76 | + namespace App\MessageHandler; |
| 77 | + |
| 78 | + use App\Message\MyMessage; |
| 79 | + |
| 80 | + class MyMessageHandler |
| 81 | + { |
| 82 | + public function __invoke(MyMessage $message) |
| 83 | + { |
| 84 | + // Message processing... |
| 85 | + } |
| 86 | + } |
| 87 | + |
| 88 | +Adapters |
| 89 | +-------- |
| 90 | + |
| 91 | +In order to send and receive messages, you will have to configure an adapter. An |
| 92 | +adapter will be responsible of communicating with your message broker or 3rd parties. |
| 93 | + |
| 94 | +Your own sender |
| 95 | +~~~~~~~~~~~~~~~ |
| 96 | + |
| 97 | +Using the ``SenderInterface``, you can easily create your own message sender. |
| 98 | +Let's say you already have an ``ImportantAction`` message going through the |
| 99 | +message bus and handled by a handler. Now, you also want to send this message as |
| 100 | +an email. |
| 101 | + |
| 102 | +First, create your sender:: |
| 103 | + |
| 104 | + namespace App\MessageSender; |
| 105 | + |
| 106 | + use App\Message\ImportantAction; |
| 107 | + use Symfony\Component\Message\SenderInterface; |
| 108 | + |
| 109 | + class ImportantActionToEmailSender implements SenderInterface |
| 110 | + { |
| 111 | + private $toEmail; |
| 112 | + private $mailer; |
| 113 | + |
| 114 | + public function __construct(\Swift_Mailer $mailer, string $toEmail) |
| 115 | + { |
| 116 | + $this->mailer = $mailer; |
| 117 | + $this->toEmail = $toEmail; |
| 118 | + } |
| 119 | + |
| 120 | + public function send($message) |
| 121 | + { |
| 122 | + if (!$message instanceof ImportantAction) { |
| 123 | + throw new \InvalidArgumentException(sprintf('Producer only supports "%s" messages.', ImportantAction::class)); |
| 124 | + } |
| 125 | + |
| 126 | + $this->mailer->send( |
| 127 | + (new \Swift_Message('Important action made')) |
| 128 | + ->setTo($this->toEmail) |
| 129 | + ->setBody( |
| 130 | + '<h1>Important action</h1><p>Made by '.$message->getUsername().'</p>', |
| 131 | + 'text/html' |
| 132 | + ) |
| 133 | + ); |
| 134 | + } |
| 135 | + } |
| 136 | + |
| 137 | +Your own receiver |
| 138 | +~~~~~~~~~~~~~~~~~ |
| 139 | + |
| 140 | +A receiver is responsible for receiving messages from a source and dispatching |
| 141 | +them to the application. |
| 142 | + |
| 143 | +Let's say you already processed some "orders" in your application using a |
| 144 | +``NewOrder`` message. Now you want to integrate with a 3rd party or a legacy |
| 145 | +application but you can't use an API and need to use a shared CSV file with new |
| 146 | +orders. |
| 147 | + |
| 148 | +You will read this CSV file and dispatch a ``NewOrder`` message. All you need to |
| 149 | +do is to write your custom CSV receiver and Symfony will do the rest. |
| 150 | + |
| 151 | +First, create your receiver:: |
| 152 | + |
| 153 | + namespace App\MessageReceiver; |
| 154 | + |
| 155 | + use App\Message\NewOrder; |
| 156 | + use Symfony\Component\Message\ReceiverInterface; |
| 157 | + use Symfony\Component\Serializer\SerializerInterface; |
| 158 | + |
| 159 | + class NewOrdersFromCsvFile implements ReceiverInterface |
| 160 | + { |
| 161 | + private $serializer; |
| 162 | + private $filePath; |
| 163 | + |
| 164 | + public function __construct(SerializerInteface $serializer, string $filePath) |
| 165 | + { |
| 166 | + $this->serializer = $serializer; |
| 167 | + $this->filePath = $filePath; |
| 168 | + } |
| 169 | + |
| 170 | + public function receive(callable $handler) : void |
| 171 | + { |
| 172 | + $ordersFromCsv = $this->serializer->deserialize(file_get_contents($this->filePath), 'csv'); |
| 173 | + |
| 174 | + foreach ($ordersFromCsv as $orderFromCsv) { |
| 175 | + $handler(new NewOrder($orderFromCsv['id'], $orderFromCsv['account_id'], $orderFromCsv['amount'])); |
| 176 | + } |
| 177 | + } |
| 178 | + |
| 179 | + public function stop(): void |
| 180 | + { |
| 181 | + // noop |
| 182 | + } |
| 183 | + } |
| 184 | + |
| 185 | +Receiver and Sender on the same bus |
| 186 | +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
| 187 | + |
| 188 | +To allow us to receive and send messages on the same bus and prevent an infinite |
| 189 | +loop, the message bus is equipped with the ``WrapIntoReceivedMessage`` middleware. |
| 190 | +It will wrap the received messages into ``ReceivedMessage`` objects and the |
| 191 | +``SendMessageMiddleware`` middleware will know it should not route these |
| 192 | +messages again to an adapter. |
0 commit comments