WooCommerce does not automatically link past guest orders to new user accounts. That should have been the default behavior, but unfortunately that’s not the case.
Anyways, here’s the solution
<?php
require_once $_SERVER["DOCUMENT_ROOT"] . "/wp-load.php";
// Get all orders with 'guest' status
$args = [
"status" => "completed", // Completed orders
"return" => "ids", // Just the ids
];
$order_ids = wc_get_orders($args);
foreach ($order_ids as $order_id) {
// Get the order
$order = wc_get_order($order_id);
// Check if the order was made by a guest
if (0 == $order->get_user_id()) {
// This order was made by a guest
// Now we get the order's billing email
$order_email = $order->get_billing_email();
// Search for a user with this email
$user = get_user_by("email", $order_email);
if ($user) {
// We found a user with this email
// Now we link the order to this user
$order->set_customer_id($user->ID);
$order->save();
}
}
}
Put this code in a .php
file and upload the file to the root of your server. Example, if you used quvor.php
then go to yourwebsite.com/quvor.php
Executing this script can take a good amount of time, depending on the number of orders you have, and your server speed. You may need to increase your max_execution_time
in PHP settings.
If it times out, you can refresh the page again. Make sure to back up your website before running the script.
If you want to link guest orders by emails(which is what I needed), you can use the below script
<?php
require_once $_SERVER["DOCUMENT_ROOT"] . "/wp-load.php";
$emails = ["[email protected]", "[email protected]"]; // put the emails here
// Loop through each email
foreach ($emails as $email) {
// Use WP_Query to get all guest orders for the current email
$args = [
"post_type" => "shop_order",
"post_status" => "any",
"meta_query" => [
[
"key" => "_billing_email",
"value" => $email,
"compare" => "=",
],
[
"key" => "_customer_user",
"value" => 0,
"compare" => "=",
],
],
];
$orders = get_posts($args);
// If there are any orders
if ($orders) {
// Get the WP_User object for the current email
$user = get_user_by("email", $email);
// If a user exists for the email
if ($user) {
// Loop through each order
foreach ($orders as $order) {
// Update the _customer_user meta field with the user's ID
update_post_meta($order->ID, "_customer_user", $user->ID);
}
}
}
}
That’s it. Once the script is successfully executed, all your store orders will be linked to users who have an account.
Once that’s done, make sure to delete the file.
Link Newly Created Customer with Past Guest Orders
The previous script won’t do anything for future orders.
Somethings the guest users when placing a new order decide to register. In this case, their previous orders are not linked to their new accounts.
To fix that, add the below code to your child theme’s functions.php
or a code snippets plugin.
<?php
/* Credits: https://stackoverflow.com/questions/71112271/how-to-link-woocommerce-guest-orders-to-customer-account-after-registration */
function action_woocommerce_created_customer( $customer_id, $new_customer_data, $password_generated ) {
// Link past orders to this newly created customer
wc_update_new_customer_past_orders( $customer_id );
}
add_action( 'woocommerce_created_customer', 'action_woocommerce_created_customer', 10, 3 );
That is it. You don’t need to remove this snippet.
6 comments
Unity Garcia
Amazing! Thanks
stephen
I get the error:
Fatal error: Uncaught Error: Call to undefined method Automattic\WooCommerce\Admin\Overrides\OrderRefund::get_billing_email()
I think its an issue with.
$order = wc_get_order($order_id);
getting refunded orders. but I dont know how to correct it !
Jason
nevermind apparently this comment section wont allow the full code.
Jason
here is a little bit better snippet with some on screen info about the process and success.
“ids”, // Just the ids
“posts_per_page” => 10, // Number of orders to retrieve per request
“paged” => $page, // Pagination
];
$order_ids = wc_get_orders($args);
$total_orders = count($order_ids);
if ($total_orders == 0) {
// No more orders to process
break;
}
echo “Processing page {$page} with {$total_orders} orders”;
foreach ($order_ids as $order_id) {
// Get the order
$order = wc_get_order($order_id);
// Check if the order is a regular order and not a refund
if ($order && is_a($order, ‘WC_Order’)) {
// Check if the order was made by a guest
if (0 == $order->get_user_id()) {
// This order was made by a guest
// Now we get the order’s billing email
$order_email = $order->get_billing_email();
// Search for a user with this email
$user = get_user_by(“email”, $order_email);
if ($user) {
// We found a user with this email
// Now we link the order to this user
$order->set_customer_id($user->ID);
$order->save();
echo “Linked order {$order_id} to user {$user->ID}”;
} else {
echo “No user found with email {$order_email} for order {$order_id}”;
}
} else {
echo “Order {$order_id} already linked to a user”;
}
} else {
echo “Order {$order_id} is a refund or invalid order”;
}
$processed_orders++;
echo “Total processed orders: {$processed_orders}”;
flush(); // Flush the output buffer to see progress in real-time
}
$page++; // Increment the page number for the next batch of orders
}
echo “Processing completed.”;
?>
Celine Van Cauwenberghe
Hi,
I have added the php to link guest orders by emails to my server about 3hrs ago, but nothing happens.
Is this still functional?
Orders remain being unlinked. I have about 1000 orders to be linked, do I need to wait longer? How will I know the php has done the job?
Would love your help.
Thanks,
Celine
Stephen Lavender
link new guest orders to existing users (by email) use this snippet:
//assign user in guest order
add_action( ‘woocommerce_new_order’, ‘action_woocommerce_new_order’, 10, 1 );
function action_woocommerce_new_order( $order_id ) {
$order = new WC_Order($order_id);
$user = $order->get_user();
if( !$user ){
//guest order
$userdata = get_user_by( ’email’, $order->get_billing_email() );
if(isset( $userdata->ID )){
//registered
update_post_meta($order_id, ‘_customer_user’, $userdata->ID );
}else{
//Guest
}
}
}