如何在WooCommerce预订中实现多订单的参会者自定义结账字段?
Got it, let's fix this multi-order scenario for your booking site. The core issue with your current code is that it only handles one cart item at a time—we need to adjust everything to track participant details per booking item instead of a single global set. Here's the revised code with step-by-step explanations:
1. Update Checkout Fields & Hidden Inputs
We'll replace the single persons hidden field with inputs that map each cart item's unique key to its participant count. This lets us link participants directly to specific bookings.
add_filter( 'woocommerce_checkout_fields', 'ppp_filter_checkout_fields' ); function ppp_filter_checkout_fields($fields){ $fields['extra_fields'] = array( 'participant_details' => array( 'type' => 'participant_details', 'required' => false, 'label' => __( 'Participant Details' ) ), ); // Add hidden inputs for each cart item's participant count (uses cart item key as identifier) foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item ) { $persons = $cart_item['booking']['_persons'][0]; echo '<input type="hidden" name="cart_item_persons[' . esc_attr($cart_item_key) . ']" value="' . esc_attr($persons) . '">'; } return $fields; }
2. Generate Participant Fields Per Cart Item
Now we loop through every cart item, and for bookings with more than 1 person, we generate participant fields tied to that item's unique key. This ensures participants are linked to the correct booking.
add_filter( 'woocommerce_form_field_participant_details', 'ppp_filter_checkout_field_group', 10, 4 ); function ppp_filter_checkout_field_group( $field, $key, $args, $value ){ $html = ''; $cart_items = WC()->cart->get_cart(); foreach($cart_items as $cart_item_key => $cart_item) { $person_count = $cart_item['booking']['_persons'][0]; $product_name = $cart_item['data']->get_name(); if ($person_count > 1) { $html .= "<h3>Participants for: $product_name</h3>"; // Start at 1 because the main booker is already counted as participant 1 for ( $i = 1; $i < $person_count; $i++) { $counter = $i + 1; $html .= "<p>Participant ". $counter ."</p>"; // Nest fields under the cart item key to avoid overlap between bookings $html .= woocommerce_form_field( "participant_details[$cart_item_key][$i][full_name]", array( "type" => "text", "return" => true, "value" => "", "required" => true, "label" => __( "Full Name" ) ) ); $html .= woocommerce_form_field( "participant_details[$cart_item_key][$i][email_address]", array( "type" => "email", "return" => true, "value" => "", "required" => true, "label" => __( "Email Address" ) ) ); } } } return $html; }
3. Update Validation for Multi-Item Participants
We now validate participants per cart item, using the item key to target the correct fields. Error messages will clearly reference which booking the missing info belongs to.
add_action( 'woocommerce_after_checkout_validation', 'ppp_checkout_field_participant_details_validation', 20, 2 ); function ppp_checkout_field_participant_details_validation( $data, $errors ){ if ( isset( $_POST['cart_item_persons'] ) ){ foreach( $_POST['cart_item_persons'] as $cart_item_key => $persons ) { $persons = esc_attr($persons); if ( $persons > 1 ){ for ( $i = 1, $j = 2; $i < $persons; $i++, $j++ ) { if ( empty( $_POST['participant_details'][$cart_item_key][$i]['full_name'] ) ) { $errors->add( 'participant_'.$cart_item_key.'_'.$i.'_full_name', __( "Please fill in participant $j's full name for this booking" ), 'error' ); } if ( empty( $_POST['participant_details'][$cart_item_key][$i]['email_address'] ) ) { $errors->add( 'participant_'.$cart_item_key.'_'.$i.'_email_address', __( "Please fill in participant $j's email address for this booking" ), 'error' ); } } } } } }
4. Sanitize & Save Participant Details Per Cart Item
We sanitize the data properly and save it as order meta, structured by cart item key so you can easily link participants to specific order items later.
// Sanitize custom field data add_filter( 'woocommerce_process_checkout_participant_details_field', 'ppp_custom_process_checkout_field_participant_details' ); function ppp_custom_process_checkout_field_participant_details( $posted ){ $clean = array(); if( !empty($posted) ){ foreach( $posted as $cart_item_key => $participants ){ $clean_participants = array(); foreach( $participants as $participant ){ $details = ppp_custom_checkout_clean_participant_details( $participant ); if( ! empty( $details ) ){ $clean_participants[] = $details; } } $clean[$cart_item_key] = $clean_participants; } } return $clean; } // Helper function to sanitize individual participant details function ppp_custom_checkout_clean_participant_details( $participant = array() ){ $details = array(); if( isset( $participant["full_name"] ) ){ $details['full_name'] = sanitize_text_field( $participant["full_name"] ); } if( isset( $participant["email_address"] ) ){ $details['email_address'] = sanitize_email( $participant["email_address"] ); // Use proper email sanitization! } return $details; } // Save sanitized data to order meta add_action( 'woocommerce_checkout_update_order_meta', 'ppp_custom_checkout_field_update_order_meta', 10, 2 ); function ppp_custom_checkout_field_update_order_meta( $order_id, $posted ){ if( ! empty( $posted["participant_details"] ) ){ update_post_meta( $order_id, "_participant_details", $posted["participant_details"] ); } else { delete_post_meta( $order_id, "_participant_details" ); } }
Key Changes Explained
- Cart Item Key Tracking: We use each cart item's unique key to group participant details, so no overlap between multiple bookings.
- Per-Item Field Generation: Fields are nested under the cart item key, ensuring participants are tied to the correct booking.
- Targeted Validation: Error messages specify which booking needs missing info, making it clearer for users.
- Structured Storage: Saved meta data is organized by cart item key, making it easy to retrieve participants for individual order items later.
Bonus: Display Participants on Admin Order Pages
If you want to show participant details in the admin order dashboard, add this code:
// Display participant details in admin order page add_action( 'woocommerce_admin_order_data_after_billing_address', 'ppp_display_participant_details_admin', 10, 1 ); function ppp_display_participant_details_admin( $order ){ $participant_details = get_post_meta( $order->get_id(), "_participant_details", true ); if( !empty($participant_details) ){ echo '<h3>Participant Details</h3>'; foreach( $participant_details as $cart_item_key => $participants ){ $order_item = $order->get_item_by_key($cart_item_key); echo '<p><strong>Booking:</strong> ' . $order_item->get_name() . '</p>'; echo '<ul>'; foreach( $participants as $index => $participant ){ $counter = $index + 2; // Since we start counting from participant 2 echo '<li>Participant ' . $counter . ': ' . $participant['full_name'] . ' (' . $participant['email_address'] . ')</li>'; } echo '</ul>'; } } }
内容的提问来源于stack exchange,提问作者PPP

