WP_Query | Help me create a search term with an ‘OR’ relation?

(Revised on March 25 2020 UTC)

So in this revised answer, I’ll just begin with the code:

  1. Define the variables: (I intentionally included only the $args part)

    // define POSTed/submitted variables here like $paged, $display_count and $direction
    
    // define the offset/'direction' stuff here
    
    // then define your $args array
    $args = array(
        'post_type'      => 'product',
        'post_status'    => 'publish',
        'orderby'        => 'menu_order',
        'order'          => 'ASC',
        'posts_per_page' => $display_count,
        // You should use 'paged' and not 'page'.
        'paged'          => $paged,
        'offset'         => $offsetcalc >= 1 ? $offsetcalc : '',
        'tax_query'      => array(),
    );
    
  2. And here’s the main code, for the “search term OR”:

    // This is basically $args['tax_query'], except that we *don't* add it to $args.
    $tax_query = array();
    
    if ( ! empty( $search_tags ) ) {
        $tax_query[] = array(
            'taxonomy' => 'product_tag',
            'terms'    => wp_parse_slug_list( $search_tags ),
            'field'    => 'slug',
        );
    }
    
    if ( ! empty( $search_categories ) ) {
        $tax_query[] = array(
            'taxonomy' => 'product_cat',
            'terms'    => wp_parse_slug_list( $search_categories ),
            'field'    => 'slug',
        );
    }
    
    if ( ! empty( $tax_query ) ) { // if empty, don't set the relation
        $tax_query['relation'] = $search_relation;
    }
    
    $_filter = true; // a (private) var for the closure below
    add_filter( 'posts_clauses',
    function ( $clauses ) use ( &$_filter, $search_term, $tax_query ) {
        if ( ! $_filter ) {
            return $clauses;
        }
    
        global $wpdb;
    
        $search_where="";
        if ( $search_term ) {
            $like="%" . $wpdb->esc_like( $search_term ) . '%';
            $search_where = $wpdb->prepare( "$wpdb->posts.post_title LIKE %s", $like );
        }
    
        if ( ! empty( $tax_query ) ) {
            $tax_query = new WP_Tax_Query( $tax_query );
            $is_and = ( 'AND' === $tax_query->relation );
    
            $tax_query = $tax_query->get_sql( $wpdb->posts, 'ID' );
            if ( empty( $tax_query['where'] ) ) { // just in case..
                if ( $search_where ) {
                    $clauses['where'] .= " AND ( $search_where )";
                }
                return $clauses;
            }
    
            $clauses['join'] .= $tax_query['join'];
            if ( $is_and ) {
                $clauses['where'] .= $search_where ?
                                     " AND ( $search_where ) {$tax_query['where']}" :
                                     $tax_query['where'];
            } else {
                $where = preg_replace( '/^ *AND\b/', '', $tax_query['where'] );
                $clauses['where'] .= $search_where ?
                                     " AND ( ( $search_where ) OR{$where} )" :
                                     $tax_query['where'];
            }
            $clauses['groupby'] = "$wpdb->posts.ID";
        } elseif ( $search_where ) {
            $clauses['where'] .= " AND ( $search_where )";
        }
    
        return $clauses;
    } );
    
    $the_query = new WP_Query( $args );
    $_filter = false; // disable the filter, to avoid issues with other WP_Query calls
    

A bit of explanation (for code #2 above)

What you’re looking for is a WHERE clause that looks something like:

(There are other conditions/variations, but this is the main one)

WHERE post_type="product" AND (
  ( post_title LIKE '%<search term>%' ) # Find posts that matched the search term,
  OR ( product_tag IN (<ids/slugs>) )   # ..or those that are in the specified tags
  OR ( product_cat IN (<ids/slugs>) )   # ..or categories.
)

So in that code #2 above, I’m using the posts_clauses hook to add that WHERE clause (and do other relevant stuff), but for generating the proper SQL clauses for the taxonomy (tag/category) queries, I’m using WP_Tax_Query (which is what WP_Query uses, btw).

Additional Notes

  1. I’m using wp_parse_slug_list() to parse the (comma-separated) tags and categories into an array of slugs. And you should also be using it to make sure we have a valid array and because the function just works well. 🙂

  2. Remember that if offset is 0, then in the $args, the offset should be set to '' (i.e. an empty string) instead of 0, to make sure the $paged works as expected.

  3. Just a personal note… sorry, I forgot about using WP_Tax_Query before. 😉

Leave a Comment