Please check below code to save custom address attribute in customer,checkout shipping and billing form and also save in order table.
Module Name : Ccc_Checkout
Script to create custom attribute for address and order
app/code/Ccc/Checkout/Setup/UpgradeData.php
<?php
namespace Ccc\Checkout\Setup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Eav\Model\Config;
use Magento\Eav\Model\Entity\Attribute\SetFactory as AttributeSetFactory;
class UpgradeData implements UpgradeDataInterface
{
private $eavSetupFactory;
/**
* @var Config
*/
private $eavConfig;
/**
* @var AttributeSetFactory
*/
private $attributeSetFactory;
public function __construct(
Config $eavConfig,
EavSetupFactory $eavSetupFactory,
AttributeSetFactory $attributeSetFactory
)
{
$this->eavSetupFactory = $eavSetupFactory;
$this->eavConfig = $eavConfig;
$this->attributeSetFactory = $attributeSetFactory;
}
/**
* {@inheritdoc}
*/
public function upgrade(
ModuleDataSetupInterface $setup,
ModuleContextInterface $context
) {
$setup->startSetup();
if (version_compare($context->getVersion(), '0.0.2','<')) {
$this->addUnitNumberFieldToAddress($setup);
}
if (version_compare($context->getVersion(), '0.0.3','<')) {
$this->updateUnitAttribute($setup);
}
$setup->endSetup();
}
/**
* put your comment there...
*
* @param mixed $setup
*/
protected function addUnitNumberFieldToAddress($setup)
{
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
$eavSetup->addAttribute('customer_address', 'unit_number', [
'type' => 'varchar',
'input' => 'text',
'label' => 'Unit Number',
'visible' => true,
'required' => false,
'user_defined' => true,
'system'=> false,
'group'=> 'General',
'sort_order' => 71,
'global' => true,
'visible_on_front' => true,
]);
$customAttribute = $this->eavConfig->getAttribute('customer_address', 'unit_number');
$customAttribute->setData(
'used_in_forms',
['adminhtml_customer_address','customer_address_edit','customer_register_address']
);
$customAttribute->save();
$installer = $setup;
$installer->getConnection()->addColumn(
$installer->getTable('quote_address'),
'unit_number',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
'length' => 255,
'comment' => 'Unit Number'
]
);
$installer->getConnection()->addColumn(
$installer->getTable('sales_order_address'),
'unit_number',
[
'type' => \Magento\Framework\DB\Ddl\Table::TYPE_TEXT,
'length' => 255,
'comment' => 'Unit Number'
]
);
}
public function updateUnitAttribute($setup)
{
$eavSetup = $this->eavSetupFactory->create(['setup' => $setup]);
$eavSetup->updateAttribute('customer_address', 'unit_number', 'sort_order', '71');
}
}
app/code/Ccc/Checkout/etc/di.xml
<?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\Checkout\Block\Checkout\LayoutProcessor">
<plugin disabled="false" name="BillingLayoutProcessor" sortOrder="99" type="Ccc\Checkout\Plugin\Block\Checkout\LayoutProcessor"/>
</type>
<type name="Magento\Quote\Model\BillingAddressManagement">
<plugin disabled="false" name="Ccc_Checkout_Plugin_Magento_Quote_Model_BillingAddressManagement" sortOrder="10" type="Ccc\Checkout\Plugin\Magento\Quote\Model\BillingAddressManagement"/>
</type>
<type name="Magento\Quote\Model\Quote\Address\BillingAddressPersister">
<plugin disabled="false" name="BillingAddressSave" sortOrder="10" type="Ccc\Checkout\Plugin\Magento\Quote\Model\Quote\Address\BillingAddressPersister"/>
</type>
<type name="Magento\Quote\Model\ShippingAddressManagement">
<plugin disabled="false" name="Ccc_Checkout_Plugin_Magento_Quote_Model_ShippingAddressManagement" sortOrder="10" type="Ccc\Checkout\Plugin\Magento\Quote\Model\ShippingAddressManagement"/>
</type>
</config>
app/code/Ccc/Checkout/etc/events.xml
<?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_success">
<observer name="custome_address_attribute_save" instance="Ccc\Checkout\Observer\SaveUnitNumberInOrder"/>
</event>
</config>
app/code/Ccc/Checkout/etc/extension_attributes.xml
<?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="unit_number" type="string"/>
</extension_attributes>
</config>
Create Plugin to display custom attribute in checkout billing and shipping form
app/code/Ccc/Checkout/Plugin/Block/Checkout/LayoutProcessor
<?php
namespace Ccc\Checkout\Plugin\Block\Checkout;
use \Magento\Checkout\Block\Checkout\LayoutProcessor as MageLayoutProcessor;
class LayoutProcessor
{
protected $_customAttributeCode = 'unit_number';
public function afterProcess(MageLayoutProcessor $subject, $jsLayout)
{
if (isset($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']
['payment']['children']['payments-list']['children']))
{
foreach ($jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['payments-list']['children'] as $key => $payment)
{
$paymentCode = 'billingAddress'.str_replace('-form','',$key);
$jsLayout['components']['checkout']['children']['steps']['children']['billing-step']['children']['payment']['children']['payments-list']['children'][$key]['children']['form-fields']['children'][$this->_customAttributeCode] = $this->getUnitNumberAttributeForAddress($paymentCode);
}
}
if(isset($jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset'])
){
$jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']['shippingAddress']['children']['shipping-address-fieldset']['children'][$this->_customAttributeCode] = $this->getUnitNumberAttributeForAddress('shippingAddress');
}
return $jsLayout;
}
public function getUnitNumberAttributeForAddress($addressType)
{
return $customField = [
'component' => 'Magento_Ui/js/form/element/abstract',
'config' => [
'customScope' => $addressType.'.custom_attributes',
'customEntry' => null,
'template' => 'ui/form/field',
'elementTmpl' => 'ui/form/element/input'
],
'dataScope' => $addressType.'.custom_attributes' . '.' . $this->_customAttributeCode,
'label' => 'Unit Number',
'provider' => 'checkoutProvider',
'sortOrder' => 71,
'validation' => [
'required-entry' => false
],
'options' => [],
'filterBy' => null,
'customEntry' => null,
'visible' => true,
];
}
}
To save custom attribute in checkout
app/code/Ccc/Checkout/Plugin/Magento/Quote/Model/ShippingAddressManagement
<?php
namespace Ccc\Checkout\Plugin\Magento\Quote\Model;
class ShippingAddressManagement
{
protected $logger;
public function __construct(
\Psr\Log\LoggerInterface $logger
) {
$this->logger = $logger;
}
public function beforeAssign(
\Magento\Quote\Model\ShippingAddressManagement $subject,
$cartId,
\Magento\Quote\Api\Data\AddressInterface $address
) {
$extAttributes = $address->getExtensionAttributes();
if (!empty($extAttributes)) {
try {
$address->setUnitNumber($extAttributes->getUnitNumber());
} catch (\Exception $e) {
$this->logger->critical($e->getMessage());
}
}
}
}
app/code/Ccc/Checkout/Plugin/Magento/Quote/Model/BillingAddressManagement
<?php
namespace Ccc\Checkout\Plugin\Magento\Quote\Model;
class BillingAddressManagement
{
protected $logger;
public function __construct(
\Psr\Log\LoggerInterface $logger
) {
$this->logger = $logger;
}
public function beforeAssign(
\Magento\Quote\Model\BillingAddressManagement $subject,
$cartId,
\Magento\Quote\Api\Data\AddressInterface $address,
$useForShipping = false
) {
$extAttributes = $address->getExtensionAttributes();
if (!empty($extAttributes)) {
try {
$address->setUnitNumber($extAttributes->getUnitNumber());
} catch (\Exception $e) {
$this->logger->critical($e->getMessage());
}
}
}
}
app/code/Ccc/Checkout/Ccc/Checkout/Plugin/Magento/Quote/Model/Quote/Address
<?php
namespace Ccc\Checkout\Plugin\Magento\Quote\Model\Quote\Address;
class BillingAddressPersister
{
protected $logger;
public function __construct(
\Psr\Log\LoggerInterface $logger
) {
$this->logger = $logger;
}
public function beforeSave(
\Magento\Quote\Model\Quote\Address\BillingAddressPersister $subject,
$quote,
\Magento\Quote\Api\Data\AddressInterface $address,
$useForShipping = false
) {
$extAttributes = $address->getExtensionAttributes();
if (!empty($extAttributes)) {
try {
$address->setUnitNumber($extAttributes->getUnitNumber());
} catch (\Exception $e) {
$this->logger->critical($e->getMessage());
}
}
}
}
To set custom attribute in extension attribute
app/code/Ccc/Checkout/view/frontend/requirejs-config.js
var config = {
config: {
mixins: {
'Magento_Checkout/js/model/payment/method-group': {
'Ccc_Checkout/js/model/payment/method-group-mixin': true
},
'Magento_Checkout/js/action/set-billing-address': {
'Ccc_Checkout/js/action/set-billing-address-mixin': true
},
'Magento_Checkout/js/action/set-shipping-information': {
'Ccc_Checkout/js/action/set-shipping-information-mixin': true
},
'Magento_Checkout/js/action/create-shipping-address': {
'Ccc_Checkout/js/action/create-shipping-address-mixin': true
},
'Magento_Checkout/js/action/place-order': {
'Ccc_Checkout/js/action/set-billing-address-mixin': true
},
'Magento_Checkout/js/action/create-billing-address': {
'Ccc_Checkout/js/action/set-billing-address-mixin': true
}
}
}
};
app/code/Ccc/Checkout/view/frontend/web/js/action/create-shipping-address-mixin.js
define([
'jquery',
'mage/utils/wrapper',
'Magento_Checkout/js/model/quote'
], function ($, wrapper,quote) {
'use strict';
return function (setShippingInformationAction) {
return wrapper.wrap(setShippingInformationAction, function (originalAction, messageContainer) {
if (messageContainer.custom_attributes != undefined) {
$.each(messageContainer.custom_attributes , function( key, value ) {
messageContainer['custom_attributes'][key] = {'attribute_code':key,'value':value};
});
}
return originalAction(messageContainer);
});
};
});
app/code/Ccc/Checkout/view/frontend/web/js/action/set-billing-address-mixin.js
define([
'jquery',
'mage/utils/wrapper',
'Magento_Checkout/js/model/quote'
], function ($, wrapper,quote) {
'use strict';
return function (setBillingAddressAction) {
return wrapper.wrap(setBillingAddressAction, function (originalAction, messageContainer) {
var billingAddress = quote.billingAddress();
if(billingAddress != undefined) {
if (billingAddress['extension_attributes'] === undefined) {
billingAddress['extension_attributes'] = {};
}
if (billingAddress.customAttributes != undefined) {
$.each(billingAddress.customAttributes, function (key, value) {
if($.isPlainObject(value)){
value = value['value'];
}
billingAddress['extension_attributes'][key] = value;
});
}
}
return originalAction(messageContainer);
});
};
});
app/code/Ccc/Checkout/view/frontend/web/js/action/set-shipping-information-mixin.js
define([
'jquery',
'mage/utils/wrapper',
'Magento_Checkout/js/model/quote'
], function ($, wrapper,quote) {
'use strict';
return function (setShippingInformationAction) {
return wrapper.wrap(setShippingInformationAction, function (originalAction, messageContainer) {
var shippingAddress = quote.shippingAddress();
if (shippingAddress['extension_attributes'] === undefined) {
shippingAddress['extension_attributes'] = {};
}
if (shippingAddress.customAttributes != undefined) {
$.each(shippingAddress.customAttributes , function( key, value ) {
if($.isPlainObject(value)){
value = value['value'];
}
shippingAddress['customAttributes'][key] = value;
shippingAddress['extension_attributes'][key] = value;
});
}
return originalAction(messageContainer);
});
};
});
To save custom attribute in orders
app/code/Ccc/Checkout/Observer/SaveUnitNumberInOrder.php
<?php
namespace Ccc\Checkout\Observer;
class SaveUnitNumberInOrder implements \Magento\Framework\Event\ObserverInterface
{
public function execute(\Magento\Framework\Event\Observer $observer) {
$order = $observer->getEvent()->getOrder();
$quote = $observer->getEvent()->getQuote();
if ($quote->getBillingAddress()) {
$order->getBillingAddress()->setUnitNumber($quote->getBillingAddress()->getExtensionAttributes()->getUnitNumber());
}
if (!$quote->isVirtual()) {
$order->getShippingAddress()->setUnitNumber($quote->getShippingAddress()->getUnitNumber());
}
return $this;
}
}
To display custom attribute in customer account you need to overright customer edit.phtml file from vendor to your theme like below :
app/design/frontend/custom_theme/theme_name/Magento_Customer/templates/address/edit.phtml
<div class="field unit_number">
<label class="label" for="unit_number"><span><?php echo $block->escapeHtml(__('Unit Number')) ?></span></label>
<div class="control">
<input type="text" name="unit_number" value="<?= $block->escapeHtmlAttr($block->getAddress()->getCustomAttribute('unit_number') ? $block->getAddress()->getCustomAttribute('unit_number')->getValue() : '') ?>" title="<?= $block->escapeHtmlAttr($block->getAddress()->getCustomAttribute('unit_number') ? $block->getAddress()->getCustomAttribute('unit_number')->getValue() : '') ?>" class="input-text <?= $block->escapeHtmlAttr($block->getAddress()->getCustomAttribute('unit_number') ? $block->getAddress()->getCustomAttribute('unit_number')->getValue() : '') ?>" id="unit_number">
</div>
</div>