How to Display All Products Purchased by User

Before diving into this tutorial I recommend you to make a look at order items and my account menu tutorials.

And below on the screenshot you can see what we are going to create.

display all products purchased by user in WooCommerce my account
Customers can easily find the products they already purchased before and… buy again.

The code below is for your current theme functions.php file or for your custom plugin.

/**
 * @snippet       Display All Products Purchased by User
 * @author        Misha Rudrastyh
 * @link          https://rudrastyh.com/woocommerce/display-purchased-products.html
 */
// here we hook the My Account menu links and add our custom one
add_filter( 'woocommerce_account_menu_items', 'misha_purchased_products_link', 40 );
function misha_purchased_products_link( $menu_links ){

	// we use array_slice() because we want our link to be on the 3rd position
	return array_slice( $menu_links, 0, 2, true )
	+ array( 'purchased-products' => 'Purchased Products' )
	+ array_slice( $menu_links, 2, NULL, true );

}

// here we register our rewrite rule
add_action( 'init', 'misha_add_products_endpoint' );
function misha_add_products_endpoint() {
	add_rewrite_endpoint( 'purchased-products', EP_PAGES );
}

// here we populate the new page with the content
add_action( 'woocommerce_account_purchased-products_endpoint', 'misha_populate_products_page' );
function misha_populate_products_page() {

	global $wpdb;

	// this SQL query allows to get all the products purchased by the current user
	// in this example we sort products by date but you can reorder them another way
	$purchased_products_ids = $wpdb->get_col( 
		$wpdb->prepare(
			"
			SELECT      itemmeta.meta_value
			FROM        " . $wpdb->prefix . "woocommerce_order_itemmeta itemmeta
			INNER JOIN  " . $wpdb->prefix . "woocommerce_order_items items
			            ON itemmeta.order_item_id = items.order_item_id
			INNER JOIN  $wpdb->posts orders
			            ON orders.ID = items.order_id
			INNER JOIN  $wpdb->postmeta ordermeta
			            ON orders.ID = ordermeta.post_id
			WHERE       itemmeta.meta_key = '_product_id'
			            AND ordermeta.meta_key = '_customer_user'
			            AND ordermeta.meta_value = %s
			ORDER BY    orders.post_date DESC
			",
			get_current_user_id()
		)
	);

	// some orders may contain the same product, but we do not need it twice
	$purchased_products_ids = array_unique( $purchased_products_ids );

	// if the customer purchased something
	if( ! empty( $purchased_products_ids ) ) {

		// it is time for a regular WP_Query
		$purchased_products = new WP_Query( array(
			'post_type' => 'product',
			'post_status' => 'publish',
			'post__in' => $purchased_products_ids,
			'orderby' => 'post__in',
			'posts_per_page' => -1,
		) );
	
		woocommerce_product_loop_start();

		while ( $purchased_products->have_posts() ) : $purchased_products->the_post();

			wc_get_template_part( 'content', 'product' );

		endwhile;

		woocommerce_product_loop_end();

		woocommerce_reset_loop();
		wp_reset_postdata();

	} else {
		echo 'Nothing purchased yet.';
	}

}

Some key points from the code above:

You can also add any icon to your menu element with a couple lines of CSS code, you can read more about it here.

.woocommerce-MyAccount-navigation ul li.woocommerce-MyAccount-navigation-link--purchased-products a:before{
	content: "\f1b2";
}
Misha Rudrastyh

Misha Rudrastyh

Hey guys and welcome to my website. For more than 10 years I've been doing my best to share with you some superb WordPress guides and tips for free.

Need some developer help? Contact me

Follow me on X