wordpresswoocommercegutenberg-blocks

Woocommerce blocks: Update custom field values based on shipping country


I'm trying to make WP WooCommerce new block-based Checkout page a parcel terminal selection when a specific shipping method is selected. Example

But I need the select field options to be given depending on the value of the shipping country field. I can build the list of options for a specific country on the PHP side. But I am facing the problem that I don't know how to get the value of the selected country on the PHP side of this new Checkout page and how to transfer the list of terminals to the custom field.

I discovered that on the PHP side in my IntegrationInterface file, in the get_script_data() function, I can get the customer information from the session and pass the terminals to block.js via JS variable wcSettings["my-blocks_data"]. But it only works when the page is loaded. It does not work if the delivery country is changed when filling out the order information.

MyBlocksIntegration.php

<?php
use \Automattic\WooCommerce\Blocks\Integrations\IntegrationInterface;

class My_Blocks_Integration implements IntegrationInterface
{
    ...
    public function get_script_data() {
        $customer = \WC()->session->get('customer');
        $country = (!empty($customer['shipping_country'])) ? $customer['shipping_country'] : ((!empty($customer['country'])) ? $customer['country'] : 'LT');

        $terminals = \Terminals::get_terminals_list($country, 'terminal');
        if ( empty($terminals) || ! is_array($terminals) ) {
            $terminals = array();
        }
        $prepared_terminals = array();
        foreach ( $group_values as $terminal_key => $terminal_name ) {
            $prepared_terminals[] = array('label' => $terminal_name, 'value' => $terminal_key);
        }

        return array(
            'terminals' => $prepared_terminals,
        );
    }
    ...
}

block.js

/**
 * External dependencies
 */
import { useEffect, useState, useCallback } from '@wordpress/element';
import { SelectControl, TextareaControl } from '@wordpress/components';
import { useSelect, useDispatch } from '@wordpress/data';
import { debounce } from 'lodash';

/**
 * Internal dependencies
 */
import { options } from './options';
import { txt } from './text';

export const Block = ({ checkoutExtensionData, extensions }) => {
    const { setExtensionData } = checkoutExtensionData;

    const debouncedSetExtensionData = useCallback(
        debounce((namespace, key, value) => {
            setExtensionData(namespace, key, value);
        }, 1000),
        [setExtensionData]
    );

    const terminalValidationErrorId = 'terminal';

    const { setValidationErrors, clearValidationError } = useDispatch(
        'wc/store/validation'
    );

    const validationError = useSelect((select) => {
        const store = select('wc/store/validation');

        return store.getValidationError(terminalValidationErrorId);
    });

    const [
        selectedTerminal,
        setSelectedTerminal,
    ] = useState('');

    /* Handle changing the select's value */
    useEffect(() => {
        setExtensionData(
            'terminals',
            'alternateShippingInstruction',
            selectedTerminal
        );

        if ( selectedTerminal !== '' ) {
            clearValidationError(terminalValidationErrorId);
            return;
        }

        if ( selectedTerminal === '' ) {
            setValidationErrors({
                [terminalValidationErrorId]: {
                    message: txt.error_terminal,
                    hidden: false
                }
            });
        }
    }, [
        setExtensionData,
        selectedTerminal,
        setValidationErrors,
        clearValidationError,
    ]);

    return (
        <div className="terminal_select_container">
            <SelectControl
                label={txt.title_terminal}
                value={selectedTerminal}
                options={options}
                onChange={setSelectedTerminal}
            />
            {(validationError?.hidden || selectedTerminal !== '') ? null : (
                <div className="wc-block-components-validation-error terminal-error">
                    <span>{validationError?.message}</span>
                </div>
            )}
        </div>
    );
};

options.js

export const options = wcSettings["my-blocks_data"].terminals;

Solution

  • I found a solution myself.

    export const Block = ({ checkoutExtensionData, extensions }) => {
        const [terminals, setTerminals] = useState([
            {
                label: txt.select_terminal,
                value: '',
            }
        ]);
    
        const shippingRates = useSelect((select) => {
            const store = select('wc/store/cart');
            return store.getCartData().shippingRates;
        });
    
        const getTerminalsByCountry = (country) => {
            return fetch(`${wcSettings.checkoutData.extensions.myplugin.ajax_url}?action=get_terminals&country=${country}`, {
                    method: 'GET',
                    credentials: 'same-origin',
                    headers: {
                        'Content-Type': 'application/json',
                    },
                })
                .then(response => response.json())
                .catch(error => {
                    console.error('Error fetching terminals:', error);
                    return [];
                });
        };
    
        const getDestinationCountry = (shippingRates) => {
            if ( ! shippingRates.length ) {
                return 'LT';
            }
    
            let country = '';
            for ( let i = 0; i < shippingRates.length; i++ ) {
                if ( ! shippingRates[i].destination.country || shippingRates[i].destination.country.trim() == "" ) {
                    continue;
                }
                country = shippingRates[i].destination.country.trim();
            }
            
            return country;
        };
    
        useEffect(() => {
            getTerminalsByCountry(getDestinationCountry(shippingRates)).then(terminals => {
                if ( terminals.data ) {
                    setTerminals(terminals.data);
                }
            });
        }, [
            shippingRates
        ]);
    
        return (
            <div className="terminal_select_container">
                <SelectControl
                    label={txt.title_terminal}
                    value={selectedTerminal}
                    options={terminals}
                    onChange={setSelectedTerminal}
                />
                {(validationError?.hidden || selectedTerminal !== '') ? null : (
                    <div className="wc-block-components-validation-error terminal-error">
                        <span>{validationError?.message}</span>
                    </div>
                )}
            </div>
        );
    };
    <?php
    if (isset($_GET['country'])) {
      $country = esc_attr($_GET['country']);
      ...
      $terminals = array(
        array('label' => $terminal_label, 'value' => $terminal_id),
        array('label' => $terminal_label, 'value' => $terminal_id),
        array('label' => $terminal_label, 'value' => $terminal_id),
        ...
      );
      wp_send_json_success($terminals);
    } else {
      wp_send_json_error('Missing country parameter');
    }
    ?>