Modifying the WooCommerce Product Query

Modifying the WooCommerce Product Query

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 afterward. I am 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.

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

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 its 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 to 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.

function bd_change_product_query( $q ){

    $product_ids_on_sale = wc_get_product_ids_on_sale();

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

}

add_action( 'woocommerce_product_query', 'bd_change_product_query' );

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 did not 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 ID. 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.

function bd_change_product_query( $q ){
    $q->set( 'post_parent', 0 );
}

add_action( 'woocommerce_product_query', 'bd_change_product_query' ); 

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 cannot skip straight to the query because WooCommerce does not have this data. So we need to add some meta. You could use a custom field, but that is not 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 will use that to our advantage.

function bd_wc_product_options_stock_status(){
    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' ) 
		)
	);
}

add_action( 'woocommerce_product_options_stock_status', 'bd_wc_product_options_stock_status' );

Then we need to save this data. Normally, I would save a checkbox as ‘yes’ versus ‘no’ like WooCommerce does. However, getting the product query correct (as you will 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()

function bd_wc_admin_process_product_object( $post_id ){
    if( isset( $_POST['_hide_if_logged_out'] ) ) {
        $product->update_meta_data( '_hide_if_out_of_stock', 'yes' );
    } else {
        $product->delete_meta_data( '_hide_if_out_of_stock' );
    }
}

add_action( 'woocommerce_admin_process_product_object', 'bd_wc_admin_process_product_object' );

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 could not 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 have done is in the case where you are not 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.

function bd_change_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 );
}

add_action( 'woocommerce_product_query', 'bd_change_product_query' );

This code  was offered by the user of our site:

function bd_change_product_query( $q ){
	
  $meta_query = $q->get( 'meta_query' );
  
	$meta_query[] = array(
		'key' => 'custom_acf_key',
		'value' => 'custom_acf_value',
		'compare' => '='
	);
  
  $q->set( 'meta_query', $meta_query );
}

add_action( 'woocommerce_product_query', 'bd_change_product_query' );

Author`s website

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.