WooCommerce 结帐块上带有附加选择字段问题的自定义送货方式

问题描述 投票:0回答:2

我编写了一个 WooCommerce 插件,将顺丰速运服务点添加到 Checkout Block 页面。下拉列表显示正确,但所选选项未传递到 PHP,因此订单注释不反映所选服务点。下面是我的 PHP 和 JS 代码的相关片段。我尝试记录 POST 数据,似乎所选选项没有正确传递给 PHP 函数。对可能出现的问题有什么见解或建议吗?

PHP 代码:

<?php
if (!defined('ABSPATH')) {
    exit;
}

if (in_array('woocommerce/woocommerce.php', apply_filters('active_plugins', get_option('active_plugins')))) {
    function sf_express_shipping_method_init() {
        if (!class_exists('WC_Shipping_SF_Express')) {
            class WC_Shipping_SF_Express extends WC_Shipping_Method {
                public function __construct() {
                    $this->id = 'sf_express';
                    $this->method_title = __('SF Express Service Points', 'sf_express');
                    $this->method_description = __('Allows customers to pick up parcels from SF Express Service Points.', 'sf_express');
                    $this->init();
                }

                function init() {
                    $this->init_form_fields();
                    $this->init_settings();
                    $this->enabled = $this->get_option('enabled');
                    $this->title = $this->get_option('title');
                    add_action('woocommerce_update_options_shipping_' . $this->id, array($this, 'process_admin_options'));
                }

                function init_form_fields() {
                    $this->form_fields = array(
                        'enabled' => array(
                            'title' => __('Enable/Disable', 'sf_express'),
                            'type' => 'checkbox',
                            'label' => __('Enable SF Express Service Points', 'sf_express'),
                            'default' => 'yes'
                        ),
                        'title' => array(
                            'title' => __('Title', 'sf_express'),
                            'type' => 'text',
                            'description' => __('This controls the title which the user sees during checkout.', 'sf_express'),
                            'default' => __('SF Express Service Points', 'sf_express'),
                            'desc_tip' => true,
                        ),
                        'service_points_csv' => array(
                            'title' => __('Service Points CSV', 'sf_express'),
                            'type' => 'textarea',
                            'description' => __('Paste the contents of your service points CSV file here.', 'sf_express'),
                            'default' => '',
                        ),
                    );
                }

                public function calculate_shipping($package = array()) {
                    $rate = array(
                        'id' => $this->id,
                        'label' => $this->title,
                        'cost' => '0',
                        'calc_tax' => 'per_order'
                    );
                    $this->add_rate($rate);
                }
            }
        }
    }

    add_action('woocommerce_shipping_init', 'sf_express_shipping_method_init');
    add_filter('woocommerce_shipping_methods', function($methods) {
        $methods['sf_express'] = 'WC_Shipping_SF_Express';
        return $methods;
    });

    add_action('wp_enqueue_scripts', function() {
        wp_enqueue_script('sf_express', plugins_url('/sf-express.js', __FILE__), array('jquery'), '1.0', true);
        wp_localize_script('sf_express', 'sf_express_params', array(
            'ajax_url' => admin_url('admin-ajax.php')
        ));
    });

    // AJAX Handler for fetching service points
    add_action('wp_ajax_fetch_sf_express_service_points', 'fetch_sf_express_service_points');
    add_action('wp_ajax_nopriv_fetch_sf_express_service_points', 'fetch_sf_express_service_points');

    function fetch_sf_express_service_points() {
        $options = get_option('woocommerce_sf_express_settings');
        $csv_data = $options['service_points_csv'];
        $lines = explode("\n", $csv_data);
        $html = '';
        foreach ($lines as $line) {
            $parts = explode(',', trim($line));
            if (count($parts) >= 2) {
                $html .= '<option value="' . esc_attr(trim($parts[0])) . '">' . esc_html(trim($parts[1])) . '</option>';
            }
        }
        echo $html;
        wp_die();
    }

    add_action('woocommerce_checkout_create_order', function($order, $data) {
        // Log the posted service point value
        error_log('Selected SF Express Service Point: ' . print_r($_POST['sf_express_service_point'], true));

        if (!empty($_POST['sf_express_service_point'])) {
            $service_point = sanitize_text_field($_POST['sf_express_service_point']);
            $order->update_meta_data('sf_express_service_point', $service_point);
            $note = 'SF Express Service Point: ' . $service_point;
            $order->add_order_note($note);
        }
    }, 10, 2);

    function debugging( $order_id ) {
        $order = wc_get_order( $order_id );

        // Log the POST data for debugging
        error_log('POST Data: ' . print_r($_POST, true));

        if (isset($_POST['sf_express_service_point']) && !empty($_POST['sf_express_service_point'])) {
            $service_point = sanitize_text_field($_POST['sf_express_service_point']);
            error_log('Service Point: ' . $service_point); // Debug logging
            $order->update_meta_data('sf_express_service_point', $service_point);
            $note = 'Testing: ' . $service_point;
            $order->add_order_note($note);
            $order->save();
        } else {
            $note = 'Testing: Service point not set';
            $order->add_order_note($note);
            $order->save();
        }
    }
    add_action('woocommerce_thankyou', 'debugging', 10, 1);


    add_action('woocommerce_checkout_process', function() {
        if (empty($_POST['sf_express_service_point']) && isset($_POST['shipping_method'][0]) && $_POST['shipping_method'][0] === 'sf_express') {
            wc_add_notice(__('Please select an SF Express Service Point.', 'sf_express'), 'error');
        }
    });

    add_action('woocommerce_admin_order_data_after_billing_address', function($order) {
        $service_point = $order->get_meta('sf_express_service_point');
        if ($service_point) {
            echo '<p><strong>' . __('SF Express Service Point', 'sf_express') . ':</strong> ' . esc_html($service_point) . '</p>';
        }
    });

    add_filter('woocommerce_email_order_meta_fields', function($fields, $sent_to_admin, $order) {
        $service_point = $order->get_meta('sf_express_service_point');
        if ($service_point) {
            $fields['sf_express_service_point'] = array(
                'label' => __('SF Express Service Point', 'sf_express'),
                'value' => esc_html($service_point),
            );
        }
        return $fields;
    }, 10, 3);

    add_action('woocommerce_order_details_after_order_table', function($order) {
        $service_point = $order->get_meta('sf_express_service_point');
        if ($service_point) {
            echo '<p><strong>' . __('SF Express Service Point', 'sf_express') . ':</strong> ' . esc_html($service_point) . '</p>';
        }
    });
}


JS代码:

jQuery(document).ready(function($) {
    console.log('SF Express script loaded');

    function addServicePointField() {
        console.log('Checking if service point field needs to be added');
        if ($('#sf_express_service_point').length === 0) {
            console.log('Adding service point field');
            var servicePointFieldHtml = '<div class="form-row form-row-wide">' +
                '<label for="sf_express_service_point">' +
                'SF Express Service Point' +
                '</label>' +
                '<select id="sf_express_service_point" name="sf_express_service_point" class="select">' +
                '<option value="">Select a service point</option>' +
                '</select>' +
                '</div>';

            // Append to the shipping method container
            $('.wc-block-components-shipping-rates-control__package').append(servicePointFieldHtml);
            updateServicePointField(); // Fetch and update the dropdown
        }
    }

    function removeServicePointField() {
        console.log('Removing service point field if it exists');
        $('#sf_express_service_point').parent().remove();
    }

    function updateServicePointField() {
        console.log('Fetching updated service points');
        $.ajax({
            url: sf_express_params.ajax_url,
            type: 'POST',
            data: {
                action: 'fetch_sf_express_service_points'
            },
            success: function(response) {
                console.log('Service points fetched successfully');
                $('#sf_express_service_point').html(response);
            }
        });
    }

    // Initial call with a delay to ensure all dynamic elements are rendered
    setTimeout(addServicePointField, 1000);

    // Re-add the service point field when the shipping method changes
    $(document).on('change', 'input[name="shipping_method[0]"]', function() {
        var shippingMethod = $(this).val();
        if (shippingMethod === 'sf_express') {
            addServicePointField();
        } else {
            removeServicePointField();
        }
    });

    // Ensure the field is included in the form submission
    $('form.checkout').on('checkout_place_order', function() {
        var servicePoint = $('#sf_express_service_point').val();
        console.log('Selected service point: ' + servicePoint); // Debugging
        if (servicePoint) {
            $('<input>').attr({
                type: 'hidden',
                name: 'sf_express_service_point',
                value: servicePoint
            }).appendTo('form.checkout');
        } else {
            console.log('No service point selected');
        }
    });

    // Trigger the addServicePointField function when the page loads and shipping method is already selected
    if ($('input[name="shipping_method[0]"]:checked').val() === 'sf_express') {
        addServicePointField();
    }
});


显示服务点选择器,但所选选项未与订单一起保存。我尝试记录 POST 数据,似乎所选选项没有正确传递给 PHP 函数。对可能出现的问题有什么见解或建议吗?

php jquery ajax woocommerce shipping-method
2个回答
0
投票

首先,当您使用 WooCommerce Checkout Block 时,请注意它只允许很少的自定义。

您将无法获取所选的“服务点”以将其保存为订单元数据,并且如果未选择任何值,您将无法验证该字段。

此外,您的代码中存在多个错误。


解决方案:使用

WC_Session
变量。

您可以做的是将选定的“服务点”保存在会话变量中。

由于您无法验证此自定义字段,唯一的方法是删除空选项,这样您就始终拥有一个值。

此外,您可以显示完整的选择字段,其中所有选项从开始隐藏,而不是通过 Ajax 显示选项。然后,我们显示或隐藏该字段,具体取决于所选的运输方式。

如果客户选择“顺丰速运”作为送货方式,我们会通过 Ajax 将所选的“服务点”保存在 WC_Session 变量中。

然后在提交数据时,如果选择的是“顺丰速运”,我们就可以从WC_Session变量中获取到选择的“服务点”。

请注意,以下挂钩似乎不适用于 Checkout 块:

  • woocommerce_checkout_create_order
    ,
  • woocommerce_checkout_update_order_meta
    ,
  • woocommerce_checkout_order_created
    .

但是

woocommerce_checkout_create_order_shipping_item
钩子有效,所以这就是我们使用它来保存选定的“服务点”的原因。

您的

class WC_Shipping_SF_Express
方法代码保持不变。我们改变一切。

请注意,以下代码仅适用于 WooCommerce Checkout Block

// Helper function: Get service points in a clean formatted array
function get_formatted_service_points_array() {
    $sf_express_settings = get_option('woocommerce_sf_express_settings');
    $service_points_csv  = isset($sf_express_settings['service_points_csv']) ? $sf_express_settings['service_points_csv'] : null;
    
    if ( ! $service_points_csv ) {
        return false;
    }

    $service_points = (array) explode("\n", $service_points_csv);
    
    if ( ! $service_points ) {
        return false;
    }
    $service_point_array = array(); // Initializing

    foreach( $service_points as $service_point ) {
        $option = (array) explode(',', trim($service_point));

        if ( isset($option[0], $option[1]) && !empty($option[0]) && !empty($option[1])  ) {
            $service_point_array[esc_attr($option[0])] = esc_html($option[1]);
        }
    }
    return $service_point_array;
}

// JQuery: Display Service Point field, show/hide the field and Send Ajax request
add_action('woocommerce_checkout_init', 'sf_express_checkout_init_js');
function sf_express_checkout_init_js() {
    $service_points = get_formatted_service_points_array();

    if ( ! $service_points ) return; // exit

    $chosen_point = WC()->session->get('chosen_service_point');

    $field_html = '<div class="form-row form-row-wide" id="sf_express_service_point_field" style="margin-top:12px; display:none">' .
        '<label for="sf_express_service_point">' . __('Select a Service Point') . '</label>
        <select id="sf_express_service_point" name="sf_express_service_point" class="wc-select">';

    foreach( $service_points as $option => $label ) {
        $selected    = $option === $chosen_point ? ' selected' : '';
        $field_html .= sprintf('<option value="%s" %s>%s</option>', $option, $selected, $label);
    }

    $field_html .= '</select></div>'; 

    wc_enqueue_js( "var chosenShipping;
    const shippingOpSel = '.wc-block-checkout__shipping-option input';
    
    function saveChosenServicePointViaAjax( value ) {
        const blockWhite = {message: null, overlayCSS: {background: '#fff', opacity: 0.6}};
            formSel = 'form.wc-block-checkout__form';

        $(formSel).block(blockWhite);

        $.ajax({
            url: '" . admin_url('/admin-ajax.php') . "',
            type: 'POST',
            data: {
                'action': 'chosen_sf_express_service_point',
                'service_point': value
            },
            success: function(response) {
                $(formSel).unblock();
                console.log('Chosen Service Point saved: '+response);  // To be removed (for testing)
            }
        });
    }

    // On start (lightly delayed):
    setTimeout(function(){
        // Append the service points select field (hidden)
        $('.wc-block-components-shipping-rates-control__package').append('{$field_html}');
        chosenShipping = $(shippingOpSel+':checked').val();

        // show service points if the chosen shipping method is 'sf_express'
        if( $(shippingOpSel+':checked').val() === 'sf_express' ) {
            $('#sf_express_service_point_field').show();
            saveChosenServicePointViaAjax( $('#sf_express_service_point').val() );
        }
        console.log('Chosen shipping (start): '+$(shippingOpSel+':checked').val()); // To be removed (for testing)
    }, 100);

    // On change: show or hide service points based on the chosen shipping method
    $(document.body).on('change', '.wc-block-checkout__shipping-option input', function() {
        chosenShipping = $(this).val();

        if (chosenShipping === 'sf_express') {
            $('#sf_express_service_point_field').show();
            saveChosenServicePointViaAjax( $('#sf_express_service_point').val() );
        } else {
            $('#sf_express_service_point_field').hide();
        }
        console.log('Chosen shipping (change): '+chosenShipping); // To be removed (for testing)
    });

    // On change: When choosing a service point 
    $(document.body).on('change', '#sf_express_service_point', function() {
        console.log('Chosen point (change): '+$(this).val()); // To be removed (for testing)
        saveChosenServicePointViaAjax( $(this).val() );
    });");
}

// AJAX Handler for fetching service points
add_action('wp_ajax_chosen_sf_express_service_point', 'save_chosen_service_point_in_session');
add_action('wp_ajax_nopriv_chosen_sf_express_service_point', 'save_chosen_service_point_in_session');

function save_chosen_service_point_in_session() {
    if ( isset($_POST['service_point']) ) {
        $service_point = esc_attr($_POST['service_point']);
        WC()->session->set('chosen_service_point', $service_point); // Set value in a session variable
    }
    wp_die(isset($service_point) ? $service_point : 'Error: no service point.');
}

// Save the chosen service point as order "shipping" item metadata (displayed on admin order shipping item)
add_action('woocommerce_checkout_create_order_shipping_item', 'save_chosen_service_point_as_order_shipping_meta', 10, 4);
function save_chosen_service_point_as_order_shipping_meta(  $item, $package_key, $package, $order ) {
    if ( 'sf_express' !== WC()->session->get( 'chosen_shipping_methods' )[$package_key] ) {
        return;
    }

    if ( $chosen_point = WC()->session->get('chosen_service_point') ) {
        $item->update_meta_data('Service point', $chosen_point );
    }
}

// Save the chosen service point as order meta
add_action( 'woocommerce_order_status_changed', 'save_chosen_service_point_as_order_meta', 10, 4 );
function save_chosen_service_point_as_order_meta( $order_id, $status_from, $status_to, $order ) {
    // If "Service point order metadata exists we exit
    if ( $order->get_meta('sfe_service_point') ) return; // Exit

    foreach ($order->get_items('shipping') as $item ) {
        if ( $item->get_method_id() !== 'sf_express' ) {
            continue;
        }

        if ( $service_point = $item->get_meta('Service point') ) {
            $all_points = get_formatted_service_points_array();
            $order->update_meta_data('sfe_service_point', $all_points[$service_point] );
            $order->add_order_note('SF Express Service Point: ' . $all_points[$service_point]);
            $order->save();
            break;
        }
    }
}

// Remove the session variable if exists
add_action( 'woocommerce_thankyou', 'remove_sfe_service_point_session_variable', 10, 1 );
function remove_sfe_service_point_session_variable( $order_id ) {
    if ( WC()->session->__isset('sfe_service_point') ) {
        WC()->session->__unset('sfe_service_point');
    }
}

// Display on admin orders after shipping address
add_action('woocommerce_admin_order_data_after_shipping_address', 'admin_order_sfe_service_point_display' );
function admin_order_sfe_service_point_display($order) {
    if ( $service_point = $order->get_meta('sfe_service_point') ) {
        printf( '<div><h3>%s:</h3> <span>%s</span></div>', 
            __('SF Express Service Point', 'sf_express'),
            esc_html($service_point)
        );
    }
}

// Display on customer orders (thankyou, order pay, view order)
add_action( 'woocommerce_order_details_after_order_table', 'customer_order_sfe_service_point_display' );
function customer_order_sfe_service_point_display( $order ) {
    if ( $service_point = $order->get_meta('sfe_service_point') ) {
        printf('<h2 class="woocommerce-order-service_point__title">%s</h2>
            <table class="woocommerce-table"><tbody><tr><th>%s</th></tr><tbody></table>',
        esc_html__( 'SF Express Service Point', 'woocommerce' ), $service_point );
    }
}

// Display custom fields data on email notifications
add_action( 'woocommerce_email_order_details', 'email_notifications_sfe_service_point_display', 20, 4 );
function email_notifications_sfe_service_point_display( $order, $sent_to_admin, $plain_text, $email ) {
    if ( $service_point = $order->get_meta('sfe_service_point') ) {

        echo '<style>
        .service-point table{width: 100%; font-family: \'Helvetica Neue\', Helvetica, Roboto, Arial, sans-serif;
            color: #737373; border: 1px solid #e4e4e4; margin-bottom:8px;}
        .service-point table th, .message table td{text-align: left; border-top-width: 4px;
            color: #737373; border: 1px solid #e4e4e4; padding: 12px; width:58%;}
        .service-point table td{text-align: left; border-top-width: 4px; color: #737373; border: 1px solid #e4e4e4; padding: 12px;}
        </style>';

        printf( '<div class="service-point"><h2>%s</h2>
        <table cellspacing="0" cellpadding="6"><tbody>
        <tr><td>%s</td></tr>
        </tbody></table></div><br>',  
        esc_html__( 'SF Express Service Point', 'woocommerce' ), $service_point );
    }
}

代码位于子主题的functions.php 文件中(或插件中)。已测试并有效。


关于客户订单:

enter image description here


关于电子邮件通知:

enter image description here


管理员订单:

enter image description here


0
投票

我已按照您的步骤操作,但是当用户未登录时,我无法获取 WC()->会话值。您知道如何为未登录的用户获取它吗?

© www.soinside.com 2019 - 2024. All rights reserved.