Symfony ExpressionLanguage : Comment utiliser ce composant ?
Le composant Symfony ExpressionLanguage : qu'est-ce que c'est ? Quand et comment l'utiliser ? Comment créer des expressions lors de cas plus complexes ?
Sommaire
Le composant messenger nous apporte un message bus. Un message bus est un composant très simple qui permet de dispatcher un objet (message). Le bus a pour rôle d'exécuter le handler approprié au message. Durant ce processus le message va passer dans une pile ordonnée de middleware.
Les middlewares sont encapsulés les uns sur les autres autours du handler.
Exemple simplifier
<?php
$handler = function ($value) {
// Le code métier à exécuter
return $value;
};
// ce middleware va encapsuler la réponse du middleware suivant avec du texte
$wrappingMiddleware = function (callable $handler) {
return function ($string) use ($handler) {
return 'before > ' . $handler('-' . $string . '-') . ' < after';
};
};
// ce middleware va logguer dans un fichier les différentes exécutions de la stack d'exécution
$loggingMiddleware = function (callable $handler) {
return function ($string) use ($handler) {
$result = $handler($string);
file_put_contents('dev.log', sprintf('Info: Input "%s" with output "%s".', $string, $result));
return $result;
};
};
// on encapsule les middlewares les uns sur les autres autour du handler
$middlewareStack = $loggingMiddleware(
$wrappingMiddleware(
$handler
)
);
// on exécute la stack de middleware ainsi que le handler
echo $middlewareStack('example string');
// will show "before > -example string- < after"
// le middleware logging aura quand à lui écrit "Info: Input "example string" with output "before > -example string- < after"" dans le fichier dev.log.
Les middlewares permettent d'ajouter un traitement avant ou après l'exécution du handler (ou du middleware suivant), c'est pourquoi l'ordre des middlewares est très important.
Dans le composant Messenger de symfony il existe 2 cas d'utilisation standard :
Un message est envoyé depuis le système vers une destination. Par exemple cette destination peut être un message broker ou un endpoint API.
Vous pourriez creer un Handler qui envoie le message à votre destination mais symfony met à disposition un SendMessageMiddleware
.
Vous n'aurez donc qu'à configurer le routage des objets vers le transport souhaité :
framework: messenger: transports: amqp: '%env(MESSENGER_TRANSPORT_DSN)%' routing: 'App\MyModel': amqp
Reportez-vous à la documentation pour les différentes syntaxes possibles.
Un message entre dans le système de manière synchrone (endpoint API/controlleur) ou asynchrone (via worker/queue).
Le message reçu est donc encapsulé avec une enveloppe ReceivedMessage
avant d'être dispatché dans le message bus.
L'enveloppe
ReceivedMessage
permet d'éviter au bus de renvoyer le message au sender (boucle infinie).
Pour traiter un message il faudra créer un handler avec une méthode __invoke
(si vous avez déjà une méthode, utilisez l'attribut handles
dans le tag de définition du service. Voir plus bas).
Par exemple :
<?php
// src/MessageHandler/MyMessageHandler.php
namespace App\MessageHandler;
use App\MyModel;
class MyModelHandler
{
public function __invoke(MyModel $message)
{
// do something with it.
}
}
et ensuite l'enregistrer dans symfony
# config/services.yaml services: App\MessageHandler\MyModelHandler: tags: [messenger.message_handler]
Si symfony n'arrive pas à deviner le type de message, vous pouvez utiliser la syntaxe complète afin de spécifier la méthode à appeler et ou le modèle supporté.
# config/services.yaml services: App\MessageHandler\MyModelHandler: tags: - {name: 'messenger.message_handler', method: 'process', handles: 'App\MyModel'}
Le composant met également à disposition une commande bin/console messenger:consume-messages {TRANSPORT_NAME}
afin de lancer un worker qui va écouter/consulter le transport et dispatcher un message dans le bus.
Dans le cas du transport amqp
le worker dispatchera chaque message de la queue du broker.
Mais il est tout à fait possible de récupérer les changements d'un résultat d'API.
Par exemple :
<?php
namespace App\Receiver;
use Symfony\Component\Messenger\Transport\ReceiverInterface;
use Symfony\Component\Messenger\Envelope;
class APIMeteoReceiver implements ReceiverInterface
{
/**
* Last API result
*/
private $result;
private $shouldStop;
public function receive(callable $handler): void
{
while (!$this->shouldStop) {
$result = file_get_contents("{URL d'une API de meteo}");
// si le résultat est le même que le précédent on attend une seconde avant de recommencer
if ($this->result === $result) {
sleep(1);
continue;
}
$this->result = $result;
// Si la météo a changé on dispatche la nouvelle météo dans le bus
$handler(new Envelope($this->result));
}
}
public function stop(): void
{
$this->shouldStop = true;
}
}
Dans l'exemple précédent on ne dispatche que lorsque la météo change
Voila un premier tour d'horizon du composant Messenger. Bien que le concept du message bus soit assez simple, l'implémentation du composant ne l'est pas autant. Et étant donné que le composant est encore expérimental et que la documentation s'étoffe petit à petit c'est donc encore un composant mal connu et peu utilisé pour le moment. Par ailleurs le composant Messenger nous apporte plein d'outils et nous laisse un grand niveau de personnalisation. C'est pourquoi une bonne compréhension du concept et une bonne prise en main sont préférables pour une exploitation maximum du potentiel.
Sachez qu'il est également possible d'implémenter vos propres Transport
Middleware
Sender
Receiver
.
Vous avez en plus de cela la possibilité de créer plusieurs bus dans l'application afin de bien compartimenter votre logique.
Auteur(s)
Anthony MOUTTE
_Développeur / Concepteur @ ElevenLabs_🚀 je suis très intéressé par la recherche et développement ainsi que les bonnes pratiques.
Vous souhaitez en savoir plus sur le sujet ?
Organisons un échange !
Notre équipe d'experts répond à toutes vos questions.
Nous contacterDécouvrez nos autres contenus dans le même thème
Le composant Symfony ExpressionLanguage : qu'est-ce que c'est ? Quand et comment l'utiliser ? Comment créer des expressions lors de cas plus complexes ?
Découvrez comment réaliser du typage générique en PHP : introduction et définition du concept, conseils et explications pas-à-pas de cas pratique.
Découvrez un cas d'usage d'intégration d'un CRM avec une application e-commerce, en asynchrone, avec Hubspot et RabbitMQ