Checking if customer has already bought something

2019-01-11 11:49发布

问题:

I would like to create a WooCommerce plugin to add some offers for customers (that have a purchase History).

How can I check a user bought something before?

Thanks.

回答1:

Update: New updated improved, light and faster version HERE


Yes it is possible creating a conditional function that return true when a customer has already at least one order with status completed.

Here is the code for this conditional function:

function has_bought() {

    // Get all customer orders
    $customer_orders = get_posts( array(
        'numberposts' => -1,
        'meta_key'    => '_customer_user',
        'meta_value'  => get_current_user_id(),
        'post_type'   => 'shop_order', // WC orders post type
        'post_status' => 'wc-completed' // Only orders with status "completed"
    ) );
    // Count number of orders
    $count = count( $customer_orders );

    // return "true" when customer has already one order
    if ( $count > 0 ) 
        return true;
    else
        return false;
}

This code is tested and works.

This code goes in function.php file of your active child theme or theme, or in a plugin php file.


USAGE (as a condition):

  • You can use it in some WooCommerce templates that you will have previously copied to your active child theme or theme.
  • In your theme php files.
  • In plugin php files.
  • Any php function or WordPress/WooCommerce.

References

  • Template Structure + Overriding Templates via a Theme
  • Check if a customer has purchased a specific products in WooCommerce


回答2:

Here is a much light and faster conditional function that will return true if a customer has already maid a purchase.

There is an optional argument $user_id, that will allow you to specify a defined user ID:

function has_bought( $user_id = 0 ) {
    global $wpdb;
    $customer_id = $user_id == 0 ? get_current_user_id() : $user_id;
    $paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );

    $results = $wpdb->get_col( "
        SELECT p.ID FROM {$wpdb->prefix}posts AS p
        INNER JOIN {$wpdb->prefix}postmeta AS pm ON p.ID = pm.post_id
        WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )
        AND p.post_type LIKE 'shop_order'
        AND pm.meta_key = '_customer_user'
        AND pm.meta_value = $customer_id
    " );

    // Count number of orders and return a boolean value depending if higher than 0
    return count( $results ) > 0 ? true : false;
}

Code goes in function.php file of your active child theme (or theme) or also in any plugin file.

This code is tested on Woocommerce 3+ and works (It should work on previous versions too).

For multiple products: Check if a customer has purchased a specific products in WooCommerce


Usage example 1 (logged in customer)

if( has_bought() )
    echo '<p>You have already maid a purchase</p>';
else
    echo '<p>Welcome, for your first purchase you will get a discount of 10%</p>';

Usage example 2 (setting the $user_id)

// Define the user ID
$user_id = 85;

if( has_bought( $user_id ) )
        echo '<p>customer have already maid a purchase</p>';
    else
        echo '<p>Customer with 0 purchases</p>';

If the $user_id is not defined and the current user is not logged in, this function will return false.

The code of this function is partially based on the built in WooCommerce function wc_customer_bought_product source code.



回答3:

Simplified version:

function has_bought() {
    // Get all customer orders
    $customer_orders = get_posts( array(
        'numberposts' => -1,
        'meta_key'    => '_customer_user',
        'meta_value'  => get_current_user_id(),
        'post_type'   => 'shop_order', // WC orders post type
        'post_status' => 'wc-completed' // Only orders with status "completed"
    ) );
    // return "true" when customer has already one order
    return count( $customer_orders ) > 0 ? true : false;
}


回答4:

If Guest Checking is enabled the this function might help

Just send $customer_email as argument and function will check in all the orders for that email and return true or false.

function has_bought($customer_email){

  $orders = get_posts(array(
  'numberposts' => -1,
  'post_type' => 'shop_order',
  'post_status' => array('wc-processing', 'wc-completed'),
  ));

$email_array = array();

foreach($orders as $order) {

$order_obj = wc_get_order($order->ID);
$order_obj_data = $order_obj->get_data();

array_push($email_array, $order_obj_data['billing']['email']);

}


if (in_array($customer_email, $email_array)) {
return true;
} else {
return false;
}

}


回答5:

Enhanced version with paid state or just status agnostic query, also improved a bit code typing, for PHP 5.4+ :

function has_bought(int $user_id = 0, bool $paid = true ) {

    global $wpdb;

    $user_id = (empty($user_id)) ? get_current_user_id() : $user_id;

    $sql_str = "
        SELECT p.ID FROM ".$wpdb->posts." AS p
        INNER JOIN 
            ".$wpdb->postmeta." AS pm ON p.ID = pm.post_id
        WHERE 
            p.post_type LIKE 'shop_order'
        AND pm.meta_key = '_customer_user'
        AND pm.meta_value = %d
    ";

    $args = [(int) $user_id];

    if ($paid === true) {
        $paid_order_statuses = array_map( 'esc_sql', wc_get_is_paid_statuses() );
        $sql_str .= "AND p.post_status IN ( 'wc-" . implode( "','wc-", $paid_order_statuses ) . "' )";
    }

    $sql = $wpdb->prepare($sql_str, $args);

    return (bool) $wpdb->get_var( $sql );
}