wc_customer_bought_product()WC 1.0

Checks if a user (by email or ID or both) has bought an item.

Returns

true|false.

Usage

wc_customer_bought_product( $customer_email, $user_id, $product_id );
$customer_email(string) (required)
Customer email to check.
$user_id(int) (required)
User ID to check.
$product_id(int) (required)
Product ID to check.

wc_customer_bought_product() code WC 10.3.3

function wc_customer_bought_product( $customer_email, $user_id, $product_id ) {
	global $wpdb;

	$result = apply_filters( 'woocommerce_pre_customer_bought_product', null, $customer_email, $user_id, $product_id );

	if ( null !== $result ) {
		return $result;
	}

	/**
	 * Whether to use lookup tables - it can optimize performance, but correctness depends on the frequency of the AS job.
	 *
	 * @since 9.7.0
	 *
	 * @param bool $enabled
	 * @param string $customer_email Customer email to check.
	 * @param int    $user_id User ID to check.
	 * @param int    $product_id Product ID to check.
	 * @return bool
	 */
	$use_lookup_tables = apply_filters( 'woocommerce_customer_bought_product_use_lookup_tables', false, $customer_email, $user_id, $product_id );

	if ( $use_lookup_tables ) {
		// Lookup tables get refreshed along with the `woocommerce_reports` transient version (due to async processing).
		// With high orders placement rate, this caching here will be short-lived (suboptimal for BFCM/Christmas and busy stores in general).
		$cache_version = WC_Cache_Helper::get_transient_version( 'woocommerce_reports' );
	} elseif ( '' === $customer_email && $user_id ) {
		// Optimized: for specific customers version with orders count (it's a user meta from in-memory populated datasets).
		// Best-case scenario for caching here, as it only depends on the customer orders placement rate.
		$cache_version = wc_get_customer_order_count( $user_id );
	} else {
		// Fallback: create, update, and delete operations on orders clears caches and refreshes `orders` transient version.
		// With high orders placement rate, this caching here will be short-lived (suboptimal for BFCM/Christmas and busy stores in general).
		// For the core, no use-cases for this branch. Themes/extensions are still valid use-cases.
		$cache_version = WC_Cache_Helper::get_transient_version( 'orders' );
	}

	$cache_group = 'orders';
	$cache_key   = 'wc_customer_bought_product_' . md5( $customer_email . '-' . $user_id . '-' . $use_lookup_tables );
	$cache_value = wp_cache_get( $cache_key, $cache_group );

	if ( isset( $cache_value['value'], $cache_value['version'] ) && $cache_value['version'] === $cache_version ) {
		$result = $cache_value['value'];
	} else {
		$customer_data = array( $user_id );

		if ( $user_id ) {
			$user = get_user_by( 'id', $user_id );

			if ( isset( $user->user_email ) ) {
				$customer_data[] = $user->user_email;
			}
		}

		if ( is_email( $customer_email ) ) {
			$customer_data[] = $customer_email;
		}

		$customer_data = array_map( 'esc_sql', array_filter( array_unique( $customer_data ) ) );
		$statuses      = array_map( 'esc_sql', wc_get_is_paid_statuses() );

		if ( count( $customer_data ) === 0 ) {
			return false;
		}

		if ( OrderUtil::custom_orders_table_usage_is_enabled() ) {
			$statuses       = array_map(
				function ( $status ) {
					return "wc-$status";
				},
				$statuses
			);
			$order_table    = OrdersTableDataStore::get_orders_table_name();
			$user_id_clause = '';
			if ( $user_id ) {
				$user_id_clause = 'OR o.customer_id = ' . absint( $user_id );
			}
			if ( $use_lookup_tables ) {
				// HPOS: yes, Lookup table: yes.
				$sql = "
SELECT DISTINCT product_or_variation_id FROM (
SELECT CASE WHEN product_id != 0 THEN product_id ELSE variation_id END AS product_or_variation_id
FROM {$wpdb->prefix}wc_order_product_lookup lookup
INNER JOIN $order_table AS o ON lookup.order_id = o.ID
WHERE o.status IN ('" . implode( "','", $statuses ) . "')
AND ( o.billing_email IN ('" . implode( "','", $customer_data ) . "') $user_id_clause )
) AS subquery
WHERE product_or_variation_id != 0
";
			} else {
				// HPOS: yes, Lookup table: no.
				$sql = "
SELECT DISTINCT im.meta_value FROM $order_table AS o
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON o.id = i.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
WHERE o.status IN ('" . implode( "','", $statuses ) . "')
AND im.meta_key IN ('_product_id', '_variation_id' )
AND im.meta_value != 0
AND ( o.billing_email IN ('" . implode( "','", $customer_data ) . "') $user_id_clause )
";
			}
			$result = $wpdb->get_col( $sql );
		} elseif ( $use_lookup_tables ) {
			// HPOS: no, Lookup table: yes.
			$result = $wpdb->get_col(
				"
SELECT DISTINCT product_or_variation_id FROM (
SELECT CASE WHEN lookup.product_id != 0 THEN lookup.product_id ELSE lookup.variation_id END AS product_or_variation_id
FROM {$wpdb->prefix}wc_order_product_lookup AS lookup
INNER JOIN {$wpdb->posts} AS p ON p.ID = lookup.order_id
INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' )
AND pm.meta_key IN ( '_billing_email', '_customer_user' )
AND pm.meta_value IN ( '" . implode( "','", $customer_data ) . "' )
) AS subquery
WHERE product_or_variation_id != 0
		"
			); // WPCS: unprepared SQL ok.
		} else {
			// HPOS: no, Lookup table: no.
			// phpcs:disable WordPress.DB.PreparedSQL.NotPrepared
			$result = $wpdb->get_col(
				"
SELECT DISTINCT im.meta_value FROM {$wpdb->posts} AS p
INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
INNER JOIN {$wpdb->prefix}woocommerce_order_items AS i ON p.ID = i.order_id
INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta AS im ON i.order_item_id = im.order_item_id
WHERE p.post_status IN ( 'wc-" . implode( "','wc-", $statuses ) . "' ) AND p.post_type = 'shop_order'
AND pm.meta_key IN ( '_billing_email', '_customer_user' )
AND im.meta_key IN ( '_product_id', '_variation_id' )
AND im.meta_value != 0
AND pm.meta_value IN ( '" . implode( "','", $customer_data ) . "' )
		"
			);
			// phpcs:enable WordPress.DB.PreparedSQL.NotPrepared
		}
		$result = array_map( 'absint', $result );

		wp_cache_set(
			$cache_key,
			array(
				'version' => $cache_version,
				'value'   => $result,
			),
			$cache_group,
			MONTH_IN_SECONDS
		);
	}
	return in_array( absint( $product_id ), $result, true );
}