javascriptphpwordpresswoocommerceform-fields

Use custom form fields outside add-to-cart form on WooCommerce single product pages


I'm modifying this code and need to place the "optionals" div in the product summary hook, if I replace the but if I replace woocommerce_before_add_to_cart_button with woocommerce_single_product_summary `the wk_add_to_cart_validation function fails for each check.

/**
 * Add a custom input field to the product page.
 */
function wk_add_text_field() { ?>
<div class="custom-field-1-wrap">
            <label for="vuoiMontare">Vuoi montare gli attacchi?</label>
            <select id="vuoiMontare" name="vuoiMontare">
                <option value="no"><?php esc_html_e( 'No', 'theme-domain' ); ?></option>
                <option value="si"><?php esc_html_e( 'Sì', 'theme-domain' ); ?></option>
            </select>

            <br>
            <div class="opzioni-attacchi">
                <label for="altezza">
                    <?php esc_html_e( 'Altezza (cm):', 'theme-domain' ); ?>
                </label>
                <input type="text" name='altezza' id='altezza' value=''>
                <br>

                <label for="peso">
                    <?php esc_html_e( 'Peso (kg):', 'theme-domain' ); ?>
                </label>
                <input type="text" name='peso' id='peso' value=''>
                <br>

                <label for="lunghezzaScarpone">
                    <?php esc_html_e( 'Lung. Scarpone (mm):', 'theme-domain' ); ?>
                </label>
                <input type="text" name='lunghezzaScarpone' id='lunghezzaScarpone' value=''>
                <br>

                <label for="eta">
                    <?php esc_html_e( 'Età:', 'webkul' ); ?>
                </label>
                <input type="text" name='eta' id='eta' value=''>
            </div>
        </div>
    <?php
}
add_action( 'woocommerce_before_add_to_cart_button', 'wk_add_text_field' );


/**
 * Validate custom input field value
 */
function wk_add_to_cart_validation( $passed, $product_id, $quantity, $variation_id = null ) {
    if ( empty( $_POST['vuoiMontare'] ) || $_POST['vuoiMontare'] === 'si' ) {
        // Check if any of the custom fields is empty
        $custom_fields = array( 'altezza', 'peso', 'lunghezzaScarpone', 'eta' );

        foreach ( $custom_fields as $field ) {
            if ( empty( $_POST[ $field ] ) ) {
                $passed = false;
                wc_add_notice( sprintf( __( '%s is a required field.', 'theme-domain' ), ucfirst( $field ) ), 'error' );
            }
        }
    }

    return $passed;
}
add_filter( 'woocommerce_add_to_cart_validation', 'wk_add_to_cart_validation', 10, 4 );

/**
 * Add custom cart item data
 */
function wk_add_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
    
    // Get form data
    $vuoiMontare = sanitize_text_field($_POST['vuoiMontare']);
    $altezza = sanitize_text_field($_POST['altezza']);
    $peso = sanitize_text_field($_POST['peso']);
    $lunghezzaScarpone = sanitize_text_field($_POST['lunghezzaScarpone']);
    $eta = sanitize_text_field($_POST['eta']);
    
    $cart_item_data['pr_field'] = array(
        'vuoiMontare' => $vuoiMontare,
        'altezza' => $altezza,
        'peso' => $peso,
        'lunghezzaScarpone' => $lunghezzaScarpone,
        'eta' => $eta,
    );
    
    return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'wk_add_cart_item_data', 10, 3 );

/**
 * Display custom item data in the cart
 */
function wk_get_item_data( $item_data, $cart_item_data ) {
    if ( isset( $cart_item_data['pr_field'] ) ) {
        $item_data[] = array(
            'key'   => __( 'Montare attacchi?', 'theme-domain' ),
            'value' => wc_clean( $cart_item_data['pr_field']['vuoiMontare'] ),
        );
        $item_data[] = array(
            'key'   => __( 'Altezza', 'theme-domain' ),
            'value' => wc_clean( $cart_item_data['pr_field']['altezza'] ) . 'cm',
        );
        $item_data[] = array(
            'key'   => __( 'Peso', 'theme-domain' ),
            'value' => wc_clean( $cart_item_data['pr_field']['peso'] ) . 'Kg',
        );
        $item_data[] = array(
            'key'   => __( 'Lunghezza scarpone', 'theme-domain' ),
            'value' => wc_clean( $cart_item_data['pr_field']['lunghezzaScarpone'] ) . 'mm',
        );
        $item_data[] = array(
            'key'   => __( 'Età', 'theme-domain' ),
            'value' => wc_clean( $cart_item_data['pr_field']['eta'] ),
        );
    }
    return $item_data;
}
add_filter( 'woocommerce_get_item_data', 'wk_get_item_data', 10, 2 );

Solution

  • For single product frontend custom form field, it requires to be inside the add-to-cart form.
    To use woocommerce_single_product_summary hook for those custom form fields, you need additional corresponding custom hidden input fields inside the add-to-cart form and some JavaScript.

    Now there are some mistakes in your code, so I have revised everything. All your field settings are now in a custom function.

    When loading the product page, only "Vuoi montare?" select field is visible. If the customer select "Si", then the other fields are displayed.

    Everything works as JavaScript populate the hidden fields with the customer inputted data.

    The complete code:

    /**
     * Custom fields data settings
     */
    function get_custom_form_fields( $screen = 'product', $fields = 'secondary' ) {
        $text_domain = 'theme-domain';
        if ( $screen === 'product' ) {
            $data0 = array('vuoi_montare' => esc_html__('Vuoi montare gli attacchi?', $text_domain) );
            $data1 = array( 
                'altezza'       => esc_html__('Altezza (cm):', $text_domain), 
                'peso'          => esc_html__('Peso (kg):', $text_domain), 
                'lung_scarpone' => esc_html__('Lung. Scarpone (mm):', $text_domain),  
                'eta'           => esc_html__('Età:', $text_domain), 
            );
        } else {
            $data0 = array('vuoi_montare' => esc_html__('Vuoi montare?', $text_domain) );
            $data1 = array( 
                'altezza'       => esc_html__('Altezza', $text_domain), 
                'peso'          => esc_html__('Peso', $text_domain), 
                'lung_scarpone' => esc_html__('Lung. Scarpone', $text_domain),  
                'eta'           => esc_html__('Età', $text_domain), 
            );
        }
    
        if ( $fields === 'all' ) {
            return array_merge($data0, $data1);
        } elseif ( $fields === 'secondary' ) {
            return $data1;
        } else {
            return $data0;
        }
    }
    
    /**
     * Display custom form fields in single product page.
    **/
    add_action( 'woocommerce_single_product_summary', 'display_custom_product_form_fields', 25 );
    function display_custom_product_form_fields() { 
        $text_domain  = 'theme-domain'; 
        $vuoi_montare = get_custom_form_fields('product', ''); 
        $label_name   = current($vuoi_montare);
        $field        = array_keys($vuoi_montare);
        $field        = current($field);
        $selector_id  = str_replace('_', '-', $field );
       
        echo '<div class="custom-field-1-wrap">
            <label for="'.$selector_id .'">'.$label_name.'</label>
            <select id="'.$selector_id .'" name="'.$field .'1">
                <option value="No">'. __('No', $text_domain).'</option>
                <option value="Si">'. __('Si', $text_domain).'</option>
            </select>
            <br>
            <div class="opzioni-attacchi" style="display:none">';
    
            foreach( get_custom_form_fields() as $field => $label_name ) {
                $selector_id = str_replace('_', '-', $field );
    
                echo '<label for="'.$selector_id.'">'.$label_name.'</label>
                <input type="text" name="'.$field.'1" id="'.$selector_id.'" value="" />
                <br>';
            }
            echo '</div>
        </div>';
    }
    
    /**
     * Add hidden input fields + jQuery script
    **/
    add_action( 'woocommerce_after_add_to_cart_button', 'add_hidden_product_form_fields', 25 );
    function add_hidden_product_form_fields() { 
        echo '<div class="custom-hidden-fields">';
    
        foreach( get_custom_form_fields('product', 'all') as $field => $label_name ) {
            echo '<input type="hidden" name="'.$field.'" value="" />';
        }
        echo '</div>';
        ?>
        <script>
        jQuery(function($){
            $('input[name=vuoi_montare]').val($('select#vuoi-montare').val());
    
            $(document.body).on('change', 'select#vuoi-montare', function(){
                if ( $(this).val() === 'Si' ) {
                    $('div.opzioni-attacchi').show();
                } else {
                    $('div.opzioni-attacchi').hide();
                }
                $('input[name=vuoi_montare]').val($(this).val());
            }).on('change', 'input#altezza', function(){
                if ( $(this).val() !== '' ) {
                    $('input[name=altezza]').val($(this).val());
                } else {
                    $('input[name=altezza]').val('');
                }
            }).on('change', 'input#peso', function(){
                if ( $(this).val() !== '' ) {
                    $('input[name=peso]').val($(this).val());
                } else {
                    $('input[name=peso]').val('');
                }
            }).on('change', 'input#lung-scarpone', function(){
                if ( $(this).val() !== '' ) {
                    $('input[name=lung_scarpone]').val($(this).val());
                } else {
                    $('input[name=lung_scarpone]').val('');
                }
            }).on('change', 'input#eta', function(){
                if ( $(this).val() !== '' ) {
                    $('input[name=eta]').val($(this).val());
                } else {
                    $('input[name=eta]').val('');
                }
            });
        });
        </script>
        <?php
    }
    
    /**
     * Custom form fields validation
    **/
    add_filter( 'woocommerce_add_to_cart_validation', 'custom_product_form_fields_validation', 10, 4 );
    function custom_product_form_fields_validation( $passed, $product_id, $quantity, $variation_id = null ) {
        if ( isset($_POST['vuoi_montare']) && $_POST['vuoi_montare'] === 'Si' ) {
            // Check if any of the custom fields is empty
            foreach( get_custom_form_fields() as $field => $label_name ) {
                if ( isset($_POST[$field]) && empty($_POST[$field]) ) {
                    $passed = false;
                    wc_add_notice( sprintf( '<strong>%s</strong> %s', __('is a required field.', 'theme-domain'), $label_name ), 'error' );
                }
            }
        }
        error_log(print_r($_POST, true));
    
        return $passed;
    }
    
    /**
     * Add custom form fields values as custom cart item data
    **/
    add_filter( 'woocommerce_add_cart_item_data', 'add_custom_cart_item_data', 10, 3 );
    function add_custom_cart_item_data( $cart_item_data, $product_id, $variation_id ) {
        if ( isset($_POST['vuoi_montare']) && $_POST['vuoi_montare'] === 'Si' ) {
            // Get form data
            $cart_item_data['custom_data']['vuoi_montare'] = esc_attr($_POST['vuoi_montare']);
    
            foreach( get_custom_form_fields() as $field => $label_name ) {
                if ( isset($_POST[$field]) && ! empty($_POST[$field]) ) {
                    $cart_item_data['custom_data'][$field] = sanitize_text_field($_POST[$field]);
                }
            }
            $cart_item_data['custom_data']['unique_key'] = md5( microtime() . rand() ); 
        }
        return $cart_item_data;
    }
    
    /**
     * Display custom cart item data
    **/
    add_filter( 'woocommerce_get_item_data', 'display_custom_cart_item_data', 10, 2 );
    function display_custom_cart_item_data( $cart_data, $cart_item ) {
        foreach( get_custom_form_fields('', 'all') as $field => $label_name ) {
            if ( isset($cart_item['custom_data'][$field]) ) {
                $cart_data[] = array(
                    'key'   => $label_name,
                    'value' => $cart_item['custom_data'][$field],
                );
            }
        }
        return $cart_data;
    }
    
    /**
     * display and save custom cart item data as order item metadata
    **/
    add_action( 'woocommerce_checkout_create_order_line_item', 'save_order_item_custom_meta_data', 10, 4 );
    function save_order_item_custom_meta_data( $item, $cart_item_key, $values, $order ) {
        foreach( get_custom_form_fields('', 'all') as $field => $label_name ) {
            if ( isset($cart_item['custom_data'][$field]) ) {
                $item->update_meta_data( $field, $values['custom_data'][$field] ); 
            }
        }
    }
    
    /**
     * Add readable "meta key" label name replacement
    **/
    add_filter('woocommerce_order_item_display_meta_key', 'filter_wc_order_item_display_meta_key', 10, 3 );
    function filter_wc_order_item_display_meta_key( $display_key, $meta, $item ) {
        foreach( get_custom_form_fields('', 'all') as $field => $label_name ) {
            if( $item->get_type() === 'line_item' && $meta->key === $field ) {
                $display_key = $label_name;
            }
        }
        return $display_key;
    }
    

    Code goes in functions.php file of your child theme (or in a plugin). Tested and works.