Custom Shipping Address Field

Adding custom field to checkout shipping address frontend

1) Create the basic skeleton of module. Make sure to create dependency in Vendor/Module/etc/module.xml to avoid bad things from happening.

1
2
3
4
5
6
7
8
<?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="EdmondsCommerce_Sms" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Checkout"/>
        </sequence>
    </module>
</config>

2) Create the Vendor/Module/Setup/InstallSchema.php with contents below

 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
44
45
46
47
48
<?php

namespace EdmondsCommerce\Sms\Setup;

use Magento\Framework\Setup\InstallSchemaInterface;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\SchemaSetupInterface;

class InstallSchema implements InstallSchemaInterface
{

    public function install(SchemaSetupInterface $setup, ModuleContextInterface $context)
    {
        $installer = $setup;

        $installer->startSetup();

        $connection = $installer->getConnection();
        $connection
            ->addColumn(
                $installer->getTable('quote_address'),
                'sms',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table ::TYPE_TEXT,
                    'nullable' => true,
                    'default' => NULL,
                    'length' => 255,
                    'comment' => 'SMS Number',
                    'after' => 'fax'
                ]
            );
        $connection
            ->addColumn(
                $installer->getTable('sales_order_address'),
                'sms',
                [
                    'type' => \Magento\Framework\DB\Ddl\Table ::TYPE_TEXT,
                    'nullable' => true,
                    'default' => NULL,
                    'length' => 255,
                    'comment' => 'SMS Number',
                    'after' => 'fax'
                ]
            );

        $installer->endSetup();
    }
}

3) Create UpgradeData.php script with the content like

 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
44
45
46
47
48
49
50
51
52
53
54
<?php

namespace  EdmondsCommerce\Sms\Setup;

use Magento\Customer\Model\Customer;
use Magento\Customer\Setup\CustomerSetup;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;

class UpgradeData implements  UpgradeDataInterface
{
    private $customerSetupFactory;

    public function __construct(\Magento\Customer\Setup\CustomerSetupFactory $customerSetupFactory)
    {
        $this->customerSetupFactory = $customerSetupFactory;
    }
    public function upgrade(ModuleDataSetupInterface $setup,
                            ModuleContextInterface $context){
        $setup->startSetup();

        /** @var CustomerSetup $customerSetup */
        $customerSetup = $this->customerSetupFactory->create(['setup' => $setup]);

        $customerSetup->addAttribute('customer_address', 'sms', [
            'label' => 'Mobile (Courier text notifications)',
            'input' => 'text',
            'type' => \Magento\Framework\DB\Ddl\Table ::TYPE_TEXT,
            'source' => '',
            'required' => false,
            'position' => 333,
            'visible' => true,
            'system' => false,
            'is_used_in_grid' => false,
            'is_visible_in_grid' => false,
            'is_filterable_in_grid' => false,
            'is_searchable_in_grid' => false,
            'backend' => ''
        ]);

        $attribute = $customerSetup->getEavConfig()->getAttribute('customer_address', 'sms')
            ->addData(['used_in_forms' => [
                'adminhtml_customer_address',
                'adminhtml_customer',
                'customer_address_edit',
                'customer_register_address',
                'customer_address',
            ]]);
        $attribute->save();

        $setup->endSetup();
    }
}

3) Create a plugin in Vendor/Module/Plugin/{YourPluginName.php} with content below which will register your extension attributes to be seen by checkout javascript.

 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
<?php

namespace EdmondsCommerce\Sms\Plugin;

use Magento\Checkout\Block\Checkout\LayoutProcessor;

class SmsPlugin
{
    public function afterProcess(LayoutProcessor $subject, $jsLayout) {
        $customAttributeCode = 'sms';
        $customField = [
            'component' => 'Magento_Ui/js/form/element/abstract',
            'config' => [
                // customScope is used to group elements within a single form (e.g. they can be validated separately)
                'customScope' => 'shippingAddress.custom_attributes',
                'customEntry' => null,
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/input',
                'tooltip' => [
                    'description' => 'Mobile (Courier text notifications)',
                ],
            ],
            'dataScope' => 'shippingAddress.custom_attributes' . '.' . $customAttributeCode,
            'label' => 'Custom SMS Attribute',
            'provider' => 'checkoutProvider',
            'sortOrder' => 0,
            'validation' => [
                'required-entry' => true
            ],
            '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;
    }
}
4) Create another plugin Vendor/Module/Plugin/{ShippngInformationManagementPlugin.php} which will set your custom field with value from extension attributes.

 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
<?php

namespace EdmondsCommerce\Sms\Plugin;

class ShippingInformationManagementPlugin
{
    protected $quoteRepository;

    public function __construct(
        \Magento\Quote\Model\QuoteRepository $quoteRepository
    ) {
        $this->quoteRepository = $quoteRepository;
    }

    /**
     * @param \Magento\Checkout\Model\ShippingInformationManagement $subject
     * @param $cartId
     * @param \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
     */
    public function beforeSaveAddressInformation(
        \Magento\Checkout\Model\ShippingInformationManagement $subject,
        $cartId,
        \Magento\Checkout\Api\Data\ShippingInformationInterface $addressInformation
    ) {
        $shippingAddress = $addressInformation->getShippingAddress();
        $shippingAddressExtensionAttributes = $shippingAddress->getExtensionAttributes();
        if ($shippingAddressExtensionAttributes) {
            $sms = $shippingAddressExtensionAttributes->getSms();
            $shippingAddress->setSms($sms);
        }

    }
}

4) Register those plugins in Vendor/Module/etc/di.xml

1
2
3
4
5
6
7
8
9
<?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 name="sms_plugin" type="EdmondsCommerce\Sms\Plugin\SmsPlugin" sortOrder="1" />
    </type>
    <type name="Magento\Checkout\Model\ShippingInformationManagement">
        <plugin name="sms_address_save_plugin" type="EdmondsCommerce\Sms\Plugin\ShippingInformationManagementPlugin" sortOrder="10"/>
    </type>
</config>

5) Create javascript field which will extend the javascript part of shipping address the file needs to be created under Vendor/Module/view/frontend/web/js/action/{set-shipping-infomation-mixin.js}. (Could be any file name, just follow standards, and I think this should be the appropriate file name) The contents of that file

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*jshint browser:true jquery:true*/
/*global alert*/
define([
    'jquery',
    'mage/utils/wrapper',
    'Magento_Checkout/js/model/quote'
], function ($, wrapper, quote) {
    'use strict';

    return function (setShippingInformationAction) {

        return wrapper.wrap(setShippingInformationAction, function (originalAction) {
            var shippingAddress = quote.shippingAddress();
            if (shippingAddress['extension_attributes'] === undefined) {
                shippingAddress['extension_attributes'] = {};
            }

            shippingAddress['extension_attributes']['sms'] = shippingAddress.customAttributes['sms'];
            // pass execution to original action ('Magento_Checkout/js/action/set-shipping-information')
            return originalAction();
        });
    };
});

6) Include that file through requirejs inside Vendor/Module/view/frontend/requirejs-config.js

1
2
3
4
5
6
7
8
9
var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/action/set-shipping-information': {
                'EdmondsCommerce_Sms/js/action/set-shipping-information-mixin': true
            }
        }
    }
};

6) And one more necessary step is creating Vendor/Module/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="sms" type="string" />
    </extension_attributes>
</config>

7) Register an observer, which will pass SMS field value from quote_address to order_address by creating this file Vendor/Module/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="sms" instance="EdmondsCommerce\Sms\Observer\SalesModelServiceQuoteSubmitBefore" shared="false" />
    </event>
</config>

7) Execute command below

1
2
3
4
php bin/magento module:enable EdmondsCommerce_Sms -c
php bin/magento setup:upgrade
php bin/magento setup:di:compile
php bin/magento setup:static:deploy

8) Check if your new field is inside checkout shipping address. Enjoy.

Good reads

Tutorial how to add new field in shipping address

What is Plugin in Magento

Alan Storm about Plugins