I'm new to WooCommerce.I'm unable to figure out where is problem here is my code
I have added a select field in billing form of checkout page.
Problem
records are not saving or updating on submitting. Problem is in Update the order meta with field value.value is not updating in database
// checkout page customization start
global $post, $woocommerce;
// Account select field
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
function custom_override_checkout_fields($fields) {
$fields['billing']['Account'] = array(
'type' => 'select',
'class' => array('billing form-row-wide'),
'label' => __('Choose an Account'),
'placeholder' => _x('Account', 'placeholder', 'woocommerce'),
'options' => array(
'' => __( 'Select Account','' ),
),
'required' => true,
);
return $fields;
}
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process');
function my_custom_checkout_field_process() {
global $woocommerce;
if (!$_POST['Account'])
$woocommerce->add_error( __('Please enter your Account.'.$_POST['Account']));
}
///**
Problem area
value is not updating in database
//* Update the order meta with field value
//**/
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 ($_POST['Account']) update_post_meta( $order_id, 'Account', esc_attr($_POST['Account']));
}
/**
* Update the user meta with field value
**/
add_action('woocommerce_checkout_update_user_meta', 'my_custom_checkout_field_update_user_meta');
function my_custom_checkout_field_update_user_meta( $user_id ) {
if ($user_id && $_POST['Account']) update_user_meta( $user_id, 'Account', esc_attr($_POST['Account']) );
}
The functions look pretty accurate to me. Are you sure they aren't updating? Or perhaps you are having difficulty is displaying them.
I've tweaked them a bit. First, to use the $posted variabled that WooCommerce sends to the function, though this is trivial as $_POST should be the same. And secondly, you are using esc_attr() when you should be using sanitize_text_field(). The former is for displaying the data in an HTML attribute while the latter is for sanitizing before saving.
//* Update the order meta with field value
//**/
add_action('woocommerce_checkout_update_order_meta', 'my_custom_checkout_field_update_order_meta', 10, 2 );
function my_custom_checkout_field_update_order_meta( $order_id, $posted ) {
if ( isset( $posted['Account'] ) ){
update_post_meta( $order_id, 'Account', sanitize_text_field( $posted['Account'] ) );
}
}
/**
* Update the user meta with field value
**/
add_action('woocommerce_checkout_update_user_meta', 'my_custom_checkout_field_update_user_meta', 10, 2 );
function my_custom_checkout_field_update_user_meta( $user_id, $posted ) {
if ( $user_id && isset( $posted['Account'] ) ){
update_user_meta( $user_id, 'Account', sanitize_text_field( $posted['Account'] ) );
}
}
Related
I created a custom field for my products using this tutorial, but I need to set a default value for this field.
I have already created hundreds of products and I need to have this field updated at those as well. I'm then using this value to order the products.
Here is the whole code:
// Display Fields
add_action('woocommerce_product_options_general_product_data', 'woocommerce_product_custom_fields');
// Save Fields
add_action('woocommerce_process_product_meta', 'woocommerce_product_custom_fields_save');
function woocommerce_product_custom_fields()
{
global $woocommerce, $post;
echo '<div class="product_custom_field">';
// Custom Product Text Field
woocommerce_wp_text_input(
array(
'id' => 'priority',
'placeholder' => 'Priority of the product - values a>b',
'label' => __('Priority of the product', 'woocommerce'),
'desc_tip' => 'true',
//'default' => '0',
)
);
echo '</div>';
}
function woocommerce_product_custom_fields_save($post_id)
{
// Custom Product Text Field
$woocommerce_custom_product_text_field = $_POST['priority'];
if (!empty($woocommerce_custom_product_text_field))
update_post_meta($post_id, 'priority', esc_attr($woocommerce_custom_product_text_field));
}
function cw_add_postmeta_ordering_args( $args_sort_cw ) {
$cw_orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) :
apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
switch( $cw_orderby_value ) {
case 'priority':
$args_sort_cw['orderby'] = 'meta_value';
$args_sort_cw['order'] = 'asc';
$args_sort_cw['meta_key'] = 'priority';
break;
}
return $args_sort_cw;
}
add_filter( 'woocommerce_get_catalog_ordering_args', 'cw_add_postmeta_ordering_args' );
function cw_add_new_postmeta_orderby( $sortby ) {
$sortby['priority'] = __( 'Recommended', 'woocommerce' );
return $sortby;
}
add_filter( 'woocommerce_default_catalog_orderby_options', 'cw_add_new_postmeta_orderby' );
add_filter( 'woocommerce_catalog_orderby', 'cw_add_new_postmeta_orderby' );
I could't find a working solution, so I'm kindly asking you for an advice. What I expect is that every existing product will have this value set and when creating a new one the default value will be displayed. Then it should be sorted according to this value. Thanks for your help!
Edit according to the first answer:
The data does't update in the database. In the front-end I can only see the products I have manually altered and not those to which the 0 was added using this function. When I try to change the altered value back to 0, it is not updated. Actually I just need a function which would refresh all the products so that the value is stored to the database.
What I would do is use the value argument in the woocommerce_wp_text_input parameters. You can then pass it the current value OR a default if nothing exists yet.
Also added new save routine via woocommerce_admin_process_product_object
// Display Fields
add_action('woocommerce_product_options_general_product_data', 'so_63588126_product_custom_fields');
// Save Fields
add_action('woocommerce_admin_process_product_object', 'so_63588126_product_custom_fields_save');
function so_63588126_product_custom_fields()
{
global $product_object;
$value = $product_object->get_meta( 'weight', true );
if ( ! $value ) {
$value = 0;
}
echo '<div class="product_custom_field">';
// Custom Product Text Field
woocommerce_wp_text_input(
array(
'id' => 'weight',
'placeholder' => 'Weight of the product - values a>b',
'label' => __('Weight of the product', 'your-textdomain'),
'desc_tip' => 'true',
'value' => $value
)
);
echo '</div>';
}
function so_63588126_product_custom_fields_save( $product )
{
// Custom Product Text Field
$woocommerce_custom_product_text_field = $_POST['weight'];
if ( ! empty( $_POST['weight'] ) ) {
$product->update_meta_data( 'weight', sanitize_text_field( wp_unslash( $_POST['weight'] ) ) );
} else {
$product->update_meta_data( 'weight', 0 );
}
}
Additional explanation:
I used $product_object->update_meta_data() as WooCommerce (since 3.0) prefers to use CRUD (create, read, update, delete) methods on objects. update_post_meta() does still work, but CRUD will be future-proof if/when Woo ever decides to use custom tables for products and product meta.
Read more about CRUD: https://docs.woocommerce.com/document/developing-using-woocommerce-crud-objects/
And regarding text domains... This is custom code so in order for it to be properly translated you would need to use your own unique text domain that is not 'woocommerce'.
Read more about text domains: https://developer.wordpress.org/themes/functionality/internationalization/#text-domain
Finally, sanitize_text_field() may not be the right choice for sanitizing, but I didn't know what kind of data you were storing there. intval() would be better for numbers... and so would setting the input to be a numerical input type by setting the type parameter to 'number' in the woocommerce_wp_text_input() args.
This is the solution which worked for me.
// Display Fields
add_action('woocommerce_product_options_general_product_data', 'woocommerce_product_custom_fields');
// Save Fields
add_action('woocommerce_process_product_meta', 'woocommerce_product_custom_fields_save');
function woocommerce_product_custom_fields()
{
global $woocommerce, $post, $product_object;
$value = $product_object->get_meta( 'priority', true );
if ( ! $value ) {
$value = 0;
}
echo '<div class="product_custom_field">';
// Custom Product Text Field
woocommerce_wp_text_input(
array(
'id' => 'priority',
'placeholder' => 'Priority - number 1>0',
'label' => __('Priority', 'woocommerce'),
'type' => 'number',
'desc_tip' => 'true',
'value' => $value
)
);
echo '</div>';
}
function woocommerce_product_custom_fields_save($post_id)
{
// Custom Product Text Field
if (array_key_exists('priority', $_POST)) {
update_post_meta($post_id, 'priority', intval($_POST['priority']));
}
}
function cw_add_postmeta_ordering_args( $args_sort_cw ) {
$cw_orderby_value = isset( $_GET['orderby'] ) ? wc_clean( $_GET['orderby'] ) :
apply_filters( 'woocommerce_default_catalog_orderby', get_option( 'woocommerce_default_catalog_orderby' ) );
switch( $cw_orderby_value ) {
case 'priority':
$args_sort_cw['orderby'] = 'meta_value';
$args_sort_cw['order'] = 'desc';
$args_sort_cw['meta_key'] = 'priority';
break;
}
return $args_sort_cw;
}
add_filter( 'woocommerce_get_catalog_ordering_args', 'cw_add_postmeta_ordering_args' );
function cw_add_new_postmeta_orderby( $sortby ) {
$sortby['priority'] = __( 'Recommended', 'woocommerce' );
return $sortby;
}
add_filter( 'woocommerce_default_catalog_orderby_options', 'cw_add_new_postmeta_orderby' );
add_filter( 'woocommerce_catalog_orderby', 'cw_add_new_postmeta_orderby' );
I adjusted the woocommerce_product_custom_fields_save() function with array_key_exists() and it works fine now.
Thanks helgatheviking for help!
I would like to synhcronize a custom checkbox field in WooCommerce checkout page with a similar one in the My Account page (when the user is logged in).
Here's my code:
// Show a checkbox in checkout page and in My account > Details
add_action( 'woocommerce_edit_account_form', 'display_checkbox_in_account_page' );
add_action( 'woocommerce_after_order_notes', 'display_checkbox_in_account_page' );
function display_checkbox_in_account_page() {
woocommerce_form_field( 'newsletter-account', array(
'type' => 'checkbox',
'class' => array('form-row-wide'),
'label' => __( 'Subscribe to my newsletter', 'woocommerce' ),
), get_user_meta(get_current_user_id(), 'newsletter-account', true ) );
}
// Save checkbox value when saved in My account > Details
add_action( 'woocommerce_save_account_details', 'save_checkbox_value_from_account_details', 10, 1 );
function save_checkbox_value_from_account_details( $user_id ) {
$value = isset( $_POST['newsletter-account'] ) ? '1' : '0';
update_user_meta( $user_id, 'newsletter-account', $value );
}
Those two blocks of code work fine: if in My account > Details I checked the checkbox, the preference is saved and I can see a checked checkbox also in the checkout page.
Now the problem is that I need a way to obtain the same result when I edit my newsletter preference from the checkout page. I think I need to use the woocommerce_checkout_create_order but I haven't any idea on how to code this:
//Save the checkbox value when customer edit it from the checkout page
add_action( 'woocommerce_checkout_create_order', 'save_checkbox_value_from_checkout_page'. 10, 1 );
function save_checkbox_value_from_checkout_page() {
//some code here
}
Any suggestions are appreciated.
Updated
The hook woocommerce_checkout_create_order is for order meta data and what you need is to save/update your checkout newsletter-account field value as user meta data…
So is better to use dedicated woocommerce_checkout_update_customer hook.
Here is your complete code (tested and working):
add_action( 'woocommerce_edit_account_form', 'display_checkbox_in_account_page' );
add_action( 'woocommerce_after_order_notes', 'display_checkbox_in_account_page' );
function display_checkbox_in_account_page() {
woocommerce_form_field( 'newsletter-account', array(
'type' => 'checkbox',
'class' => array('form-row-wide'),
'label' => __( 'Subscribe to my newsletter', 'woocommerce' ),
), get_user_meta( get_current_user_id(), 'newsletter-account', true ) );
}
// Save/update checkbox value when saved in My account > Details
add_action( 'woocommerce_save_account_details', 'save_checkbox_value_from_account_details', 10, 1 );
function save_checkbox_value_from_account_details( $user_id ) {
$value = isset( $_POST['newsletter-account'] ) ? '1' : '0';
update_user_meta( $user_id, 'newsletter-account', $value );
}
// Save/update custom checkout field value as user meta data
add_action('woocommerce_checkout_update_customer','custom_checkout_checkbox_update_customer', 100, 2 );
function custom_checkout_checkbox_update_customer( $customer, $data ){
$value = isset( $_POST['newsletter-account'] ) ? '1' : '0';
update_user_meta( $customer->get_id(), 'newsletter-account', $value ); // Updated
}
Code goes in functions.php file of your active child theme (or active theme).
Now if you want to grab that information additionally to the order as order meta data you will use:
add_action('woocommerce_checkout_create_order','custom_checkout_checkbox_add_order_meta', 100, 2 );
function custom_checkout_checkbox_add_order_meta( $order, $data ){
$value = isset( $_POST['newsletter-account'] ) ? '1' : '0';
$order->update_meta_data( 'newsletter-account', $value );
}
I need to replace some character in my custom checkout field.
this is the whole code of my custom checkout field , (maybe we could use str_replace here)
/* Add the field to the checkout */
add_action( 'woocommerce_after_checkout_billing_form', 'my_custom_checkout_field' );
function my_custom_checkout_field( $checkout ) {
echo '<div id="my_custom_checkout_field">';
woocommerce_form_field( 'phone_sabet', array(
'type' => 'tel',
'required' => true,
'clear' => true,
'class' => array('my-field-class form-row-first'),
'label' => __(''),
'placeholder' => __(''),
'description' => '',
), $checkout->get_value(('phone_sabet')));
echo '</div>';
}
this is the part of code when the custom field going to update
/* Update the order meta with field value */
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['phone_sabet'] ) ) {
update_post_meta( $order_id, 'Phone', sanitize_text_field( $_POST['phone_sabet'] ) );
}
}
i tired to use str_replace and change it to below but no luck.
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['phone_sabet'] ) ) {
update_post_meta( $order_id, 'Phone', sanitize_text_field( $_POST['phone_sabet'] ) );
$getMeta = get_post_meta( get_the_ID(), 'Phone', true);
$newMeta = str_replace(array('۱'), '1', $getMeta);
update_post_meta(get_the_ID(), 'Phone', $newMeta);
}
}
and this is the part of when checkout field going to process. its okay if we could done it with str_replace here.
/* Process the checkout */
add_action('woocommerce_checkout_process', 'my_custom_checkout_field_process');
function my_custom_checkout_field_process() {
if ( $_POST['phone_sabet'] )
// do something
}
The correct hook is woocommerce_checkout_update_order_meta, so you could try this:
## Save the order meta with custom field value
add_action( 'woocommerce_checkout_update_order_meta', 'custom_update_order_meta' );
function custom_update_order_meta( $order_id ) {
if ( ! empty( $_POST['phone_sabet'] ) ) {
// Replace before saving translating )
$phone_sabet = str_replace( array('۱'), array('1'), $_POST['phone_sabet'] );
update_post_meta( $order_id, 'phone', sanitize_text_field( $phone_sabet ) );
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and works
I added a custom field on the woocommerce checkout page that is populated by URL but I am struggling to find a way to make this field not editable.
I added the following code to function.php:
//Add custom field
function custom_woocommerce_checkout_fields( $checkout_fields = array() ) {
$checkout_fields['order']['imei'] = array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => __('IMEI'),
'placeholder' => __('imei'),
'default' => $_GET['imei'],
);
return $checkout_fields;
}
add_filter( 'woocommerce_checkout_fields', 'custom_woocommerce_checkout_fields' );
What should I change in my code to accomplish this ?
Try this code. It work for me. Just need to add custom attribute parameter to the fields array.
//Display custom field
function custom_woocommerce_checkout_fields( $checkout_fields = array() ) {
$checkout_fields['order']['imei'] = array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => __('IMEI'),
'placeholder' => __('imei'),
'default' => $_GET['imei'],
'custom_attributes' => array( 'disabled' => true)
);
return $checkout_fields;
}
add_filter( 'woocommerce_checkout_fields', 'custom_woocommerce_checkout_fields' );
// Save custom field
add_action( 'woocommerce_checkout_update_order_meta', 'custom_checkout_field_update_order_meta' );
function custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['imei'] ) ) {
update_post_meta( $order_id, '_imei', sanitize_text_field( $_POST['imei'] ) );
}
}
You should try this text <imput> field with the readonly property (meaning not editable).
You should need to have your 'emei' set in the checkout url like:
http://www.example.com/checkout/?imei=3545454653 to make the field appear with the value as I have this condition: if( empty($_GET['imei'])) return;set in the function.
The code:
// Display
add_action( 'woocommerce_after_order_notes', 'custom_woocommerce_checkout_fields' );
function custom_woocommerce_checkout_fields( $checkout ) {
// Only display field if the 'emei' is set in the checkout url
if( empty($_GET['imei'])) return;
echo '<p class="form-row my-field-class form-row-wide woocommerce-validated" id="imei_field">
<label for="imei" class="">'.__('IMEI').'</label>
<input type="text" class="input-text " name="imei" id="imei" placeholder="'.__('IMEI').'" value="'.$_GET['imei'].'" readonly>
</p>';
}
// Save
add_action( 'woocommerce_checkout_update_order_meta', 'custom_checkout_field_update_order_meta' );
function custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['imei'] ) ) {
update_post_meta( $order_id, '_imei', sanitize_text_field( $_POST['imei'] ) );
}
}
Code goes in function.php file of your active child theme (or theme) or also in any plugin file.
Tested and work on WooCommerce versions 3+
To get the value (for a defined $order_id):
$imei = get_post_meta( $order_id, '_imei', true );
I'm using Wordpress together with WooCommerce for my shop.
I want to output the value of my custom field in my cart and in the email order confirmation.
I have created a custom field in my functions.php:
// Display Fields
add_action( 'woocommerce_product_options_general_product_data', 'woo_add_custom_general_fields' );
// Save Fields
add_action( 'woocommerce_process_product_meta', 'woo_add_custom_general_fields_save' );
function woo_add_custom_general_fields() {
global $woocommerce, $post;
echo '<div class="options_group">';
// Input
woocommerce_wp_text_input(
array(
'id' => '_gram',
'label' => __( 'somelabel', 'woocommerce' ),
'placeholder' => '',
'description' => __( 'sometext', 'woocommerce' ),
'type' => 'number',
'custom_attributes' => array(
'step' => 'any',
'min' => '0'
)
)
);
echo '</div>';
}
function woo_add_custom_general_fields_save( $post_id ){
// Number Field
$woocommerce_number_field = $_POST['_gram'];
if( !empty( $woocommerce_number_field ) )
update_post_meta( $post_id, '_gram', esc_attr( $woocommerce_number_field ) );
}
On my pages I'm using:
get_post_meta( get_the_ID(), '_gram', true );
To show the value and that work perfect.
Now I want to show this same value under the product name in my cart and in the email confirmations.
But I cant figure out how to do this.
Does anyone knows how to do this?
Well it's a long process, you have to implement two filters & one action to accomplish this.
Also there is a plugin for this exact purpose along with lot of other options related to woocommerce custom fields.
Here is the direct solution for your question.
/**
* Here we are trying to add your custom data as Cart Line Item
* SO that we can add this custom data on your cart, checkout, order and email later
*/
function save_custom_data( $cart_item_data, $product_id ) {
$custom_data = get_post_meta( $product_id, '_gram', true );
if( $custom_data != null && $custom_data != "" ) {
$cart_item_data["gram"] = $custom_data;
}
return $cart_item_data;
}
add_filter( 'woocommerce_add_cart_item_data', 'save_custom_data', 10, 2 );
/**
* Here we are trying to display that custom data on Cart Table & Checkout Order Review Table
*/
function render_custom_data_on_cart_checkout( $cart_data, $cart_item = null ) {
$custom_items = array();
/* Woo 2.4.2 updates */
if( !empty( $cart_data ) ) {
$custom_items = $cart_data;
}
if( isset( $cart_item["gram"] ) ) {
$custom_items[] = array( "name" => "Gram", "value" => $cart_item["gram"] );
}
return $custom_items;
}
add_filter( 'woocommerce_get_item_data', 'render_custom_data_on_cart_checkout', 10, 2 );
/**
* We are adding that custom data ( gram ) as Order Item Meta,
* which will be carried over to EMail as well
*/
function save_custom_order_meta( $item_id, $values, $cart_item_key ) {
if( isset( $values["gram"] ) ) {
wc_add_order_item_meta( $item_id, "Gram", $values["gram"] );
}
}
add_action( 'woocommerce_add_order_item_meta', 'save_custom_order_meta', 10, 3 );