我编写了一个 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 函数。对可能出现的问题有什么见解或建议吗?
首先,当您使用 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 文件中(或插件中)。已测试并有效。
关于客户订单:
关于电子邮件通知:
管理员订单:
我已按照您的步骤操作,但是当用户未登录时,我无法获取 WC()->会话值。您知道如何为未登录的用户获取它吗?