Woocommerce not saving form field - woocommerce

i cannot figure out what i am missing.
I created a custom textfield at the woocommerce checkout with a predefined value. Function is pretty standard:
function one_more_field(){
$product_name='';
global $product;
foreach( WC()->cart->get_cart() as $cart_item ){
$productvariation = $cart_item['variation_id'];
}
woocommerce_form_field( 'variatie', array(
'type' => 'text',
'value' => $productvariation,
),
$productvariation );
}
add_action( 'woocommerce_after_checkout_billing_form', 'one_more_field' );
I can see the field in the checkout and the variation ID is nicely inserted.
Then step 2: saving the value to the user meta data:
function reigel_woocommerce_checkout_update_user_meta( $customer_id, $posted ) {
$dob4 = 'test';
if (isset($posted['variatie'])) {
$dob4 = $posted['variatie'];
update_user_meta( $customer_id, 'variatie', $dob4);
}
add_action( 'woocommerce_checkout_update_user_meta', 'reigel_woocommerce_checkout_update_user_meta', 10, 2 );
However, the value is not saved to the user meta data.
What am i missing here?

Oh it turns out to be very straightforward: just add required => yes to the array of field arguments and it is saved. Cant really understand why though...
So new code (step 1) is:
function one_more_field(){
$product_name='';
global $product;
foreach( WC()->cart->get_cart() as $cart_item ){
$productvariation = $cart_item['variation_id'];
}
woocommerce_form_field( 'variatie', array(
'type' => 'text',
'value' => $productvariation,
'required' => 'true'
),
$productvariation );
}
add_action( 'woocommerce_after_checkout_billing_form', 'one_more_field' );

Related

Add custom field to product inventory tab and display value on single product page where meta ends

I'm using the following code in my theme functions.php file to add a additional data field to the product inventory tab:
// Add Custom Field to woocommerce inventory tab for product
add_action('woocommerce_product_options_inventory_product_data', function() {
woocommerce_wp_text_input([
'id' => '_number_in_package',
'label' => __('Number of Pages', 'txtdomain'),
'type' => 'number',
]);
});
add_action('woocommerce_process_product_meta', function($post_id) {
$product = wc_get_product($post_id);
$num_package = isset($_POST['_number_in_package']) ? $_POST['_number_in_package'] : '';
$product->update_meta_data('_number_in_package', sanitize_text_field($num_package));
$product->save();
});
add_action('woocommerce_product_meta_start', function() {
global $post;
$product = wc_get_product($post->ID);
$num_package = $product->get_meta('_number_in_package');
if (!empty($num_package)) {
printf('<div class="custom-sku">%s: %s</div>', __('Number of Pages', 'txtdomain'), $num_package);
}
});
add_filter('woocommerce_product_data_tabs', function($tabs) {
$tabs['additional_info'] = [
'label' => __('Additional info', 'txtdomain'),
'target' => 'additional_product_data',
'class' => ['hide_if_external'],
'priority' => 25
];
return $tabs;
});
However, on the single product page, the custom field is added before category and ISBN. I want to place the custom field at the end after the product ISBN. Any advice?
Some comments/suggestions regarding your code attempt
To save fields you can use the woocommerce_admin_process_product_object hook, opposite the outdated woocommerce_process_product_meta hook
WooCommerce contains by default no ISBN field, but it looks like the woocommerce_product_meta_end hook will answer your question
So you get:
// Add custom field
function action_woocommerce_product_options_inventory_product_data() {
woocommerce_wp_text_input( array(
'id' => '_number_in_package',
'label' => __( 'Number of Pages', 'woocommerce' ),
'description' => __( 'This is a custom field, you can write here anything you want.', 'woocommerce' ),
'desc_tip' => 'true',
'type' => 'number'
) );
}
add_action( 'woocommerce_product_options_inventory_product_data', 'action_woocommerce_product_options_inventory_product_data' );
// Save custom field
function action_woocommerce_admin_process_product_object( $product ) {
// Isset
if ( isset( $_POST['_number_in_package'] ) ) {
// Update
$product->update_meta_data( '_number_in_package', sanitize_text_field( $_POST['_number_in_package'] ) );
}
}
add_action( 'woocommerce_admin_process_product_object', 'action_woocommerce_admin_process_product_object', 10, 1 );
// Display on single product page
function action_woocommerce_product_meta_end() {
global $product;
// Is a WC product
if ( is_a( $product, 'WC_Product' ) ) {
// Get meta
$number = $product->get_meta( '_number_in_package' );
// NOT empty
if ( ! empty ( $number ) ) {
echo '<p>' . $number . '</p>';
}
}
}
add_action( 'woocommerce_product_meta_end', 'action_woocommerce_product_meta_end', 10 );

Add checkbox to product inventory tab in WooCommerce and have the checkbox checked by default

I got this snippet in here to add checkbox custom field which is auto set and it’s working fine.
// Displaying quantity setting fields on admin product pages
add_action( 'woocommerce_product_options_pricing', 'add_custom_field_product_options_pricing' );
function add_custom_field_product_options_pricing() {
global $product_object;
echo '</div><div class="options_group">';
$values = $product_object->get_meta('_cutom_meta_key');
woocommerce_wp_checkbox( array( // Checkbox.
'id' => '_cutom_meta_key',
'label' => __( 'Custom label', 'woocommerce' ),
'value' => empty($values) ? 'yes' : $values,
'description' => __( 'Enable this to make something.', 'woocommerce' ),
) );
}
// Save quantity setting fields values
add_action( 'woocommerce_admin_process_product_object', 'save_custom_field_product_options_pricing' );
function save_custom_field_product_options_pricing( $product ) {
$product->update_meta_data( '_cutom_meta_key', isset($_POST['_cutom_meta_key']) ? 'yes' : 'no');
}
My question: how to move this checkbox to be on the inventory tab and have the checkbox checked by default?
I’ve tried changing :
add_action( 'woocommerce_product_options_pricing', 'add_custom_field_product_options_pricing' );
to:
add_action( 'woocommerce_product_options_inventory_product_data', 'add_custom_field_product_options_pricing' );
AND
add_action( 'woocommerce_admin_process_product_object', 'save_custom_field_product_options_pricing' );
to:
add_action( 'woocommerce_process_product_meta', 'save_custom_field_product_options_pricing' );
But to no avail. Any advice?
woocommerce_admin_process_product_object replaces the outdated woocommerce_process_product_meta hook so you should definitely not replace it with it
To have the checkbox checked by default you can add value to the args from woocommerce_wp_checkbox()
So you get:
// Add checkbox
function action_woocommerce_product_options_inventory_product_data() {
global $product_object;
// Get meta
$value = $product_object->get_meta( '_cutom_meta_key' );
// Checkbox
woocommerce_wp_checkbox( array(
'id' => '_cutom_meta_key', // Required, it's the meta_key for storing the value (is checked or not)
'label' => __( 'Custom label', 'woocommerce' ), // Text in the editor label
'desc_tip' => false, // true or false, show description directly or as tooltip
'description' => __( 'Enable this to make something', 'woocommerce' ), // Provide something useful here
'value' => empty( $value ) ? 'yes' : $value // Checked by default
) );
}
add_action( 'woocommerce_product_options_inventory_product_data', 'action_woocommerce_product_options_inventory_product_data', 10, 0 );
// Save Field
function action_woocommerce_admin_process_product_object( $product ) {
// Update meta
$product->update_meta_data( '_cutom_meta_key', isset( $_POST['_cutom_meta_key'] ) ? 'yes' : 'no' );
}
add_action( 'woocommerce_admin_process_product_object', 'action_woocommerce_admin_process_product_object', 10, 1 );

Updating custom product meta box field - WooCommerce

I'm trying to update a custom product meta box field using the updating system from WooCommerce core. Here is my code:
The new field Shipping info
add_action( 'woocommerce_product_options_shipping', 'my_product_options_shipping' );
function my_product_options_shipping() {
global $post;
$shipping_info = get_post_meta( $post->ID, '_shipping_info', true );
woocommerce_wp_text_input(
array(
'id' => '_shipping_info',
'value' => $shipping_info,
'label' => __( 'Shipping info', 'woocommerce' ),
'placeholder' => __( 'Shipping in two days', 'woocommerce' ),
)
);
}
And this is the function that adds the new field as prop in WC_Meta_Box_Product_Data::save
add_action( 'woocommerce_admin_process_product_object', 'my_admin_process_product_object' );
function my_admin_process_product_object( $product ) {
$product->set_props(
array(
'shipping_info' => isset( $_POST['_shipping_info'] ) ? wc_clean( wp_unslash( $_POST['_shipping_info'] ) ) : null,
)
);
}
I think I'm missing a step. Shouldn't it be saved automatically from function WC_Meta_Box_Product_Data::save which is attached to woocommerce_process_product_meta?
EDIT
I found the missing step. I need to add my custom post meta in the protected array $extra_data from abstract class WC_Data.
I'm not too good at OOP, so how I can access that array to push my custom data?
I can see you're just doing $shipping_info = get_post_meta( $post->ID, '_shipping_info', true );
So why not save the trouble and just use update_post_meta
add_action( 'woocommerce_admin_process_product_object', 'my_admin_process_product_object' );
function my_admin_process_product_object( $product ) {
update_post_meta($product->get_id(), '_shipping_info', wc_clean( wp_unslash( $_POST['_shipping_info'] ) ) );
}

get_post_meta not showing the value of custom field when called

I've had the following code to add a custom order field in the woocommerce checkout:
function delivery_time_slots_array () {
return array(
'' => _('Choose a time slot'),
'5pm - 6pm' => __('5pm - 6pm', 'woocommerce'),
'6pm - 7pm' => __('6pm - 7pm', 'woocommerce'),
'7pm - 8pm' => __('7pm - 8pm', 'woocommerce'),
'8pm - 9pm' => __('8pm - 9pm', 'woocommerce')
);
}
add_filter( 'woocommerce_checkout_fields' , 'custom_override_checkout_fields' );
$fields['order']['order_delivery_time'] = array(
'type' => 'select',
'label' => __('Delivery time (choose an hour slot between 5pm & 9pm)', 'woocommerce'),
'class' => array('form-row-wide'),
'required' => true,
'options' => delivery_time_slots_array()
);
Then, followed by the below code to update the order meta with field value:
add_action( 'woocommerce_checkout_update_order_meta', 'custom_checkout_field_update_order_meta', 10, 2 );
function custom_checkout_field_update_order_meta( $order_id ) {
if ( ! empty( $_POST['order_delivery_time'] ) ) {
update_post_meta( $order_id, 'Delivery time', sanitize_text_field($_POST['order_delivery_time']) );
}
}
This all displays correctly and returns the value within the Orders section of woocommerce. However, I can't then access this data to return/display it on the thank-you page. I'm using the following code at present:
<li class="delivery_slot">
<?php _e( 'Delivery Slot', 'woocommerce' ); ?>
<strong><?php
$delivery_slots = delivery_time_slots_array();
$delivery_slot = get_post_meta($order_id, 'Delivery time', true);
if( isset($delivery_slots[$delivery_slot]) )
echo $delivery_slots[$delivery_slot];
?></strong>
I've reviewed 2 hours of posts here & on Google including the below example (which my code mirrors), but cannot get the value to display:
How to get the value instead of order_id with get_post_meta()
If you attach it to any of the hooks in the order-details.php template then the $order object will be available in your callback function
function so_34215698_display_order_meta( $order ){
$delivery_slots = delivery_time_slots_array()
$delivery_slot = get_post_meta($order->id, 'Delivery time', true);
if( isset($delivery_slots[$delivery_slot]) )
echo $delivery_slots[$delivery_slot];
}
add_action( 'woocommerce_order_details_after_order_table', 'so_34215698_display_order_meta' );

Update user meta after woocommerce checkout form process

I am using woocommerce with Wordpress and have added some custom fields to the checkout:
add_action('woocommerce_after_order_notes', 'my_custom_checkout_field');
function my_custom_checkout_field( $checkout ) {
$extra_fields = array('job_title', 'company', 'telephone', 'occupation');
foreach($extra_fields as $key => $value) {
woocommerce_form_field($value, array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => __($label),
'value' => '',
), $checkout->get_value( $value ));
}
}
Now currently, these appear in the checkout fine, not sure if using woocommerce_after_order_notes is right in this case. I have also added some custom fields to the user meta that correspond to the fields added to the checkout - which all display in the user profile page:
function add_contact_methods( $contactmethods ) {
$contactmethods['job_title'] = 'Job Title';
$contactmethods['company'] = 'Company Name';
$contactmethods['telephone'] = 'Telephone';
$contactmethods['occupation'] = 'Occupation';
$contactmethods['refer'] = 'How you heard about us?';
return $contactmethods;
}
add_filter('user_contactmethods','add_contact_methods',10,1);
As you can imagine, if I update any of these field in any profile page, it works fine but what I cant seem to do is update the user meta when a new user makes a purchase, it does not update the user meta for these fields in the database.
I understand alot of how this works, and understand that I must hook into a Woocommerce process to add the fields into the process. So I have added this code into my functions too:
add_action('woocommerce_checkout_update_user_meta', 'my_custom_checkout_field_update_user_meta');
function my_custom_checkout_field_update_user_meta( $user_id ) {
global $extra_fields;
foreach($extra_fields as $key => $value) {
if ($_POST[$value]) update_user_meta( $user_id, $value, esc_attr($_POST[$value]));
}
}
Now the twist is, this works if a user who is already signed in as a member, makes a repurchase and goes through the checkout - the reason this works is because $user_id already exists, but when a new user is checking out, they do not yet exist as a user, hence the function cannot update the user meta of NIL where $user_id does not exist.
My question is, how do I hook into the checkout process, presumably AFTER the user has been created, so I that I can get the $user_id returned, and execute this function to update the user meta.
class-wc-checkout.php line 639 creates the new user with $this->customer_id = wp_insert_user( apply_filters( 'woocommerce_new_customer_data', $new_customer_data ) ); The new customer data is an array listed just above that line.
Following that, you can access the user id with line 649's action do_action( 'woocommerce_created_customer', $this->customer_id );
It is unlikey, in your case, you will need to use the filter, but simply add the action 'woocommerce_created_customer', pull in the id, and add the meta.
When customer is not logged in checkout page should be acceptable field customer want to create a new account.Below sample code change in checkout page when customer order a new item and update user meta data.
function user_extra_meta_fields(){
return array(
'job_title' => __( 'Job Title', 'yourtext_domain'),
'company' => __( 'Company Name', 'yourtext_domain'),
'telephone' => __( 'Telephone', 'yourtext_domain'),
'occupation' => __( 'Occupation', 'yourtext_domain'),
'refer' => __( 'How you heard about us?', 'yourtext_domain'),
);
}
function add_contact_methods( $contactmethods ) {
$contactmethods = array_merge( $contactmethods, user_extra_meta_fields());
return $contactmethods;
}
add_filter('user_contactmethods','add_contact_methods',10,1);
add_action('woocommerce_after_order_notes', 'my_custom_checkout_field');
function my_custom_checkout_field( $checkout ) {
foreach( user_extra_meta_fields() as $name => $label) {
$value = '';
if( is_user_logged_in() )
$value = get_user_meta( get_current_user_id(), $name, true );
woocommerce_form_field( $name, array(
'type' => 'text',
'class' => array('my-field-class form-row-wide'),
'label' => $label,
), $value );
}
}
add_action( 'woocommerce_checkout_process', 'user_fields_woocommerce_checkout_process' );
function user_fields_woocommerce_checkout_process(){
if( is_user_logged_in() )
add_action('woocommerce_checkout_update_user_meta', 'my_custom_checkout_field_update_user_meta' );
else
add_action( 'woocommerce_created_customer', 'my_custom_checkout_field_update_user_meta' );
}
function my_custom_checkout_field_update_user_meta( $user_id ) {
foreach( array_keys( user_extra_meta_fields() ) as $meta_name ){
if( isset( $_POST[$meta_name] ) ){
$meta_value = $_POST[$meta_name] ? esc_attr($_POST[$meta_name]) : '';
update_user_meta( $user_id, $meta_name, $meta_value );
}
}
}
// if want to validate field
add_action( 'woocommerce_after_checkout_validation', 'user_fields_woocommerce_after_checkout_validation' );
function user_fields_woocommerce_after_checkout_validation( $posted ){
$validate = true;
if( ! is_user_logged_in() && empty( $posted['createaccount'] ) )
$validate = false;
if( $validate == false )
return;
$meta_data = user_extra_meta_fields();
foreach( array_keys( $meta_data ) as $meta_name ){
if( empty($_POST[$meta_name]) )
wc_add_notice( sprintf( __(' <strong>%s</strong> is required.', 'yourtext_domain'), $meta_data[$meta_name] ), 'error' );
}
}

Resources