Magento
implements the observer pattern through MagentoFrameworkEventManagerInterface
. In app/etc/di.xml
, there is a preference for ManagerInterface
that points to the MagentoFrameworkEventManagerProxy
class type. The Proxy
class further extends the MagentoFrameworkEventManager
class that implements the actual event dispatch method.
Events are dispatched by calling a dispatch method on the instance of the EventManager
class and passing the name and some data, which is optional, to it. Here's an example of a Magento
core event:
$this->eventManager->dispatch( 'customer_customer_authenticated', ['model' => $this->getFullCustomerObject($customer), 'password' => $password] );
The $this->eventManager
is an instance of the previously mentioned EventManager
class. In this case, the event name equals to customer_customer_authenticated
, while the data passed to the event is the array with two elements. The preceding event is fired when the authenticate method is called on MagentoCustomerModelAccountManagement
, that is, when a customer logs in.
Dispatching an event only makes sense if we expect someone to observe it and execute their code when the event is dispatched. Depending on the area from which we want to observe events, we can define observers in one of the following XML files:
app/code/{vendorName}/{moduleName}/etc/events.xml
app/code/{vendorName}/{moduleName}/etc/frontend/events.xml
app/code/{vendorName}/{moduleName}/etc/adminhtml/events.xml
Let's define an observer that will log an e-mail address of an authenticated user into a var/log/system.log
file. We can use the Foggyline_Office
module and add some code to it. As we are interested in the storefront, it makes sense to put the observer in the etc/frontend/events.xml
module.
Let's define the app/code/Foggyline/Office/etc/frontend/events.xml
file with content, as follows:
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework: Event/etc/events.xsd"> <event name="customer_customer_authenticated"> <observer name="foggyline_office_customer_authenticated" instance="FoggylineOfficeObserverLogCustomerEmail" /> </event> </config>
Here, we are specifying a foggyline_office_customer_authenticated
observer for the customer_customer_authenticated
event. The observer is defined in the LogCustomerEmail
class that is placed in the Observer
module directory. The Observer
class has to implement the MagentoFrameworkEventObserverInterface
class. The Observer
interface defines a single execute method. The execute method hosts the observer code and is executed when the customer_customer_authenticated
event is dispatched.
Let's go ahead and define the FoggylineOfficeObserverLogCustomerEmail
class in the app/code/Foggyline/Office/Observer/LogCustomerEmail.php
file, as follows:
namespace FoggylineOfficeObserver; use MagentoFrameworkEventObserverInterface; class LogCustomerEmail implements ObserverInterface { protected $logger; public function __construct( PsrLogLoggerInterface $logger ) { $this->logger = $logger; } /** * @param MagentoFrameworkEventObserver $observer * @return self */ public function execute(MagentoFrameworkEventObserver $observer) { //$password = $observer->getEvent()->getPassword(); $customer = $observer->getEvent()->getModel(); $this->logger->info('FoggylineOffice: ' . $customer-> getEmail()); return $this; } }
The execute
method takes a single parameter called $observer
of the MagentoFrameworkEventObserver
type. The event that we are observing is passing two pieces of data within the array, namely the model
and password
. We can access this by using the $observer->getEvent()->get{arrayKeyName}
expression. The $customer
object is an instance of the MagentoCustomerModelDataCustomerSecure
class, which contains properties such as email
, firstname
, lastname
, and so on. Thus, we can extract the e-mail address from it and pass it to logger's info
method.
Now that we know how to observe existing events, let's see how we can dispatch our own events. We can dispatch events from almost anywhere in the code, with or without data, as shown in the following example:
$this->eventManager->dispatch('foggyline_office_foo'); // or $this->eventManager->dispatch( 'foggyline_office_bar', ['var1'=>'val1', 'var2'=>'val2'] );
It is worth noting that there are two types of events; we can group them in the following way according to the way their name is assigned:
$this->eventManager->dispatch('event_name', ...)
$this->eventManager->dispatch({expression}.'_event_name', ...)
The static events have a fixed string for a name, while the dynamic ones have a name that is determined during the runtime. Here's a nice example of the core Magento
functionality from the afterLoad
method that is defined under lib/internal/Magento/Framework/Data/AbstractSearchResult.php
, which showcases how to use both types of events:
protected function afterLoad() { $this->eventManager->dispatch ('abstract_search_result_load_after', ['collection' => $this]); if ($this->eventPrefix && $this->eventObject) { $this->eventManager->dispatch($this->eventPrefix . '_load_after', [$this->eventObject => $this]); } }
We can see a static event (abstract_search_result_load_after
) and a dynamic event ($this->eventPrefix . '_load_after'
). The $this->eventPrefix
is an expression that gets evaluated during the runtime. We should be careful when using dynamic events as they are triggered under multiple situations. Some interesting dynamic events are the one defined on classes like the following ones:
MagentoFrameworkModelAbstractModel
$this->_eventPrefix . '_load_before'
$this->_eventPrefix . '_load_after'
$this->_eventPrefix . '_save_commit_after'
$this->_eventPrefix . '_save_before'
$this->_eventPrefix . '_save_after'
$this->_eventPrefix . '_delete_before'
$this->_eventPrefix . '_delete_after'
$this->_eventPrefix . '_delete_commit_after'
$this->_eventPrefix . '_clear'
MagentoFrameworkModelResourceModelDbCollectionAbstractCollection
$this->_eventPrefix . '_load_before'
$this->_eventPrefix . '_load_after'
MagentoFrameworkAppActionAction
'controller_action_predispatch_' . $request-> getRouteName()
'controller_action_predispatch_' . $request-> getFullActionName()
'controller_action_postdispatch_' . $request-> getFullActionName()
'controller_action_postdispatch_' . $request-> getRouteName()
MagentoFrameworkViewResultLayout
'layout_render_before_' . $this->request-> getFullActionName()
These events are fired on the model
, collection
, controller
, and layout
classes, which are probably among the most used backend elements that often require observing and interacting. Even though we can say that the full event name is known during the runtime along with the dynamic event, this can be assumed even before the runtime.
For example, assuming that we want to observe 'controller_action_predispatch_' . $request->getFullActionName()
for the Foggyline_Office
module's Crud
controller action, the actual full event name will be 'controller_action_predispatch_foggyline_office_test_crud'
, given that $request->getFullActionName()
will resolve to foggyline_office_test_crud
during the runtime.
3.21.93.44