Dynamically ask person name using woocommerce bookings.? - php

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]"));
}
}
}

Related

Set decimal quantity step at product level

I am trying to combine the two responses from [https://stackoverflow.com/questions/62943477/set-quantity-minimum-maximum-and-step-at-product-level-in-woocommerce ] and Decimal quantity step for specific product categories in WooCommerce but I can't figure out how to edit the first lot of code so that it allows decimal quantity steps.
// Displaying quantity setting fields on admin product pages
add_action( 'woocommerce_product_options_pricing', 'wc_qty_add_product_field' );
function wc_qty_add_product_field() {
global $product_object;
$values = $product_object->get_meta('_qty_args');
woocommerce_wp_text_input( array(
'id' => 'qty_step',
'type' => 'number',
'label' => __( 'Quantity step', 'woocommerce-quantity-step' ),
'placeholder' => '',
'desc_tip' => 'true',
'description' => __( 'Optional. Set quantity step (a number greater than 0)', 'woocommerce' ),
'custom_attributes' => array( 'step' => 'any', 'min' => '1'),
'value' => isset($values['qty_step']) && $values['qty_step'] > 1 ? (int) $values['qty_step'] : 1,
) );
echo '</div>';
}
// Save quantity setting fields values
add_action( 'woocommerce_admin_process_product_object', 'wc_save_product_quantity_settings' );
function wc_save_product_quantity_settings( $product ) {
if ( isset($_POST['qty_args']) ) {
$values = $product->get_meta('_qty_args');
'qty_step' => isset($_POST['qty_step']) && $_POST['qty_step'] > 1 ? (int) wc_clean($_POST['qty_step']) : 1,
) );
} else {
$product->update_meta_data( '_qty_args', array() );
}
}
// The quantity settings in action on front end
add_filter( 'woocommerce_quantity_input_args', 'filter_wc_quantity_input_args', 99, 2 );
function filter_wc_quantity_input_args( $args, $product ) {
if ( $product->is_type('variation') ) {
$parent_product = wc_get_product( $product->get_parent_id() );
$values = $parent_product->get_meta( '_qty_args' );
} else {
$values = $product->get_meta( '_qty_args' );
}
if ( ! empty( $values ) ) {
// Step value
if ( isset( $values['qty_step'] ) && $values['qty_step'] > 1 ) {
$args['step'] = $values['qty_step'];
}
}
return $args;
}
// Ajax add to cart, set "min quantity" as quantity on shop and archives pages
add_filter( 'woocommerce_loop_add_to_cart_args', 'filter_loop_add_to_cart_quantity_arg', 10, 2 );
function filter_loop_add_to_cart_quantity_arg( $args, $product ) {
$values = $product->get_meta( '_qty_args' );
if ( ! empty( $values ) ) {
// Min value
if ( isset( $values['qty_min'] ) && $values['qty_min'] > 1 ) {
$args['quantity'] = $values['qty_min'];
}
}
return $args;
}
I've tried editing the values to a decimal unit but it's not stepping up by that value and it's not saving the value.

Add a select field on WooCommerce single product pages that allows an additional product to be added to cart

I am looking to add a selection of products to some of our single product pages.
I have added a select field that is grabbing all of the products of a specific category and adding them as options.
Based on the users selection, I would like to add the selected product to the cart along with the main product. This only if the additional product is not yet in the cart.
Here is my code attempt:
function comp_bracket_selection( $selection ) {
echo '<div><br></div>';
global $product;
$domain = 'woocommerce';
$args = array(
'category' => 'compressor-brackets',
'limit' => -1,
);
$product_array = wc_get_products( $args );
foreach ($product_array as $comp_product) {
$comp_id = $comp_product->get_id();
echo 'Product id: ' . $comp_id . ' | ';
$options[$comp_id] = $comp_product->get_name();
}
array_unshift($options, 'Choose an option');
woocommerce_form_field('compressor-options', array(
'id' => 'compressor-selection',
'type' => 'select',
'label' => __('Choose Compressor & Bracket', $domain),
'required' => false,
'options' => $options,
),''
);
echo '<p>Number of Compressor & Bracket options: ' . sizeof( $product_array ) . '</p>';
}
add_action( 'woocommerce_before_add_to_cart_button', 'comp_bracket_selection' );
function custom_add_to_cart() {
if (isset($_POST['compressor-options'])) {
$product_id = $_POST['compressor-options'];
$found = false;
if ( sizeof( WC()->cart->get_cart() ) > 0 ) {
foreach ( WC()->cart->get_cart() as $cart_item_key => $values ) {
$_product = $values['data'];
if ( $_product->id == $product_id )
$found = true;
}
if ( ! $found )
WC()->cart->add_to_cart( $product_id );
} else {
WC()->cart->add_to_cart( $product_id );
}
}
}
add_action( 'woocommerce_add_to_cart', 'custom_add_to_cart' );
But I have issues getting the selected products added to the cart. Any advice would be much appreciated.
Some notes on your code attempt/question:
The mistake you make is using array_unshift() causing all numerical array keys will be modified to start counting from zero
You use $_POST['compressor-options'] and $_POST['compressor-selection'] while they should be equal
To check whether a product is already in cart you can use WC_Cart::find_product_in_cart()
So you get:
function action_woocommerce_before_add_to_cart_button() {
global $product;
$domain = 'woocommerce';
$args = array(
'category' => 'compressor-bracket',
'limit' => -1,
);
// Retrieving products
$product_array = wc_get_products( $args );
// NOT empty
if ( ! empty( $product_array ) ) {
foreach ( $product_array as $product ) {
$product_id = $product->get_id();
echo 'Product id: ' . $product_id . ' | ';
$options[$product_id] = $product->get_name();
}
$options = array( 0 => __( 'Choose an option', $domain ) ) + $options;
// Add select field
woocommerce_form_field( 'compressor-options', array(
'id' => 'compressor-selection',
'type' => 'select',
'label' => __( 'Choose Compressor & Bracket', $domain ),
'required' => false,
'options' => $options,
),'' );
echo '<p>Number of Compressor & Bracket options: ' . sizeof( $product_array ) . '</p>';
}
}
add_action( 'woocommerce_before_add_to_cart_button', 'action_woocommerce_before_add_to_cart_button' );
function filter_woocommerce_add_to_cart( $cart_item_key, $product_id, $quantity, $variation_id, $variation, $cart_item_data ) {
if ( isset( $_POST['compressor-options'] ) ) {
// Get product ID
$the_product_id = sanitize_text_field( $_POST['compressor-options'] );
// WC Cart
if ( WC()->cart ) {
// Get cart
$cart = WC()->cart;
// If cart is NOT empty
if ( ! $cart->is_empty() ) {
// Cart id
$product_cart_id = $cart->generate_cart_id( $the_product_id );
// Find product in cart
$in_cart = $cart->find_product_in_cart( $product_cart_id );
// NOT in cart
if ( ! $in_cart ) {
$cart->add_to_cart( $the_product_id );
}
} else {
$cart->add_to_cart( $the_product_id );
}
}
}
}
add_action( 'woocommerce_add_to_cart', 'filter_woocommerce_add_to_cart', 10, 6 );
Note: if you want to exclude the current product from the select option list
Change:
$args = array(
'category' => 'compressor-bracket',
'limit' => -1,
);
To:
$args = array(
'category' => 'compressor-bracket',
'limit' => -1,
'exclude' => array( $product->get_id() ),
);

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 =)

Dynamic checkout custom fields per persons and items in Woocommerce bookings

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.

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.

Categories