Viewed   75 times

I'm trying to re-order 2 custom checkout fields added with the help of woocommerce_checkout_init filter, just that when I apply woocommerce_checkout_fields filter to re-order fields, it doesn't recognize them and they are null.
I think it's because the filter woocommerce_checkout_init goes after woocommerce_checkout_fields.

How Can I solve this?

Here is my code:

add_action( 'woocommerce_checkout_init', 'wc_add_confirm_email_checkout', 10, 2 );
function wc_add_confirm_email_checkout( $checkout ) {
    $checkout->checkout_fields['billing']['billing_email2'] = array(
        'type'              => 'text',
        'label'             => __( 'Confirm Email Address', 'woocommerce' ),
        'required'          => true,
        'placeholder'       => _x( 'Confirm Email Address', 'placeholder', 'woocommerce' )
    );
}
add_action( 'woocommerce_checkout_init', 'wc_add_confirm_password_checkout', 10, 2 );
function wc_add_confirm_password_checkout( $checkout ) {
    //var_dump($checkout->checkout_fields);
    if ( get_option( 'woocommerce_registration_generate_password' ) == 'no' ) {
        $checkout->checkout_fields['account']['account_password2'] = array(
            'type'              => 'password',
            'label'             => __( 'Confirm password', 'woocommerce' ),
            'required'          => true,
            'placeholder'       => _x( 'Confirm Password', 'placeholder', 'woocommerce' )
        );
    }
}

add_filter('woocommerce_checkout_fields','reorder_woo_fields');
function reorder_woo_fields($fields) {
    $fields2['billing']['billing_first_name'] = $fields['billing']['billing_first_name'];
    $fields2['billing']['billing_last_name'] = $fields['billing']['billing_last_name'];
    $fields2['billing']['billingooglg_email'] = $fields['billing']['billing_email'];
    $fields2['billing']['billing_email2'] = $fields['billing']['billing_email2'];
    $fields2['billing']['account_password'] = $fields['account']['account_password'];
    $fields2['billing']['account_password2'] = $fields['account']['account_password2'];
    $fields2['billing']['billing_address_1'] = $fields['billing']['billing_address_1'];
    $fields2['billing']['billing_postcode'] = $fields['billing']['billing_postcode'];
    var_dump($fields2);
    //return $fields2;
}

 Answers

5

For WooCommerce 3+ (update):

Since WooCommerce 3.0 checkout fields have changed a little bit so is not possible to reorder fields as before.

There is a new 'priority' argument that handle fields order, for checkout fields and my account fields as well.

Here is fully functional example for WooCommerce 3+:

// REORDERING CHECKOUT BILLING FIELDS (WOOCOMMERCE 3+)
add_filter( "woocommerce_checkout_fields", "reordering_checkout_fields", 15, 1 );
function reordering_checkout_fields( $fields ) {

    ## ---- 1. REORDERING BILLING FIELDS ---- ##

    // Set the order of the fields
    $billing_order = array(
        'billing_first_name',
        'billing_last_name',
        'billing_email',
        'billing_phone',
        'billing_company',
        'billing_address_1',
        'billing_address_2',
        'billing_postcode',
        'billing_city',
        'billing_state',
        'billing_country'
    );

    $count = 0;
    $priority = 10;

    // Updating the 'priority' argument
    foreach($billing_order as $field_name){
        $count++;
        $fields['billing'][$field_name]['priority'] = $count * $priority;
    }

    ## ---- 2. CHANGING SOME CLASSES FOR BILLING FIELDS ---- ##

    $fields['billing']['billing_email']['class'] = array('form-row-first');
    $fields['billing']['billing_phone']['class'] = array('form-row-last');

    $fields['billing']['billing_postcode']['class'] = array('form-row-first');
    $fields['billing']['billing_city']['class'] = array('form-row-last');
    
    ## ---- RETURN THE BILLING FIELDS CUSTOMIZED ---- ##

    return $fields;
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.


Before WooCommerce 3

I am not completely sure, but you there are some things that you can't do like merging billing fields with account fields. If you want to do that is going to be much more complicated than what you are trying to do here. In that case you will need to rewrite/create some checkout templates…

Another thing is that billing_email and billing_phone are sharing the same line together with 'class' => 'form-row-first' and 'class' => 'form-row-last'. When not this class is define 'class' => 'form-row-wide'… So you are going to need overriding these 'class' too.

After that you dont need to use 'woocommerce_checkout_init' hook…
You can still use 'woocommerce_checkout_fields'.
Also you can merge all of them in one function this way:

/*
 * Creating, overriding and reordering custom fields.
 */
add_filter( "woocommerce_checkout_fields", "custom_override_checkout_fields", 11, 1 );
function custom_override_checkout_fields( $fields ) {

    // Creating 'billing_email2' field
    $fields['billing']['billing_email2'] = array(
        'type'          => 'text',
        'label'         => __( 'Confirm Email Address', 'woocommerce' ),
        'placeholder'   => _x( 'Confirm Email Address', 'placeholder', 'woocommerce' ),
        'required'      => true,
        'class'         => array('form-row-last'),
        'clear'         => true
    );

    // =======> I don't really know if you need this one  <========
    // it already exist (see in first reference link at bottom).

    // Creating 'account_password2' field 
    if ( get_option( 'woocommerce_registration_generate_password' ) == 'no' ) {
        $fields['account']['account_password2'] = array(
            'type'          => 'password',
            'label'         => __( 'Confirm password', 'woocommerce' ),
            'placeholder'   => _x( 'Confirm Password', 'placeholder', 'woocommerce' ),
            'required'      => true,
            'class'         => array('form-row-wide') //,
            // 'clear'         => true
        );
    }

    // Overriding existing billing_phone field 'class' property 
    $fields['billing']['billing_phone']['class'] = array('form-row-wide');


    // Reordering billing fields
    $order = array(
        "billing_first_name",
        "billing_last_name",
        "billing_email",
        "billing_email2",
        "billing_phone",
        "billing_company",
        "billing_address_1",
        "billing_address_2",
        "billing_postcode",
        "billing_country"
    );

    foreach($order as $field)
    {
        $ordered_fields[$field] = $fields["billing"][$field];
    }

    $fields["billing"] = $ordered_fields;

    return $fields;
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

As I have said before, I think that you can't merge billing fields with account fields.
As 'account_password2' already exist if you refer to official documentation (see below in first reference link), you may not need to create it. You will have to test this and to fine tune it. But this is the way to do it.


References:

  • Official: Customizing checkout fields using actions and filters
  • How to reorder billing fields in WooCommerce Checkout template?
Wednesday, September 28, 2022
1

The following code will hide all payment gateways except "Cash on delivery" (COD) when "Ship to a different address?" is checked (and the shipping checkout fields are shown):

// The jQuery Ajax request
add_action( 'wp_footer', 'checkout_billing_area_script' );
function checkout_billing_area_script() {
    // Only checkout page
    if( is_checkout() && ! is_wc_endpoint_url() ):

    // Remove "ship_different" custom WC session on load
    if( WC()->session->get('ship_different') ){
        WC()->session->__unset('ship_different');
    }
    // jQuery Ajax code below
    ?>
    <script type="text/javascript">
    jQuery( function($){
        if (typeof wc_checkout_params === 'undefined')
            return false;

        var a = '#ship-to-different-address-checkbox', b = '';

        // Ajax function
        function triggerSTDA( value ){
             $.ajax({
                type: 'POST',
                url: wc_checkout_params.ajax_url,
                data: {
                    'action': 'ship_different_address',
                    'ship_different': value,
                },
                success: function (result) {
                    $('body').trigger('update_checkout');
                    console.log(result); // For testing (to be removed)
                }
            });
        }

        $(a).on( 'change', function(){
            b = $(this).prop('checked') === true ? 'yes' : 'no';
            triggerSTDA( b );
        });
    });
    </script>
    <?php
    endif;
}

// The Wordpress Ajax PHP receiver
add_action( 'wp_ajax_ship_different_address', 'get_ajax_ship_different_address' );
add_action( 'wp_ajax_nopriv_ship_different_address', 'get_ajax_ship_different_address' );
function get_ajax_ship_different_address() {
    if ( isset($_POST['ship_different']) ){
        WC()->session->set('ship_different', esc_attr($_POST['ship_different']));
        echo $_POST['ship_different'];
    }
    die();
}

// Show/hide payment gateways
add_filter( 'woocommerce_available_payment_gateways', 'conditionally_hide_payment_gateways', 100, 1 );
function conditionally_hide_payment_gateways( $available_gateways ) {
    if( WC()->session->get('ship_different') == 'yes' ) {
        foreach( $available_gateways as $gateways_id => $gateways ){
            if( $gateways_id !== 'cod' ) {
                unset($available_gateways[$gateways_id]);
            }
        }
    }
    return $available_gateways;
}

Code goes in function.php file of your active child theme (or active theme). Tested and work.

Tuesday, August 2, 2022
3

The following function will make for Vietnam the state field as a required in woocommerce:

add_filter( 'woocommerce_get_country_locale', 'custom_country_locale', 10, 1 );
function custom_default_address_fields( $locale ) {
    $locale['VN']['state']['required'] = true;

    return $locale;
}

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

Explanations: Each country have specific "locale" fields settings in WooCommerce. We are adding/altering the default WooCommerce country locale settings that are defined on WC_Countries get_country_locale() method

Monday, October 31, 2022
 
1

Filters allow you to modify information, but you must return that information from your function.

So, in this case, you're simply missing a return $fields; in your function:

function onboarding_update_fields( $fields = array() ) {
   // check if it's set to prevent notices being thrown
   $token = ( ! empty( $_GET['token'] ) ) ? $_GET['token'] : '';

   // yoda-style to prevent accidental assignment
   if( 'testtoken' == $token ) {
       // if you are having issues, it's useful to do this below:
       var_dump( $fields );
       // remove the var_dump once you've got things working

       // if all you want to change is the value, then assign ONLY the value
       $fields['billing']['billing_first_name']['value'] = 'Joe';
       // the way you were doing it before was removing core / required parts of the array - do not do it this way.
       // $fields['billing']['billing_first_name']['value'] = array( 'value' => 'Joe');

   }
   // you must return the fields array 
   return $fields;
}

add_filter( 'woocommerce_checkout_fields', 'onboarding_update_fields' );

Update:
After seeing for some reason the above doesn't work, I sniffed some code on another plugin, and they way they do it (and it clearly works) is like so:

function onboarding_update_fields( $fields = array() ) {
   $token = ( ! empty( $_GET['token'] ) ) ? $_GET['token'] : '';

   if( 'testtoken' == $token ) {
       // Assign the value to the $_POST superglobal
       $_POST['billing_first_name'] = 'Joe';
   }

   return $fields;
}

So - to be positive that this didn't overwrite / stomp user-entered information, I might suggest considering doing it something like this (of course test to be sure it works):

function onboarding_update_fields( $fields = array() ) {
   $token = ( ! empty( $_GET['token'] ) ) ? $_GET['token'] : '';

   if( 'testtoken' == $token ) {
       // Assign the value to the $_POST superglobal ONLY if not already set
       if ( empty( $POST['billing_first_name'] ) ) {
           $_POST['billing_first_name'] = 'Joe';
       }
   }

   return $fields;
}
Saturday, August 6, 2022
 
2

Update 2

To remove "(optional)" text from checkout fields labels introduced with Woocommerce release 3.4, just as it was before, you will need to add the following code:

// PHP: Remove "(optional)" from our non required fields
add_filter( 'woocommerce_form_field' , 'remove_checkout_optional_fields_label', 10, 4 );
function remove_checkout_optional_fields_label( $field, $key, $args, $value ) {
    // Only on checkout page
    if( is_checkout() && ! is_wc_endpoint_url() ) {
        $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
        $field = str_replace( $optional, '', $field );
    }
    return $field;
}

// JQuery: Needed for checkout fields to Remove "(optional)" from our non required fields
add_filter( 'wp_footer' , 'remove_checkout_optional_fields_label_script' );
function remove_checkout_optional_fields_label_script() {
    // Only on checkout page
    if( ! ( is_checkout() && ! is_wc_endpoint_url() ) ) return;

    $optional = '&nbsp;<span class="optional">(' . esc_html__( 'optional', 'woocommerce' ) . ')</span>';
    ?>
    <script>
    jQuery(function($){
        // On "update" checkout form event
        $(document.body).on('update_checkout', function(){
            $('#billing_country_field label > .optional').remove();
            $('#billing_address_1_field label > .optional').remove();
            $('#billing_postcode_field label > .optional').remove();
            $('#billing_state_field label > .optional').remove();
            $('#shipping_country_field label > .optional').remove();
            $('#shipping_address_1_field label > .optional').remove();
            $('#shipping_postcode_field label > .optional').remove();
            $('#shipping_state_field label > .optional').remove();
        });
    });
    </script>
    <?php
}

Code goes in function.php file of your active child theme (or active theme). Tested and works in Woocommerce version 3.4+.

You could merge the included jQuery code with your existing jQuery code…

Wednesday, August 10, 2022
Only authorized users can answer the search term. Please sign in first, or register a free account.
Not the answer you're looking for? Browse other questions tagged :