MANIPULATING MAGENTO
Make it do what you want
PHP developer at
Joke Puts - @jokeputs
Goal
Bug free and future proof
customizations
How
Keep the interactions with
the core to a minimum
Topics
• Preferences
• Plugins
• Observers
• Cases
PREFERENCE
Replacing behavior through dependency injection
Dependency injection
“Dependency injection is the concept
of the external environment injecting
dependencies for an object instead
of that object manually creating them
internally.”
Dependency injection
• Constructor injection
• Object manager
MagentoQuoteModelQuoteAddressValidator
public function __construct(
AddressRepositoryInterface $addressRepository,
CustomerRepositoryInterface $customerRepository,
Session $customerSession
) {
$this->addressRepository = $addressRepository;
$this->customerRepository = $customerRepository;
$this->customerSession = $customerSession;
}
Interfaces
• API directory
• Service contract
• (Relatively) stable across upgrades
MagentoFrameworkObjectManagerObjectManager
public function create(
$type, array
$arguments = []
) {
$type = ltrim($type, '');
return $this->_factory->create(
$this->_config->getPreference($type),
$arguments
);
}
di.xml
<preference
for="MagentoCustomerApiDataCustomerInterface"
type="MagentoCustomerModelDataCustomer" />
module.xml
<module name="Namespace_Module"
setup_version="1.0.0">
<sequence>
<module
name="Magento_Customer"/>
</sequence>
</module>
<module name="PHPro_AModule"
setup_version="1.0.0">
<sequence>
<module name="Magento_Customer"/>
</sequence>
</module>
<module name="PHPro_ZModule"
setup_version="1.0.0">
<sequence>
<module name="Magento_Customer"/>
</sequence>
</module>
PLUGINS
Adding and modifying behavior before, after and
around
Plugin
“A plugin is a class that modifies the
behavior of public class functions by
running code before, after or around
that function call.”
di.xml
<type
name="MagentoCatalogModelResourceModelCate
gory">
<plugin name="category_delete_plugin"
type="MagentoCatalogUrlRewriteModelCat
egoryPluginCategoryRemove"/>
</type>
Before
• Runs before the method
• Modify the arguments of the
method
• Return null to indicate that the
arguments haven’t changed
Before
public function beforeSetName(
Product $subject,
$name
) {
// Do something
}
After
• Runs after the method
• Modify the output of the method
• Should have a return value
• Since 2.2: Access to arguments of
the method
After
public function afterGetName(
Product $subject,
$result
) {
// Do something
return $result;
}
Around
• Runs both before and after the
method
• Modify the entire method
• Modify arguments and output
• Receives a callable
Around
public function aroundUpdateData(
Address $subject,
callable $proceed,
AddressInterface $address
) {
// Do something
$result = $proceed($address);
// Do something
return $result;
}
Not calling the callable
✖Other around plugins (sort order)
✖Other after plugins (sort order)
✖Original method
Call order prior original
1. Sort order from low to high
2. Order of the uasort() array
3. Before plugins
4. Around plugins (before calling the
callable)
Plugin A with sort order 1: around
Call order prior original
Plugin A with sort order 1: before
Plugin B with sort order 0: around
Plugin C with sort order 0: before
Plugin B with sort order 0: around
Call order prior original
Plugin A with sort order 1: before
Plugin A with sort order 1: around
Plugin C with sort order 0: before
Plugin A with sort order 1: around
Plugin B with sort order 0: around
Call order prior original
Plugin A with sort order 1: before
Plugin C with sort order 0: before
Call order post original
1. Sort order from high to low*
2. Reversed order of the uasort()
array
3. Around plugins (after calling the
callable)
4. After plugins
* Only if an around plugin exists
Plugin A with sort order 1: around
Plugin B with sort order 0: around
Call order post original
Plugin D with sort order 1: after
Plugin B with sort order 0: after
Plugin A with sort order 1: around
Plugin B with sort order 0: around
Call order post original
Plugin D with sort order 1: after
Plugin B with sort order 0: after
Plugin A with sort order 1: around
Plugin B with sort order 0: around
Call order post original
Plugin D with sort order 1: after
Plugin B with sort order 0: after
PHPro/AModule/etc/di.xml à after plugin
<type
name="MagentoStoreModelAddressRenderer">
<plugin name="renderer_a"
type="PHProAModulePluginRenderer"
sortOrder="10"/>
</type>
PHPro/ZModule/etc/di.xml à after plugin
<type
name="MagentoStoreModelAddressRenderer">
<plugin name="renderer_z"
type="PHProZModulePluginRenderer"
sortOrder="20"/>
</type>
OBSERVERS
Modifying data and running new behavior
Observers
“Observers are executed whenever
the event they are configured to
watch is dispatched by the event
manager.”
MagentoCustomerModelSession
public function
setCustomerAsLoggedIn($customer)
{
$this->setCustomer($customer);
$this->_eventManager->dispatch(
'customer_login',
['customer' => $customer]
);
events.xml
<event name="customer_login">
<observer name="wishlist"
instance="MagentoWishlistObserver
CustomerLogin" />
</event>
MagentoWishlistObserverCustomerLogin
class CustomerLogin implements
ObserverInterface
{
public function execute(
Observer $observer
) {
$this->wishlistData->calculate();
}
}
Events
• Find events in the core
• Look at the passed data
• $observer->getEvent()
->get{key}()
Model events
• {event_prefix}_load_before
• {event_prefix}_load_after
• {event_prefix}_save_before
• {event_prefix}_save_after
• {event_prefix}_delete_before
• {event_prefix}_delete_after
• {event_prefix}_save_commit_after
• {event_prefix}_delete_commit_after
• {event_prefix}_clear
Model events
• Extend MagentoFrameworkModel
AbstractModel
• Set $_eventPrefix
• Set $_eventObject
Collection events
• {event_prefix}_load_before
• {event_prefix}_load_after
Collection events
• Extend MagentoFrameworkModel
ResourceModelDbCollection
AbstractCollection
• Set $_eventPrefix
• Set $_eventObject
Controller events
• controller_action_predispatch_{route_name}
• controller_action_predispatch_{full_action_name}
• controller_action_postdispatch_{full_action_name}
• controller_action_postdispatch_{route_name}
Controller events
• Extend
MagentoFrameworkAppAction
Action
Dispatch order
1. Observers in the global area
a. Module sequence order
b. Order in events.xml
2. Observers in adminhtml or frontend area
a. Module sequence order
b. Order in events.xml
PHPro/AModule/etc/adminhtml/events.xml
<event name="sales_order_save_after">
<observer name="phpro_a_order_save"
instance="PHProAModuleObserverAfterOrderSa
ve" />
</event>
PHPro/ZModule/etc/events.xml
<event name="sales_order_save_after">
<observer name="phpro_z_order_save"
instance="PHProZModuleObserverAfterOrderSa
ve" />
</event>
RECAP
What should I use?
Preferences
Replace behavior
Preferences
• (+) Replace entire class or interface
implementation
• (-) Only one preference can be active
• (-) Replace entire class to replace one
line of code
Plugins
Add and modify behavior
Plugins
• (+) Manipulate behavior before, after
and around one method
• (+) Access to arguments and output
• (+) Multiple plugins for one method
• (-) Only public methods
Observers
Modify data and start new behavior
Observers
• (+) Multiple observers for one event can
be active
• (-) Event has to exist
• (-) Only access to the passed data
CASES
Enough with the theory…
Case #1
Modify the JSON response from
an external module
Case #2
Call an external service
when an order reaches
the state “complete”
Case #3
Redirect to a store view based
on the customer’s location
(full page cache)
Case #4
Customers are not stored in Magento
and should be accessed using an
external service
Case #5
Change the store address
template in an e-mail template
Questions?

Manipulating Magento - Meet Magento Netherlands 2018