Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine

Mensaje, Manejador y el Bus

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Messenger es lo que se conoce como "Bus de Mensajes"... que es una especie de herramienta genérica que puede utilizarse para realizar un par de patrones de diseño diferentes, pero similares. Por ejemplo... Messenger puede utilizarse como un "Bus de comandos", un "Bus de consultas", un "Bus de eventos" o... un "Bus escolar". Oh... espera... este último nunca se ha implementado... vale, puede utilizarse para los tres primeros. De todos modos, si estos términos no significan absolutamente nada para ti... ¡genial! Hablaremos de lo que significa todo esto a lo largo del camino.

Patrón del bus de comandos

La mayoría de la gente utiliza Messenger como un "bus de comandos"... que es una especie de patrón de diseño. La idea es la siguiente. Ahora mismo, hacemos todo el trabajo en el controlador. Bueno, vale, hemos organizado las cosas en servicios, pero nuestro controlador llama a esos métodos directamente. Está bien organizado, pero sigue siendo básicamente procedimental: puedes leer el código de arriba a abajo.

Con un bus de comandos, separas lo que quieres que ocurra -llamado "comando"- del código que hace ese trabajo. Imagina que trabajas como camarero o camarera en un restaurante y alguien quiere una pizza margarita... ¡con albahaca fresca extra! Mmm. ¿Vuelves corriendo a la cocina y la preparas tú mismo? Probablemente no... En su lugar, anotas el pedido. Pero... digamos que, en lugar de eso, escribes una "orden": cocina una pizza al estilo margarita con extra de albahaca fresca. A continuación, "envías" esa orden a la cocina. Y finalmente, un chef hace toda la magia para que la pizza esté lista. Mientras tanto, puedes recibir más pedidos y enviar más "órdenes" a la cocina.

Esto es un bus de comandos: creas una orden simple e informativa "cocinar una pizza", se la das a algún "sistema" central... que recibe esa elegante palabra "bus", y se asegura de que algo vea esa orden y la "maneje"... en este caso, un "chef" cocina la pizza. Y ese "bus" central es probablemente lo suficientemente inteligente como para que diferentes personas "manejen" diferentes órdenes: el chef cocina la pizza, pero el bar tender prepara los pedidos de bebidas.

Creación de la clase de comandos

Vamos a recrear esa misma idea... ¡en código! La "orden" que queremos emitir es: añadir Ponka a esta imagen. En Messenger, cada comando es una simple clase PHP. En el directoriosrc/, crea un nuevo directorio Message/. Podemos poner nuestras clases de comando, o de "mensaje", en cualquier lugar... pero esta es una buena forma de organizar las cosas. Crea una nueva clase PHP llamada AddPonkaToImage... porque eso describe la intención de lo que queremos que ocurra: queremos que alguien añada ponka a la imagen. Dentro... por ahora... no hagas nada.

... lines 1 - 2
namespace App\Message;
class AddPonkaToImage
{
}

Una clase de mensaje es tu código: puede tener el aspecto que quieras. Más adelante hablaremos de ello.

Creación de la clase manejadora

Comando, ¡hecho! El paso 2 es crear la clase "manejadora": el código que realmente añadirá Ponka a una imagen. Una vez más, esta clase puede vivir en cualquier lugar, pero vamos a crear un nuevo directorio MessageHandler/ para mantener las cosas organizadas. La clase manejadora también puede llamarse de cualquier manera... pero a menos que te guste confundirte... llámala AddPonkaToImageHandler.

... lines 1 - 2
namespace App\MessageHandler;
... lines 4 - 5
use Symfony\Component\Messenger\Handler\MessageHandlerInterface;
class AddPonkaToImageHandler implements MessageHandlerInterface
{
... lines 10 - 13
}

A diferencia del mensaje, la clase manejadora tiene algunas reglas. En primer lugar, una clase manejadora debe implementar MessageHandlerInterface... que en realidad está vacía. Es una interfaz "marcadora". Hablaremos de por qué se necesita esto dentro de un rato. Y, en segundo lugar, la clase debe tener una función pública llamada __invoke() con un único argumento que sea de tipo indicativo con la clase de mensaje. Así, AddPonkaToImage, y luego cualquier nombre de argumento: $addPonkaToImage. Dentro, hmm, para ver cómo funciona todo esto, vamos a dump($addPonkaToImage).

... lines 1 - 4
use App\Message\AddPonkaToImage;
... lines 6 - 7
class AddPonkaToImageHandler implements MessageHandlerInterface
{
public function __invoke(AddPonkaToImage $addPonkaToImage)
{
dump($addPonkaToImage);
}
}

Conectando el mensaje y el manejador

Bien, retrocedamos. A grandes rasgos, así es como va a funcionar esto. En nuestro código, crearemos un objeto AddPonkaToImage y le diremos a Messenger -el bus de mensajes- que lo "maneje". Messenger verá nuestro objeto AddPonkaToImage, irá a buscar el servicio AddPonkaToImageHandler, llamará a su método __invoke() y le pasará el objetoAddPonkaToImage. Eso es... ¡todo lo que hay que hacer!

Pero espera... ¿cómo sabe Messenger que el objeto AddPonkaToImage debe ser "manejado" por AddPonkaToImageHandler? Por ejemplo, si tuviéramos varias clases de comandos y manejadores, ¿cómo sabría qué manejador maneja cada mensaje?

Busca tu terminal y ejecuta:

php bin/console debug:messenger

Este es un comando impresionante: nos muestra un mapa de qué manejador será llamado para cada mensaje. Ahora mismo sólo tenemos 1, pero... sí, de alguna manera ya sabe que AddPonkaToImage debe ser manejado por AddPonkaToImageHandler. ¿Cómo?

Lo sabe gracias a dos cosas. En primer lugar, ese MessageHandlerInterface vacío es una "bandera" que indica a Symfony que se trata de un "manejador" de Messenger. Y en segundo lugar, Messenger busca un método llamado __invoke() y lee el tipo-indicación de su argumento para saber qué clase de mensaje debe manejar. Así que, AddPonkaToImage.

Y sí, puedes configurar todo esto de otra manera, e incluso omitir la adición de la interfaz utilizando una etiqueta. Hablaremos de esto más adelante... pero normalmente no es algo de lo que debas preocuparte.

Ah, y si no estás familiarizado con el método __invoke(), ignorando a Messenger por un minuto, es un método mágico que puedes poner en cualquier clase de PHP para hacerla "ejecutable": puedes tomar un objeto y llamarlo como una función... si tiene este método:

$handler = new AddPonkaToImageHandler();
$handler($addPonkaToImage);

Este detalle no es en absoluto importante para entender Messenger, pero explica por qué se eligió este, por lo demás, "extraño" nombre de método.

Enviar el mensaje

¡Ufff! Comprobación de estado: tenemos una clase de mensaje, tenemos una clase de manejador, y gracias a alguna astucia de Symfony, Messenger sabe que están vinculados entre sí. Lo último que tenemos que hacer es... ¡enviar realmente el comando, o "mensaje", al bus!

Dirígete a ImagePostController. Esta es la ruta que sube nuestra imagen y añade Ponka a ella. Busca el bus de mensajes añadiendo un nuevo argumento con el tipoMessageBusInterface.

... lines 1 - 15
use Symfony\Component\Messenger\MessageBusInterface;
... lines 17 - 21
class ImagePostController extends AbstractController
{
... lines 24 - 38
public function create(Request $request, ValidatorInterface $validator, PhotoFileManager $photoManager, EntityManagerInterface $entityManager, PhotoPonkaficator $ponkaficator, MessageBusInterface $messageBus)
{
... lines 41 - 77
}
... lines 79 - 109
}

Entonces... justo encima de todo el código de la imagen Ponka -dejaremos todo eso ahí por el momento- di $message = new AddPonkaToImage(). Y luego$messageBus->dispatch($message).

... lines 1 - 5
use App\Message\AddPonkaToImage;
... lines 7 - 21
class ImagePostController extends AbstractController
{
... lines 24 - 38
public function create(Request $request, ValidatorInterface $validator, PhotoFileManager $photoManager, EntityManagerInterface $entityManager, PhotoPonkaficator $ponkaficator, MessageBusInterface $messageBus)
{
... lines 41 - 60
$message = new AddPonkaToImage();
$messageBus->dispatch($message);
/*
* Start Ponkafication!
*/
... lines 67 - 77
}
... lines 79 - 109
}

Eso es todo! dispatch() es el único método de ese objeto... no hay nada más complicado que esto.

Así que... ¡probemos! Si todo funciona, este objeto AddPonkaToImage debería pasarse a __invoke() y luego lo volcaremos. Como todo esto ocurrirá en una petición AJAX, utilizaremos un truco del perfilador para ver si ha funcionado.

Vuelve a actualizar la página... para estar seguro. Sube una nueva foto y... cuando termine, abajo en la barra de herramientas de depuración de la web, pasa el ratón sobre el icono de la flecha para encontrar... ¡bien! Aquí está esa petición AJAX. Mantendré pulsada la tecla Comando y haré clic en el enlace para abrirlo en una nueva pestaña. Este es el perfilador de esa petición AJAX. Haz clic en el enlace "Depurar" de la izquierda.

¡Ja! ¡Ahí está! ¡Esto nos muestra que nuestro código dump() se ejecutó durante la petición AJAX! ¡Ha funcionado! Pasamos el mensaje al bus de mensajes y éste llama al manejador.

Por supuesto... nuestro manejador no hace nada todavía. A continuación, vamos a trasladar toda la lógica de Ponkaficación de nuestro controlador al manejador.

Leave a comment!

2
Login or Register to join the conversation

Hey thank you so mush for the tuto, I have just 2 small questions

1/In this case wish class represent our Command if we talk CQRS design pattern ? Is it the handler !

2/In wish case we need to create a sender that implement the SenderInterface and a receiver just like it's described under the doc https://symfony.com/doc/cur...
Cheers :)

Reply

Hey ahmedbhs

> 1/In this case wish class represent our Command if we talk CQRS design pattern ? Is it the handler !

Yes, a handler can be seen as a command, usually they don't return nothing but it's not forbidden either

> 2/In wish case we need to create a sender that implement the SenderInterface and a receiver just like it's described under the doc

You want to create your own Sender when you need to send a message to a different system. Maybe to another system that you own via its API. And I couldn't think of a better example for a custom Receiver than the one from the docs, sorry!

Cheers!

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