Magento 2 Observers & Events : Guide complet du développeur
L’architecture événementielle de Magento 2 vous permet de réagir à presque tout ce qui se produit dans le système — passage de commande, inscription client, sauvegarde de produit, actions admin — sans modifier le code principal ni étendre les classes. Les observers écoutent des événements nommés et exécutent une logique lorsque ces événements sont déclenchés. Ce guide couvre comment déclarer des observers, quels événements intégrés sont disponibles, comment déclencher vos propres événements personnalisés, et quand choisir les observers plutôt que les plugins .
En résumé : Un observer est une classe qui écoute un événement nommé et exécute du code lorsque cet événement est déclenché. Déclarez-le dans
events.xml(global) ouevents.xmllimité à une zone (frontend,adminhtml,webapi_rest,graphql). Utilisez les observers lorsque vous devez réagir à un événement qui n’a pas de méthode spécifique sur laquelle appliquer un plugin — par exemple, “après la commande passée” (sales_order_place_after) n’est pas lié à un seul appel de méthode.
Table des matières
- Événements vs Plugins : quand utiliser quoi
- Déclarer un observer dans events.xml
- Signature de la classe Observer
- Événements par zone : Frontend, Adminhtml, API Web, GraphQL
- Événements intégrés les plus utiles dans Magento 2
- Déclencher des événements personnalisés
- Passer des données aux observers via l’événement
- Ordre d’exécution des observers et arrêt de la propagation
- Bonnes pratiques et pièges courants
- FAQ
Événements vs Plugins : quand utiliser quoi
Avant d’écrire un observer, vérifiez si un plugin serait plus propre. Voici une règle empirique :
| Scénario | Meilleure approche |
|---|---|
| Modifier les arguments ou la valeur de retour d’une méthode spécifique | Plugin |
| Réagir à “quelque chose s’est produit” (commande passée, client connecté) | Observer |
| Exécuter une logique qui s’étend sur plusieurs classes non liées | Observer |
| Besoin d’englober toute l’exécution d’une méthode | Plugin (around) |
| L’événement dont vous avez besoin n’existe pas encore | Plugin, ou déclenchez un événement personnalisé |
La différence clé : les plugins s’accrochent aux méthodes, les observers s’accrochent aux événements. Les événements peuvent être déclenchés de n’importe où — modèles, contrôleurs, helpers, même d’autres observers — et plusieurs observers peuvent écouter le même événement sans se connaître.
Déclarer un observer dans events.xml
Créez etc/events.xml dans votre module (pour les événements globaux) ou limitez-le à une zone :
<!-- app/code/Vendor/Module/etc/events.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="sales_order_place_after">
<observer name="vendor_module_order_placed"
instance="Vendor\Module\Observer\OrderPlaced"
shared="false"/>
</event>
</config>
L’attribut name sur <observer> doit être unique dans le périmètre de l’événement. Utilisez un préfixe (le nom de votre vendeur/module) pour éviter les conflits. shared="false" crée une nouvelle instance à chaque fois — utilisez-le sauf si l’observer est sans état.
Signature de la classe Observer
Chaque observer implémente \Magento\Framework\Event\ObserverInterface avec une seule méthode execute() :
<?php
declare(strict_types=1);
namespace Vendor\Module\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Framework\Event\Observer;
class OrderPlaced implements ObserverInterface
{
public function __construct(
private readonly \Psr\Log\LoggerInterface $logger
) {}
public function execute(Observer $observer): void
{
$order = $observer->getEvent()->getOrder();
$this->logger->info('Order placed: ' . $order->getIncrementId());
}
}
L’objet Observer vous donne accès à getEvent(), qui renvoie l’objet de données de l’événement. De là, vous récupérez les données spécifiques en utilisant des getters nommés d’après les clés de données de l’événement. Getters courants : getOrder(), getProduct(), getCustomer(), getQuote().
Événements par zone : Frontend, Adminhtml, API Web, GraphQL
Le events.xml global se déclenche dans toutes les zones. Limitez-le à une zone spécifique en plaçant le fichier dans le répertoire etc/ de la zone :
app/code/Vendor/Module/
├── etc/
│ └── events.xml ← global (all areas)
├── etc/frontend/
│ └── events.xml ← frontend only
├── etc/adminhtml/
│ └── events.xml ← admin panel only
├── etc/webapi_rest/
│ └── events.xml ← REST API only
└── etc/graphql/
└── events.xml ← GraphQL only
Ceci est utile lorsque le même module nécessite un comportement différent dans différentes zones. Par exemple, enregistrer une commande dans le frontend mais envoyer une notification admin dans adminhtml.
Événements intégrés les plus utiles dans Magento 2
Événements de checkout et de ventes
| Nom de l’événement | Données disponibles |
|---|---|
sales_order_place_after | order |
sales_order_save_after | order |
checkout_cart_add_product_complete | product, request |
checkout_onepage_controller_success_action | order_ids |
sales_quote_collect_totals_after | quote |
Événements client
| Nom de l’événement | Données disponibles |
|---|---|
customer_register_success | customer, account_controller |
customer_login | customer |
customer_logout | customer |
customer_address_save_after | customer_address, customer |
Événements de catalogue et de produits
| Nom de l’événement | Données disponibles |
|---|---|
catalog_product_save_after | product |
catalog_product_load_after | product |
catalog_category_save_after | category |
catalog_product_is_salable_before | product |
Vous trouverez la liste complète dans vendor/magento/framework/Event/etc/events.xsd et en recherchant ->dispatch( dans le code source de Magento.
Déclencher des événements personnalisés
Déclencher vos propres événements rend votre module extensible — d’autres développeurs peuvent s’y intégrer sans modifier votre code.
use Magento\Framework\Event\ManagerInterface;
class SomeService
{
public function __construct(
private readonly ManagerInterface $eventManager
) {}
public function doSomething(string $sku, array $data): void
{
// ... business logic ...
$this->eventManager->dispatch(
'vendor_module_something_done',
['sku' => $sku, 'result' => $data]
);
}
}
Convention pour les noms d’événements : minuscules, préfixe vendeur, segments séparés par des underscores. Le deuxième argument est un tableau associatif — chaque clé devient disponible via getSku(), getResult(), etc. sur l’objet événement de l’observer.
Passer des données aux observers via l’événement
Lors du déclenchement, les clés du tableau deviennent les noms des getters sur les données de l’événement :
$this->eventManager->dispatch('custom_event', [
'order' => $order,
'items' => $items,
'source' => 'cron'
]);
Dans l’observer :
public function execute(Observer $observer): void
{
$order = $observer->getEvent()->getOrder();
$items = $observer->getEvent()->getItems();
$source = $observer->getEvent()->getSource();
}
Les clés de données suivent la convention camelCase. Les événements intégrés de Magento utilisent des noms singuliers pour les objets uniques (order, product, customer) et pluriels pour les collections (order_ids, items).
Ordre d’exécution des observers et arrêt de la propagation
Les observers pour le même événement s’exécutent dans l’ordre où ils apparaissent dans events.xml. Si vous devez contrôler l’ordre entre modules, utilisez le paramètre sort_order sur l’élément <observer> (les nombres plus bas s’exécutent en premier) :
<event name="sales_order_place_after">
<observer name="first_module" instance="Vendor\First\Observer" sort_order="10"/>
<observer name="second_module" instance="Vendor\Second\Observer" sort_order="20"/>
</event>
Pour empêcher l’exécution des observers suivants, appelez stopPropagation() :
public function execute(Observer $observer): void
{
if (!$this->config->isEnabled()) {
$observer->stopPropagation();
return;
}
// ... process ...
}
Utilisez-le avec parcimonie — arrêter la propagation interrompt silencieusement d’autres modules qui dépendent du même événement.
Bonnes pratiques et pièges courants
Gardez les observers légers. Un observer ne doit pas contenir de logique métier complexe. Déléguez aux services ou modèles. Les observers sont appelés de manière synchrone — un observer lent bloque toute la requête.
N’injectez jamais une classe concrète qui déclenche des événements dans son constructeur. Cela crée des boucles infinies. Par exemple, n’injectez pas un repository dans un observer qui écoute l’événement de sauvegarde de cette même entité.
Utilisez
shared="false"pour les observers qui ont un état. Si un observer a des dépendances qui changent entre les appels (comme une valeur de registry), créer une nouvelle instance à chaque fois empêche les données périmées.Préférez les plugins aux observers lorsque vous devez modifier les arguments d’une méthode ou les valeurs de retour. Les plugins sont type-safe et plus prévisibles. Utilisez les observers uniquement pour les scénarios de “réaction”.
Ne comptez pas sur l’ordre d’exécution des observers entre modules. Contrôlez l’ordre uniquement dans votre propre module. Si votre observer doit s’exécuter avant ou après celui d’un autre module, envisagez d’utiliser un plugin à la place.
Testez avec le profilage d’événements activé. Ajoutez
?debug=eventsà votre URL (avec le mode développeur) pour voir quels événements se déclenchent sur une page. Vérifiez que votre observer est appelé quand prévu et pas appelé quand il ne devrait pas l’être.
FAQ
Q : Puis-je utiliser l’injection de dépendances dans les observers ?
Oui. Toutes les dépendances des observers sont injectées via le constructeur. Le conteneur DI de Magento les résout automatiquement.
Q : Quelle est la différence entre events.xml et frontend/events.xml ?
Le events.xml global se déclenche dans toutes les zones. Les fichiers limités à une zone ne se déclenchent que lorsque Magento s’exécute dans cette zone (boutique frontend, panneau admin, API REST ou GraphQL).
Q : Comment trouver quelles données un événement fournit ?
Vérifiez l’appel dispatch() dans le code source. Les clés du tableau passées à dispatch() deviennent les noms des getters. Par exemple, si le code dispatch ['order' => $order], votre observer appelle $observer->getEvent()->getOrder().
Q : Un observer peut-il écouter plusieurs événements ?
Pas directement. Chaque classe d’observer nécessite une déclaration séparée dans events.xml. Cependant, vous pouvez créer une classe de service unique et l’appeler depuis plusieurs méthodes execute() d’observers.
Q : Les observers sont-ils disponibles dans GraphQL ?
Oui. GraphQL est sa propre zone (graphql). Placez votre events.xml dans etc/graphql/ pour limiter les observers aux requêtes GraphQL uniquement.
Besoin d’un module personnalisé avec des observers et événements correctement câblés ? Consultez mon service de développement de modules personnalisés ou lisez le guide des plugins pour le sujet complémentaire sur les intercepteurs.