0

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>
New contributor
Adam is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.
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

New contributor
Adam is a new contributor to this site. Take care in asking for clarification, commenting, and answering. Check out our Code of Conduct.

Your Answer

Adam is a new contributor. Be nice, and check out our Code of Conduct.

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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