If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.
With a Subscription, click any sentence in the script to jump to that part of the video!
Login SubscribeAbre DeleteImagePostHandler
. Lo principal que necesita saber un bus de mensajes es el vínculo entre la clase de mensajes DeleteImagePost
y su manejador. Necesita saber que cuando despachamos un objeto DeleteImagePost
, debe llamar aDeleteImagePostHandler
.
¿Cómo sabe Messenger que estas dos clases están conectadas? Lo sabe porque nuestro manipulador implementa MessageHandlerInterface
-esto lo "marca" como manipulador de mensajes- y porque su método __invoke()
tiene un tipo de referencia con DeleteImagePost
. Si sigues estas dos reglas -implementar esa interfaz y crear un método __invoke()
con un argumento con tipo de referencia con la clase de mensajes- entonces... ¡ya está!
Busca tu terminal y ejecuta:
php bin/console debug:messenger
¡Si! Esto lo demuestra: DeleteImagePost
es manejado por DeleteImagePostHandler
.
Entonces... en config/services.yaml
, nos pusimos un poco más elegantes. Al organizar cada tipo de mensaje -comandos, eventos y consultas- en diferentes directorios, pudimos añadir una etiqueta a cada servicio. Esto da un poco más de información a Messenger. Dice:
Quiero que hagas la conexión normal entre la
DeleteImagePost
clase de mensaje yDeleteImagePostHandler
... pero sólo quiero que le digas al "bus de comandos" sobre esa conexión... porque ese es el único bus al que voy a enviar ese mensaje.
También lo vemos en debug:messenger
: el bus de comandos conoce la conexiónDeleteImagePost
y DeleteImagePostHandler
y los otros dos buses conocen otros enlaces de mensajes y manejadores de mensajes. Ah, y como recordatorio, si todo esto de las "etiquetas" te confunde... sáltalo. Organiza un poco más las cosas, pero puedes tener con la misma eficacia un solo bus que lo maneje todo.
En cualquier caso, este sistema es rápido de usar, pero hay algunas cosas que no puedes cambiar. Por ejemplo, el método de tu manejador debe llamarse __invoke()
... eso es lo que busca Symfony. Y como una clase sólo puede tener un método llamado __invoke()
, esto significa que no puedes tener un único manejador que maneje varias clases de mensajes diferentes. De todas formas, no me gusta hacer esto, prefiero una clase de mensaje por manejador... pero es una limitación técnica.
Ahora que hemos revisado todo esto... resulta que esto es sólo una parte de la historia. Si queremos, podemos tomar más control de cómo se vincula una clase de mensaje con su manejador... incluyendo alguna configuración extra.
¿Cómo? En lugar de implementar MessageHandlerInterface
, implementaMessageSubscriberInterface
.
... lines 1 - 9 | |
use Symfony\Component\Messenger\Handler\MessageSubscriberInterface; | |
... lines 11 - 12 | |
class DeleteImagePostHandler implements MessageSubscriberInterface | |
{ | |
... lines 15 - 38 | |
} |
Esto es un cambio menos grande de lo que parece. Si abresMessageSubscriberInterface
, extiende MessageHandlerInterface
. Así, seguimos implementando efectivamente la misma interfaz... pero ahora nos vemos obligados a tener un nuevo método: getHandledMessages()
.
En la parte inferior de mi clase, iré a Código -> Generar -o Comando + N en un Mac- y seleccionaré "Implementar métodos".
En cuanto implementemos esta interfaz, en lugar de buscar por arte de magia el método__invoke()
y comprobar el tipo-indicación del argumento para saber qué clase de mensaje debe manejar, Symfony llamará a este método. ¿Nuestro trabajo aquí? Decirle exactamente qué clases manejamos, qué método debe llamar y... ¡algunas otras cosas divertidas!
... lines 1 - 12 | |
class DeleteImagePostHandler implements MessageSubscriberInterface | |
{ | |
... lines 15 - 34 | |
public static function getHandledMessages(): iterable | |
{ | |
// TODO: Implement getHandledMessages() method. | |
} | |
} |
Lo más fácil que puedes poner aquí es yield DeleteImagePost::class
. No pienses demasiado en ese rendimiento... es sólo azúcar sintáctico. También podrías devolver un array con una cadena DeleteImagePost::class
dentro.
... lines 1 - 12 | |
class DeleteImagePostHandler implements MessageSubscriberInterface | |
{ | |
... lines 15 - 34 | |
public static function getHandledMessages(): iterable | |
{ | |
yield DeleteImagePost::class; | |
} | |
} |
¿Qué diferencia supone eso? Vuelve a ejecutar debug:messenger
.
php bin/console debug:messenger
Y... no supuso ninguna diferencia. Con esta configuración súper sencilla, le hemos dicho a Messenger que esta clase maneja objetos DeleteImagePost
... y luego Messenger sigue asumiendo que debe ejecutar un método llamado __invoke()
.
Pero técnicamente, esta sugerencia de tipo ya no es necesaria. Elimina eso y vuelve a ejecutarlo:
php bin/console debug:messenger
Sigue viendo la conexión entre la clase de mensaje y el manejador.
Vale... pero como probablemente deberíamos utilizar las sugerencias de tipo... esto no es tan interesante todavía. ¿Qué más podemos hacer?
Bueno, asignando esto a un array, podemos añadir algo de configuración. Por ejemplo, podemos decir 'method' => '__invoke'
. Sí, ahora podemos controlar qué método llamará Messenger. Esto es especialmente útil si decides que quieres añadir otro rendimiento para manejar un segundo mensaje... y quieres que Messenger llame a un método diferente.
... lines 1 - 12 | |
class DeleteImagePostHandler implements MessageSubscriberInterface | |
{ | |
... lines 15 - 34 | |
public static function getHandledMessages(): iterable | |
{ | |
yield DeleteImagePost::class => [ | |
'method' => '__invoke' | |
]; | |
} | |
} |
¿Qué más podemos poner aquí? Una opción es priority
- pongámosla a... 10.
... lines 1 - 12 | |
class DeleteImagePostHandler implements MessageSubscriberInterface | |
{ | |
... lines 15 - 34 | |
public static function getHandledMessages(): iterable | |
{ | |
yield DeleteImagePost::class => [ | |
'method' => '__invoke', | |
... lines 39 - 41 | |
'priority' => 10, | |
]; | |
} | |
} |
Esta opción es... mucho menos interesante de lo que puede parecer en un principio. Ya hablamos antes de los transportes prioritarios: en config/packages/messenger.yaml
creamos dos transportes - async
y async_priority_high
- y dirigimos mensajes diferentes a cada uno. Lo hicimos así para que, al ejecutar nuestro trabajador, podamos decirle que siempre lea primero los mensajes de async_priority_high
antes de leer los de async
. Eso hace que async_priority_high
sea un lugar al que enviemos mensajes de "mayor" prioridad.
La opción priority
es aquí... menos potente. Si envías un mensaje a un transporte con prioridad 0 y luego envías otro mensaje a ese mismo transporte con prioridad 10, ¿qué crees que pasará? ¿Qué mensaje se tratará primero?
La respuesta: el primer mensaje enviado, el de menor prioridad. Básicamente, Messenger siempre leerá los mensajes según el principio de "primero en entrar, primero en salir": siempre leerá primero los mensajes más antiguos. El priority
no influye en esto.
Entonces... ¿qué hace? Bueno, si DeleteImagePost
tuviera dos manejadores... y uno tuviera la prioridad por defecto de cero y otro tuviera 10, el manejador con prioridad 10 sería llamado primero. Esto no suele ser importante, pero podría serlo si tuvieras dos manejadores de eventos y realmente necesitaras que ocurrieran en un orden determinado.
A continuación, vamos a hablar de otra opción que puedes pasar aquí, la más potente. Se llama from_transport
y te permite, más o menos, enviar diferentes "manejadores" de un mensaje a diferentes transportes para que cada uno pueda ser consumido independientemente.
"Houston: no signs of life"
Start the conversation!
// 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
}
}