Skip to content
beginner Phase 21 · Module Configuration Files

events.xml - Module Configuration Files

Understanding Magento 2 events.xml: event names, observer configuration, observer classes, and the disabled flag for event-driven architecture

30m
0 problems
Topic Progress 0%

Event-Driven Architecture in Magento

Why Events?

Magento 2 uses an event-driven architecture to allow modules to communicate without direct dependencies. Instead of Module A calling Module B directly, Module A dispatches an event, and any module can listen to it via an observer.

This decouples modules — Module A doesn't know or care who's listening.

How Events Work

Code dispatches event → Event Manager notifies observers → Observers execute their logic

The dispatch call:

$eventManager = $this->eventManager;
$eventManager->dispatch('catalog_product_save_after', ['product' => $product]);

The observer configuration in events.xml:

<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="catalog_product_save_after">
        <observer name="my_observer" instance="Vendor\MyModule\Observer\ProductAfterSave"/>
    </event>
</config>

Event Naming Convention

Events follow a pattern: {entity}_{action}_{timing}

  • catalog_product_save_after — after a catalog product is saved
  • sales_order_place_before — before an order is placed
  • customer_login — when a customer logs in

The timing suffix (_before, _after) indicates when the observer fires relative to the main action.

areas.xml

Events can be area-specific. Place events.xml in:

  • etc/events.xml — global (all areas)
  • etc/frontend/events.xml — frontend only
  • etc/adminhtml/events.xml — admin only
  • etc/crontab/events.xml — cron only

This ensures observers only run in the appropriate area.

Configuring events.xml

Basic events.xml Structure

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
    <event name="catalog_product_save_after">
        <observer name="unique_observer_name"
                  instance="Vendor\Module\Observer\ProductObserver"
                  disabled="false"/>
    </event>
</config>

Observer Attributes

Attribute Required Description
name Yes Unique name for this observer within the event
instance Yes Fully qualified class name of the observer
disabled No Set to true to disable the observer (default: false)

Multiple Observers per Event

A single event can have multiple observers:

<event name="catalog_product_save_after">
    <observer name="inventory_sync"
              instance="Vendor\Inventory\Observer\SyncInventory"/>
    <observer name="search_reindex"
              instance="Vendor\Search\Observer\ReindexProduct"/>
    <observer name="audit_log"
              instance="Vendor\Audit\Observer\LogProductChange"/>
</event>

Observers execute in the order they are loaded, which depends on module load order.

Disabling Observers

Temporarily disable an observer without removing it:

<observer name="debug_logger"
          instance="Vendor\Debug\Observer\Logger"
          disabled="true"/>

This is useful for:

  • Debugging without observer side effects
  • Temporarily disabling third-party observer behavior
  • Performance testing with specific observers turned off

Creating Observer Classes

Observer Class Structure

Observers implement Magento\Framework\Event\ObserverInterface:

namespace Vendor\Module\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;

class ProductAfterSave implements ObserverInterface
{
    public function execute(Observer $observer)
    {
        $product = $observer->getEvent()->getProduct();
        // Your logic here
        return $this;
    }
}

Accessing Event Data

The Observer object provides access to data passed during dispatch:

// Dispatched with: $this->dispatch('sales_order_place_before', ['order' => $order]);

public function execute(Observer $observer)
{
    $order = $observer->getEvent()->getOrder();
    // or
    $order = $observer->getEvent()->getData('order');

    $orderId = $order->getIncrementId();
    $total = $order->getGrandTotal();
    // ... process order data
}

Observer with Dependencies

Observers can receive dependencies via constructor injection:

namespace Vendor\Module\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Psr\Log\LoggerInterface;
use Vendor\Module\Service\NotificationService;

class OrderPlaced implements ObserverInterface
{
    public function __construct(
        private LoggerInterface $logger,
        private NotificationService $notificationService
    ) {}

    public function execute(Observer $observer)
    {
        $order = $observer->getEvent()->getOrder();
        $this->logger->info('Order placed: ' . $order->getIncrementId());
        $this->notificationService->notifyNewOrder($order);
        return $this;
    }
}

Common Magento Events

Event Name When Dispatched
catalog_product_save_after After product save
catalog_product_save_before Before product save
customer_login Customer authenticates
customer_register_success After customer registration
sales_order_place_before Before order placement
sales_order_save_after After order save
checkout_cart_product_add_after After adding item to cart
controller_action_predispatch Before any controller action
controller_action_postdispatch After any controller action

Always check existing events in vendor/magento/ before creating custom ones.

Event Dispatching and Best Practices

Dispatching Custom Events

Create your own events for other modules to observe:

namespace Vendor\Module\Service;

class WarrantyService
{
    public function __construct(
        private \Magento\Framework\Event\ManagerInterface $eventManager
    ) {}

    public function processWarranty($warranty)
    {
        // Before processing
        $this->eventManager->dispatch(
            'vendor_warranty_process_before',
            ['warranty' => $warranty]
        );

        // Process warranty
        $result = $this->doProcessing($warranty);

        // After processing
        $this->eventManager->dispatch(
            'vendor_warranty_process_after',
            ['warranty' => $warranty, 'result' => $result]
        );

        return $result;
    }
}

Best Practices

  1. Use descriptive event names — vendor_module_action_timing pattern
  2. Always pass relevant objects — observers need data to work with
  3. Keep observers lightweight — heavy logic belongs in services
  4. Don't modify dispatched objects unexpectedly — observers should not break assumptions
  5. Document your events — other developers need to know what data is passed
  6. Use area-specific events.xml — don't load frontend observers in admin

Performance Considerations

Every dispatched event adds overhead. Avoid dispatching events in tight loops:

// Bad: dispatches event for every product in a loop
foreach ($products as $product) {
    $this->eventManager->dispatch('product_processed', ['product' => $product]);
}

// Better: dispatch once after the loop
$this->eventManager->dispatch('products_batch_processed', ['products' => $products]);

Debugging Events

To see which events are dispatched, add logging:

// In a custom module, observe controller_action_predispatch
public function execute(Observer $observer)
{
    $action = $observer->getEvent()->getControllerAction();
    $this->logger->info('Event dispatched for: ' . get_class($action));
}

Or check the event list in the Magento codebase:

grep -r "dispatch('" vendor/magento/ --include="*.php" | head -50

Quiz

1. What interface must an observer class implement?

Question 1 options

2. How do you temporarily disable an observer in events.xml?

Question 2 options

3. Which events.xml location makes an observer run only in the admin panel?

Question 3 options

Flashcards

Question

What does events.xml configure?

Answer

The mapping between event names and observer classes that listen to those events

Question

How do you access event data in an observer?

Answer

$observer->getEvent()->getProductName() or $observer->getEvent()->getData('name')

Question

What is the timing suffix pattern for events?

Answer

_before (before action) and _after (after action)

Question

Where do area-specific events.xml files go?

Answer

etc/frontend/events.xml, etc/adminhtml/events.xml, etc/crontab/events.xml

Question

What is the observer class requirement?

Answer

Must implement Magento\Framework\Event\ObserverInterface with an execute() method

Revision Notes

Key Takeaways

  • 1. events.xml maps event names to observer classes
  • 2. Observers implement ObserverInterface and receive event data via the Observer object
  • 3. Events can be scoped to areas: frontend, adminhtml, crontab
  • 4. Use disabled='true' to temporarily turn off observers
  • 5. Follow naming convention: entity_action_timing (e.g., product_save_after)

Interview Tips

  • Explain the observer pattern and how it decouples modules
  • Know how to access event data in an observer
  • Discuss when to use events vs direct service calls
  • Be aware of performance implications of dispatching events in loops

Cheat Sheet

events.xml:
  <event name="entity_action_timing">
    <observer name="unique_name"
              instance="Vendor\Module\Observer\ClassName"
              disabled="false"/>
  </event>

Observer:
  implements ObserverInterface
  execute(Observer $observer):
    $observer->getEvent()->getProduct()

Area-specific:
  etc/frontend/events.xml
  etc/adminhtml/events.xml