Events and observers
An event is a "something happened" message that code sends at a certain moment: a product was opened, an order was placed, a customer logged in. An observer is a class that is subscribed to an event and runs when it happens. This is how a module reacts to core actions without interfering with core code.
Where the files live
A subscription is described in an events.xml file, and observer classes go into the Observer folder. Both files are created by hand.
app/code/Acme/Hello/
├── Observer/
│ └── LogProductView.php new file
└── etc/
├── module.xml
└── frontend/
├── events.xml new file
└── routes.xmlA file in etc/frontend applies only to the storefront, in etc/adminhtml only to the admin panel, and etc/events.xml applies everywhere.
Subscribing
When a customer opens a product page, the core dispatches the catalog_controller_product_view event. Let's subscribe to it:
<?xml version="1.0"?>
<!-- app/code/Acme/Hello/etc/frontend/events.xml -->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:Event/etc/events.xsd">
<event name="catalog_controller_product_view">
<observer name="acme_hello_log_product_view" instance="Acme\Hello\Observer\LogProductView"/>
</event>
</config>The name attribute of observer is any unique name, instance is the observer class.
The observer
// app/code/Acme/Hello/Observer/LogProductView.php
namespace Acme\Hello\Observer;
use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Psr\Log\LoggerInterface;
class LogProductView implements ObserverInterface
{
public function __construct(private LoggerInterface $logger) {}
public function execute(Observer $observer): void
{
$product = $observer->getEvent()->getData('product');
$this->logger->info('Product viewed: ' . $product->getSku());
}
}Run bin/magento cache:clean and open any product page: a line with its SKU appears in the var/log/system.log file. Data travels with the event — here it is the product under the product key.
Common events
| Event | When it happens | Data |
|---|---|---|
catalog_controller_product_view | A product page is opened | product |
catalog_product_save_after | A product is saved | product |
checkout_cart_product_add_after | A product is added to the cart | quote_item, product |
sales_order_place_after | An order is placed | order |
customer_login | A customer logs in | customer |
The easiest way to find other core events is to search the vendor/magento folder for dispatch(.
Your own event
A module can dispatch its own events so that other modules can react to them. Ask for Magento\Framework\Event\ManagerInterface in the constructor:
// app/code/Acme/Hello/Model/Greeter.php
use Magento\Framework\Event\ManagerInterface;
public function __construct(private ManagerInterface $eventManager) {}
public function greet(string $name): string
{
$this->eventManager->dispatch('acme_hello_greeted', ['name' => $name]);
return 'Hello, ' . $name . '!';
}An observer subscribed to acme_hello_greeted gets the name through $observer->getEvent()->getData('name'). By convention an event name starts with the module name.
Plugin or observer
| Approach | When it fits |
|---|---|
| Observer | You need to react to something that has already happened |
| Plugin | You need to change the arguments or the result of a method |
Common mistake
Heavy work is done in an observer: sending emails, calling an external service. An observer runs inside the customer's request, and placing an order waits until it finishes. Such work is postponed — into a scheduled job or a message queue, both covered in the cron lesson.