1

I've tried multiple ways and sources of trying to save data in Magento 2.3 and I cannot think of what more I can try to get this working. I'm trying to add a simple textarea and save the data somehow but struggling to find documentation or tutorials that fully explain how to save submitted data at the checkout and why it is saved that way.

It's probably a much simpler fix than I think as I'm somewhat new to Magento, you can find all the files below.

set-shipping-information.js

/*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) {

            let shippingAddress = quote.shippingAddress();

            console.log(shippingAddress);
            console.log(shippingAddress.customAttributes);

            if (shippingAddress['extension_attributes'] === undefined) {
                shippingAddress['extension_attributes'] = {};
            }

            // shippingAddress['extension_attributes']['custom_field'] = shippingAddress.customAttributes['custom_field'];

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

checkout_index_index.xml

<?xml version="1.0"?>
<page xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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="steps" xsi:type="array">
                                    <item name="children" xsi:type="array">
                                        <item name="shipping-step" xsi:type="array">
                                            <item name="children" xsi:type="array">

                                                <item name="shippingAddress" xsi:type="array">
                                                    <item name="children" xsi:type="array">


                                                    <item name="before-shipping-method-form" xsi:type="array">
                                                    <item name="component" xsi:type="string">uiComponent</item>
                                                    <item name="displayArea" xsi:type="string">before-shipping-method-form</item>
                                                    <item name="children" xsi:type="array">


                                                        <item name="custom-checkout-form-container" xsi:type="array">
                                                            <item name="component" xsi:type="string">KingfisherDirect_Checkout/js/view/delivery-instructions</item>
                                                            <item name="provider" xsi:type="string">checkoutProvider</item>
                                                            <item name="config" xsi:type="array">
                                                                <item name="template" xsi:type="string">KingfisherDirect_Checkout/delivery-instructions</item>
                                                            </item>
                                                            <item name="sortOrder" xsi:type="string">3</item>
                                                            <item name="children" xsi:type="array">


                                                                <item name="custom-checkout-form-fieldset" xsi:type="array">
                                                                    <!-- uiComponent is used as a wrapper for form fields (its template will render all children as a list) -->
                                                                    <item name="component" xsi:type="string">uiComponent</item>
                                                                    <!-- the following display area is used in template (see below) -->
                                                                    <item name="displayArea" xsi:type="string">custom-checkout-form-fields</item>
                                                                    <item name="children" xsi:type="array">

                                                                        <item name="textarea_field" xsi:type="array">
                                                                            <item name="component" xsi:type="string">Magento_Ui/js/form/element/boolean</item>
                                                                            <item name="config" xsi:type="array">
                                                                                <!--customScope is used to group elements within a single form (e.g. they can be validated separately)-->
                                                                                <item name="customScope" xsi:type="string">customCheckoutForm</item>
                                                                                <item name="template" xsi:type="string">ui/form/field</item>
                                                                                <item name="elementTmpl" xsi:type="string">ui/form/element/textarea</item>
                                                                            </item>
                                                                            <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                            <item name="dataScope" xsi:type="string">customCheckoutForm.textarea_field</item>
                                                                            <!--<item name="label" xsi:type="string">Textarea Field</item>-->
                                                                            <item name="sortOrder" xsi:type="string">1</item>
                                                                        </item>

                                                                       <item name="checkbox_field" xsi:type="array">
                                                                            <item name="component" xsi:type="string">Magento_Ui/js/form/element/boolean</item>
                                                                            <item name="config" xsi:type="array">
                                                                                <!--customScope is used to group elements within a single form (e.g. they can be validated separately)-->
                                                                                <item name="customScope" xsi:type="string">customCheckoutForm</item>
                                                                                <item name="template" xsi:type="string">ui/form/field</item>
                                                                                <item name="elementTmpl" xsi:type="string">ui/form/element/checkbox</item>
                                                                            </item>
                                                                            <item name="provider" xsi:type="string">checkoutProvider</item>
                                                                            <item name="dataScope" xsi:type="string">customCheckoutForm.checkbox_field</item>
                                                                            <item name="label" xsi:type="string">Checkbox Field</item>
                                                                            <item name="sortOrder" xsi:type="string">2</item>
                                                                        </item>
                                                                    </item>
                                                                </item>
                                                            </item>
                                                        </item>



                                                            </item>
                                                        </item>
                                                    </item>
                                                </item>

                                            </item>
                                        </item>

                                    </item>
                                </item>
                            </item>
                        </item>
                    </item>
                </argument>
            </arguments>
        </referenceBlock>
    </body>
</page>

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\OrderInterface">-->
        <!--<attribute code="textarea_field" type="string" />-->
    <!--</extension_attributes>-->
    <extension_attributes for="Magento\Quote\Api\Data\AddressInterface">
        <attribute code="checkbox_field" type="string" />
    </extension_attributes>
</config>

TestProcessor.php

<?php
namespace KingfisherDirect\Checkout\Block;

use Magento\Checkout\Block\Checkout\LayoutProcessorInterface;

class TestProcessor implements LayoutProcessorInterface
{
    /**
     *
     *
     * @param array $jsLayout
     *
     * @return array
     */
    public function process($jsLayout)
    {
        $customAttributeCode = 'checkbox_field';
        $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' => 'this is what the field is for',
                ],
            ],
            'dataScope' => 'shippingAddress.custom_attributes' . '.' . $customAttributeCode,
            'label' => 'Custom 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;
    }
}

di.xml

<?xml version="1.0"?>
<!--
/**
 * Copyright © Magento, Inc. All rights reserved.
 * See COPYING.txt for license details.
 */
-->
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">
    <type name="Magento\Checkout\Block\Onepage">
        <arguments>
            <argument name="layoutProcessors" xsi:type="array">
                <item name="test" xsi:type="object">KingfisherDirect\Checkout\Block\TestProcessor</item>
            </argument>
        </arguments>
    </type>
</config>
0

Try following way:

Step 1: SR/MagentoStackExchange/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="SR_MagentoStackExchange::add_address_field"
                type="SR\MagentoStackExchange\Plugin\Checkout\Block\Checkout\LayoutProcessor" sortOrder="1"/>
    </type>
</config>

Step 2: SR/MagentoStackExchange/Plugin/Checkout/Block/Checkout/LayoutProcessor.php


<?php
namespace SR\MagentoStackExchange\Plugin\Checkout\Block\Checkout;

class LayoutProcessor
{
    public function afterProcess(
        \Magento\Checkout\Block\Checkout\LayoutProcessor $subject,
        $jsLayout
    ) {

        $customAttributeCode = 'custom_field';
        $jsLayout['components']['checkout']['children']['steps']['children']['shipping-step']['children']
        ['shippingAddress']['children']['shipping-address-fieldset']['children'][$customAttributeCode] = $this->addNewField($customAttributeCode);
        return $jsLayout;
    }

    private function addNewField($customAttributeCode)
    {

        $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' => 'this is what the field is for',
                ],
            ],
            'dataScope' => 'shippingAddress.custom_attributes' . '.' . $customAttributeCode,
            'label' => 'Custom Attribute',
            'provider' => 'checkoutProvider',
            'sortOrder' => 1000,
            'validation' => [
                'required-entry' => false
            ],
            'options' => [],
            'filterBy' => null,
            'customEntry' => null,
            'visible' => true,
        ];

         return $customField;
    }
}

Step 3: SR/MagentoStackExchange/view/frontend/requirejs-config.js


var config = {
    config: {
        mixins: {
            'Magento_Checkout/js/action/set-shipping-information': {
                'SR_MagentoStackExchange/js/action/set-shipping-information-mixin': true
            }
        }
    }
};

Step 4: SR/MagentoStackExchange/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) {
            var shippingAddress = quote.shippingAddress();
            if (shippingAddress['extension_attributes'] === undefined) {
                shippingAddress['extension_attributes'] = {};
            }

            $.each(shippingAddress.customAttributes, function(index, item) {
                shippingAddress['extension_attributes'][item.attribute_code] = item.value;
            });

            // pass execution to original action ('Magento_Checkout/js/action/set-shipping-information')
            return originalAction();
        });
    };
});

Step 5: SR/MagentoStackExchange/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="custom_field" type="string" />
    </extension_attributes>
</config>

Step 6: SR/MagentoStackExchange/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\Model\ShippingInformationManagement">
        <plugin name="SR_MagentoStackExchange::ShippingInformationManagement" type="SR\MagentoStackExchange\Plugin\Checkout\Model\ShippingInformationManagement" sortOrder="1"/>
    </type>
</config>

Step 7: SR/MagentoStackExchange/Plugin/Checkout/Model/ShippingInformationManagement.php You now able to get data for saving. Use before/after/around plugin which suite your requirement.


<?php
namespace SR\MagentoStackExchange\Plugin\Checkout\Model;

class ShippingInformationManagement
{
    /**
     * @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
    ) {
        $address = $addressInformation->getShippingAddress();
        $extAttributes = $address->getExtensionAttributes();
        error_log(print_r($extAttributes,1));
    }
}
0

I ended up following the example set in the following Git Repo

It's a lot of files to add and a lot to take in but I believe it all makes the most sense of anything I've found.

https://github.com/sbodak/magento2-checkout-custom-form

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.