Nested loops based on WooCommerce cart items and quantity

57 Views Asked by At

I use a for loop, which takes attendee details for an event. Currently, it generates the statements based on the total number of items in the cart, displaying in checkout some group of fields based on total item quantity.

I'd like to update it to a nested loop: for example, if the customer puts two for event (variable product) for Day 1 (product variation) in their basket, and one ticket for Day 2 (second variation), they would see:

Day 1 Fields for Attendee 1 Fields for Attendee 2

Day 2 Fields for Attendee 1

Here is my code:

//Custom WooCommerce Checkout Fields based on Quantity
add_action( 'woocommerce_before_order_notes', 'person_details' );

function person_details($checkout) {
    global $woocommerce;

    $count = $woocommerce->cart->cart_contents_count;
     
    for($k=1; $k<= $count; $k++) {
        $i++;
        print ('<h3>Please enter details of attendee '.$i.'</h3>');

        woocommerce_form_field( 'school_year'.$i, array(
            'type'            => 'radio',
            'required'        => true,
            'class'           => array('my-field-class form-row-wide'),
            'label'         => __('School Year'),
            'options'         => array(
                'Reception' => 'Reception',
                'Year 1'    => 'Year 1',
                'Year 2'    => 'Year 2',
                'Year 3'    => 'Year 3',
                'Year 4'    => 'Year 4',
                'Year 5'    => 'Year 5',
                'Year 6'    => 'Year 6',
                'Year 7'    => 'Year 7',
            ),
        ), $checkout->get_value( 'school_year'.$i ));   

        woocommerce_form_field( 'cstm_full_name'.$i, array(
            'type'          => 'text',
            'required'        => true,
            'class'         => array('my-field-class form-row-wide'),
            'label'         => __('Child\s Full name'),
            'placeholder'   => __('Enter full name'),
        ), $checkout->get_value( 'cstm_full_name'.$i ));

        echo '<div class="clear"></div>';

        woocommerce_form_field( 'cstm_phone'.$i, array(
            'type'          => 'tel',
            'required'        => true,
            'class'         => array('my-field-class form-row-first'),
            'label'         => __('Emergency Contact Number'),
            'placeholder'   => __('Enter phone number'),
        ), $checkout->get_value( 'cstm_phone'.$i ));

        echo '<div class="clear"></div>';

        woocommerce_form_field( 'photo_consent'.$i, array(
            'type'            => 'radio',
            'required'        => true,
            'class'           => array('my-field-class form-row-wide'),
            'label'         => __('Photograph Consent?'),
            'options'         => array(
                'Yes'         => 'Yes',
                'No'    => 'No',
            ),
        ), $checkout->get_value( 'photo_consent'.$i ));   
    }
}

/**
 * Save value of fields
 */
 
add_action('woocommerce_checkout_update_order_meta', 'customise_checkout_field_update_order_meta');
 
function customise_checkout_field_update_order_meta($order_id) {
    global $woocommerce;

    $count = $woocommerce->cart->cart_contents_count;

    for($k=1; $k<= $count; $k++) {
        $i++;
        if (!empty($_POST['school_year'.$i])) {
            update_post_meta($order_id, 'School Year of Attendee'.$i, sanitize_text_field($_POST['school_year'.$i]));
        }
        if (!empty($_POST['cstm_full_name'.$i])) {
            update_post_meta($order_id, 'Name of Attendee' .$i, sanitize_text_field($_POST['cstm_full_name'.$i]));
        }
        if (!empty($_POST['cstm_phone'.$i])) {
            update_post_meta($order_id, 'Emergency Contact' .$i, sanitize_text_field($_POST['cstm_phone'.$i]));
        }
        if (!empty($_POST['photo_consent'.$i])) {
            update_post_meta($order_id, 'Attendee Photo Consent' .$i, sanitize_text_field($_POST['photo_consent'.$i]));
        }
    }
}

/**
 * Add fields to order emails
 **/
add_filter('woocommerce_email_order_meta_keys', 'my_custom_checkout_field_order_meta_keys');
function my_custom_checkout_field_order_meta_keys( $keys ) {

    for($k=1; $k<= 50; $k++) {
    
        $i++;
        $keys[] = 'School Year of Attendee'.$i;
        $keys[] = 'Name of Attendee'.$i;
        $keys[] = 'Emergency Contact'.$i;
        $keys[] = 'Attendee Photo Consent'.$i;
    
    }   
    return $keys;
}

I have tried nesting another for each loop, using $woocommerce->cart->get_cart(); but I'm a bit out of my depth…

1

There are 1 best solutions below

2
LoicTheAztec On

I have enabled nested loops, revisiting your code, to get what you are expecting in your question. Here is my code:

// Field settings
function get_attendee_fields() {
    return array(
        'at_school_year'   => __('School Year of Attendee', 'woocommerce'),
        'at_full_name'     => __('Name of Attendee', 'woocommerce'),
        'at_emerg_phone'   => __('Emergency Contact', 'woocommerce'),
        'at_photo_cons'    => __('Attendee Photo Consent', 'woocommerce'),
    );
}

// Custom WooCommerce Checkout Fields based on Quantity
add_action( 'woocommerce_before_order_notes', 'generate_persons_details_fields' );
function generate_persons_details_fields($checkout) {
    $day = 0;

    // Some CCS for the radio button (optional)
    echo ('<style> .radio-inline > span > input,  .radio-inline > span > label { display:inline-block; }
        .radio-inline > span > label { margin: 0 10px 0 4px; }</style>');

    $fields = get_attendee_fields(); // load key / label pairs field settings
    $keys = array_keys($fields); // Get only the keys

        // 1st loop - Loop through cart items
    foreach ( WC()->cart->get_cart() as $cart_item ) {
        $quantity = $cart_item['quantity'];
        $day++;

        echo ('<h3>Day '.$day.'</h3>');

            // 2nd Loop - On quantity
        for ( $i=1; $i <= $quantity; $i++ ) {

            echo ('<h3>Please enter details of attendee '.$i.'</h3>');

            $index = '_'.$day.'_'.$i;

            woocommerce_form_field( $keys[0].$index, array( // Changed to "select" type
                'type'          => 'select',
                'required'      => true,
                'class'         => array('form-row-wide'),
                'label'         => __('School Year', 'woocommerce'),
                'options'       => array(
                    'Reception' => __('Reception'),
                    'Year 1'    => __('Year 1'),
                    'Year 2'    => __('Year 2'),
                    'Year 3'    => __('Year 3'),
                    'Year 4'    => __('Year 4'),
                    'Year 5'    => __('Year 5'),
                    'Year 6'    => __('Year 6'),
                    'Year 7'    => __('Year 7'),
                ),
            ), $checkout->get_value($keys[0].$index));  

            woocommerce_form_field( $keys[1].$index, array(
                'type'          => 'text',
                'required'      => true,
                'class'         => array('form-row-wide'),
                'label'         => __('Child‘s Full name', 'woocommerce'),
                'placeholder'   => __('Enter full name'),
            ), $checkout->get_value($keys[1].$index));

            echo '<div class="clear"></div>';

            woocommerce_form_field( $keys[2].$index, array(
                'type'          => 'tel',
                'required'      => true,
                'class'         => array('form-row-first'),
                'label'         => __('Emergency Contact Number', 'woocommerce'),
                'placeholder'   => __('Enter phone number'),
            ), $checkout->get_value($keys[2].$index));

            echo '<div class="clear"></div>';

            woocommerce_form_field( $keys[3].$index, array(
                'type'            => 'radio',
                'required'        => true,
                'class'           => array('radio-inline form-row-wide'),
                'label'           => __('Photograph Consent?', 'woocommerce'),
                'options'         => array(
                    'Yes'         => 'Yes',
                    'No'          => 'No',
                ),
            ), $checkout->get_value($keys[3].$index));   
        }
    }
}

// Save the data as order meta data
add_action('woocommerce_checkout_update_order_meta', 'customise_checkout_field_update_order_meta');
function customise_checkout_field_update_order_meta( $order_id ) {
    $day    = 0;
    $update = false;
    $order  = wc_get_order( $order_id );

    // 1st Loop - Order items
    foreach ( $order->get_items() as $item ) {
        $quantity = $item->get_quantity();
        $day++;

        // 2nd Loop - Item quantity
        for ( $i=1; $i <= $quantity; $i++ ) {
            $index = '_'.$day.'_'.$i;

            // 3rd Loop - Attendee fields
            foreach ( get_attendee_fields() as $key => $label ) {
                if (isset($_POST[$key.$index])) {
                    $order->update_meta_data($key.$index, esc_attr($_POST[$key.$index]));
                    $update = true;
                }
            }
        }
    }
    if ( $update ) $order->save();
}

// Add order meta fields to emails
add_filter('woocommerce_email_order_meta_fields', 'my_custom_checkout_field_order_meta_keys', 10, 3 );
function my_custom_checkout_field_order_meta_keys( $fields, $sent_to_admin, $order ) {
    $day = 0;
    
    // 1st Loop - Order items
    foreach ( $order->get_items() as $item ) {
        $quantity = $item->get_quantity();
        $day++;

        // 2nd Loop - Item quantity
        for ( $i=1; $i <= $quantity; $i++ ) {
            $index = '_'.$day.'_'.$i;

            // Display 1st the DAY number and the Attendee number (Optional)
            $fields['section'.$index] = array( 
                'label' => 'Day ' . $day,
                'value' => 'Attendee ' . $i
            );

            // 3rd Loop - Display Attendee data (4 fields) 
            foreach ( get_attendee_fields() as $key => $label ) {
                $fields[$key.$index] = array( 
                    'label' => $label,
                    'value' => $order->get_meta($key.$index)
                );
            }
        }
    } 
    return $fields;
}

Code goes in functions.php file of the active child theme (or active theme). Tested and works.