Service Contracts
Magento is a modular system that enables third-party developers to customize and overwrite the core parts of its framework. This flexibility, however, comes at a price. Business logic tends to leak across the layers of the Magento system, which manifests as a duplicated and inconsistent code.
Merchants might be reluctant to upgrade Magento because customized extensions that they have purchased might not be compatible with new versions of Magento. Also, Magento and third-party developers can find it difficult to track and report the dependencies that customized extensions have on other extensions.
To address these issues, the Magento system introduces service contracts.
Extension Attributes
Extension attributes are new in Magento 2. They are used to extend functionalities and often use more complex data types than custom attributes. Extension Attributes are used to allow for customization of the strict Service Contracts. These attributes do not appear on the GUI.
How to define Extension Attributes?
1 2 3 4 5 6 |
<config> <extension_attributes for="Path\To\Interface"> <attribute code="name_of_attribute" type="datatype"> </attribute> </extension_attributes> </config> |
How to use extension attributes in Magento 2 Checkout?
1. Create a module.xml and registration.php to register your module.
For example, app/code/Dckap/ExtensionAttributes /etc/module.xml
1 2 3 4 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Module/etc/module.xsd"> <module name="Dckap_ExtensionAttributes" setup_version="1.0.0" /> </config> |
app/code/Dckap/ExtensionAttributes/registration.php
1 2 3 4 5 6 7 |
<?php \Magento\Framework\Component\ComponentRegistrar::register( \Magento\Framework\Component\ComponentRegistrar::MODULE, Dckap_ExtensionAttributes, __DIR__ ); |
2. Create the install script in the Setup directory.
app/code/Dckap/ExtensionAttributes/Setup/InstallSchema.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<?php namespace Vendor\ModuleName\Setup; use Magento\Framework\Setup\InstallSchemaInterface; use Magento\Framework\Setup\ModuleContextInterface; use Magento\Framework\Setup\SchemaSetupInterface; class InstallSchema implements InstallSchemaInterface { /** * {@inheritdoc} * @SuppressWarnings(PHPMD.ExcessiveMethodLength) */ public function install(SchemaSetupInterface $setup, ModuleContextInterface $context) { $installer = $setup; $installer->startSetup(); /* While module install, creates columns in quote_address and sales_order_address table */ $eavTable1 = $installer->getTable('quote_address'); $eavTable2 = $installer->getTable('sales_order_address'); $columns = [ 'sample_attribute' => [ 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 'nullable' => false, 'comment' => 'freightcollect', ] ]; $connection = $installer->getConnection(); foreach ($columns as $name => $definition) { $connection->addColumn($eavTable1, $name, $definition); $connection->addColumn($eavTable2, $name, $definition); } $installer->endSetup(); } } |
3. Define the extension attribute in XML
app/code/Dckap/ExtensionAttributes/etc/extension_attributes.xml
1 2 3 4 5 6 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:Api/etc/extension_attributes.xsd"> <extension_attributes for="Magento\Quote\Api\Data\AddressInterface"> <attribute code="sample_attribute" type="string" /> </extension_attributes> </config> |
4. Create the field for extension attributes in knockout HTML
app/code/Dckap/ExtensionAttributes/view/frontend/web/template/shipping-address/shipping-method-item.html
1 |
<input type="text" name="freight_collect_account_number" id="freight_collect_account_number" data-bind="value: sample_attribute, valueUpdate: 'afterkeydown', event: { 'keyup': check }"> |
5. Assigning the value for extension attributes in knockout js
app/code/Dckap/ExtensionAttributes/view/frontend/web/js/view/shipping.js
Observe the input field in initialize function
1 2 3 |
initialize: function (config) { this.sample_attribute = ko.observable(); } |
Set the value to extension attributes in shipping information
1 2 3 4 5 6 7 |
setShippingInformation: function () { var sample_attribute_value = $("#freight_collect_account_number").val(); if (quote.shippingAddress().extensionAttributes == undefined) { quote.shippingAddress().extensionAttributes = {}; } quote.shippingAddress().extensionAttributes.sample_attribute=sample_attribute_value; }; |
6. Save the extension attribute value using the plugin
Define the plugin in di.xml
app/code/Dckap/ExtensionAttributes/etc/di.xml
1 2 3 4 5 6 7 |
<?xml version="1.0"?> <config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd"> <type name="Magento\Quote\Model\Quote\Address"> <plugin name="add_freight_collect_information" type="Vendor\ModuleName\Plugin\Quote\FreightCollect" sortOrder="10" disabled="false"/> </type> </config> |
Save the value of extension attributes in qoute address
app/code/Dckap/ExtensionAttributes/Plugin/Quote/FreightCollect.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
<?php namespace Vendor\ModuleName\Plugin\Quote; use Magento\Quote\Model\Cart\ShippingMethodConverter; use Magento\Quote\Api\Data\ShippingMethodInterface; use Magento\Quote\Api\Data\ShippingMethodExtensionFactory; class FreightCollect { /** * Hook into setShippingMethod. * As this is magic function processed by __call method we need to hook around __call * to get the name of the called method. after__call does not provide this information. * * @param Address $subject * @param callable $proceed * @param string $method * @param mixed $vars * @return Address */ public function around__call($subject, $proceed, $method, $vars) { $result = $proceed($method, $vars); if($subject->getExtensionAttributes()){ $subject->setSampleAttribute($subject->getExtensionAttributes()->getSampleAttribute()); } return $result; } } |
7. Retrieve the extension attributes value using observer
Define the observer in events.xml
app/code/Dckap/ExtensionAttributes/etc/events.xml
1 2 3 4 5 6 |
<?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="sales_model_service_quote_submit_before"> <observer name="ModuleName_sales_address_save" instance="Vendor\ModuleName\Observer\Observeorder" /> </event> </config> |
Get the value of extension attributes and save in order using the observer
app/code/Dckap/ExtensionAttributes/Observer/ Observeorder.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
<?php namespace Vendor\ModuleName\Observer; class Observeorder implements \Magento\Framework\Event\ObserverInterface { public function execute(\Magento\Framework\Event\Observer $observer) { /* before quote submit save the freight list values in sales_order_address table */ $order = $observer->getEvent()->getOrder(); $quote = $observer->getEvent()->getQuote(); $order->getShippingAddress()->setSampleAttribute($quote->getShippingAddress()->getSampleAttribute()); return $this; } } |
I hope the article was useful. Please let us know if you have any queries or comments.
References
https://devdocs.magento.com/guides/v2.3/extension-dev-guide/service-contracts/service-contracts.html
Magento development services include extensions which can be customized according to the store owner needs. Learn about extension attributes in Magento 2
Hi,
why the method around__call is used in the plugin while this method is nowhere defined in the parent class
Thank you for the article! This is nice!