Magento2 add custom data on checkout and show on order.

Magento2 programmatically add custom data to checkout and show on order.

Step 1. Register your module Vendor_Module:
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="Vendor_Module" setup_version="1.3.1">
       
    </module>
</config>

registration.php

<?php


// @codeCoverageIgnoreStart
\Magento\Framework\Component\ComponentRegistrar::register(
    \Magento\Framework\Component\ComponentRegistrar::MODULE,
    'Vendor_Module',
    __DIR__
);
// @codeCoverageIgnoreEnd

Step 2 Create a installData.php or upgradeData.php and addAttribute to quote and order. I use custom data name “contact_type”.

<?php
/**
 * Created by PhpStorm.
 * User: thelightsp
 * Date: 6/30/19
 * Time: 7:45 AM
 */

namespace Vendor\Module\Setup;

use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleContextInterface;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\UpgradeDataInterface;
use Magento\Quote\Setup\QuoteSetupFactory;
use Magento\Sales\Setup\SalesSetup;
use Magento\Sales\Setup\SalesSetupFactory;
use Magento\Framework\DB\Ddl\Table;

class UpgradeData implements UpgradeDataInterface
{
    /**
     * @var EavSetupFactory
     */
    public $eavSetupFactory;

    /**
     * @var \Magento\Framework\ObjectManagerInterface
     */
    public $objectManager;

    /**
     * @var \Magento\Catalog\Model\ResourceModel\Eav\Attribute
     */
    public $eavAttribute;

    /**
     * @var \Magento\Framework\App\State
     */
    protected $state;
    /**
     * @var QuoteSetupFactory
     */
    protected $quoteSetupFactory;
    /**
     * @var SalesSetupFactory
     */
    protected $salesSetupFactory;
    /**
     * UpgradeData constructor.
     * @param EavSetupFactory $eavSetupFactory
     * @param \Magento\Framework\ObjectManagerInterface $objectManager
     * @param \Magento\Framework\App\State $state
     * @param \Magento\Catalog\Model\ResourceModel\Eav\Attribute $eavAttribute
     * @param QuoteSetupFactory $quoteSetupFactory
     * @param SalesSetupFactory $salesSetupFactory
     */
    public function __construct(
        EavSetupFactory $eavSetupFactory,
        \Magento\Framework\ObjectManagerInterface $objectManager,
        \Magento\Framework\App\State $state,
        \Magento\Catalog\Model\ResourceModel\Eav\Attribute $eavAttribute,
        QuoteSetupFactory $quoteSetupFactory,
        SalesSetupFactory $salesSetupFactory
    ) {
        $this->eavSetupFactory = $eavSetupFactory;
        $this->objectManager = $objectManager;
        $this->eavAttribute = $eavAttribute;
        $this->state = $state;
        $this->quoteSetupFactory = $quoteSetupFactory;
        $this->salesSetupFactory = $salesSetupFactory;

    }


    /**
     * @param ModuleDataSetupInterface $setup
     * @param ModuleContextInterface $context
     */
    public function upgrade(ModuleDataSetupInterface $setup, ModuleContextInterface $context)
    {
        $setup->startSetup();
        if (version_compare($context->getVersion(), '1.3.1', '<')) {
            /** @var QuoteSetup $quoteSetup */
            $quoteSetup = $this->quoteSetupFactory->create(['setup' => $setup]);
            $quoteSetup
                ->addAttribute(
                    'quote',
                    'contact_type',
                    ['type' => Table::TYPE_TEXT, 'required' => false]
                );

            /** @var SalesSetup $salesSetup */
            $salesSetup = $this->salesSetupFactory->create(['setup' => $setup]);
            $salesSetup
                ->addAttribute(
                    'order',
                    'contact_type',
                    ['type' => Table::TYPE_TEXT, 'required' => false]
                );
        }

    }

}

Step 3: Add custom data component on layout: view/frontend/layout/checkout_index_index.xml

<?xml version="1.0"?>

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" layout="checkout" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        <referenceBlock name="checkout.root">
            <arguments>
                <argument name="jsLayout" xsi:type="array">
                    <item name="components" xsi:type="array">
                        <item name="checkout" xsi:type="array">
                            <item name="children" xsi:type="array">
                                <item name="sidebar" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="summary" xsi:type="array">
                                            <item name="children" xsi:type="array">
                                                <item name="itemsAfter" xsi:type="array">
                                                    <item name="children" xsi:type="array">
                                                        <item name="contact-type" xsi:type="array">
                                        <item name="component" xsi:type="string">Vendor_Module/js/view/sidebar/contact-type</item>
                                        <item name="sortOrder" xsi:type="string">12</item>
                                        <item name="displayArea" xsi:type="string">contact-type</item>
                                        <item name="dataScope" xsi:type="string">contactType</item>
                                        <item name="provider" xsi:type="string">checkoutProvider</item>
                                    </item>
                                                        </item>
                                                    </item>
                                                </item>
                                            </item>
                                        </item>
                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

Step 4: Add component of your field: view/frontend/web/js/view/sidebar/contact-type.js

define(
    [
        'ko',
        'uiComponent'
    ],
    function (ko, Component ) {
        'use strict';

        return Component.extend({
            defaults: {
                template: 'Vendor_Module/sidebar/contact-type',
                inputValue: ''
            },
            isVisible: true,

            /**
             * @inheritdoc
             */
            initObservable: function () {
                this._super().observe({
                    CheckVal1: ko.observable(false)
                    
                });
                var contactType= [];

                this.CheckVal1.subscribe(function (newValue) {
                    
                    window.checkoutConfig.contact_type = newValue;

                });

                return this;
            }


        });
    }
);

Step 5: add component template: view/frontend/web/template/sidebar/contact-type.html

<div class="aw-sidebar_contact-type" data-bind="visible: isVisible">
    <span data-role="title" class="toggle" data-bind="i18n:'Contacts'"></span>
    <div class="content" data-role="content">

        <div class="contact">
            <div>
                <div class="control">
                    <input type="checkbox" data-bind='checked: CheckVal1'  id="contact[phone]" name="contact_phone" value="1" class="checkbox"> <label></label>
                </div>
                <label for="contact[phone]">Your checkbox</label>
                
            </div>
         </div>   
    </div>
</div>

Step 6: Add your attribute value to payment extension attribute:view/frontend/web/js/model/place-order-mixin.js


define([
    'jquery',
    'mage/utils/wrapper',
    'Magento_Checkout/js/model/full-screen-loader'
], function (
    $,
    wrapper,
    fullScreenLoader
) {
    'use strict';

    return function (placeOrderAction) {
        return wrapper.wrap(placeOrderAction, function (originalAction, paymentData, messageContainer) {
          
            paymentData['extension_attributes']['contact_type'] = window.checkoutConfig.contact_type;

            return originalAction(paymentData, messageContainer).fail(
                function () {
                    fullScreenLoader.stopLoader();
                }
            );
        });
    };
});

Step 7: add your extension_attributes in Vendor_Modules/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\PaymentInterface">
        <attribute code="contact_type" type="string" />
    </extension_attributes>
    <extension_attributes for="Magento\Quote\Api\Data\CartInterface">
        <attribute code="contact_type" type="string" />
    </extension_attributes>
    <extension_attributes for="Magento\Sales\Api\Data\OrderInterface">
        <attribute code="contact_type" type="string" />
    </extension_attributes>
</config>

Step 8: Add plugin to modify data payment from extension attribute and order interface:

<?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\Api\PaymentInformationManagementInterface">
       <plugin name="contactType" type="Vendor\Module\Model\Plugin\ContactType"/>
  </type>
 <type name="Magento\Sales\Api\OrderRepositoryInterface">
        <plugin name="contacttype-order-repository-plugin" type="Vendor\Module\Plugin\Order\OrderRepositoryPlugin"/>
    </type>
</config>
<?php

namespace Vendor\Module\Model\Plugin;

use Magento\Checkout\Api\PaymentInformationManagementInterface;
use Magento\Quote\Api\Data\PaymentInterface;


class ContactType
{
    /**
     * @param PaymentInformationManagementInterface $subject
     * @param int $cartId
     * @param PaymentInterface $paymentMethod
     * @return void
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function beforeSavePaymentInformationAndPlaceOrder(
        PaymentInformationManagementInterface $subject,
        $cartId,
        PaymentInterface $paymentMethod
    ) {
       if($paymentData->getExtensionAttributes()->getContactType()){
                $quote = $this->quoteRepository->getActive($cartId);
                $quote->setContactType($paymentData->getExtensionAttributes()->getContactType());
                $this->quoteRepository->save($quote);
            }
    }

    /**
     * @param PaymentInformationManagementInterface $subject
     * @param int $cartId
     * @param PaymentInterface $paymentMethod
     * @return void
     * @SuppressWarnings(PHPMD.UnusedFormalParameter)
     */
    public function beforeSavePaymentInformation(
        PaymentInformationManagementInterface $subject,
        $cartId,
        PaymentInterface $paymentMethod
    ) {
        if($paymentData->getExtensionAttributes()->getContactType()){
                $quote = $this->quoteRepository->getActive($cartId);
                $quote->setContactType($paymentData->getExtensionAttributes()->getContactType());
                $this->quoteRepository->save($quote);
            }
    }
}

Step 9: add data to event quote submit before: Vendor_Module/etc/events.xml

<?xml version="1.0" encoding="UTF-8"?>


<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="aw_osc_quote_submit_before" instance="Vendor\Module\Observer\QuoteSubmitBeforeObserver" />
    </event>
</config>

<?php

namespace Vendor\Module\Observer;

use Magento\Framework\Event\Observer;
use Magento\Framework\Event\ObserverInterface;
use Magento\Quote\Model\Quote;
use Magento\Sales\Api\Data\OrderInterface;

class QuoteSubmitBeforeObserver implements ObserverInterface
{
    /**
     * {@inheritdoc}
     */
    public function execute(Observer $observer)
    {
        $event = $observer->getEvent();
        /** @var OrderInterface $order */
        $order = $event->getOrder();
        /** @var Quote $quote */
        $quote = $event->getQuote();
        $order->setContactType($quote->getContactType());
      
        return $this;
    }
}

Step 10: Set your custom data to order:

<?php

namespace Vendor\Module\Plugin\Order;

use Magento\Sales\Api\OrderRepositoryInterface;
use Magento\Sales\Api\Data\OrderInterface;
use Magento\Sales\Api\Data\OrderExtensionFactory;
use Magento\Sales\Api\Data\OrderExtensionInterface;

class OrderRepositoryPlugin
{
    /**
     * @var OrderExtensionFactory
     */
    private $orderExtensionFactory;

    /**
     * @param OrderExtensionFactory $orderExtensionFactory
     */
    public function __construct(
        OrderExtensionFactory $orderExtensionFactory
    ) {
        $this->orderExtensionFactory = $orderExtensionFactory;
    }

    /**
     * Add extension attributes to order
     *
     * @param OrderRepositoryInterface $subject
     * @param OrderInterface $order
     * @return OrderInterface
     */
    public function afterGet(OrderRepositoryInterface $subject, OrderInterface $order)
    {
        $this->setExtensionAttributes($order);
        return $order;
    }

    /**
     * Set extension attributes to order entity
     *
     * @param OrderInterface $order
     */
    private function setExtensionAttributes(OrderInterface $order)
    {
        /** @var OrderExtensionInterface $extensionAttributes */
        $extensionAttributes = $order->getExtensionAttributes();
        if ($extensionAttributes === null) {
            $extensionAttributes = $this->orderExtensionFactory->create();
        }
        $extensionAttributes->setContactType($order->getContactType());
        $order->setExtensionAttributes($extensionAttributes);
    }
}

That is all step add your custom data to checkout and order via checkout and database.

Step 11: Show custom data to order view.

Create layout referer and show your block: Vendor/Module/view/adminhtml/layout/sales_order_view.xml

Create your block: Vendor/Module/Block/Adminhtml/Order/View/ContactType.php

Create your template: Vendor/Module/view/adminhtml/templates/order/view/contacttype.phtml

<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:View/Layout/etc/page_configuration.xsd">
    <body>
        
        <referenceBlock name="order_additional_info">
            <block class="Vendor\Module\Block\Adminhtml\Order\View\ContactType" name="order.view.contacttype" template="Vendor_Module::order/view/contacttype.phtml"/>
        </referenceBlock>
    </body>
</page>
<?php
/**
 * Created by PhpStorm.
 * User: thelightsp
 * Date: 6/30/19
 * Time: 4:00 PM
 */

namespace Vendor\Module\Block\Adminhtml\Order\View;


class ContactType extends \Magento\Sales\Block\Adminhtml\Order\AbstractOrder
{
    /**
     * ContactType constructor.
     * @param \Magento\Backend\Block\Template\Context $context
     * @param \Magento\Framework\Registry $registry
     * @param \Magento\Sales\Helper\Admin $adminHelper
     * @param array $data
     */
    public function __construct(
        \Magento\Backend\Block\Template\Context $context,
        \Magento\Framework\Registry $registry,
        \Magento\Sales\Helper\Admin $adminHelper,
        array $data = [])
    {
        parent::__construct($context, $registry, $adminHelper, $data);
    }

    /**
     * @return string
     * @throws \Magento\Framework\Exception\LocalizedException
     */
    public function getContactType(){
        return $this->getOrder()->getContactType();
    }
}
<div class="admin__page-section-item" style="display: block;width: 100%">
    <div class="admin__page-section-item-title">
        <span class="title"><?= $block->escapeHtml(__('Customer Communication')) ?></span>
    </div>
    <address class="admin__page-section-item-content">
        <?= /* @noEscape */ $block->getContactType(); ?>
    </address>
</div>

That is all step create custom data add to checkout and show on your order. Hope it will help you well.

This Post Has One Comment

  1. Hello, what is step 8, where should I add it?

Leave a Reply