Magento 2 Add Custom Field in Checkout Page

Magento 2 Add Custom Field in Checkout Page Programmatically

by admin

This article shows you step by step for Magento 2 add custom field in checkout page without relying on any third-party extension. And here’s how!

As you know, cart abandonment is a big problem for any business. And in eCommerce stores, the cart abandonment rate varies between 60% and 80%.

Luckily, there are a lot of practices to help you reduce the cart abandonment rate. And adding custom fields to the Magento checkout page is one of the best solutions. By collecting additional information, you can fulfill customers’ demands better. Therefore they would be more likely to complete their orders.

However, if you know little to nothing about coding or want to save your time, we strongly recommend using the Magento 2 Order Attributes extension by BSS. This extension provides you with many advanced features to add custom check fields and improve checkout experiences.

Add Custom Checkout Field Magento 2

The Downside Of Default Magento 2 Checkout Page

By default, the Magento checkout page only shows mandatory informative fields and doesn’t offer extra checkout fields. However, your business usually requires more than that basic information to:

  • Understand your customers better
  • Collect data to do some research
  • Fulfill customers’ additional demands

Therefore, the default checkout page can bring down the competitiveness of your store.

>>> EXPLORE NOW 8 Essential Tips for Magento 2 Checkout Page Customization to improve the process!

Magento 2 Add Custom Field in Checkout Page: Programmatical Instruction

Note: In this intrusion, we will show you how to add the “Delivery Date” field as an example. But you can add any custom checkout field following these steps.

Step 1: Create a module

The first step you need to do is to create the registration.php file as below:

registration.php

<?php
use \Magento\Framework\Component\ComponentRegistrar;
ComponentRegistrar::register(ComponentRegistrar::MODULE, 'Deliverydate', __DIR__);

etc\module.xml:

<?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="Magezon_Deliverydate" setup_version="1.0.0">
        <sequence>
            <module name="Magento_Sales"/>
        </sequence>
    </module>
</config>

You can change the Module name according to the additional field you want to add. In the code, we are using the name “Deliverydate.”

>>> Looking to boost your online store? >>> EXPLORE NOW: Plugins for Magento Store

Step 2: Add a new column to some tables

Next, you need to add a new column for your new custom field to the following tables:

  • quote
  • sales_order
  • sales_order_grid
<?php
namespace Magezon\Deliverydate\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();

        $installer->getConnection()->addColumn(
            $installer->getTable('quote'),
            'delivery_date',
            [
                'type' => 'datetime',
                'nullable' => false,
                'comment' => 'Delivery Date',
            ]
        );

        $installer->getConnection()->addColumn(
            $installer->getTable('sales_order'),
            'delivery_date',
            [
                'type' => 'datetime',
                'nullable' => false,
                'comment' => 'Delivery Date',
            ]
        );

        $installer->getConnection()->addColumn(
            $installer->getTable('sales_order_grid'),
            'delivery_date',
            [
                'type' => 'datetime',
                'nullable' => false,
                'comment' => 'Delivery Date',
            ]
        );

        $setup->endSetup();
    }
}

Because we are creating a “Delivery Date” checkout field in this example, the field type is set as “datetime.” You can also set the input type to “text” to collect string data.

Step 3: Add this order attribute field to the checkout page

etc\frontend\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 name="add_delivery_date_field"
                type="Magezon\Deliverydate\Plugin\Checkout\LayoutProcessorPlugin" sortOrder="10"/>
    </type>
</config>


Magezon\Deliverydate\Plugin\Checkout\LayoutProcessor.php:

<?php
namespace Magezon\Deliverydate\Plugin\Checkout;

class LayoutProcessorPlugin
{
    /**
     * @param \Magento\Checkout\Block\Checkout\LayoutProcessor $subject
     * @param array $jsLayout
     * @return array
     */
    public function afterProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
        array  $jsLayout
    ) {

        $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['before-form']['children']['delivery_date'] = [
            'component' => 'Magento_Ui/js/form/element/date',
            'config' => [
                'customScope' => 'shippingAddress',
                'template' => 'ui/form/field',
                'elementTmpl' => 'ui/form/element/date',
                'options' => [],
                'id' => 'delivery_date'
            ],
            'dataScope' => 'shippingAddress.delivery_date',
            'label' => __('Delivery Date'),
            'provider' => 'checkoutProvider',
            'visible' => true,
            'validation' => [],
            'sortOrder' => 200,
            'id' => 'delivery_date'
        ];


        return $jsLayout;
    }
}

After this step, you can check how it looks in the frontend.

Step 4: Save the selected attribute value of the custom field

There are several ways to do this, but we use Knockout JS as the simplest way in this instruction.

First, override some .js files by adding them to the requirejs-config.js.

var config = {
config: {
    mixins: {
        'Magento_Checkout/js/action/place-order': {
            'Magezon_Deliverydate/js/order/place-order-mixin': true
        },
    }
};

For the place-order-mixin.js file, save the attribute value of your new custom field to the quote by using Ajax.

define([
    'jquery',
    'mage/utils/wrapper',
    'Magento_CheckoutAgreements/js/model/agreements-assigner',
    'Magento_Checkout/js/model/quote',
    'Magento_Customer/js/model/customer',
    'Magento_Checkout/js/model/url-builder',
    'mage/url',
    'Magento_Checkout/js/model/error-processor',
    'uiRegistry'
], function (
    $, 
    wrapper, 
    agreementsAssigner,
    quote,
    customer,
    urlBuilder, 
    urlFormatter, 
    errorProcessor,
    registry
) {
    'use strict';

    return function (placeOrderAction) {

        /** Override default place order action and add agreement_ids to request */
        return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, messageContainer) {
            agreementsAssigner(paymentData);
            var isCustomer = customer.isLoggedIn();
            var quoteId = quote.getQuoteId();

            var url = urlFormatter.build('mgzcustom/quote/save');

            var deliveryDate = $('[name="delivery_date"]').val();

            if (deliveryDate) {

                var payload = {
                    'cartId': quoteId,
                    'delivery_date': deliveryDate,
                    'is_customer': isCustomer
                };

                if (!payload.delivery_date) {
                    return true;
                }

                var result = true;

                $.ajax({
                    url: url,
                    data: payload,
                    dataType: 'text',
                    type: 'POST',
                }).done(
                    function (response) {
                        result = true;
                    }
                ).fail(
                    function (response) {
                        result = false;
                        errorProcessor.process(response);
                    }
                );
            }

            return originalAction(paymentData, messageContainer);
        });
    };
});

Then create the Controller\Quote\Save.php file.

<?php

namespace Magezon\Deliverydate\Controller\Quote;

class Save extends \Magento\Framework\App\Action\Action
{
    protected $quoteIdMaskFactory;

    protected $quoteRepository;

    public function __construct(
        \Magento\Framework\App\Action\Context $context,
        \Magento\Quote\Model\QuoteIdMaskFactory $quoteIdMaskFactory,
        \Magento\Quote\Api\CartRepositoryInterface $quoteRepository
    ) {
        parent::__construct($context);
        $this->quoteRepository = $quoteRepository;
        $this->quoteIdMaskFactory = $quoteIdMaskFactory;
    }

    /**
     * @return \Magento\Framework\Controller\Result\Raw
     */
    public function execute()
    {
        $post = $this->getRequest()->getPostValue();
        if ($post) {
            $cartId       = $post['cartId'];
            $deliveryDate = $post['delivery_date'];
            $loggin       = $post['is_customer'];

            if ($loggin === 'false') {
                $cartId = $this->quoteIdMaskFactory->create()->load($cartId, 'masked_id')->getQuoteId();
            }

            $quote = $this->quoteRepository->getActive($cartId);
            if (!$quote->getItemsCount()) {
                throw new NoSuchEntityException(__('Cart %1 doesn\'t contain products', $cartId));
            }

            $quote->setData('delivery_date', $deliveryDate);
            $this->quoteRepository->save($quote);
        }
    }
}

Now we need to create a file etc/frontend/routes.xml to set up a router so that Ajax can send the data of your new custom checkout field to the controller.

<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:App/etc/routes.xsd">
    <router id="standard">
        <route id="mgzcustom" frontName="mgzcustom">
            <module name="Magezon_Deliverydate" />
        </route>
    </router>
</config>

After that, you save the selected values of your custom field to the sale_order.table. It’s time to create the etc/events.xml file.

<?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="save_delivery_date_to_order" instance="Magezon\Deliverydate\Observer\SaveToOrder" />
    </event>
</config>

And file Observer\SaveToOrder.php

<?php
namespace Magezon\Deliverydate\Observer;
class SaveToOrder implements \Magento\Framework\Event\ObserverInterface
{   
    public function execute(\Magento\Framework\Event\Observer $observer)
    {
        $event = $observer->getEvent();
        $quote = $event->getQuote();
     $order = $event->getOrder();
           $order->setData('delivery_date', $quote->getData('delivery_date'));
    }
}

Step 5: Display the attribute value in the sales order grid

To show the attribute value of the custom field in the order grid, you need to create the sales_order_grid.xml.

Then you add an extra column named as the name of your custom field (it’s Delivery Date in this example) to the path: View\adminhtml\ui_component\sales_order_grid.xml.

<?xml version="1.0"?>
<listing xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:module:Magento_Ui:etc/ui_configuration.xsd">
    <columns name="sales_order_columns">
        <column name="delivery_date">
            <argument name="data" xsi:type="array">
                <item name="config" xsi:type="array">
                    <item name="visible" xsi:type="boolean">true</item>
                    <item name="label" xsi:type="string" translate="true">Delivery Date</item>
                    <item name="dateFormat" xsi:type="string">Y-MM-dd</item>
                    <item name="component" xsi:type="string">Magento_Ui/js/grid/columns/date</item>
                    <item name="dataType" xsi:type="string">date</item>
                </item>
            </argument>
        </column>
    </columns>
</listing>

As you can see in the image below, we have had a new column named Delivery Date.

Checkout Magento 2

>>> Ready to dive deeper into Magento? Discover our comprehensive guide on Magento 2 tutorial for beginners (2024 update)

Conclusion: Magento 2 Add Custom Field in Checkout Page

Above, we have shown you how to add custom field in checkout page magento 2 programmatically. We hope this article is helpful and good luck to you!

>>> CHECK OUT guide to Customize Checkout Fields in Magento without using third-party extensions HERE!

If the process of Magento 2 adding custom fields in checkout page using code seems too complicated, consider installing the Magento 2 Order Attributes module by BSS Commerce for an easier solution. With this Magento module, you can add as many fields as you want on the checkout page effortlessly, collecting valuable customer insights through field responses for informed decision-making.

Next Reading Suggestions

© 2019 BSS Commerce owned by THANH CONG INTER ., JSC. All Rights Reserved.
Business registration certificate no. 0106064469 issued by Hanoi Department of Planning and Investment on 19 December 2019.
Legal Representative: Mr. Nguyen Quang Trung.