Modifying the WooCommerce Product Query

I recently answered a few questions on Stack Overflow and they all seemed to related to how to change what products show up in the shop loop… or any other of the shop archives. I figured I would compile them altogether for reference and inspiration.

WooCommerce builds a custom query for products in its WC_Query class by hooking into the classic pre_get_posts hook and changing WordPress’s query parameters to get the desired products. WooCommerce then removes itself from the query afterwards. I’m not 100% sure of why, but I presume there is a good reason. It might be running in WooCommerce, but it is still a regular WordPress query and so the regular WP_Query Parameters apply.

Like most WooCommerce code there is a convenient action hook right in the middle of the product_query() method which will allow us to hook in and make whatever changes we might like to the product query. Note that $q is the query and $this is the WC_Query class instance.

PHP

1

2

do_action('woocommerce_product_query',$q,$this);

Example 1: Display Only On-Sale products in Shop

WooCommerce has a shortcode for displaying the most recent on-sale products. So I took a look at how it sets up it’s query args for get_posts(). Turns out WC has a built-in function that returns the IDs of any on-sale products: wc_get_product_ids_on_sale(). So we get grab those ids and then query for those specific posts using the post__in parameter of WP_Query.

As is typical in dealing with WordPress query objects we can use get() and set() methods to retrieve info about the query or set new parameters.

PHP

1

2

3

4

5

6

7

8

9

10

add_action('woocommerce_product_query','so_20990199_product_query');

functionso_20990199_product_query($q){

$product_ids_on_sale=wc_get_product_ids_on_sale();

$q->set('post__in',(array)$product_ids_on_sale);

}

Example 2: Hide Products that are Part of a Grouped Product

The person who asked this question has a lot of products that part of other grouped products and didn’t want to display the items individually and in their grouped versions. Turns out that items that are part of a group have the grouped product as their post_parent. Any grouped product (or other top-level, non-grouped product) will have 0 as a post parent. Therefore, we can set the query argument to require a post parent of 0 and effectively eliminate any grouped items.

PHP

1

2

3

4

5

6

add_action('woocommerce_product_query','so_27975262_product_query');

functionso_27975262_product_query($q){

$q->set('post_parent',0);

}

Example 3: Hide Specific Out of Stock Items

WooCommerce has a setting that allows you to hide all out of stock items from your store. But what if you don’t want a nuclear option and want to have a little finer control over which items are going to be hidden.

We can’t skip straight to the query, because WooCommerce doesn’t have this data. So we need to add some meta. You could use a custom field, but that isn’t as friendly as adding a little checkbox to the product data metabox. I thought it would be appropriate if we placed it near the stock status inputs. WooCommerce already has functions for creating most of the standard input types so we’ll use that to our advantage.

woocommerce_wp_checkbox(array('id'=>'_hide_if_out_of_stock','wrapper_class'=>'show_if_simple show_if_variable','label'=>__('Hide this product from archives when out of stock?','your-plugin-domain')));

}

Then we need to save this data. Normally, I’d save a checkbox as ‘yes’ versus ‘no’ like WooCommerce does. However, getting the product query correct (as you’ll see a little later on), required that the meta exist when you wanted to hide the item and not exist at all otherwise… hence the if/else update_post_meta() versus delete_post_meta()

And now we can get to the query. Because WooCommerce already has a few keys in its default meta query and the default meta query operator is AND (meaning my conditions had to be met in addition to what WooCommerce was already looking for), I couldn’t figure out a way to query for posts that had meta equal to a specific key. But because WordPress supports EXISTS and NOT EXISTS comparisons, we can look for posts that way. What I’ve done is in the case where you aren’t mass hiding all out of stock items via the plugin option, this code will modify the meta query so that any item that does not have the meta key _hide_if_out_of_stock will be shown. That is a counter-intuitive way of saying that any product where the box “hide when out of stock” is checked will be hidden.

PHP

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

add_action('woocommerce_product_query','so_27971630_product_query');

functionso_27971630_product_query($q){

$meta_query=$q->get('meta_query');

if(get_option('woocommerce_hide_out_of_stock_items')=='no'){

$meta_query[]=array(

'key'=>'_hide_if_out_of_stock',

'compare'=>'NOT EXISTS'

);

}

$q->set('meta_query',$meta_query);

}

Those are the three uses of modifying WooCommerce’s product query that I have encountered in the past few days. Anybody else doing anything interesting to the product query?