In WooCommerce checkout page, I'm adding a custom field and everything works fine if I use woocommerce_after_checkout_billing_form hook or woocommerce_before_checkout_form hook.
The problem is that I need the field to be above the Billing Details title but when I use woocommerce_checkout_before_customer_details hook everything disappears (even the sidebar payment panel), only my custom field title is visible.
My code:
// Create Custom checkout Field
add_action('woocommerce_checkout_before_customer_details', 'create_custom_field');
function create_custom_field($checkout) {
global $woocommerce;
$cart = $woocommerce->cart->get_cart();
foreach($cart as $key => $value)
{
$bespoke = $woocommerce->cart->get_item_data($value);
if (strpos($bespoke, 'yes') !== false) {
echo '<div id="customise_checkout_field"><h3>' . __('Bespoke Details') . '</h3>';
woocommerce_form_field('bespoke_field', array(
'type' => 'textarea',
'class' => array('my-field-class form-row-wide'),
'label' => __('Tell us about your idea') ,
'placeholder' => __('Please explain what you want as detailed as possible...') ,
'required' => true,),
$checkout->get_value('bespoke_field'));
echo '</div>';
}
}
}
Any thoughts? Your guidance is truly appreciated.
$checkout doesn't exist for this hook, and then it's not defined. So It makes a blank page (an error). But you can use WC()->checkout as replacement.
The WC_cart method get_item_data() is deprecated and wc_get_formatted_cart_item_data() function replace it since Woocommerce version 3.3.
Also global $woocommerce and $woocommerce->cart are now replaced with simply WC()->cart.
Try this instead:
// Add a Custom checkout Field
add_action( 'woocommerce_checkout_before_customer_details', 'add_custom_checkout_field' );
function add_custom_checkout_field() {
// Loop through cart items
foreach( WC()->cart->get_cart() as $cart_item ) {
$bespoke = wc_get_formatted_cart_item_data( $cart_item );
if ( strpos($bespoke, 'yes') !== false ) {
echo '<div id="customise_checkout_field">';
echo '<h3>' . __('Bespoke Details') . '</h3>';
woocommerce_form_field('bespoke_field', array(
'type' => 'textarea',
'class' => array('my-field-class form-row-wide'),
'label' => __( "Tell us about your idea", "woocommerce" ),
'placeholder' => __( "Please explain what you want as detailed as possible...", "woocommerce" ),
'required' => true,
), WC()->checkout->get_value('bespoke_field') );
echo '</div>';
}
}
}
Code goes in function.php file of the active child theme (or active theme). Tested and works.
Related
I am building my first woocommerce site, Im learning how to create custom fields for products. I would like to create a text field in general tab, save that field and display on the front end to customers.
Here is the code I used to display the text field in the general tab of products.
function prefix_add_text_input() {
$args = array(
'label' =>__('Serial Number', 'woocommerce'), // Text in the label in the editor.
'placeholder' => '', // Give examples or suggestions as placeholder
'class' => '',
'style' => '',
'wrapper_class' => '',
'value' => '', // if empty, retrieved from post_meta
'id' => 'serial_number', // required, will be used as meta_key
'name' => '', // name will be set automatically from id if empty
'type' => '',
'desc_tip' => 'true',
'data_type' => '',
'custom_attributes' => '', // array of attributes you want to pass
'description' => 'Enter the serial number on your rifle here'
);
woocommerce_wp_text_input( $args );
}
How do I get the field to save, and display on the front end. Ideally display in the tabs with product description?
Here below you will find the way to save your product custom field value and display it in the product description tab section:
// Add a Custom product Admin Field
add_action( 'woocommerce_product_options_general_product_data', 'add_custom_product_general_field' );
function add_custom_product_general_field() {
echo '<div class="options_group">';
woocommerce_wp_text_input( array(
'id' => '_serial_number', // required, will be used as meta_key
'label' =>__('Serial Number', 'woocommerce'), // Text in the label in the editor.
'desc_tip' => 'true',
'description' => __('Enter the serial number on your rifle here', 'woocommerce')
) );
echo '</div>';
}
// Save the field value
add_action( 'woocommerce_admin_process_product_object', 'save_custom_product_general_field' );
function save_custom_product_general_field( $product ){
if( isset($_POST['_serial_number']) )
$product->update_meta_data( '_serial_number', sanitize_text_field( $_POST['_serial_number'] ) );
}
// Display the custom field value below product description tab
add_filter( 'the_content', 'display_product_serial_number' );
function display_product_serial_number( $content ) {
// Only for single product pages
if ( is_product() ) {
global $product;
if( $value = $product->get_meta( '_serial_number' ) ) {
$content .= '<p><strong>' . __("Serial number:", "woocommerce") . '<strong> ' . $value . '<p>';
}
}
return $content;
}
Code goes in functions.php file of your active child theme (or active theme). Tested and works.
I have a checkbox which if checked opens a drop down field (requirements). If you try and send the Woocommerce order it conditionally checks if the field has content, if it doesn't it returns an error.
This all works accept when the requirements field does have information input it still treats it as not having content and returns the error.
As this is the basic way that forms should work I don't understand why I'm getting such a result. Here's the code:
/**
* Add a message field to the WC checkout
*/
add_action( 'woocommerce_before_checkout_form', 'custom_checkout_field' );
function custom_checkout_field( $checkout ) {
echo '<div id="message"><h3>' . __( '<i class="fas fa-envelope"></i> Message' ) . '</h3><p style="margin: 0 0 8px;">Would you like to leave a message?</p>';
woocommerce_form_field( 'checkbox', array(
'type' => 'checkbox',
'class' => array( 'msg-checkbox' ),
'label' => __( 'Yes' ),
), $checkout->get_value( 'checkbox' ) );
woocommerce_form_field( 'requirements', array(
'type' => 'text',
'class' => array('msg'),
'label' => __('Please input your message.'),
'placeholder' => __(''),
), $checkout->get_value( 'requirements' ));
echo '</div>';
}
/**
* Process checkout with additional custom field
*/
add_action('woocommerce_checkout_process', 'custom_checkout_field_process');
function custom_checkout_field_process() {
// Check if set, if not add an error.
if(isset($_POST['checkbox']) && ( empty($_POST['requirements'])));
wc_add_notice( __( 'Please let us know what you would like to do' ), 'error' );
}
I hope that makes sense. basically it works to a point but the validation for the input field doesn't work when it has content.
Thanks.
Update: Code improvements and added a show/hide functionality with jQuery
The only problem comes from the hook that you are using for your custom checkout fields. It output those fields outside the checkout form, so the values are never submitted and then always empty.
Instead you will use woocommerce_checkout_before_customer_details action hook for your first hooked function that will output those fields inside the checkout form this time:
// Output custom checkout fields
add_action( 'woocommerce_checkout_before_customer_details', 'custom_checkout_field_before_billing' );
function custom_checkout_field_before_billing() {
$domain = 'woocommerce';
// Hide the message text field on load
?>
<style>p#requirements_field{display:none;}</style>
<div id="message">
<h3><i class="fa fa-envelope"></i><?php _e( 'Message', 'woocommerce' ); ?></h3>
<?php
woocommerce_form_field( 'checkbox_msg', array(
'type' => 'checkbox',
'class' => array( 'msg-checkbox' ),
'label' => __( 'Would you like to leave a message?', 'woocommerce' ),
), WC()->checkout->get_value( 'cb_msg' ));
woocommerce_form_field( 'requirements', array(
'type' => 'text',
'class' => array('msg t_msg'),
'label' => __('Please input your message.'),
'placeholder' => __(''),
), WC()->checkout->get_value( 'requirements' ));
echo '</div>';
// jQuery: show hide the text requirement field
?><script>
jQuery(document).ready(function($) {
var a = '#requirements_field';
$('input#checkbox_msg').change( function(){
if( $(this).is(':checked') )
$(a).show();
else
$(a).hide();
});
});
</script><?php
}
// Process checkout with additional custom field
add_action('woocommerce_after_checkout_validation', 'custom_checkout_field_validation_process', 20, 2 );
function custom_checkout_field_validation_process( $data, $errors ) {
// Check if set, if not add an error.
if( isset($_POST['checkbox_msg']) && empty($_POST['requirements']) )
$errors->add( 'requirements', __( "Please let us know what you would like to do filling up the message field.", "woocommerce" ) );
}
Code goes in function.php file of your active child theme (or active theme).
Tested and works.
I am using the following code to check if a product ID is in the cart, and if so, add extra checkout fields:
add_action('woocommerce_after_order_notes', 'conditional_checkout_field');
function conditional_checkout_field( $checkout ) {
echo '<div id="conditional_checkout_field">';
$product_id = 326;
$product_cart_id = WC()->cart->generate_cart_id( $product_id );
$in_cart = WC()->cart->find_product_in_cart( $product_cart_id );
// Check if the product is in the cart and show the custom field if it is
if ($in_cart ) {
echo '<h3>'.__('Products in your cart require the following information').'</h3>';
woocommerce_form_field( 'custom_field_license', array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => __('License Number'),
'placeholder' => __('Placeholder to help describe what you are looking for'),
), $checkout->get_value( 'custom_field_license' ));
}
}
This works just fine. However, how do I check for multiple product ID's in the cart? For instance, if product ID 326 or 245 are in the cart, show the conditional checkout fields? I feel like it is probably something simple, but I'm not sure how to go about doing it.
I have make some changes in your function to get it work for many product IDs. Also I have added the required option to the field. So your code sould be something like:
add_action('woocommerce_after_order_notes', 'conditional_checkout_field', 10, 1);
function conditional_checkout_field( $checkout ) {
// Set here your product IDS (in the array)
$product_ids = array( 37, 53, 70 );
$is_in_cart = false;
// Iterating through cart items and check
foreach( WC()->cart->get_cart() as $cart_item_key => $cart_item )
if( in_array( $cart_item['data']->get_id(), $product_ids ) ){
$is_in_cart = true; // We set it to "true"
break; // At east one product, we stop the loop
}
// If condition match we display the field
if( $is_in_cart ){
echo '<div id="conditional_checkout_field">
<h3 class="field-license-heading">'.__('Products in your cart require the following information').'</h3>';
woocommerce_form_field( 'custom_field_license', array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'required' => true, // Added required
'label' => __('License Number'),
'placeholder' => __('Placeholder to help describe what you are looking for'),
), $checkout->get_value( 'custom_field_license' ));
echo '</div>';
}
}
Code goes in function.php file of your active child theme (active theme or in any plugin file).
This code is tested and works.
I have a custom field for a Woocommerce's product, and I want to display it's value in order emails.
As I'm using custom product submission form, I added this code for custom field below to create a custom field:
<?php
WCVendors_Pro_Form_Helper::select( array(
'post_id' => $object_id,
'class' => 'select2',
'id' => 'wcv_custom_product_ingredients',
'label' => __( 'What time?', 'wcvendors-pro' ),
'placeholder' => __( 'Pick a time', 'wcvendors-pro' ),
'wrapper_start' => '<div class="all-100">',
'wrapper_end' => '</div>',
'desc_tip' => 'true',
'description' => __( 'Pick a time', 'wcvendors-pro' ),
'options' => array( '12:00 midnight' => __('12:00 midnight', 'wcv_custom_product_ingredients'), '12:15 midnight'=> __('12:15 midnight', 'wcv_custom_product_ingredients') )
) );
?>
I also tried adding code below to functions.php, but this only displays "What time?" without value in order emails...
add_action('woocommerce_email_after_order_table', 'wcv_ingredients_email');
function wcv_ingredients_email() {
$output = get_post_meta( get_the_ID(), 'wcv_custom_product_ingredients', true );
echo 'What time? ' . $output . '<br>';
}
What could be the issue?
You are using the correct hook, but you just forgot the hook arguments in the hooked function.
Also as you are targeting a product custom field value, and as in an order you can have many items (products), the code below will display this value for each order item.
Try the code below, it works now:
// Tested on WooCommerce version 2.6.x and 3+ — For simple products only.
add_action('woocommerce_email_after_order_table', 'wcv_ingredients_email', 10, 4);
function wcv_ingredients_email( $order, $sent_to_admin, $plain_text, $email ){
foreach($order->get_items() as $item_values){
// Get the product ID for simple products (not variable ones)
$product_id = $item_values['product_id'];
$output = get_post_meta( $product_id, 'wcv_custom_product_ingredients', true );
echo 'What time? ' . $output . '<br>';
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
I have this situation - I made a changes in one of the woocommerce email templates, but I`m sure - these changes will be lost after next woocommerce update.
As I know, I should use theme functions to bypass this problem.
This is the code before changes:
echo '<ul class="wc-bacs-bank-details order_details bacs_details">' . PHP_EOL;
// BACS account fields shown on the thanks page and in emails
$account_fields = apply_filters( 'woocommerce_bacs_account_fields', array(
'account_number'=> array(
'label' => __( 'Account Number', 'woocommerce' ),
'value' => $bacs_account->account_number
),
'sort_code' => array(
'label' => $sortcode,
'value' => $bacs_account->sort_code
),
'iban' => array(
'label' => __( 'IBAN', 'woocommerce' ),
'value' => $bacs_account->iban
),
'bic' => array(
'label' => __( 'BIC', 'woocommerce' ),
'value' => $bacs_account->bic
)
), $order_id );
foreach ( $account_fields as $field_key => $field ) {
if ( ! empty( $field['value'] ) ) {
echo '<li class="' . esc_attr( $field_key ) . '">' . esc_attr( $field['label'] ) . ': <strong>' . wptexturize( $field['value'] ) . '</strong></li>' . PHP_EOL;
}
}
echo '</ul>';
Here is the custom account field code that I want to insert:
'merkis' => array(
'label' => $merkis,
'value' => $pasutijums
)
How can I insert my custom code without overriding that core file?
Thanks
Never Override core files and always use the WooCommerce included hooks to make code customizations.
If you haven't find the way to make this change through a custom hooked function, as you will see in your provided code, you can use woocommerce_bacs_account_fields filter hook to add your custom code, without overriding any WooCommerce core files.
So the code for adding a new field in BACS account fields, is going to be:
add_filter( 'woocommerce_bacs_account_fields', 'custom_bacs_account_field', 10, 2);
function custom_bacs_account_field( $account_fields, $order_id ) {
$account_fields['merkis' ] = array(
'label' => $merkis,
'value' => $pasutijums
);
return $account_fields;
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
This code is tested and works…