Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Eventos y bus de eventos

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

With a Subscription, click any sentence in the script to jump to that part of the video!

Login Subscribe

Messenger es un "bus de mensajes". Y resulta que un "mensaje" es un término bastante genérico en informática. De hecho, hay tres tipos de mensajes de los que oirás hablar habitualmente.

Los mensajes: Comandos, Eventos y Consultas

El primer tipo de mensaje es un "comando". Y ese es el tipo que hemos estado creando hasta ahora: creamos clases de mensajes que suenan como un comando:AddPonkaToImage o DeleteImagePost y cuyos manejadores realizan alguna acción. Cuando creas clases de mensajes y manejadores con este aspecto, estás utilizando Messenger como un "bus de comandos". Y una de las, digamos, "reglas" de los comandos es que cada comando debe tener exactamente un manejador. Ese es el patrón de diseño "comando".

El segundo tipo de mensaje es un "evento". Si creas una clase de "evento" y la pasas a Messenger, entonces estás utilizando Messenger como un bus de "eventos". La diferencia entre el aspecto de una clase "comando" y el de una clase "evento" es sutil: se reduce a las convenciones de nomenclatura y a lo que, en última instancia, intentas conseguir. Un evento se envía después de que ocurra algo y puede tener de cero a muchos manejadores. No te preocupes, pronto veremos cómo es esto.

El tercer tipo de mensaje es una "consulta", de la que hablaremos más adelante. Por ahora, vamos a centrarnos en entender los eventos y en qué se diferencian de los comandos... porque... puede ser súper confuso. Y Messenger, al ser un "bus de mensajes" genérico, funciona perfectamente con ambos.

Crear un segundo bus

Antes de crear nuestro primer evento, cerraré algunas cosas y luego abriréconfig/packages/messenger.yaml. Si nuestra aplicación aprovecha tanto los comandos como los eventos, está totalmente bien utilizar un solo bus para manejar todo eso. Pero, en aras de complicarnos un poco la vida y aprender más, vamos a seguir utilizando nuestro bus existente sólo como bus de comandos y a crear un nuevo bus para utilizarlo sólo con eventos.

Para ello, bajo la clave buses:, añade una nueva llamada, qué tal, event.bus. Ponla en ~ que es nula... sólo porque no tenemos ninguna otra configuración que debamos poner aquí todavía. Esto hará que se añada un nuevo servicio MessageBus al contenedor.

framework:
messenger:
buses:
... lines 4 - 7
event.bus: ~
... lines 9 - 32

Hasta ahora, siempre que hemos necesitado el bus de mensajes -como en ImagePostController - lo hemos autocableado utilizando la sugerencia de tipo MessageBusInterface. La pregunta ahora es: ¿cómo podemos acceder al nuevo servicio de bus de mensajes?

Busca tu terminal y ejecuta

php bin/console debug:autowiring

... que... ¡estalla! Mi error:

Configuración no válida para la ruta framework.messenger: debes especificar default_bus

Copiar el nombre del bus por defecto. Una vez que definas más de un bus, necesitas una clavedefault_bus establecida para tu bus "principal". Esto le dice a Symfony qué servicio de MessageBus debe pasarnos cuando utilicemos la sugerencia de tipo MessageBusInterface.

framework:
messenger:
default_bus: messenger.bus.default
buses:
... lines 6 - 9
event.bus: ~
... lines 11 - 34

Prueba de nuevo el comando debug:autowiring... y busca "mess".

php bin/console debug:autowiring

Ah, ¡ahora tenemos dos entradas! Esto me dice que si usamos el type-hintMessageBusInterface, obtendremos el servicio messenger.bus.default. Ignora la parte debug.traced - eso es sólo Symfony añadiendo algunas herramientas de depuración. Pero ahora, si usas el type-hint MessageBusInterface y nombras el argumento $eventBus, ¡te pasará el nuevo servicio de bus de eventos!

Se trata de una nueva característica de Symfony 4.2, en la que puedes autoconectar cosas mediante una combinación del tipo-hint y el nombre del argumento. Symfony tomó el nombre de nuestro bus - event.bus - e hizo posible utilizar el nombre del argumento $eventBus.

Diferencias entre los buses

¡Genial! ¡Ya sabemos cómo obtener el bus de eventos! Pero... ¿cuál es la diferencia entre estos dos buses? ¿Se comportan de forma diferente? La respuesta es... ¡no!

Un bus no es más que un conjunto de middleware. Si tienes dos objetos de bus que tienen el mismo middleware... entonces... ¡esos buses de mensajes son efectivamente idénticos! Así que, aparte del hecho de que, hasta ahora, sólo hemos añadido nuestro AuditMiddlewareal primer bus, estos buses funcionarán y actuarán de forma idéntica. Por eso, aunque haya creado un servicio para manejar comandos y otro para manejar eventos... ah... realmente podríamos enviar todos nuestros comandos y eventos a un solo servicio.

A continuación, vamos a crear un evento, a aprender qué aspecto tiene, por qué podríamos utilizarlos y en qué se diferencian de los comandos.

Leave a comment!

3
Login or Register to join the conversation
Default user avatar
Default user avatar Sergey Smile | posted hace 4 años

Thanks a lot for awesome tutorial.
Using events through messenger sounds like a good idea. They can be async, can be handled by different handlers.
But what about if i need be possible to handle multiple events by one handler like i can do it in event subscriber.

For example:
I have event NewDataSaved witch has Handler UpdateTotalAmount. And now i'm implementing new feature and dispatch new event DataUpdated.
Is it possible to use UpdateTotalAmount handler to handle both events: NewDataSaved and NewDataUpdated?

Reply

Hey Sergey Smile!

Great question :). Yes, this is possible, but instead of just relying on the "add __invoke() with the type-hint for the event that should be handled" magic, you'll need to make your handler implement MessageSubscriberInterface, which will allow you to say exactly which messages your handler handlers and which methods should be called :). https://symfony.com/doc/current/messenger.html#handler-subscriber-options

Another option would be to make both event classes implement the same interface and have your handler type-hint that interface. I believe (could be wrong) that your handler would then handle any messages that implement that interface.

Let me know if that helps!

Cheers!

Reply
Default user avatar

Thank you for the fastest answer in the world ;)
My bad, I feel like an ... I assure you that MessageSubscriberInterface was missing in docs last time I was reading it...

So, your faith is true. I just tried with Interface for events and it works. But I think using MessageSubscriberInterface is more obviously way.

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

Este tutorial está construido con Symfony 4.3, pero funcionará bien en Symfony 4.4 o 5.

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-ctype": "*",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "doctrine/annotations": "^1.0", // v1.8.0
        "doctrine/doctrine-bundle": "^1.6.10", // 1.11.2
        "doctrine/doctrine-migrations-bundle": "^1.3|^2.0", // v2.0.0
        "doctrine/orm": "^2.5.11", // v2.6.3
        "intervention/image": "^2.4", // 2.4.2
        "league/flysystem-bundle": "^1.0", // 1.1.0
        "phpdocumentor/reflection-docblock": "^3.0|^4.0", // 4.3.1
        "sensio/framework-extra-bundle": "^5.3", // v5.3.1
        "symfony/console": "4.3.*", // v4.3.2
        "symfony/dotenv": "4.3.*", // v4.3.2
        "symfony/flex": "^1.9", // v1.18.7
        "symfony/framework-bundle": "4.3.*", // v4.3.2
        "symfony/messenger": "4.3.*", // v4.3.4
        "symfony/property-access": "4.3.*", // v4.3.2
        "symfony/property-info": "4.3.*", // v4.3.2
        "symfony/serializer": "4.3.*", // v4.3.2
        "symfony/validator": "4.3.*", // v4.3.2
        "symfony/webpack-encore-bundle": "^1.5", // v1.6.2
        "symfony/yaml": "4.3.*" // v4.3.2
    },
    "require-dev": {
        "easycorp/easy-log-handler": "^1.0.7", // v1.0.7
        "symfony/debug-bundle": "4.3.*", // v4.3.2
        "symfony/maker-bundle": "^1.0", // v1.12.0
        "symfony/monolog-bundle": "^3.0", // v3.4.0
        "symfony/stopwatch": "4.3.*", // v4.3.2
        "symfony/twig-bundle": "4.3.*", // v4.3.2
        "symfony/var-dumper": "4.3.*", // v4.3.2
        "symfony/web-profiler-bundle": "4.3.*" // v4.3.2
    }
}
userVoice