Wordpress - WP_Query with two post types, but requiring category on only one of those post types

I happened to be working on something similar today and just remembered you were looking for something like this, so let me share it with you.

Here's how one can use the posts_where filter to restrict the WP_Query, to posts in some custom post type cpt1 OR another post type cpt2 that's attached to some taxonomy term.

Here's an example:

add_filter( 'posts_where', 'wpse_posts_where' );
$loop = new WP_Query( $args );


 * Restrict WP_Query to ( cpt1 OR cpt2 attached to a given term in some taxonomy).
 * @see http://wordpress.stackexchange.com/a/173889/26350

function wpse_posts_where( $where )
    global $wpdb;

    // Run this filter callback only once:
    remove_filter( current_filter(), __FUNCTION__ );

    // Modify this to your needs:  
    $cpt1       = 'image';     
    $cpt2       = 'video';    
    $taxonomy   = 'post_tag';     // Related to cpt2
    $term_slug  = 'editor-pick';  // Related to cpt2    

    // Get the term info for the term_taxonomy_id:
    $term = get_term_by( 'slug', $term_slug, $taxonomy );

    // Modify the SQL query:
    if( ! is_wp_error( $term ) )
        $where .= " AND ( {$wpdb->posts}.post_type = '{$cpt1}' 
            OR {$wpdb->posts}.post_type = '{$cpt2}' 
            AND {$wpdb->posts}.post_status = 'publish'
            AND {$wpdb->posts}.ID IN (
                SELECT object_id FROM {$wpdb->term_relationships} 
                WHERE term_taxonomy_id IN ( {$term->term_taxonomy_id} ) ) ) ";
    return $where;

where you might have to modify it further to your needs.

Unfortunately, you can't. At least not with a single WP_Query, that is. What you could do, however, is run two separate queries - one for each post type - then combine the results. Like so:

$args = array( 
    'post_type'     => 'image',
    'category_name' => $cat,
    'meta_key'      => 'total_votes',
    'orderby'       => 'date meta_value_num',
$loop = new WP_Query( $args );

$args = array( 
    'post_type'     => 'video',
    'category_name' => $cat,
    'tag'           => 'editor-pick',
    'meta_key'      => 'total_votes',
    'orderby'       => 'date meta_value_num',
$loop2 = new WP_Query( $args );

$loop->posts = array_merge( $loop->posts, $loop2->posts );
$loop->found_posts += $loop2->found_posts;
$loop->max_num_pages = ceil( $loop->found_posts / $loop->query_vars[ 'posts_per_pages' ] );

Note: This is untested code. I hope it may be of help to you, though.


Wp Query