Are you sure Product Add-Ons might not be more suited to this? If not, I think what you need is a custom input type. This should get you started.
/* Add a new checkout field
*
* These is our base field group, later on, depending on product quanitiy, $key = 'passenger_title_1' for example
*
*/
function supreme_filter_checkout_fields($fields){
$fields['extra_fields'] = array(
'passenger_details' => array(
'type' => 'passenger_details',
'required' => false,
'label' => __( 'Passenger Details' )
),
);
return $fields;
}
add_filter( 'woocommerce_checkout_fields', 'supreme_filter_checkout_fields' );
What the heck is a "passenger_details" input? Well, we're going to define that because woocommerce_form_field()
has a catch all in case the type doesn't match any of WooCommerce's defaults. See 'woocommerce_form_field_' . $args['type']
Here is where we're going to list the group of fields that you want for each passenger.
function supreme_filter_checkout_field_group( $field, $key, $args, $value ){
$op_cart_count = WC()->cart->get_cart_contents_count();
$html = '';
for ( $i = 1; $i <= $op_cart_count; $i++) {
$html .= woocommerce_form_field( "passenger_details[$i][title]", array(
"type" => "select",
"return" => true,
"value" => "",
"options" => array( "mr" => __( "Mr" ), "mrs" => __( "Mrs" ), "miss" => __( "Miss" ) ),
"required" => false,
"label" => __( "Title" )
)
);
$html .= woocommerce_form_field( "passenger_details[$i][first_name]", array(
"type" => "text",
"return" => true,
"value" => "",
"required" => false,
"label" => __( "First Name" )
)
);
$html .= woocommerce_form_field( "passenger_details[$i][middle_name]", array(
"type" => "text",
"return" => true,
"value" => "",
"required" => false,
"label" => __( "Middle Name" )
)
);
$html .= woocommerce_form_field( "passenger_details[$i][last_name]", array(
"type" => "text",
"return" => true,
"value" => "",
"required" => false,
"label" => __( "Last Name" )
)
);
$html .= woocommerce_form_field( "passenger_details[$i][date_of_birth]", array(
"type" => "text",
"return" => true,
"value" => "",
"class" => array("date-of-birth form-row-wide"),
"required" => false,
"label" => __("Date of Birth"),
"placeholder" => __("Select Date"),
"options" => array("" => __("Date of Birth", "woocommerce" ))
)
);
$html .= woocommerce_form_field( "passenger_details[$i][dietary_required]", array(
"type" => "select",
"return" => true,
"value" => "",
"class" => array("dietary-required form-row-wide"),
"options" => array( "" => __( "Please Select" ), "y" => __( "Yes" ), "n" => __( "No" ) ),
"required" => false,
"label" => __( "Dietary Requirements?" )
)
);
$html .= woocommerce_form_field( "passenger_details[$i][dietary_preference]", array(
"type" => "select",
"return" => true,
"value" => "",
"class" => array("dietary-requirements form-row-wide"),
"options" => array( "v" => __( "Vegetarian" ), "GF" => __( "Gluten Free" ) ),
"required" => false,
"label" => __( "Meal Preferences" )
)
);
}
return $html;
}
add_filter( 'woocommerce_form_field_passenger_details', 'supreme_filter_checkout_field_group', 10, 4 );
// display the extra field on the checkout form
function supreme_extra_checkout_fields(){
$checkout = WC()->checkout();
foreach ( $checkout->checkout_fields['extra_fields'] as $key => $field ) :
woocommerce_form_field( $key, $field, $checkout->get_value( $key ) );
endforeach;
}
add_action( 'woocommerce_checkout_after_customer_details' ,'supreme_extra_checkout_fields' );
EDIT Handle sanitization and saving of data. Note that I changed the supreme_filter_checkout_field_group
function above and removed the ID parameter.
~~Using the id
argument we can dynamically set the input name. The resulting markup should be something like:~~
<input type="text" class="input-text " name="last_name" id="passenger_details[2]last_name" placeholder="" value="">
This was wrong. the name
parameter needs to be an array! And the name is pulled from the first parameter of woocommerce_form_field()
. The ID is irrelevant to our needs, though does need to be unique to maintain proper markup. This is the result we need and should now be getting with the above modification:
<input type="text" class="input-text " name="passenger_details[2][last_name]" id="passenger_details[2][last_name]" placeholder="" value="">
Now on submit the $_POST['passenger_data']
should be an array (passenger) of arrays (passenger details). And after my edit, it is!
Now, to process this data and sanitize the individual fields in the passenger data we create a function for sanitizing our custom field type:
/**
* Sanitize our custom field
*
*/
function supreme_custom_process_checkout_field_passenger_details( $posted ){
$clean = array();
foreach( $posted as $passenger ){
$details = supreme_custom_checkout_clean_passenger_details( $passenger );
if( ! empty( $details ) ){
$clean[] = $details;
}
}
return $clean;
}
add_filter( 'woocommerce_process_checkout_passenger_details_field', 'supreme_custom_process_checkout_field_passenger_details' );
function supreme_custom_checkout_clean_passenger_details( $passenger = array() ){
$details = array();
if( isset( $passenger["title"] ) ){
$details['title'] = sanitize_text_field( $passenger["title"] );
}
if( isset( $passenger["first_name"] ) ){
$details['first_name'] = sanitize_text_field( $passenger["first_name"] );
}
if( isset( $passenger["middle_name"] ) ){
$details['middle_name'] = sanitize_text_field( $passenger["middle_name"] );
}
if( isset( $passenger["last_name"] ) ){
$details['last_name'] = sanitize_text_field( $passenger["last_name"] );
}
if( isset( $passenger["date_of_birth"] ) ){
$details['date_of_birth'] = $date = preg_replace("([^0-9/])", "", $passenger["date_of_birth"] );
}
if( isset( $passenger["dietary_required"] ) ){
$details['dietary_required'] = $passenger["dietary_required"] == "y" ? "y": "n";
}
if( isset( $passenger["dietary_preference"] ) && isset( $passenger["dietary_required"] ) && $passenger["dietary_required"] == "y" ){
$details['dietary_preference'] = $passenger["dietary_preference"] == "GF" ? "GF": "v";
}
return $details;
}
Now that it is sanitized, updating is just a matter of saving the post meta:
/**
* update_post_meta
*
*/
function supreme_custom_checkout_field_update_order_meta( $order_id, $posted ){
if( ! empty( $posted["passenger_details"] ) ){
update_post_meta( $order_id, "_passenger_details", $posted["passenger_details"] );
} else {
delete_post_meta( $order_id, "_passenger_details" );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'supreme_custom_checkout_field_update_order_meta', 10, 2 );
Now, we definitely have all the fields saved in the _passenger_details
post meta key and we can loop through that array any time we need to display it. For example, here it is on the admin order page:
// display the extra data in the order admin panel
function kia_display_order_data_in_admin( $order ){
$passenger_details = get_post_meta( $order->id, "_passenger_details", true );
if( ! empty( $passenger_details ) ) {
$passenger_defaults = array(
"title" => "",
"first_name" => "",
"middle_name" => "",
"last_name" => "",
"date_of_birth" => "",
"dietary_required" => "",
"dietary_preference" => ""
);
?>
<div class="passenger_data">
<h4><?php _e( "Passenger Details", "your-slug" ); ?></h4>
<?php
$i = 1;
foreach( $passenger_details as $passenger ){
$passenger = wp_parse_args( $passenger, $passenger_defaults );
echo "<p><strong>" . sprintf( __( "Passenger #%s", "your-slug" ), $i ) . "</strong>" . "<br/>";
echo __( "Title", "your-slug" ) . ' : ' . $passenger["title"] . "<br/>";
echo __( "First Name", "your-slug" ) . ' : ' . $passenger["first_name"] . "<br/>";
echo __( "Middle Name", "your-slug" ) . ' : ' . $passenger["middle_name"] . "<br/>";
echo __( "Last Name", "your-slug" ) . ' : ' . $passenger["last_name"] . "<br/>";
echo __( "Date of Birth", "your-slug" ) . ' : ' . $passenger["date_of_birth"] . "<br/>";
echo __( "Dietary Required", "your-slug" ) . ' : ' . $passenger["dietary_required"] . "<br/>";
echo __( "Dietary Preference", "your-slug" ) . ' : ' . $passenger["dietary_preference"] . "<br/>";
echo "</p>";
$i++;
}
?>
</div>
<?php }
}
add_action( "woocommerce_admin_order_data_after_order_details", "kia_display_order_data_in_admin" );
That still leaves the thankyou page and emails now, but the data is there.