Two women window shopping

WooCommerce Function of the Week: wc_get_customer_last_order

This week we'll take a look at WooCommerce orders and a neat WooCommerce core function called wc_get_customer_last_order.

Nothing mysterious here β€” pass a customer ID, and with a PHP one-liner, you get immediately the customer's latest order!

This can be used in several scenarios. Think for example of an email campaign that triggers six months after the customer's last order to entice them to reorder. (In marketing, you'd call it a “Win-Back” campaign.)

But before looking at some case studies, let's analyze the function syntax and source code.

Function Syntax

wc_get_customer_last_order() function is under woocommerce\includes\wc-user-functions.php:

/**
 * Get info about customer's last order.
 *
 * @since 2.6.0
 * @param int $customer_id Customer ID.
 * @return WC_Order|bool Order object if successful or false.
 */
function wc_get_customer_last_order( $customer_id ) {
	$customer = new WC_Customer( $customer_id );

	return $customer->get_last_order();
}

I personally go mad when I see blank lines for no apparent reason within WooCommerce core functions, but that's just me. πŸ˜ƒ

The wc_get_custmer_last_order() function returns the output of another function called get_last_order() that you can find under woocommerce\includes\data-stores\class-wc-customer-data-store.php:

/**
 * Gets the customers last order.
 *
 * @since 3.0.0
 * @param WC_Customer $customer Customer object.
 * @return WC_Order|false
 */
public function get_last_order( &$customer ) {
	$last_order = apply_filters(
		'woocommerce_customer_get_last_order',
		get_user_meta( $customer->get_id(), '_last_order', true ),
		$customer
	);
	if ( '' === $last_order ) {
		global $wpdb;
		$last_order = $wpdb->get_var(
			// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
			"SELECT posts.ID
			FROM $wpdb->posts AS posts
			LEFT JOIN {$wpdb->postmeta} AS meta on posts.ID = meta.post_id
			WHERE meta.meta_key = '_customer_user'
			AND   meta.meta_value = '" . esc_sql( $customer->get_id() ) . "'
			AND   posts.post_type = 'shop_order'
			AND   posts.post_status IN ( '" . implode( "','", array_map( 'esc_sql', array_keys( wc_get_order_statuses() ) ) ) . "' )
			ORDER BY posts.ID DESC"
			// phpcs:enable
		);
		update_user_meta( $customer->get_id(), '_last_order', $last_order );
	}
	if ( ! $last_order ) {
		return false;
	}
	return wc_get_order( absint( $last_order ) );
}

Well, that's an easy one to understand: get_last_order calls the WordPress database, selects the posts and postmeta tables, sets the customer ID, and looks for the shop order with the greatest ID β€” which means it's the most recent order.

In return, we get the $order object.

Brilliant!

Simply declare a customer ID, call wc_get_customer_last_order( $customer_id ), and get whatever data you need.

Take a look at my WooCommerce: Get Order Info (total, items, etc) From $order Object tutorial to learn how to get any kind of data from that last order, such as the paid-on date, its product IDs, or even the total amount transacted.

Case Studies

Earlier we mentioned a Win-Back campaign as a scenario where your customers' last orders might be a way to re-engage them. It could be as simple as defining a time period (based on your business and customer patterns), and then sending a “come back!” email. Or display a message to customers who didn't place an order within that timeframe.

Let's consider the second option β€” displaying a notification message to logged-in customers if they haven't placed an order in the last 6 months. Here's how I do it:

/**
 * @snippet       Win-Back notice | WooCommerce My Account
 * @how-to        Get CustomizeWoo.com FREE
 * @author        Rodolfo Melogli
 * @compatible    WooCommerce 6
 * @donate $9     https://businessbloomer.com/bloomer-armada/
 */
 
add_action( 'woocommerce_account_content', 'bbloomer_notice_if_ordered_7_months_ago' );

function bbloomer_notice_if_ordered_7_months_ago() {
	$customer_id = get_current_user_id();
	$last_order = wc_get_customer_last_order( $customer_id );
	if ( ! $last_order ) return; // BAIL IF NO PREVIOUS ORDERS
	if ( time() > strtotime( $last_order->get_date_created() . ' +6 months' ) ) {
		wc_add_notice( 'Been a while! Use coupon code XXXYYYZZZ for 10% off today!', 'notice' ); // WP_PRINT_NOTICE MAY WORK BETTER? PLEASE TEST
	}
}

Screenshot:

If today is July 11, 2022 or later, this message will appear because the last order for the logged-in customer will have been at least six months in the past.

Other case studies could see the use of wc_get_customer_last_order function to determine a specific product offer. (For example, “Buy product XYZ again ad get 15% off today!”)

To do this, you'd simply loop through the order items, and then use my WooCommerce: Set / Override Product Price Programmatically guide to set the price for the logged-in customer.

Also, you could use wc_get_customer_last_order for special offers, such as giving away a freebie in case the last order total is > $200.

What would you use the wc_get_customer_last_order for? I'm curious β€” let me know in the comments!

Similar Posts

3 Comments

  1. Nice snippet. Thanks. However if someone gets holds of the coupon code and shares it with the friends, they would be able to use the code as well. We also need to prevent it at coupon level, so when user applies the coupon, we only allow it if the customer hasn’t placed any order for last 6 months. Not sure which filter for coupon can be used in combination with this here. Any ideas?

    1. There’s a technical answer to that question I’m sure Rodolfo can answer, but it seems like a generally bad move to worry about blocking coupon abuse when you can simply limit the coupon settings to one per person and set a cap on how many will be accepted. Then keep an eye on what happens. If additional people (existing or new customers) place orders they wouldn’t have made without a coupon, is that a problem? In what actual business case would it be bad for a coupon to be used beyond its intended recipients? Those are leads and sales!

Comments are closed.