Introduction
Recently, I tried to understand how to add a custom field to shipping address form fields and save its value into database. The Magento DevDoc doesn’t help a lot. It will only add a field in the shipping address form but will not save it into database. In this tutorial, I will show you how to add a custom field in the shipping address form and save the value into database.
Create a new Module
For this tutorial, I will create a module called, ‘Jeff_CustomField’. You can find howto about creation Magento 2 extension in my previous blog. In order to save the value into database, I will create InstallScema.php to add column to ‘quote’ and ‘sales_order’.
startSetup(); $eavTable1 = $installer->getTable('quote'); $eavTable2 = $installer->getTable('sales_order'); $columns = [ 'customvar' => [ 'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT, 'nullable' => true, 'comment' => 'Custom Var' ] ]; $connection = $installer->getConnection(); foreach($columns as $name => $definition) { $connection->addColumn($eavTable1, $name, $definition); $connection->addColumn($eavTable2, $name, $definition); } $installer->endSetup(); } }
Add the field to the shipping address form dynamically
First, we add a plugin for the LayoutProcessor:
And the Plugin class:
logger = $logger; } public function afterProcess(LayoutProcessor $subject, array $jsLayout) { $customAttributeCode = 'customvar'; $customField = [ 'component' => 'Magento_Ui/js/form/element/abstract', 'config' => [ 'customScope' => 'shippingAddress.custom_attributes', 'customEntry' => null, 'template' => 'ui/form/field', 'elementTmpl' => 'ui/form/element/input', 'tooltip' => [ 'description' => 'this is custom var field', ], ], 'dataScope' => 'shippingAddress.custom_attributes' . '.' . $customAttributeCode, 'label' => 'Custom Var', 'provider' => 'checkoutProvider', 'sortOrder' => 0, 'validation' => [ 'required-entry' => false ], 'options' => [], 'filterBy' => null, 'customEntry' => null, 'visible' => true, ]; $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children'][$customAttributeCode] = $customField; return $jsLayout; } }
After creating these files and clear cache, a new field will show up in the address form field:
Create our own ‘shipping-save-processor/default.js’
I will rewrite the ‘module-checkout/view/frontend/web/js/model /shipping-save-processor/default.js’ with my own file ‘shipping-save-processor.js’.
#Jeff/CustomField/view/frontend/web/js/shipping-save-processor.js define([ 'jquery', 'ko', 'Magento_Checkout/js/model/quote', 'Magento_Checkout/js/model/resource-url-manager', 'mage/storage', 'Magento_Checkout/js/model/payment-service', 'Magento_Checkout/js/model/payment/method-converter', 'Magento_Checkout/js/model/error-processor', 'Magento_Checkout/js/model/full-screen-loader', 'Magento_Checkout/js/action/select-billing-address' ], function( $, ko, quote, resourceUrlManager, storage, paymentService, methodConverter, errorProcessor, fullScreenLoader, selectBillingAddressAction ) { 'use strict'; return { saveShippingInformation: function() { var payload; var customvar = $('[name="custom_attributes[customvar]"]').val(); if(!quote.billingAddress()) { selectBillingAddressAction(quote.shippingAddress()); } payload = { addressInformation: { shipping_address: quote.shippingAddress(), billing_address: quote.billingAddress(), shipping_method_code: quote.shippingMethod().method_code, shipping_carrier_code: quote.shippingMethod().carrier_code, extension_attributes: { customvar: customvar } } }; console.log('debue customvar', customvar); fullScreenLoader.startLoader(); return storage.post( resourceUrlManager.getUrlForSetShippingInformation(quote), JSON.stringify(payload) ).done( function(response) { quote.setTotals(response.totals); paymentService.setPaymentMethods(methodConverter(response.payment_methods)); fullScreenLoader.stopLoader(); } ).fail( function(response) { errorProcessor.process(response); fullScreenLoader.stopLoader(); } ); } }; });
And create a ‘requirejs-config.js’ file, let Magento use our own javascript instead of the default.
var config = { config: { mixins: { 'Magento_Checkout/js/action/set-shipping-information': { 'Jeff_CustomField/js/action/set-shipping-information-mixin' : true } } }, "map": { "*": { "Magento_Checkout/js/model/shipping-save-processor/default" : "Jeff_CustomField/js/shipping-save-processor" } } };
Above the mixins, which is adding some additional functions for the original function for Javascript, is from the Magento DevDoc. This is redundant part and make people confusing.
Create extension attributes ShippingInformationInterface
#Jeff/CustomField/etc/extension_attributes.xml
Save the custom field value into quote
update our ‘di.xml’ file by adding additional plugin to save the value to quote.
#Jeff/CustomField/etc/di.xml ........
And Plugin class:
quoteRepository = $quoteRepository; } public function beforeSaveAddressInformation( \Magento\Checkout\Model\ShippingInformationManagement $subject, $cartId, \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation ) { if(!$extAttributes = $addressInformation->getExtensionAttributes()) { return; } $quote = $this->quoteRepository->getActive($cartId); $quote->setCustomvar($extAttributes->getCustomvar()); } }
Save the value into sales order
We are going to use ‘sales_model_service_quote_submit_before’ event to save the value into sales_order tables.
#Jeff/CustomField/etc/events.xml
And the Observer class:
getEvent()->getOrder(); $quote = $observer->getEvent()->getQuote(); $order->setData('customvar', $quote->getCustomvar()); return $this; } }
Final Results
After all these files placed, You can save the custom field into database (quote and sales_order).
You can download the source code at my GitHub