Dynamic checkout custom fields per persons and items in Woocommerce bookings - php

For a bookings website I'm trying to create a function which makes it possible to add an attendee list, based on the amount of persons. I've already got the code for a single booking, thanks to LoicTheAztec. That part is working fine.
I also need the same functionality for multiple bookings. How can I achieve this?
Here is the code:
//* Add a new checkout field
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 a "persons" hidden input field
foreach( WC()->cart->get_cart() as $cart_item ) {
$persons = $cart_item['booking']['_persons'][0];
}
echo '<input type="hidden" name="persons" value="' . $persons . '">';
return $fields;
}
//* Add the field to the checkout
add_filter( 'woocommerce_form_field_participant_details', 'ppp_filter_checkout_field_group', 10, 4 );
function ppp_filter_checkout_field_group( $field, $key, $args, $value ){
$op_cart_count = WC()->cart->get_cart_contents_count();
$items = WC()->cart->get_cart();
foreach($items as $item) {
$person = $item['booking']['_persons'][0];
}
if ($person > 1) {
$html = '';
$html .= "<h3>Deelnemers</h3>";
for ( $i = 1; $i < $person; $i++) {
$counter = $i + 1;
$html .= "Deelnemer ". $counter . "<br>";
$html .= woocommerce_form_field( "participant_details[$i][full_name]", array(
"type" => "text",
"return" => true,
"value" => "",
"required" => true,
"label" => __( "Naam" )
)
);
$html .= woocommerce_form_field( "participant_details[$i][email_address]", array(
"type" => "email",
"return" => true,
"value" => "",
"required" => true,
"label" => __( "Emailadres" )
)
);
}
return $html;
}
}
//* display the extra field on the checkout form
add_action( 'woocommerce_checkout_after_customer_details' ,'ppp_extra_checkout_fields' );
function ppp_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;
}
//* Validate custom checkout fields
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['persons'] ) && $_POST['persons'] > 1 ){
for ( $i = 1, $j = 2; $i < esc_attr( $_POST['persons'] ); $i++, $j++ ) {
if ( empty( $_POST['participant_details'][$i]['full_name'] ) )
$errors->add( 'participant_'.$i.'_full_name', __( "Please fill in the participant $j full name" ), 'error' );
if ( empty( $_POST['participant_details'][$i]['email_address'] ) )
$errors->add( 'participant_'.$i.'_email_address', __( "Please fill in the participant $j email address" ), 'error' );
}
}
}
//* Sanitize our custom field
function ppp_custom_process_checkout_field_participant_details( $posted ){
$clean = array();
foreach( $posted as $participant ){
$details = ppp_custom_checkout_clean_participant_details( $participant );
if( ! empty( $details ) ){
$clean[] = $details;
}
}
return $clean;
}
add_filter( 'woocommerce_process_checkout_participant_details_field', 'ppp_custom_process_checkout_field_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_text_field( $participant["email_address"] );
}
return $details;
}
//* Update_post_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" );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'ppp_custom_checkout_field_update_order_meta', 10, 2 );

You missed an additional loop (the cart items loop) … I have revisited completely your code make it work for multiple item (different items).
So the fields are now dynamically generated by items and by persons on each item:
// Conditional function: Check if at least there is more than one person in cart items
function ppp_check_persons(){
$enabled = array();
foreach( WC()->cart->get_cart() as $cart_item) {
if( isset($cart_item['booking']['_persons'][0])
&& $cart_item['booking']['_persons'][0] > 1 )
{
$enabled = true;
break;
}
}
return $enabled;
}
// Add a new checkout field
add_filter( 'woocommerce_checkout_fields', 'ppp_filter_checkout_fields' );
function ppp_filter_checkout_fields($fields){
$fields['extra_fields'] = array( 'participant' => array(
'type' => 'participant',
'required' => false,
'label' => __( 'Participant Details' )
) );
$cart_items = WC()->cart->get_cart();
$count = 1;
echo '<input type="hidden" name="items_count" value="' . sizeof($cart_items) . '">';
if( ! ppp_check_persons() ) return $fields; // Exit (not enough persons)
// Add "persons" hidden input fields for each cart_item
foreach( WC()->cart->get_cart() as $cart_item ) {
$persons = $cart_item['booking']['_persons'][0];
echo '<input type="hidden" name="persons_'.$count.'" value="' . $persons . '">';
$count++;
}
return $fields;
}
// Add the field to the checkout
add_filter( 'woocommerce_form_field_participant', 'ppp_filter_checkout_field_group', 10, 4 );
function ppp_filter_checkout_field_group( $field, $key, $args, $value ){
if( ! ppp_check_persons() ) return $fields; // Exit (not enough persons)
ob_start();
echo "<h3>Deelnemers</h3>";
$cart_items = WC()->cart->get_cart();
$nb_items = sizeof($cart_items);
$count = 1;
// Loop through cart items;
foreach( $cart_items as $cart_item) {
$persons = $cart_item['booking']['_persons'][0];
if ( $persons > 1 && $persons != 1 ) {
echo '<h4 style="font-size:135%;">Item'.$count.'</h4>';
for ( $i = 1, $j = 2; $i < $persons; $i++, $j++)
{
echo '<span style="margin-bottom:12px;display:inline-block;font-size:112%;">' . __("Deelnemer") . ' ' . $j . '</span>';
echo woocommerce_form_field( "participant[$count][$j][fname]", array(
"type" => "text",
"return" => true,
"value" => "",
"required" => true,
"label" => __( "Naam" )
) );
echo woocommerce_form_field( "participant[$count][$j][email]", array(
"type" => "email",
"return" => true,
"value" => "",
"required" => true,
"label" => __( "Emailadres" )
) );
}
echo '<br>';
}
$count++;
}
return ob_get_clean();
}
// display the extra field on the checkout form
add_action( 'woocommerce_checkout_after_customer_details' ,'ppp_extra_checkout_fields' );
function ppp_extra_checkout_fields(){
$checkout = WC()->checkout();
foreach ( $checkout->checkout_fields['extra_fields'] as $key => $field )
{
woocommerce_form_field( $key, $field, $checkout->get_value( $key ) );
}
}
// Validate custom checkout fields
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['items_count']) && ! empty($_POST['items_count']) ) ) return; // Exit
for ( $h = 1; $h <= esc_attr( $_POST['items_count'] ); $h++ )
{
if ( isset( $_POST['persons_'.$h] ) && $_POST['persons_'.$h] > 1 )
{
for ( $i = 1, $j = 2; $i < esc_attr( $_POST['persons_'.$h] ); $i++, $j++ )
{
if ( isset( $data['participant'][$h][$j]['fname'] ) && empty( $data['participant'][$h][$j]['fname'] ) )
$errors->add( 'participant_'.$h.'_'.$j.'_fname', __( "Please fill in the participant $j full name (Item $h)" ), 'error' );
if ( isset( $data['participant'][$h][$j]['email'] ) && empty( $data['participant'][$h][$j]['email'] ) )
$errors->add( 'participant_'.$h.'_'.$j.'_email', __( "Please fill in the participant $j email address (Item $h)" ), 'error' );
}
}
}
}
// Save custom fields values in the order as meta data
add_action( 'woocommerce_checkout_create_order', 'ppp_custom_fields_checkout_create_order', 20, 2 );
function ppp_custom_fields_checkout_create_order( $order, $data ){
if ( !( isset($_POST['items_count']) && ! empty($_POST['items_count']) ) ) return; // Exit
// Save number of different items in cart
$order->update_meta_data( '_items_count', esc_attr( $_POST['items_count'] ) );
for ( $h = 1; $h <= esc_attr( $_POST['items_count'] ); $h++ )
{
if ( isset( $_POST['persons_'.$h] ) && $_POST['persons_'.$h] > 1 )
{
// Save number of persons for each item in cart
$order->update_meta_data( '_item_'.$h.'_persons', esc_attr( $_POST['persons_'.$h] ) );
for ( $i = 1, $j = 2; $i < esc_attr( $_POST['persons_'.$h] ); $i++, $j++ )
{
// Save number of persons for each item in cart
if ( isset( $data['participant'][$h][$j]['fname'] ) ){
$full_name = sanitize_text_field( $data['participant'][$h][$j]['fname'] );
$order->update_meta_data( '_participant_'.$h.'_'.$j.'_fname', $full_name );
}
if ( isset( $data['participant'][$h][$j]['email'] ) ){
$email_address = sanitize_text_field( $data['participant'][$h][$j]['email'] );
$order->update_meta_data( '_participant_'.$h.'_'.$j.'_email', $email_address );
}
}
}
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
Displaying the data in orders front-end and backend and/or email notifications is just too broad for this answer that handle everything else. To display theis data in admin order edit pages, below customers details, is not a good idea and should be done in a custom metabox instead.

Related

Dynamically ask person name using woocommerce bookings.?

I am using woo commerce bookings plugin to book tours but i want to get each person type name individually on checkout page depending on number of persons dynamically. I have found post on stack overflow which did not worked for me it keep says undefined offset 0 as error on checkout page.
I tried this function already.
//* Add a new checkout field
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 a "persons" hidden input field
foreach( WC()->cart->get_cart() as $cart_item ) {
$persons = $cart_item['booking']['_persons'][0];
}
echo '<input type="hidden" name="persons" value="' . $persons . '">';
return $fields;
}
//* Add the field to the checkout
add_filter( 'woocommerce_form_field_participant_details', 'ppp_filter_checkout_field_group', 10, 4 );
function ppp_filter_checkout_field_group( $field, $key, $args, $value ){
$op_cart_count = WC()->cart->get_cart_contents_count();
$items = WC()->cart->get_cart();
foreach($items as $item) {
$person = $item['booking']['_persons'][0];
}
if ($person > 1) {
$html = '';
$html .= "<h3>Deelnemers</h3>";
for ( $i = 1; $i < $person; $i++) {
$counter = $i + 1;
$html .= "Deelnemer ". $counter . "<br>";
$html .= woocommerce_form_field( "participant_details[$i][full_name]", array(
"type" => "text",
"return" => true,
"value" => "",
"required" => true,
"label" => __( "Naam" )
)
);
$html .= woocommerce_form_field( "participant_details[$i][email_address]", array(
"type" => "email",
"return" => true,
"value" => "",
"required" => true,
"label" => __( "Emailadres" )
)
);
}
return $html;
}
}
//* display the extra field on the checkout form
add_action( 'woocommerce_checkout_after_customer_details' ,'ppp_extra_checkout_fields' );
function ppp_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;
}
//* Validate custom checkout fields
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['persons'] ) && $_POST['persons'] > 1 ){
for ( $i = 1, $j = 2; $i < esc_attr( $_POST['persons'] ); $i++, $j++ ) {
if ( empty( $_POST['participant_details'][$i]['full_name'] ) )
$errors->add( 'participant_'.$i.'_full_name', __( "Please fill in the participant $j full name" ), 'error' );
if ( empty( $_POST['participant_details'][$i]['email_address'] ) )
$errors->add( 'participant_'.$i.'_email_address', __( "Please fill in the participant $j email address" ), 'error' );
}
}
}
//* Sanitize our custom field
function ppp_custom_process_checkout_field_participant_details( $posted ){
$clean = array();
foreach( $posted as $participant ){
$details = ppp_custom_checkout_clean_participant_details( $participant );
if( ! empty( $details ) ){
$clean[] = $details;
}
}
return $clean;
}
add_filter( 'woocommerce_process_checkout_participant_details_field', 'ppp_custom_process_checkout_field_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_text_field( $participant["email_address"] );
}
return $details;
}
//* Update_post_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" );
}
}
add_action( 'woocommerce_checkout_update_order_meta', 'ppp_custom_checkout_field_update_order_meta', 10, 2 );
here is website where i am tryingto achieve this.
Website
// Add checkout custom text fields
add_action( 'woocommerce_before_order_notes', 'add_checkout_custom_text_fields', 20, 1 );
function add_checkout_custom_text_fields( $checkout) {
$index = 0;
// 1st Loop through cart items
foreach(WC()->cart->get_cart() as $cart_item){
$index++;
// 2nd Loop through each unit related to item quantity
for($i = 1; $i <= $cart_item['booking']['Adults']; $i++){
woocommerce_form_field("Adult[$index][$i]", array(
'type' =>'text',
'class'=>array('my-field-class form-row-wide'),
'label'=>__('Adult Name')." ($i)",
'placeholder'=>__('Enter adult name'),
), $checkout->get_value("Adult[$index][$i]"));
}
for($i = 1; $i <= $cart_item['booking']['Childs']; $i++){
woocommerce_form_field("Child[$index][$i]", array(
'type' =>'text',
'class'=>array('my-field-class form-row-wide'),
'label'=>__('Child Name')." ($i)",
'placeholder'=>__('Enter child name'),
), $checkout->get_value("Child[$index][$i]"));
}
}
}

Woocommerce print simple product attributes on cart and checkout, like variable attributes

I want to show attibutes from a simple product, in cart and checkout. Just like variable products does, but there is only one attibute. See image below:
Is this possible to achive with PHP?
I was thinking about something like using echo $product->get_attributes()
Add the follows code snippets to achieve your above task -
function modify_woocommerce_get_item_data( $item_data, $cart_item ) {
if( $item_data || $cart_item['data']->is_type( 'variation' ) ) return $item_data;
if ( $cart_item['data']->is_type( 'simple' ) ) {
$attributes = array_filter( $cart_item['data']->get_attributes(), 'wc_attributes_array_filter_visible' );
foreach ( $attributes as $attribute ) {
$values = array();
if ( $attribute->is_taxonomy() ) {
$attribute_taxonomy = $attribute->get_taxonomy_object();
$attribute_values = wc_get_product_terms( $cart_item['data']->get_id(), $attribute->get_name(), array( 'fields' => 'all' ) );
foreach ( $attribute_values as $attribute_value ) {
$value_name = esc_html( $attribute_value->name );
if ( $attribute_taxonomy->attribute_public ) {
$values[] = '' . $value_name . '';
} else {
$values[] = $value_name;
}
}
} else {
$values = $attribute->get_options();
foreach ( $values as &$value ) {
$value = make_clickable( esc_html( $value ) );
}
}
$item_data[] = array(
'key' => wc_attribute_label( $attribute->get_name() ),
'value' => apply_filters( 'woocommerce_attribute', wpautop( wptexturize( implode( ', ', $values ) ) ), $attribute, $values ),
);
}
}
return $item_data;
}
add_filter( 'woocommerce_get_item_data', 'modify_woocommerce_get_item_data', 99, 2 );
Codes goes to your active theme's functions.php
Yes you can. Please try adding a filter as bellow,
add_filter('woocommerce_cart_item_name', function($name, $cart_item) {
//has attributes
if ($cart_item['data']->is_type( 'simple' ) && $attributes = $cart_item['data']->get_attributes()) {
$name .= " - ";
foreach ($attributes as $att)
$name .= $att->get_name() . " : " . implode(',', $att->get_options());
}
return $name;
}, 10, 2);

Collect checked Checkboxes and save values in Woocommerce order meta

I need kind of a seat reservation extension for woocommerce. Since most of the available plugins don't fit to well or don't work at all, i decided to try it myself.
Create the field (Product Meta) to store which seats are booked
add_action( 'woocommerce_product_data_panels', 'srd_seats' );
function srd_seats() {
global $woocommerce, $post;
?><div id="srd_seating_data" class="panel woocommerce_options_panel"><?php
woocommerce_wp_checkbox(
array(
'id' => '_seat_1',
'label' => __('1', 'woocommerce' ),
)
);
woocommerce_wp_checkbox(
array(
'id' => '_seat_2',
'label' => __('2', 'woocommerce' ),
)
);
woocommerce_wp_checkbox(
array(
'id' => '_seat_3',
'label' => __('3', 'woocommerce' ),
)
);
woocommerce_wp_checkbox(
array(
'id' => '_seat_4',
'label' => __('4', 'woocommerce' ),
)
);
woocommerce_wp_checkbox(
array(
'id' => '_seat_5',
'label' => __('5', 'woocommerce' ),
)
);
Saving the data
add_action( 'woocommerce_process_product_meta', 'srd_seatings_save' );
function srd_seatings_save( $post_id ){
$woocommerce_checkbox = isset( $_POST['_seat_1'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_1', $woocommerce_checkbox );
$woocommerce_checkbox = isset( $_POST['_seat_2'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_2', $woocommerce_checkbox );
$woocommerce_checkbox = isset( $_POST['_seat_3'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_3', $woocommerce_checkbox );
$woocommerce_checkbox = isset( $_POST['_seat_4'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_4', $woocommerce_checkbox );
$woocommerce_checkbox = isset( $_POST['_seat_5'] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_5', $woocommerce_checkbox );
Output as checkboxes on checkout Page
Output all values as checkboxes: Booked seats get a special treatment and are disabled.
add_action( 'woocommerce_before_order_notes' , 'srd_seat_reservation');
function srd_seat_reservation($checkout){
// Loop through cart item quantity
foreach( WC()->cart->get_cart() as $cart_item ) {
$seats = array( '_seat_1',
'_seat_2',
'_seat_3',
'_seat_4',
'_seat_5',
);
foreach ($seats as $seat) {
$seatnumber = preg_replace('/[^0-9.]+/', '', $seat);
if ( get_post_meta( $cart_item[ 'product_id' ], $seat, true) == 'yes'){
echo '<input type="checkbox" disabled="disabled" checked="checked">'.$seatnumber.'<br>';}
else {
echo '<input type="checkbox" name="checkboxseat[]" value="'.$seatnumber.'">'.$seatnumber.'<br>';
}
}
}
}
Validate the selected fields
Customer needs to select exactly as many seats as there are items in cart.
add_action('woocommerce_checkout_process', 'srd_seating_process');
function srd_seating_process() {
$quantity = WC()->cart->get_cart_contents_count(); // Get cart quantity
$checked_arr = count($_POST['checkboxseat']); // Get Number of checked Checkboxes
if ($quantity > $checked_arr)
wc_add_notice( __( 'Bitte wählen Sie für jeden Teilnehmer einen Sitzplatz.', 'woocommerce' ), 'error' );
if ($quantity < $checked_arr)
wc_add_notice( __( 'Sie haben zu viele Sitzplätze ausgewählt.', 'woocommerce' ), 'error' );
}
My problem The next step would be to save the value ($seatnumber) of all selected checkboxes to the order meta and to update the product meta. I am struggeling to collect the selected checkboxes and save the values. Please see below my attempt of achieving it. Any help is highly appreciated! =)
add_action('woocommerce_checkout_create_order', 'srd_seating_create_order', 20, 2 );
function srd_seating_create_order( $order, $data ) {
foreach($_POST['checkboxseat'] as $check) {
$order->update_meta_data( 'Sitzplatz' , $check );
}
}
I prefer to answer in this thread than in your last related question.
You say that your code works, but there is some mistakes and oversights, so here is the complete revisited code, that will really save the data to the order and display it everywhere:
1) On product backend (settings):
// Add a custom tab to product pages settings
add_filter( 'woocommerce_product_data_tabs', 'add_custom_product_tab_settings' , 99 , 1 );
function add_custom_product_tab_settings( $product_data_tabs ) {
$product_data_tabs['seats'] = array(
'label' => __( 'Seats', 'my_text_domain' ),
'target' => 'seats_content',
);
return $product_data_tabs;
}
// The content of your custom product tab
add_action( 'woocommerce_product_data_panels', 'custom_product_tab_settings_content' );
function custom_product_tab_settings_content() {
global $post;
echo '<div id="seats_content" class="panel woocommerce_options_panel"><div class="options_group">';
$seats_number = (int) get_post_meta( $post->ID, '_seats', true );
$style1 = 'style="font-size:14px;font-weight:bold;"';
$style2 = 'style="color:#E32636;"';
$style3 = 'style="font-size:14px;font-style:italic;"';
if( ! ( $seats_number > 0 ) ) {
echo "<p $style1>" . sprintf( __('First, %s and %s (save)', 'woocommerce'),
"<span $style2>" . __('define the number of seats', 'woocommerce') . "</span>",
"<span $style2>" . __('Click on "Update"', 'woocommerce') . "</span>" ) . "</p>";
}
// Set the number of seats
woocommerce_wp_text_input( array(
'id' => '_seats',
'label' => __('Number of seats'),
'type' => 'number',
'value' => ( $seats_number > 0 ? $seats_number : 0 ),
));
echo '</div><div class="options_group">';
if( $seats_number > 0 ) {
// Loop through defined seats and display a checkbox for each
for ( $i = 1; $i <= $seats_number; $i++) {
woocommerce_wp_checkbox( array(
'id' => '_seat_'.$i,
'label' => $i,
));
}
} else {
echo "<p $style3>" . __('No defined seats number yet', 'woocommerce') . '</p>';
}
echo '</div></div>';
}
// Saving the data from your custom tab fields settings
add_action( 'woocommerce_process_product_meta', 'save_custom_product_data' );
function save_custom_product_data( $post_id ){
if( isset($_POST['_seats']) ){
// Update the seats number
update_post_meta( $post_id, '_seats', esc_attr($_POST['_seats']) );
// Loop through seats
for ( $i = 1; $i <= esc_attr($_POST['_seats']); $i++) {
$value = isset( $_POST['_seat_'.$i] ) ? 'yes' : 'no';
update_post_meta( $post_id, '_seat_'.$i, $value );
}
}
}
a) Step one: You will have to set first the number of seats for the product (when the value is zero, there is no checkboxes fields for the seats) and save.
b) Step two Now you get the number of checkboxes corresponding to the number of seats:
2) Front end - On checkout page (Display the available bookable seats fields):
add_action( 'woocommerce_before_order_notes' , 'checkout_seats_reservation');
function checkout_seats_reservation($checkout){
$item_count = 0;
echo '<h3 id="booking-seats">'.__("Book your purchased seats", "Woocommerce").'</h3>';
// Loop through cart item quantity
foreach( WC()->cart->get_cart() as $cart_item ) {
$item_count++;
$pid = $cart_item['product_id']; // The product Id
echo '<div class="item-seats item-'.$item_count.' product-'.$pid.'">
<p>Item '.$item_count.': "'. $cart_item['data']->get_name().'"
<em>(Purchased seats: <strong>'.$cart_item['quantity'].'</strong>)<em><p>
<ul style="list-style: none">';
// Get the number of seats for the current item (product)
$seats_number = get_post_meta( $pid, '_seats', true );
for ( $i = 1; $i <= $seats_number; $i++) {
if ( get_post_meta( $pid, '_seat_'.$i, true) === 'yes') {
echo '<li><input type="checkbox" disabled="disabled" checked="checked"><label>'.$i.'</label></li>';
} else {
echo '<li><input type="checkbox" name="checkboxseat_'.$pid.'[]" value="'.$i.'"><label>'.$i.'</label></li>';
}
}
echo '<ul></p></div>';
}
}
// Checkout Fields validation
add_action('woocommerce_checkout_process', 'seats_checkout_fields_validation');
function seats_checkout_fields_validation() {
$cart = WC()->cart;
$quantity = $cart->get_cart_contents_count(); // Get cart quantity
$seats_count = 0;
$unavailable = false;
// Loop through cart items
foreach( $cart->get_cart() as $cart_item ){
$pid = $cart_item['product_id']; // The product ID
if( isset($_POST['checkboxseat_'.$pid]) ){
$checkboxseat = (array) $_POST['checkboxseat_'.$pid];
// Loop through selected
foreach( $checkboxseat as $seat ) {
// Check that selected seats are still available when order is submitted
if ( get_post_meta( $pid, '_seat_'.$seat, true) === 'yes') {
$unavailable = true;
break;
}
$seats_count++;
}
}
}
if( $unavailable ) {
wc_add_notice( __( 'Error: Some selected seats are not available anymore.', 'woocommerce' ), 'error' );
} elseif ( $quantity > $seats_count ) {
wc_add_notice( __( 'Bitte wählen Sie für jeden Teilnehmer einen Sitzplatz.', 'woocommerce' ), 'error' );
} elseif ( $quantity < $seats_count ) {
wc_add_notice( __( 'Sie haben zu viele Sitzplätze ausgewählt.', 'woocommerce' ), 'error' );
}
}
On checkout page, where checkboxes appear (Here 2 items):
3) Save the submitted data to Order, Update related products and display the chosen seats everywhere:
// Update the order meta data with the chosen seats raw array data
add_action('woocommerce_checkout_create_order', 'save_seats_reservation_to_order_and_update_product', 10, 2);
function save_seats_reservation_to_order_and_update_product( $order, $data ) {
$seats_arr = array();
// Loop through order items
foreach( $order->get_items() as $item ) {
$pid = $item->get_product_id();
if( isset( $_POST['checkboxseat_'.$pid]) ) {
$seats = (array) $_POST['checkboxseat_'.$pid];
// Loop through submitted seats
foreach ( $seats as $seat ){
// Update seats data in the product
update_post_meta( $pid, '_seat_'.$seat, 'yes' );
}
// Set and format our main array with the order item chosen seats
$seats_arr[] = $seats;
}
}
// Save selected seats multi-dimentional array data
$order->update_meta_data( '_sitzplatz', $seats_arr );
}
// Save chosen seats to each order item as custom meta data and display order items chosen seats everywhere
add_action('woocommerce_checkout_create_order_line_item', 'save_order_item_seats_reservation', 10, 4 );
function save_order_item_seats_reservation( $item, $cart_item_key, $values, $order ) {
// Get the chosen seats data from the order
if( isset( $_POST['checkboxseat_'.$item->get_product_id()]) ) {
$sitzplatz = $_POST['checkboxseat_'.$item->get_product_id()];
$value = implode( ', ', $sitzplatz );
// Save selected item seats to each order item as custom meta data
$item->update_meta_data( __('Sitzplatz'), $value );
}
}
On Order received (thank you):
On email notifications:
On Admin Order edit pages:
All code goes in function.php file of your active child theme (or active theme). Tested and works.
You should need to restyle things a bit to make it nicer.
// Update the order meta with the seats chosen
add_action('woocommerce_checkout_create_order', 'srd_seat_create_order', 20, 2);
function srd_seat_create_order( $order, $data ) {
$checks = $_POST['checkboxseat'];
foreach( WC()->cart->get_cart() as $cart_item ) {
foreach ($checks as $check){
$order->update_meta_data( '_sitzplatz'.$check , $check);
update_post_meta( $cart_item[ 'product_id'], $check , 'yes' );
}
}}
UPDATE Now it works =)

Get nested custom fields meta data on admin order edit pages in Woocommerce

In woocommerce I have added custom fields on checkout page based on cart count.
// Adding Custom Fields based on Cart Count
add_action( 'woocommerce_before_checkout_billing_form', 'srd_custom_einstiegswahl');
function srd_custom_einstiegswahl($checkout){
global $woocommerce;
$items = $woocommerce->cart->get_cart();
$i = 1;
foreach($items as $item => $values) {
$_product = $values['data']->post;
$quantity = $values['quantity'];
$x = 1;
while ($x <= $quantity) {
// Add fields here
echo '<h6>Reiseteilnehmer '.$x . '</h6>';
woocommerce_form_field( 'attendee_surname_'.$x, array(
'type' => 'text',
'class' => array('checkout_vorname'),
'label' => __('Vorame'),
'placeholder' => __(''),
'required' => true
), $checkout->get_value( 'attendee_surname_'.$x ));
woocommerce_form_field( 'attendee_name_'.$x, array(
'type' => 'text',
'class' => array('checkout_nachname'),
'label' => __('Nachname'),
'placeholder' => __(''),
'required' => true
), $checkout->get_value( 'attendee_name_'.$x ));
echo '<select name=einstiegswahl'.$x .'>';
// Select field populated by Custom Product Meta
echo '<option value="" style="display:none"> Bitte wählen Sie Ihren Einstiegsort </option>';
// Loop through cart items
foreach ( WC()->cart->get_cart() as $cart_item ) {
// Get the custom field data
$einstiegsorte = get_post_meta( $cart_item[ 'product_id' ], '_einstiegsorte', true );
if( ! empty($einstiegsorte) ){
// if it's multiline we split it in an array
$select_field_items = explode( "\n", $einstiegsorte );
// If the array has more than one item
if( sizeof( $select_field_items ) > 1 ){
foreach( $select_field_items as $value )
echo '<option value="'. $value .'">' . $value . '</option>';
}
// If there is only one line
else {
// we clean it
$value = str_replace('\n', '', $einstiegsorte);
echo '<option value="'. $value .'">' . $value . '</option>';
}
}
}
echo '</select>'; $checkout->get_value( 'einstiegswahl'.$x );
$x++;
}
$i++;
}
}
// Process the checkout
add_action('woocommerce_checkout_process', 'srd_teilnehmer_fields_process');
function srd_teilnehmer_fields_process() {
global $woocommerce;
$items = $woocommerce->cart->get_cart();
$i = 1;
foreach($items as $item => $values) {
$_product = $values['data']->post;
$quantity = $values['quantity'];
$x = 1;
for($x = 1; $x <= $quantity; $x++ ) {
//while ($x <= $quantity) {
if (!$_POST['attendee_surname_'.$x] || !$_POST['attendee_name_'.$x] || !$_POST['einstiegswahl'.$x]) wc_add_notice( __( 'Bitte wählen Sie Ihren Einstiegsort', 'woocommerce' ), 'error' );
}
}
}
// Update the order meta with field value
add_action('woocommerce_checkout_update_order_meta', 'srd_teilnehmer_update_order_meta');
function srd_teilnehmer_update_order_meta( $order_id ) {
global $woocommerce;
$items = $woocommerce->cart->get_cart();
$i = 1;
foreach($items as $item => $values) {
$_product = $values['data']->post;
$quantity = $values['quantity'];
$x = 1;
for($x = 1; $x <= $quantity; $x++ ) {
if ( $_POST['attendee_name_'.$x] ) update_post_meta( $order_id, 'attendee_surname_'.$x , sanitize_text_field($_POST['attendee_name_'.$x]) );
if ($_POST['attendee_surname_'.$x]) update_post_meta( $order_id, 'attendee_name_'.$x, esc_attr($_POST['attendee_surname_'.$x]));
if ($_POST['einstiegswahl'.$x]) update_post_meta( $order_id, ' Teilnehmer Einstiegsort' .$x, esc_attr($_POST['einstiegswahl'.$x]));
}}}
All works fine and fields get validated and saved.
The problem: I am not able to get and display the values in admin Order edit pages.
Since I do not know at this point how many fields were created based on the amount ordered, I first need to get the a number of ordered items.
Here is that related code:
add_action( 'woocommerce_admin_order_data_after_billing_address', 'srd_teilnehmer_checkout_field_display_admin_order_meta', 10, 1 );
//add_action( 'woocommerce_email_order_meta', 'srd_teilnehmer_checkout_field_display_admin_order_meta', 10, 3 );
function srd_teilnehmer_checkout_field_display_admin_order_meta($order) {
$order = wc_get_order( $order_id );
$items = $item_data->get_quantity();
foreach($items as $item => $values) {
$order_quantity = $values['quantity'];
$x = 1;
while ($x <= $order_quantity) {
echo '<p><strong>'.__('Nachname'.$x).':</strong> ' . get_post_meta( $order->id, 'attendee_name_'.$x, true ) . '</p>';
}
}}
How can I get and display those custom field values in admin Order edit pages?
How to display that custom field values in email notifications?
Any help will be appreciated.
You are complicating a little bit things and there is some mistakes in your code.
It should be much better to display the attendees booking data as a custom meta box in admin shop orders edit pages (Here on the right column).
So I have completely revisited your code:
// Adding Custom Fields based on Cart Count
add_action( 'woocommerce_before_checkout_billing_form', 'srd_custom_einstiegswahl');
function srd_custom_einstiegswahl( $checkout ){
$count = 1;
// Loop through cart items
foreach( WC()->cart->get_cart() as $cart_item ) {
$options = array( '' => __("Bitte wählen Sie Ihren Einstiegsort") );
$einstiegsorte = get_post_meta( $cart_item[ 'product_id' ], '_einstiegsorte', true );
if( ! empty($einstiegsorte) ){
$option_items = explode( "\n", $einstiegsorte );
if( sizeof( $option_items ) > 1 ){
foreach( $option_items as $value )
$options[$value] = $value;
} else {
$value = str_replace('\n', '', $einstiegsorte);
$options[$value] = $value;
}
}
// Loop through cart item quantity
for($i = 1; $i <= $cart_item['quantity']; $i++ ) {
$j = $count.'_'.$i;
echo '<h6>Reiseteilnehmer '.$i . '</h6>';
woocommerce_form_field( '_teilnehmer_vorame_'.$j, array(
'type' => 'text',
'class' => array('checkout_vorname'),
'label' => __('Vorame'),
'required' => true,
), $checkout->get_value( '_teilnehmer_vorame_'.$j ));
woocommerce_form_field( '_teilnehmer_nachname_'.$j, array(
'type' => 'text',
'class' => array('checkout_nachname'),
'label' => __('Nachname'),
'required' => true,
), $checkout->get_value( '_teilnehmer_nachname_'.$j ));
woocommerce_form_field( '_teilnehmer_einstiegswahl_'.$j, array(
'type' => 'select',
'class' => array('checkout_einstiegswahl'),
'label' => __('Einstiegswahl'),
'required' => true,
'options' => $options,
), $checkout->get_value( '_teilnehmer_einstiegswahl_'.$j ));
}
$count++;
}
}
// Custom checkout fields validation
add_action('woocommerce_checkout_process', 'srd_teilnehmer_fields_process');
function srd_teilnehmer_fields_process() {
$count = 1;
// Loop through cart items
foreach( WC()->cart->get_cart() as $cart_item ) {
// Loop through cart item quantity
for($i = 1; $i <= $cart_item['quantity']; $i++ ) {
$j = $count.'_'.$i;
if (!$_POST['_teilnehmer_vorame_'.$j] || !$_POST['_teilnehmer_nachname_'.$j] || !$_POST['_teilnehmer_einstiegswahl_'.$j])
wc_add_notice( __( 'Bitte wählen Sie Ihren Einstiegsort', 'woocommerce' ), 'error' );
}
$count++;
}
}
// Update the order meta data with checkout custom fields values
add_action('woocommerce_checkout_create_order', 'srd_teilnehmer_checkout_create_order', 20, 2 );
function srd_teilnehmer_checkout_create_order( $order, $data ) {
$count = 1;
// Loop through cart item quantity
foreach( WC()->cart->get_cart() as $cart_item ) {
// Loop through item quantity
for($i = 1; $i <= $cart_item['quantity']; $i++ ) {
$j = $count.'_'.$i;
if ( isset($_POST['_teilnehmer_vorame_'.$j]) )
$order->update_meta_data( '_teilnehmer_vorame_'.$j , sanitize_text_field($_POST['_teilnehmer_vorame_'.$j]) );
if ( isset($_POST['_teilnehmer_nachname_'.$j]) )
$order->update_meta_data( '_teilnehmer_nachname_'.$j, sanitize_text_field($_POST['_teilnehmer_nachname_'.$j]) );
if ( isset($_POST['_teilnehmer_einstiegswahl_'.$j]) )
$order->update_meta_data( '_teilnehmer_einstiegswahl_'.$j, sanitize_text_field($_POST['_teilnehmer_einstiegswahl_'.$j]) );
}
$count++;
}
}
// Adding Custom metabox in admin orders edit pages (on the right column)
add_action( 'add_meta_boxes', 'add_reiseteilnehmer_metabox' );
function add_reiseteilnehmer_metabox(){
add_meta_box(
'attendees',
__('Reiseteilnehmer'),
'reiseteilnehmer_inhalt',
'shop_order',
'side', // or 'normal'
'default' // or 'high'
);
}
// Adding the content for the custom metabox
function reiseteilnehmer_inhalt() {
$order = wc_get_order(get_the_id());
$count = 1;
$key_labels = array( 'vorame', 'nachname', 'einstiegswahl' );
// Loop through order items
foreach ( $order->get_items() as $item ){
echo '<h4 style="margin:1em 0 .5em;">Order item '.$count.'</h4>';
// Loop through item quantity
for($i = 1; $i <= $item->get_quantity(); $i++ ) {
echo '<div style="background-color:#eee;padding:2px;margin-bottom:.4em;">
<h4 style="margin:.5em 0;padding:2px;">Reiseteilnehmer '.$i.'</h4>
<table style="text-align:left;margin-bottom:.7em;" cellpadding="2"><tbody>';
$style = ' style="padding:2px 0;"';
// Loop through attendee fields
foreach( $key_labels as $key ){
$value = get_post_meta( $order->get_id(), '_teilnehmer_'.$key.'_'.$count.'_'.$i, true );
echo '<tr><th>'.ucfirst($key).':</th><td>'.$value.'</td></tr>';
}
echo '</tbody></table></div>';
}
$count++;
}
}
Code goes in function.php file of your active child theme (or active theme). Tested and works.
To display that in emails and in front end "Order Received" and My account "Order view" pages, you will have to ask a new question as it is just too broad to be answered in one answer.

Add custom fields as cart item meta and order item meta in WooCommerce

This is a plugin on how to add add cart item meta & order item meta for my WooCommerce order. Initially my code below worked well for input type=text. It returns the label for value and the inputed value.
On conversion to type=checkbox the code returns label and value="on" for those that are checked.
I would like to return the only value names of checked values (ignore the values unchecked).
A refactor to help include more checkboxes options would be helpful to reduce written code.
My code:
<?php
global $woocommerce, $product, $post;
add_action( 'woocommerce_before_add_to_cart_button', 'add_fields_before_add_to_cart' );
function add_fields_before_add_to_cart( ) {
?>
<div class="simple-selects">
<div class="col-md-6">
<h3>Main meals</h3>
<p><input type="checkbox" name="mm_chicken_cutlet_bento" id="mm_chicken_cutlet_bento"><?php _e( "Chicken Cutlet Bento", "aoim"); ?></p>
<p><input type="checkbox" name="mm_roasted_pork_rib_bento" id="mm_roasted_pork_rib_bento"><?php _e( "Roasted Pork Rib Bento", "aoim"); ?></p>
</div>
</div>
<?php
}
/**
* Add data to cart item
*/
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_data', 25, 2 );
function add_cart_item_data( $cart_item_meta, $product_id ) {
if ( isset( $_POST ['mm_chicken_cutlet_bento'] ) && isset( $_POST ['mm_roasted_pork_rib_bento'] ) ) {
$custom_data = array() ;
$custom_data [ 'mm_chicken_cutlet_bento' ] = isset( $_POST ['mm_chicken_cutlet_bento'] ) ? sanitize_text_field ( $_POST ['mm_chicken_cutlet_bento'] ) : "" ;
$custom_data [ 'mm_roasted_pork_rib_bento' ] = isset( $_POST ['mm_roasted_pork_rib_bento'] ) ? sanitize_text_field ( $_POST ['mm_roasted_pork_rib_bento'] ): "" ;
$cart_item_meta ['custom_data'] = $custom_data ;
}
return $cart_item_meta;
}
/**
* Display custom data on cart and checkout page.
*/
add_filter( 'woocommerce_get_item_data', 'get_item_data' , 25, 2 );
function get_item_data ( $other_data, $cart_item ) {
if ( isset( $cart_item [ 'custom_data' ] ) ) {
$custom_data = $cart_item [ 'custom_data' ];
$other_data[] = array( 'name' => 'Chicken Cutlet Bento', 'display' => $custom_data['mm_chicken_cutlet_bento'] );
$other_data[] = array( 'name' => 'Roasted Pork Rib Bento', 'display' => $custom_data['mm_roasted_pork_rib_bento'] );
}
return $other_data;
}
/**
* Add order item meta.
*/
add_action( 'woocommerce_add_order_item_meta', 'add_order_item_meta' , 10, 2);
function add_order_item_meta ( $item_id, $values ) {
if ( isset( $values [ 'custom_data' ] ) ) {
$custom_data = $values [ 'custom_data' ];
wc_add_order_item_meta( $item_id, 'Chicken Cutlet Bento', $custom_data['mm_chicken_cutlet_bento'] );
wc_add_order_item_meta( $item_id, 'Roasted Pork Rib Bento', $custom_data['mm_roasted_pork_rib_bento'] );
}
}
?>
Update (related to comments):
Limit the functionality to only one product ID
Add all checkboxes values as a coma separated string
To get easily the label names of your checkboxes as values and to "refactor to help include more checkboxes options would be helpful to reduce written code" I have added a simple function where you will set the key/value pairs for each checkbox you want to display and process…
So I have revisited all your code:
// HERE set the array of pairs keys/values for your checkboxes
function custom_checkboxes(){
return array(
'mm_chicken_cutlet_bento' => __( "Chicken Cutlet Bento", "aoim"),
'mm_roasted_pork_rib_bento' => __( "Roasted Pork Rib Bento", "aoim"),
);
}
// Displaying the checkboxes
add_action( 'woocommerce_before_add_to_cart_button', 'add_fields_before_add_to_cart' );
function add_fields_before_add_to_cart( ) {
global $product;
if( $product->get_id() != 2 ) return; // Only for product ID "2"
?>
<div class="simple-selects">
<div class="col-md-6">
<h3><?php _e("Main meals", "aoim"); ?></h3>
<?php foreach( custom_checkboxes() as $key => $value ): ?>
<p><input type="checkbox" name="<?php echo $key; ?>" id="<?php echo $key; ?>"><?php echo ' ' . $value; ?></p>
<?php endforeach; ?>
</div>
</div>
<?php
}
// Add data to cart item
add_filter( 'woocommerce_add_cart_item_data', 'add_cart_item_data', 25, 2 );
function add_cart_item_data( $cart_item_data, $product_id ) {
if( $product_id != 2 ) return $cart_item_data; // Only for product ID "2"
// Set the data for the cart item in cart object
$data = array() ;
foreach( custom_checkboxes() as $key => $value ){
if( isset( $_POST[$key] ) )
$cart_item_data['custom_data'][$key] = $data[$key] = $value;
}
// Add the data to session and generate a unique ID
if( count($data > 0 ) ){
$cart_item_data['custom_data']['unique_key'] = md5( microtime().rand() );
WC()->session->set( 'custom_data', $data );
}
return $cart_item_data;
}
// Display custom data on cart and checkout page.
add_filter( 'woocommerce_get_item_data', 'get_item_data' , 25, 2 );
function get_item_data ( $cart_data, $cart_item ) {
if( $cart_item['product_id'] != 2 ) return $cart_data; // Only for product ID "2"
if( ! empty( $cart_item['custom_data'] ) ){
$values = array();
foreach( $cart_item['custom_data'] as $key => $value )
if( $key != 'unique_key' ){
$values[] = $value;
}
$values = implode( ', ', $values );
$cart_data[] = array(
'name' => __( "Option", "aoim"),
'display' => $values
);
}
return $cart_data;
}
// Add order item meta.
add_action( 'woocommerce_add_order_item_meta', 'add_order_item_meta' , 10, 3 );
function add_order_item_meta ( $item_id, $cart_item, $cart_item_key ) {
if ( isset( $cart_item[ 'custom_data' ] ) ) {
$values = array();
foreach( $cart_item[ 'custom_data' ] as $key => $value )
if( $key != 'unique_key' ){
$values[] = $value;
}
$values = implode( ', ', $values );
wc_add_order_item_meta( $item_id, __( "Option", "aoim"), $values );
}
}
This code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and works.
You will get something like this:
I have added "Option", as label to avoid the value repetition…
LoicTheAztec's solution mostly still works, but the action to add metadata from cart to order is deprecated now.
Instead of woocommerce_add_order_item_meta (deprecated) you can use woocommerce_checkout_create_order_line_item like so:
add_action(
'woocommerce_checkout_create_order_line_item',
function(WC_Order_Item_Product $cartItem, string $cartItemKey, array $values): void {
if (!empty($values['custom_data']) && is_array($values['custom_data'])) {
$values = [];
foreach ($values['custom_data'] as $key => $value) {
if ($key !== 'unique_key'){
$values[] = $value;
}
}
$cartItem->add_meta_data(__('Option', 'aoim'), implode(', ', $values), true);
}
},
10,
3
);

Categories