How to filter products using filter products by attribute widget and OR logic between different product attribute types?

Issue #1 (nested tax_query with OR relation)


After going through WordPress core and WooCommerce core, it appears
query_type_bag-type=or (query_type_.$attribute) query arg is only changing the value of the operator key on the individual taxonomy array for the attribute.

For example:

  • query_type_pa_color=and = operator => 'AND'
  • query_type_pa_color=or = operator => 'IN'

You can see in the screenshot below the way the tax_query is built with an example URL structure such as:

...?filter_color=blue&query_type_color=or&filter_size=small&query_type_size=or

The issue here is that the tax_query relation is set to ‘AND’ by default, and in my example we can also see there is another tax_query array for product_visibility.

![enter image description here

What we want to do is nest the tax_queries for our attributes and set a relation on that nested tax_query to OR.

Example of our nested tax_query:

enter image description here

To achieve this you need to hook onto the pre_get_posts filter, an example below is provided (modify the attributes to suite your configuration):

add_filter(
    'pre_get_posts',
    function( $query ) {

        if ( $query->is_main_query() ) {

            $tax_query = $query->get( 'tax_query' );

            // get the index of each taxonomy set in the tax_query
            $taxonomy_indexes = array_column( $tax_query, 'taxonomy' );

            $allowed = array(
                'pa_color',
                'pa_size',
            );

            // set nested query relation to OR
            $nested_tax_query = array( 'relation' => 'OR' );

            foreach ( $taxonomy_indexes as $key => $taxonomy ) {

                if ( ! in_array( $taxonomy, $allowed ) ) {
                    continue;
                }

                // push each taxonomy onto new array
                $nested_tax_query[] = $tax_query[ $key ];

                // we don't want this taxonomy in the initial tax_query
                // as it was provided in $query->query_vars['tax_query']
                unset( $tax_query[ $key ] );

            }

            // put our nested query back onto $tax_query ( $query->get('tax_query') )
            $tax_query[] = $nested_tax_query;

            // overwrite $query->query_vars['tax_query'] with newly built $tax_query array
            $query->set( 'tax_query', $tax_query );

        }

    }
);

This will give you the result you are after.

UPDATE:

Issue #2 (WooCommerce attribute widget hides unmatched terms/taxonomies after $_GET request)


To answer your other question in the comments regarding how the WooCommerce attribute widget works.

This resolves the above stated issue. However, in default woocommerce filter by attribute widget, the attributes for bag-type do not show when we have filtered with wallet-type using this url – https://www.example.com/product-category/customized-bags/?filter_wallet-type=womens-wallet&query_type_wallet-type=or. How to resolve this?

I ran a test and I see that issue is unfortunately the way that WooCommerce attribute widget is designed. Whatever attributes you select and filter (as seen in the URL), the widget then excludes all the other terms of that attribute taxonomy and also any other attribute taxonomies themselves, in your case bag-type.

The class responsible for generating the widget is:

  • woocommerce/includes/widgets/class-wc-widget-layered-nav.php

There are filters/hooks in there you could probably adjust to work around this. But… I would just create my own filters instead of using the WooCommerce one (if it were me). In fact you could probably extend the WooCommerce widget with a sub class and overwrite methods too.

E.g. class Custom_WC_Widget_Layered_Nav extends WC_Widget_Layered_Nav {}

The class WC_Widget_Layered_Nav is already extending WC_Widget which then extends WP_Widget.

Refer to:

enter image description here