In this example, you can learn how to add additional attributes for customer address
Steps
- Add additional attributes to EAV table
- Create extension attributes to link with models to save additional attributes values in databases
- Create fieldset.xml file to copy the additional attributes value to order table
- Ui component customization to show the data in admin
Add additional attributes to EAV table
In this example, district and sub_district attributes will be added in the EAV table using following code
<?php
/**
* CreateAddressAdditionalAttributes
*
* @copyright Copyright © 2021 Vasan. All rights reserved.
* @author psureshvasan@vasan.com
*/
namespace Vasan\Customer\Setup\Patch\Data;
use Magento\Customer\Model\Customer;
use Magento\Customer\Setup\CustomerSetupFactory;
use Magento\Eav\Model\Entity\Attribute\SetFactory as AttributeSetFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Framework\Setup\Patch\PatchInterface;
class CreateAddressAdditionalAttributes implements DataPatchInterface
{
/**
* @var ModuleDataSetupInterface
*/
protected $moduleDataSetup;
/**
* @var CustomerSetupFactory
*/
protected $customerSetupFactory;
/**
* @var AttributeSetFactory
*/
protected $attributeSetFactory;
/**
* CreateAddressAdditionalAttributes constructor.
* @param ModuleDataSetupInterface $moduleDataSetup
* @param CustomerSetupFactory $customerSetupFactory
* @param AttributeSetFactory $attributeSetFactory
*/
public function __construct(
ModuleDataSetupInterface $moduleDataSetup,
CustomerSetupFactory $customerSetupFactory,
AttributeSetFactory $attributeSetFactory
) {
$this->moduleDataSetup = $moduleDataSetup;
$this->customerSetupFactory = $customerSetupFactory;
$this->attributeSetFactory = $attributeSetFactory;
}
public static function getDependencies()
{
return [];
}
public function getAliases()
{
return [];
}
public function apply()
{
$customerSetup = $this->customerSetupFactory->create(['setup' => $this->moduleDataSetup]);
$customerEntity = $customerSetup->getEavConfig()->getEntityType('customer_address');
$attributeSetId = $customerEntity->getDefaultAttributeSetId();
$attributeSet = $this->attributeSetFactory->create();
$attributeDefaultGroupId = $attributeSet->getDefaultGroupId($attributeSetId);
$customerSetup->removeAttribute('customer_address', 'district');
$customerSetup->addAttribute('customer_address', 'district', [
'type' => 'varchar',
'label' => 'District',
'input' => 'text',
'required' => true,
'sort_order' => 101,
'position' => 101,
'visible' => true,
'system' => false,
'user_defined' => true,
'group' => 'General',
'global' => true,
'visible_on_front' => true,
]);
$district = $customerSetup->getEavConfig()->getAttribute('customer_address', 'district');
if ($district->getId()) {
$district->addData([
'used_in_forms' => ['adminhtml_customer_address',
'customer_account_create', 'customer_register_address', 'customer_address_edit']
]);
$district->save();
}
$customerSetup->removeAttribute('customer_address', 'sub_district');
$customerSetup->addAttribute('customer_address', 'sub_district', [
'type' => 'varchar',
'label' => 'Sub District',
'input' => 'text',
'required' => true,
'sort_order' => 102,
'position' => 102,
'visible' => true,
'system' => false,
'user_defined' => true,
'group' => 'General',
'global' => true,
'visible_on_front' => true,
]);
$subDistrict = $customerSetup->getEavConfig()->getAttribute('customer_address', 'sub_district');
if ($subDistrict->getId()) {
$subDistrict->addData([
'used_in_forms' => ['adminhtml_customer_address',
'customer_account_create', 'customer_register_address', 'customer_address_edit']
]);
$subDistrict->save();
}
}
}
Create extension attributes to link with models to save additional attributes values in databases
The extension_attributes.xml should be created inside etc folder
<?xml version="1.0"?>
<!--
/**
* extension_attributes
*
* @copyright Copyright © 2021 Vasan. All rights reserved.
* @author psureshvasan@vasan.com
*/
-->
<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="district" type="string" />
<attribute code="sub_district" type="string" />
</extension_attributes>
<extension_attributes for="Magento\Sales\Api\Data\OrderAddressInterface">
<attribute code="district" type="string" />
<attribute code="sub_district" type="string" />
</extension_attributes>
<extension_attributes for="Magento\Customer\Api\Data\AddressInterface">
<attribute code="district" type="string" />
<attribute code="sub_district" type="string" />
</extension_attributes>
</config>
Create fieldset.xml file to copy the additional attributes value to order table
The fieldset.xml file should be created in etc folder
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* fieldset
*
* @copyright Copyright © 2021 Vasan. All rights reserved.
* @author psureshvasan@vasan.com
*/
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="urn:magento:framework:DataObject/etc/fieldset.xsd">
<scope id="global">
<fieldset id="sales_convert_quote_address">
<field name="sub_district">
<aspect name="to_order_address" />
<aspect name="to_customer_address" />
</field>
<field name="district">
<aspect name="to_order_address" />
<aspect name="to_customer_address" />
</field>
</fieldset>
<fieldset id="order_address">
<field name="sub_district">
<aspect name="to_customer_address" />
</field>
<field name="district">
<aspect name="to_customer_address" />
</field>
</fieldset>
</scope>
</config>
Ui component customization to show the data in admin
The customer_address_form.xml file should be created to show additional attributes in backend address creation
<?xml version="1.0" encoding="UTF-8"?>
<!--
/**
* Copyright © Magento, Inc. All rights reserved.
* See COPYING.txt for license details.
*/
-->
<form xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd" component="Magento_Customer/js/form/components/form">
<fieldset name="general">
<field name="city" component="Magento_Ui/js/form/element/select" formElement="select">
<settings>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
<dataType>text</dataType>
<label translate="true">City</label>
</settings>
<formElements>
<select>
<settings>
<options class="Vasan\Customer\Model\Config\Source\CityWithRegion"/>
<filterBy>
<field>region_id</field>
<target>${ $.provider }:${ $.parentScope }.region_id</target>
</filterBy>
</settings>
</select>
</formElements>
</field>
<field name="district" component="Magento_Ui/js/form/element/select" formElement="select">
<settings>
<validation>
<rule name="required-entry" xsi:type="boolean">true</rule>
</validation>
<dataType>text</dataType>
<label translate="true">District</label>
</settings>
<formElements>
<select>
<settings>
<options class="Vasan\Customer\Model\Config\Source\District"/>
<filterBy>
<field>city</field>
<target>${ $.provider }:${ $.parentScope }.city</target>
</filterBy>
</settings>
</select>
</formElements>
</field>
<field name="sub_district" component="Vasan_Customer/js/form/element/subdistrict" formElement="select">
<settings>
<dataType>text</dataType>
<label translate="true">Sub District</label>
</settings>
<formElements>
<select>
<settings>
<caption translate="true">-- Please Select a Sub District--</caption>
</settings>
</select>
</formElements>
</field>
<field name="postcode" component="Vasan_Customer/js/form/element/postcode" formElement="select">
<settings>
<dataType>text</dataType>
<label translate="true">Postcode</label>
</settings>
<formElements>
<select>
<settings>
<caption translate="true">-- Please Select a Postcode --</caption>
</settings>
</select>
</formElements>
</field>
</fieldset>
</form>
The custom select ui components are used to load the data of sub_district and postal code
The ui componets fetch the data based on the from previous component value and ajax is used to fetch only relevant data and avoid slowness.
subdistrict.js
define([
'jquery',
'underscore',
'uiRegistry',
'mage/storage',
'Magento_Ui/js/form/element/select'
], function ($, _, registry, storage, Select) {
'use strict';
return Select.extend({
defaults: {
skipValidation: false,
imports: {
update: '${ $.parentName }.district:value'
}
},
update: function (value) {
var latestValue = this.value._latestValue;
var serviceUrl = '/rest/V1/directory/getdistrictdata/' + value;
var self = this;
if (value !== '') {
$('body').trigger('processStart');
storage.get(serviceUrl)
.done(function (response) {
var dataString = JSON.stringify(response);
var data = JSON.parse(dataString) ;
self.setOptions(data);
self.source.set(self.dataScope, latestValue);
$('body').trigger('processStop');
}).fail(function (response) {
$('body').trigger('processStop');
});
}
}
});
});
postcode.js
define([
'jquery',
'underscore',
'uiRegistry',
'mage/storage',
'Magento_Ui/js/form/element/select'
], function ($, _, registry, storage, Select) {
'use strict';
return Select.extend({
defaults: {
skipValidation: false,
savedPostcode: '',
imports: {
update: '${ $.parentName }.sub_district:value'
}
},
/**
*
*
* @param {String} value - country
*/
setDifferedFromDefault: function (value) {
this._super();
if (value) {
this.savedPostcode = value;
}
},
update: function (value) {
var serviceUrl = '/rest/V1/directory/getpostcodedata/' + value;
var self = this;
if (value !== '') {
$('body').trigger('processStart');
storage.get(serviceUrl)
.done(function (response) {
var dataString = JSON.stringify(response);
var data = JSON.parse(dataString) ;
self.setOptions(data);
self.source.set(self.dataScope, self.savedPostcode);
$('body').trigger('processStop');
}).fail(function (response) {
$('body').trigger('processStop');
});
}
}
});
});
Posted by vasan to vasan's deck (2021-07-23 15:30)