4

I have a simple WP_Query:

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

How can I alter this so that it return only videos with the tag editor-pick while still returning all images regardless of tags?

I'm looking for a filter to add onto the MySQL query so that order and pagination remain intact.

  • Where is this query happening? A widget? – Tom J Nowell Dec 3 '14 at 1:50
  • Yes, you're going to want to use a query filter, along with some more advanced sql queries to achieve this goal. Just using the WP_Query object won't achieve this alone. – Hybrid Web Dev Dec 3 '14 at 4:56
  • 1
    I'm sure @tf is pretty close to solve this for you with his solution (+1), but in the meanwhile I wonder if this kind of experiment can help you? – birgire Dec 30 '14 at 19:03
  • @birgire, that's exactly what I need! Thanks so much for putting together that plugin! – jetlej Dec 30 '14 at 23:59
  • 1
    The reason I wrote that it wasn't supported is that in general one sub-query might not contain any meta query, so ordering by a meta value might then not make sense. We must order by a field that's contained in both (all) sub-queries. I just played with it and found a way to order the combined query (where all the sub-queries contain a meta query) by a meta value, I will update that plugin version (plus some bug fixes) on Github in couple of days. Then we could continue on the Issue tracker over there. – birgire Dec 31 '14 at 11:00
4
+50

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 );

where

/**
 * 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.

  • 1
    You are a god! Thanks so much! One note for people perusing this answer in the future, you would have to still ask for both images and videos in the WP_Query args ($args). – jetlej Jan 4 '15 at 9:00
  • This is really really awesome!!!! – Ahmed Ginani Apr 17 '18 at 11:07
3

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.

  • I'd prefer to use a filter to add onto the MySQL query so that order and pagination are not messed with. – jetlej Dec 3 '14 at 0:16
0
// tell WordPress about our new query var

function wpse52480_query_vars( $query_vars ){   
    $query_vars[] = 'my_special_query';
    return $query_vars;
}
add_filter( 'query_vars', 'wpse52480_query_vars' );

// check if our query var is set in any query

function wpse52480_pre_get_posts( $query ){    
    if( isset( $query->query_vars['my_special_query'] ) )

        // do special stuff

    return $query;
}
add_action( 'pre_get_posts', 'wpse52480_pre_get_posts' );

and in the template:

// set the query var (along with whatever others) to trigger the filter

$args = array(
    'my_special_query' => true
);
$my_secondary_loop = new WP_Query( $args );
0

You can use the taxonomy params to achieve this https://codex.wordpress.org/Class_Reference/WP_Query#Taxonomy_Parameters

  • 2
    Can you explain a little bit more. – bravokeyl Oct 13 '16 at 18:21

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.