Kudos to Appleman1234 for two answers, both of which will work.
ApppleMan1234 the first answer he gave as an example is to look at all the products, and then filter them by calling wc_customer_bought_product() . This will certainly work. If you have n products, you will make n+1 queries to the database.
His second sentence is a link to a post written by Brages Singh, who posted the decision on fusedpress.com on June 2, 2013. The original post is no longer available. I found a cached copy on Google.
The Brajesh Singh solution requests custom orders, then requests the details of the order and the latter requests the product identifier in the order item metadata. This solution will always be only 3 queries. If your store has only 1 or 2 products, this solution is much better.
Here is a slightly edited version of Brajesh Singh's code.
/** * Get all Products Successfully Ordered by the user * @return bool|array false if no products otherwise array of product ids */ function so28362162_get_all_products_ordered_by_user() { $orders = so28362162_get_all_user_orders(get_current_user_id(), 'completed'); if(empty($orders)) { return false; } $order_list = '(' . join(',', $orders) . ')';//let us make a list for query //so, we have all the orders made by this user that were completed. //we need to find the products in these orders and make sure they are downloadable. global $wpdb; $query_select_order_items = "SELECT order_item_id as id FROM {$wpdb->prefix}woocommerce_order_items WHERE order_id IN {$order_list}"; $query_select_product_ids = "SELECT meta_value as product_id FROM {$wpdb->prefix}woocommerce_order_itemmeta WHERE meta_key=%s AND order_item_id IN ($query_select_order_items)"; $products = $wpdb->get_col($wpdb->prepare($query_select_product_ids, '_product_id')); return $products; } /** * Returns all the orders made by the user * @param int $user_id * @param string $status (completed|processing|canceled|on-hold etc) * @return array of order ids */ function so28362162_get_all_user_orders($user_id, $status = 'completed') { if(!$user_id) { return false; } $args = array( 'numberposts' => -1, 'meta_key' => '_customer_user', 'meta_value' => $user_id, 'post_type' => 'shop_order', 'post_status' => 'publish', 'tax_query' => array( array( 'taxonomy' => 'shop_order_status', 'field' => 'slug', 'terms' => $status ) ) ); $posts = get_posts($args); //get the post ids as order ids return wp_list_pluck($posts, 'ID'); }
Combining this with the product loop from the question, plus the not obsolete wc_get_template_part() and adding posts_per_page=-1 gives us
<ul class="products"> <?php $args = array( 'post_type' => 'product', 'post__in' => so28362162_get_all_products_ordered_by_user(), 'posts_per_page' => -1 ); $loop = new WP_Query($args); if($loop->have_posts()) { while($loop->have_posts()) : $loop->the_post(); wc_get_template_part('content', 'product'); endwhile; } else { echo __('No products found'); } wp_reset_postdata(); ?> </ul>