SOLVED: How to customize Bricks AJAX Filter to exclude Products with Out-of-Stock Variations

Browser: Version 141.0.7390.55 (Official Build) (arm64)
OS: macOS
URL: Παιδικές Μπλούζες - MyBabyLand

1. Main Goal
I want the native Bricks Filter element on my WooCommerce archive pages to work correctly with variable products. When a user filters by a specific product attribute (like Size “M”), the results should only show products where the “M” variation is currently in stock.

2. The Core Problem
The Bricks AJAX filtering functionality is not respecting the stock status of individual product variations. When a filter is applied, the initial result loaded via AJAX incorrectly includes products where the selected variation is out of stock.

3. Troubleshooting Steps Taken (What We Have Tried)
We have attempted to solve this issue with multiple methods, all of which have failed to fix the AJAX behavior.

Attempt #1: Modifying the Query with Standard WordPress/WooCommerce Hooks
We used several PHP code snippets with hooks like pre_get_posts and woocommerce_product_query to modify the product query.
Result: This worked only on a full page reload, but the AJAX filter results were still incorrect.

Attempt #2: Using the Bricks-Specific Hook
We used the recommended bricks/posts/query_vars PHP filter to modify the query.
Result: Same as above. It only worked on a page refresh, not during the AJAX call.

Attempt #3: JavaScript-Forced Reload
We tried a workaround to force a page reload after the AJAX completes. We created a JavaScript function (forceReloadAfterFilter) and tried to trigger it using:

  1. A general event listener for bricks.posts.updated.
  2. The native Bricks Interactions panel (Trigger: Query AJAX loader complete → Action: JavaScript (Function)).

Result: This solution did not work as expected and felt overly complex.

Attempt #4: Hiding Out-of-Stock Variations Globally (Fundamental Approach)
We changed our strategy to hide out-of-stock variations from WooCommerce entirely, so the filters wouldn’t see them.

  1. We used the woocommerce_variation_is_visible PHP filter to hide variations if !$variation->is_in_stock().
  2. When that didn’t work, we made the logic more direct, hiding variations if their stock quantity was _stock_quantity <= 0.

Result: Incredibly, even this did not solve the issue. The out-of-stock variations still seem to be visible to the Bricks filter during the AJAX request.

Attempt #5: Standard Debugging
We have confirmed the issue is not caused by:

  • Caching: All server, plugin, and browser caches have been cleared repeatedly.
  • Product Settings: Variations are correctly set to “Out of stock” and “Do not allow backorders.”
  • Plugin Conflicts: We deactivated all other plugins, and the issue persisted.

4. Summary for Support
In short, the Bricks AJAX filter does not correctly check the stock status of product variations. Standard PHP fixes work on a page refresh but are ignored by the AJAX handler. Even hiding the variations at the core WooCommerce level doesn’t seem to stop the AJAX filter from finding them. We need a reliable way to make the AJAX results match the results of a full page load.

Hi @jeisblack ,

I don’t think this is possible by tweaking query_vars for a WP_Query logic.

When you perform a filter for attribute taxonomy (size = M), Bricks will just perform a normal taxonomy query.

image

If you tick this “Hide out of stock” query setting. Bricks will also only use the normal WooCommerce taxonomy query to exclude all items that with outofstock visibility assigned. (taxonomy = product_visibility) Which means, if your other variations set to In stock, only “M” out of stock, the parent product is still considered “In stock” and will be showing in the frontend.

Please can you share with me what code you use for Attempts 1 and 2?

Regards,
Jenn

Hi, thanks for your reply, I tried with the help of AI the follows:

Attempt 1, this code works but not with Ajax Filtering, if you refresh the page manually it brings only the products with current variation only in stock that filtered.

add_filter( 'bricks/posts/query_vars', 'bricks_filter_instock_variations_query', 10, 2 );

function bricks_filter_instock_variations_query( $query_vars, $element ) {
  
    // if ( $element->id !== 'το_id_του_element_σου' ) {
    //     return $query_vars;
    // }
    
    if ( isset( $_GET['_size'] ) && ! empty( $_GET['_size'] ) ) {

        global $wpdb;

        $selected_sizes = (array) $_GET['_size'];
        $safe_sizes = array_map( 'sanitize_title', $selected_sizes );
        $placeholders = implode( ',', array_fill( 0, count( $safe_sizes ), '%s' ) );
 
        $parent_ids = $wpdb->get_col( $wpdb->prepare(
            "SELECT DISTINCT pp.ID FROM {$wpdb->posts} AS p
             INNER JOIN {$wpdb->posts} AS pp ON p.post_parent = pp.ID
             INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
             INNER JOIN {$wpdb->postmeta} AS pm2 ON p.ID = pm2.post_id
             WHERE p.post_type = 'product_variation'
             AND pp.post_type = 'product'
             AND pm.meta_key = 'attribute_pa_megethos' AND pm.meta_value IN ($placeholders)
             AND pm2.meta_key = '_stock_status' AND pm2.meta_value = 'instock'",
            ...$safe_sizes
        ) );
        
        if ( empty( $parent_ids ) ) {
            $query_vars['post__in'] = array(0);
        } else {
            
            $query_vars['post__in'] = $parent_ids;
        }
    }

    return $query_vars;
}

Attempt #2L with this code I tried to hide those Variations in Woo Core and “trick” filters to not showing them but it didn’t work at all

add_filter( 'woocommerce_variation_is_visible', 'hide_out_of_stock_variations_globally', 10, 2 );

function hide_out_of_stock_variations_globally( $is_visible, $variation_id ) {
    $variation = wc_get_product( $variation_id );

   
    if ( ! $variation->is_in_stock() ) {
        return false;
    }

    
    return $is_visible;
}

Hi @jeisblack

Please try this code and read my comments.
Replace the query id and filter id in my example with your own one.

// Use priority 1000 or higher to overwrite Bricks logic
add_filter( 'bricks/posts/query_vars', 'bricks_filter_instock_variations_query', 1000, 4 );

function bricks_filter_instock_variations_query( $query_vars, $settings, $element_id, $element_name ) {
  
    // You must use the query BRICKS ID, NOT CSS ID, my example query ID is iygeqr
    if ( $element_id !== 'iygeqr' ) {
      return $query_vars;
    }
    
    // error_log( print_r( \Bricks\Query_Filters::$active_filters, true ) );

    // Check if there are active filters and if the query with ID iygeqr exists
    if ( isset( \Bricks\Query_Filters::$active_filters['iygeqr'] ) && is_array( \Bricks\Query_Filters::$active_filters['iygeqr'] ) ) {

      $active_filters = \Bricks\Query_Filters::$active_filters['iygeqr'];
      $size_filter_active = false;
      $size_filter_value = []; // Assumed this is a checkbox filter

      // Check if filter_id mhfwnz is inside the active filters
      foreach( $active_filters as $filter ) {
        if ( isset( $filter['filter_id'] ) && $filter['filter_id'] === 'mhfwnz' ) {
          // Found the filter, break the loop
          $size_filter_active = true;
          // Get the filter value
          $size_filter_value = isset( $filter['value'] ) ? (array) $filter['value'] : [];
          break;
        }
      }

      if ( ! $size_filter_active ) {
        return $query_vars;
      }

      global $wpdb;

      $selected_sizes = (array) $size_filter_value;
      $safe_sizes = array_map( 'sanitize_title', $selected_sizes );
      $placeholders = implode( ',', array_fill( 0, count( $safe_sizes ), '%s' ) );

      $parent_ids = $wpdb->get_col( $wpdb->prepare(
          "SELECT DISTINCT pp.ID FROM {$wpdb->posts} AS p
            INNER JOIN {$wpdb->posts} AS pp ON p.post_parent = pp.ID
            INNER JOIN {$wpdb->postmeta} AS pm ON p.ID = pm.post_id
            INNER JOIN {$wpdb->postmeta} AS pm2 ON p.ID = pm2.post_id
            WHERE p.post_type = 'product_variation'
            AND pp.post_type = 'product'
            AND pm.meta_key = 'attribute_pa_megethos' AND pm.meta_value IN ($placeholders)
            AND pm2.meta_key = '_stock_status' AND pm2.meta_value = 'instock'",
          ...$safe_sizes
      ) );
      
      if ( empty( $parent_ids ) ) {
          $query_vars['post__in'] = array(0);
      } else {
          
          $query_vars['post__in'] = $parent_ids;
      }
    }

  return $query_vars;
}

Note: I didn’t check if your SQL is correct or not, I just adjusted your code so that you can get it to work in normal page reload + query filters rest endpoint.

1 Like

OMG thanks a lot it worked! I think maybe in the future update this will be included.