One way or another working with WooCommerce you wind up needing custom fields and preferably Advanced Custom Fields or ACF fields. This tutorial will talk about (advanced) custom fields in WooCommerce and the many way these can be used. This might wind up being a long post and lots or resources will be used and referred to so bear with me.
Table of Contents
- Basic Single Product Page
- ACF and Custom Tabs
- ACF WooCommerce Order Form
- Divi & Advanced Custom Fields
- Advanced Custom Fields to WooCommerce Attributes
- ACF Field WooCommerce Category
- ACF Below Product Image
- Product Page & Product Table Plugin
- Product Page Admin Only Note
- More to be added based on content below
- My Account Custom Fields
- Custom Checkout Fields
Basic Single Product Page Display
What if you want to add an ACF field with your own code to the theme? Well that is just like adding any other advanced custom field type to any template really. As shown at the beginning you just need to focus on the correct product type and field type and then load the data you entered in the backend properly there.
To add an ACF field to the single product page in WooCommerce after the summary you can use a basic setup like this:
// Add ACF Info to Product display page
add_action( 'woocommerce_after_single_product_summary', "ACF_product_content", 10 );
function ACF_product_content(){
echo '<h2> ACF Content </h2>';
if (function_exists('the_field')){
echo '<p>Woohoo, the_field function exists! </p>';
the_field('test_field');
}
}
It uses the WooCommerce woocommerce_after_single_product_summary
hook to add information after the product description. And there are more hooks in WooCommerce for other locations of course.
NB The actual code to load the field in the backend is not mentioned here. This as that is quite easily done using the ACF GUI.
See https://support.advancedcustomfields.com/forums/topic/acf-with-woocommerce/
ACF & Custom Tabs
You can also use advanced custom fields to add custom tabs to WooCommerce. This is actually often needed. Many products tend to require extra information in a custom tab. Liquid Web wrote adding custom tabs with ACF. What they do is create a repeater field using a custom file added to the theme. Here is a forked snippet for the repeater field:
<?php if( function_exists('acf_add_local_field_group') ): acf_add_local_field_group(array ( 'key' => 'acf_product_options', 'title' => 'Product Options', 'fields' => array ( array ( 'key' => 'acf_product_options_tabbedcontent_label', 'label' => 'Tabbed Content', 'name' => '', 'type' => 'tab', 'instructions' => '', 'required' => 0, 'conditional_logic' => 0, 'wrapper' => array ( 'width' => '', 'class' => '', 'id' => '', ), 'placement' => 'top', 'endpoint' => 0, ), array ( 'key' => 'acf_product_options_tabbedcontent_tabs', 'label' => 'Tabs', 'name' => 'tabs', 'type' => 'repeater', 'instructions' => '', 'required' => 0, 'conditional_logic' => 0, 'wrapper' => array ( 'width' => '', 'class' => '', 'id' => '', ), 'min' => '', 'max' => '', 'layout' => 'row', 'button_label' => 'Add Tab', 'sub_fields' => array ( array ( 'key' => 'acf_product_options_tabbedcontent_tab_title', 'label' => 'Tab Title', 'name' => 'tab_title', 'type' => 'text', 'instructions' => '', 'required' => 0, 'conditional_logic' => 0, 'wrapper' => array ( 'width' => '', 'class' => '', 'id' => '', ), 'default_value' => '', 'placeholder' => '', 'prepend' => '', 'append' => '', 'maxlength' => '', 'readonly' => 0, 'disabled' => 0, ), array ( 'key' => 'acf_product_options_tabbedcontent_tab_content', 'label' => 'Tab Content', 'name' => 'tab_content', 'type' => 'wysiwyg', 'instructions' => '', 'required' => 0, 'conditional_logic' => 0, 'wrapper' => array ( 'width' => '', 'class' => '', 'id' => '', ), 'default_value' => '', 'tabs' => 'all', 'toolbar' => 'full', 'media_upload' => 1, ), ), ), ), 'location' => array ( array ( array ( 'param' => 'post_type', 'operator' => '==', 'value' => 'product', ), ), ), 'menu_order' => 0, 'position' => 'normal', 'style' => 'default', 'label_placement' => 'top', 'instruction_placement' => 'label', 'hide_on_screen' => '', )); endif;
Then they load the custom tab with another function in functions.php. This code adds the extra WooCommerce product tab and then loads the repeater field inside the tab. Here is a forked snippet for this WooCommerce product tab addition.
<?php function hwid_load_custom_tab( $tab_key, $tab_info ) { echo apply_filters( 'the_content', $tab_info['tabContent'] ); } function hwid_add_content_tabs( $tabs ) { global $post; $custom_tabs = get_field( 'tabs', $post->ID ); foreach( $custom_tabs as $index => $tab ) { $tabs['customTab-' . $index] = array( 'title' => $tab['tab_title'], 'priority' => 20 + $index, 'tabContent' => $tab['tab_content'], 'callback' => 'hwid_load_custom_tab' ); } return $tabs; } add_filter( 'woocommerce_product_tabs', 'hwid_add_content_tabs' );
NB Snippets made by AJ Morrris https://gist.github.com/ajmorris for Liquid Web
Booster also allows you to add custom tabs. See https://booster.io/features/woocommerce-custom-product-tabs/ . You can choose to add tabs for all product pages or for specific ones.
ACF WooCommerce Order Form
If you would like to display additional information about your products in checkout / at the order form you can read about at WP Major. Major focusses on getting ACF fields in the order form and they use the WooCommerce Product Table plugin. But it does explain how to create them for WooCommerce products so that helps
Remember, for this tutorial we’re mainly focused on taking that custom field data and getting it into a WooCommerce order form ..
Divi & Advanced Custom Fields
The Elegant Themes Divi theme as an ACF module these days. It does however not work with all the custom field types yet. It just works with single fields, tables and repeater fields.
However, since the end of 2018 Divi Builder has dynamic content:
Not only does Divi support that use of standard dynamic WordPress content, it also supports the use of custom field data. Whether you have created your own custom fields, or registered a new custom field with a plugin like Advanced Custom Fields, that dynamic data can now be used within the Divi Builder and connected to any module content area.
Advanced Custom Fields to WooCommerce Attributes
Sometimes you need to add a custom field to a product attribute.
A third and important way to group products is to use attributes. There are two uses of this data type that are relevant for WooCommerce: WooCommerce widgets and variable products
Jordan Smith came up with some nice code for that. Snippet forked and added below. This code you can add to your child theme or basic theme’s functions.php. It ads an ACF custom rule type, rule values and then ads it to product attributes.
<?php // Adds a custom rule type. add_filter( 'acf/location/rule_types', function( $choices ){ $choices[ __("Other",'acf') ]['wc_prod_attr'] = 'WC Product Attribute'; return $choices; } ); // Adds custom rule values. add_filter( 'acf/location/rule_values/wc_prod_attr', function( $choices ){ foreach ( wc_get_attribute_taxonomies() as $attr ) { $pa_name = wc_attribute_taxonomy_name( $attr->attribute_name ); $choices[ $pa_name ] = $attr->attribute_label; } return $choices; } ); // Matching the custom rule. add_filter( 'acf/location/rule_match/wc_prod_attr', function( $match, $rule, $options ){ if ( isset( $options['taxonomy'] ) ) { if ( '==' === $rule['operator'] ) { $match = $rule['value'] === $options['taxonomy']; } elseif ( '!=' === $rule['operator'] ) { $match = $rule['value'] !== $options['taxonomy']; } } return $match; }, 10, 3 );
ACF Field WooCommerce Category
Adding an advanced custom field to a WooCommerce category is similar in ways to adding one to an attribute:
// step 1 add a location rule type add_filter('acf/location/rule_types', 'acf_wc_product_type_rule_type'); function acf_wc_product_type_rule_type($choices) { // first add the "Product" Category if it does not exist // this will be a place to put all custom rules assocaited with woocommerce // the reason for checking to see if it exists or not first // is just in case another custom rule is added if (!isset($choices['Product'])) { $choices['Product'] = array(); } // now add the 'Category' rule to it if (!isset($choices['Product']['product_cat'])) { // product_cat is the taxonomy name for woocommerce products $choices['Product']['product_cat_term'] = 'Product Category Term'; } return $choices; } // step 2 skip custom rule operators, not needed // step 3 add custom rule values add_filter('acf/location/rule_values/product_cat_term', 'acf_wc_product_type_rule_values'); function acf_wc_product_type_rule_values($choices) { // basically we need to get an list of all product categories // and put the into an array for choices $args = array( 'taxonomy' => 'product_cat', 'hide_empty' => false ); $terms = get_terms($args); foreach ($terms as $term) { $choices[$term->term_id] = $term->name; } return $choices; } // step 4, rule match add_filter('acf/location/rule_match/product_cat_term', 'acf_wc_product_type_rule_match', 10, 3); function acf_wc_product_type_rule_match($match, $rule, $options) { if (!isset($_GET['tag_ID'])) { // tag id is not set return $match; } if ($rule['operator'] == '==') { $match = ($rule['value'] == $_GET['tag_ID']); } else { $match = !($rule['value'] == $_GET['tag_ID']); } return $match; }
NB Code course John Huebner ACF
ACF Below Product Image
To display an advanced custom field below the product image can be done with relative ease. We found a snippet at Business Bloomer by Rodolfo Melogi as a nice example:
/**
* @snippet Display Advanced Custom Fields @ Single Product - WooCommerce
* @how-to Get CustomizeWoo.com FREE
* @sourcecode https://businessbloomer.com/?p=22015
* @author Rodolfo Melogli
* @compatible WooCommerce 3.5.7
* @donate $9 https://businessbloomer.com/bloomer-armada/
*/
add_action( 'woocommerce_product_thumbnails', 'bbloomer_display_acf_field_under_images', 30 );
function
bbloomer_display_acf_field_under_images() {
echo
'<b>Trade Price:</b> '
. get_field('trade');
// Note: 'trade' is the slug of the ACF
}
It uses the woocommerce_product_thumbnails
hook to add elements below the product thumbnails.
Product Page & Product Table Plugin
To display an ACF field on a product page you can also use ACF to create the fields and the WooCommerce Product Table Plugin. You can read all about it at Barn2 . You can use this plugin to show a lot of product data on top of standard ones.
- Product image, name, price
- Short or long description
- Categories and tags
- Attributes and variations
- Star rating from reviews
- Embedded audio and video
Product Page Admin Only Note
Sometimes the end user wants to add notes in the backend for his use only. A field that is only shown in the admin area. In the admin area for a specific product. How would you go about this? Well, you do of course not have to print / display fields in the frontend so if you do not load them there you can just create them using the advanced field interface. Another way, if somehow your theme autoloads all ACF fields, is to hide them frontend with CSS.
I would use a text area or a field as ACF field type if the note is very short. That would suffice to add a note in the backend.
WC Register Form ACF Fields
To add fields to the WooCommerce Account registration form you could use https://wordpress.org/plugins/acf-woocommerce-account-fields/ . For us it did not work well though. Seems to be outdated somewhat.
You can also add custom ones using your own code. See https://stackoverflow.com/a/49054519/460885 where the awesome LoicTheAztec use the WooCommerce hook woocommerce_register_form to add fields and where he does validation as well.
And to save the form custom fields as well we use the following below as shown in SO thread:
// To save WooCommerce registration form custom fields. add_action( 'woocommerce_created_customer', 'wc_save_registration_form_fields' ); function wc_save_registration_form_fields( $customer_id ) { if ( isset($_POST['role']) ) { if( $_POST['role'] == 'reseller' ){ $user = new WP_User($customer_id); $user->set_role('reseller'); } } }
NB This is similar to https://wpvilla.in/assign-specific-role-on-registration/
WooCommerce Checkout Fields
Here is another example showing a field at the end of the WooCommerce registration form’s notes or on checkout:
/**
Add custom fields to user / checkout
*/
add_action( 'woocommerce_after_order_notes', 'my_custom_checkout_field' );
function my_custom_checkout_field( $checkout ) {
echo '<div id="bv_custom_checkout_field"><h2>Measurements</h2>'; /* Weight */ woocommerce_form_field( 'weight_customer', array( 'type' => 'text', 'class' => array('my-class form-row-wide'), 'label' => __('Your weight'), 'placeholder' => __('Your weight'), ), get_user_meta( get_current_user_id(),'weight_customer' , true ) ); echo '</div>';
}
/**
Verification
*/
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process');
function my_custom_checkout_field_process() {
// Check
if ( ! $_POST['weight_customer'] )
wc_add_notice( __( 'Do not forget weight.' ), 'error' );
}
Update field
*/
add_action( 'woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta' );
function my_custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['weight_customer'] ) ) {
update_user_meta( get_current_user_id(), 'weight_customer', sanitize_text_field( $_POST['weight_customer'], '' ));
}
}
Here woocommerce_after_order_notes and woocommerce_checkout_update_order_meta are the hooks used. So data is added after the order notes and the woocommerce checkout update order meta hook is used to store the extra fields. For other location see a good visual guide at https://businessbloomer.com/woocommerce-visual-hook-guide-checkout-page/ .
Snippet source Max @ ACF Forum
Custom Checkout Fields
If you need to make tweak to fields at /checkout you can use stuff discussed at https://docs.woocommerce.com/document/tutorial-customising-checkout-fields-using-actions-and-filters/ . To for example change the order comment placeholder you use:
// Hook in add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' ); // Our hooked in function - $fields is passed via the filter! function custom_override_checkout_fields( $fields ) { $fields['order']['order_comments']['placeholder'] = 'My new placeholder'; return $fields; }
Now if you want to add more billing fields and or add them before or after exisitng you can use something like
/** * Simple checkout field addition example. * * @param array $fields List of existing billing fields. * @return array List of modified billing fields. */ function jeroensormani_add_checkout_fields( $fields ) { $fields['billing_FIELD_ID'] = array( 'label' => __( 'FIELD LABEL' ), 'type' => 'text', 'class' => array( 'form-row-wide' ), 'priority' => 35, 'required' => true, ); return $fields; } add_filter( 'woocommerce_billing_fields', 'jeroensormani_add_checkout_fields' );
If you want to position them you need to know the priorities of the current ones:
- First name – 10
- Last name – 20
- Company name – 30
- Country – 40
- Street address – 50
- Apartment, suite, unit etc. (optional) – 60
- Town / City – 70
- State – 80
- Postcode / ZIP – 90
- Phone – 100
- Email – 110
Also, do not forget validation. Here a basic one for checking if a real number is entered. Good for a VAT number for example
** * Add custom field validation for BTW or KvK Number */ function js_custom_checkout_field_validation( $data, $errors ) { foreach ( WC()->checkout()->get_checkout_fields() as $fieldset_key => $fieldset ) { foreach ( $fieldset as $key => $field ) { if ( isset( $field['validate'] ) && in_array( 'btw-number', $field['validate'] ) ) { if ( ! empty( $data[ $key ] ) && ! preg_match( '/[a-z0-9]{10}/', $data[ $key ] ) ) { $errors->add( 'validation', 'Looks like your club number is invalid.' ); } } } } } add_action( 'woocommerce_after_checkout_validation', 'js_custom_checkout_field_validation', 10, 2 );
NB source Jeroen Sormani
NBB Checkout Manager Plugin is very useful too although paid if in need of conditionals
NBBB There is Also WooCommerce Booster https://booster.io/features/woocommerce-checkout-customization/ , but no conditionals offered.
My Account / WooCommerce Registration
Like any WooCommerce page there are multiple hooks to adjust or add things to /my-account. See https://businessbloomer.com/woocommerce-visual-hook-guide-account-pages/.
To adjust or add more fields things are more complicated like for the checkout, but this is also possible. Business Bloomer has a snippet to add first and last name for example:
/**
@snippet Add First & Last Name to My Account Register Form - WooCommerce
@how-to Get CustomizeWoo.com FREE
@sourcecode https://businessbloomer.com/?p=21974
@author Rodolfo Melogli
@credits Claudio SM Web
@compatible WC 3.5.2
@donate $9 https://businessbloomer.com/bloomer-armada/
*/
///////////////////////////////
// 1. ADD FIELDS
add_action( 'woocommerce_register_form_start', 'bbloomer_add_name_woo_account_registration' );
function bbloomer_add_name_woo_account_registration() {
?>
<p class="form-row form-row-first"> <label for="reg_billing_first_name"><?php _e( 'First name', 'woocommerce' ); ?> <span class="required">*</span></label> <input type="text" class="input-text" name="billing_first_name" id="reg_billing_first_name" value="<?php if ( ! empty( $_POST['billing_first_name'] ) ) esc_attr_e( $_POST['billing_first_name'] ); ?>" /> </p> <p class="form-row form-row-last"> <label for="reg_billing_last_name"><?php _e( 'Last name', 'woocommerce' ); ?> <span class="required">*</span></label> <input type="text" class="input-text" name="billing_last_name" id="reg_billing_last_name" value="<?php if ( ! empty( $_POST['billing_last_name'] ) ) esc_attr_e( $_POST['billing_last_name'] ); ?>" /> </p> <div class="clear"></div> <?php
}
///////////////////////////////
// 2. VALIDATE FIELDS
add_filter( 'woocommerce_registration_errors', 'bbloomer_validate_name_fields', 10, 3 );
function bbloomer_validate_name_fields( $errors, $username, $email ) {
if ( isset( $_POST['billing_first_name'] ) && empty( $_POST['billing_first_name'] ) ) {
$errors->add( 'billing_first_name_error', ( 'Error: First name is required!', 'woocommerce' ) );
}
if ( isset( $_POST['billing_last_name'] ) && empty( $_POST['billing_last_name'] ) ) {
$errors->add( 'billing_last_name_error', ( 'Error: Last name is required!.', 'woocommerce' ) );
}
return $errors;
}
///////////////////////////////
// 3. SAVE FIELDS
add_action( 'woocommerce_created_customer', 'bbloomer_save_name_fields' );
function bbloomer_save_name_fields( $customer_id ) {
if ( isset( $_POST['billing_first_name'] ) ) {
update_user_meta( $customer_id, 'billing_first_name', sanitize_text_field( $_POST['billing_first_name'] ) );
update_user_meta( $customer_id, 'first_name', sanitize_text_field($_POST['billing_first_name']) );
}
if ( isset( $_POST['billing_last_name'] ) ) {
update_user_meta( $customer_id, 'billing_last_name', sanitize_text_field( $_POST['billing_last_name'] ) );
update_user_meta( $customer_id, 'last_name', sanitize_text_field($_POST['billing_last_name']) );
}
}
NB Also see https://github.com/woocommerce/woocommerce/issues/7667 and https://www.cloudways.com/blog/add-woocommerce-registration-form-fields/
To work with conditionals things get more complicated and of course radio buttons or select boxes are also a bit tougher still. You could use jQuery like this for example showing a field when radio button toggled:
$('.customer-type-radio input[type="radio"]').on('click', function () { $('.company-field').slideToggle(); });
For code above by Business Bloomer you would need to focus on other html fields of course, but often after making choices like checkboxes or radio buttons you would like to toggle other fields.
WC Booster has some basic options including adding a user role select box https://booster.io/features/woocommerce-my-account/ . Does not seem to offer custom fields though.
There is also Yithemes Yith Woocommerce Customiz My Account Page . Does cost another €54.99. But that is really for the my-account overview for logged in users. For the WooCommerce Registration Form https://woocommerce.com/products/custom-user-registration-fields-for-woocommerce/ is recommended. It costs $49 USD.
Shop Page ACF
If you would like to add a new ACF location to do stuff on the WooCommerce Shop Page you can use
add_filter( 'acf/location/rule_values/page_type', function ( $choices ) { $choices['woo_shop_page'] = 'WooCommerce Shop Page'; return $choices; }); add_filter( 'acf/location/rule_match/page_type', function ( $match, $rule, $options ) { if ( $rule['value'] == 'woo_shop_page' && isset( $options['post_id'] ) ){ if ( $rule['operator'] == '==' ){ $match = ( $options['post_id'] == wc_get_page_id( 'shop' ) ); } if ( $rule['operator'] == '!=' ){ $match = ( $options['post_id'] != wc_get_page_id( 'shop' ) ); } } return $match; }, 10, 3 );
Here is a solution and easiest way of creating a field in register form using ACF:
https://krazyplugins.com/product/acf-for-woocommerce/
Thanks