WP_Query Authors OR Categories

Custom feed slug

To create a custom feed slug, e.g.

example.tld/billyfeed/

we can use the add_feed() but we have to flush the rewrite rules to activate it. We can do that e.g. by visiting the Permalinks settings page in the backend.

To generate the feed we can use e.g. do_feed_rss2() or do_feed_atom().

Here’s an example:

add_action( 'init', function()
{
    add_feed( 'billyfeed', function()
    {
        do_feed_rss2();
    });
});

Modify the custom feed query

Next we need to modify our custom feed so that we either get the authors or categories.

Here’s a workaround using the posts_clauses filter but without using string replacements with str_replace().

/**
 * Custom feed example.tld/billyfeed that queries authors or categories
 * Supports pagination
 *
 * @link http://wordpress.stackexchange.com/a/207876/26350
 */
add_filter( 'posts_clauses', function( $clauses, \WP_Query $q ) 
{    
    //---------------------------------------
    // Only target our custom feed
    //---------------------------------------
    if( ! $q->is_feed( 'billyfeed' ) || ! $q->is_main_query() )
        return $clauses;

    global $wpdb;       

    //---------------------------------------
    // Input - we edit this to our needs
    //---------------------------------------
    $tax_query = [ 
        [
            'taxonomy' => 'category',
            'field'    => 'slug',
            'terms'    => [ 'red', 'green', 'blue' ]
        ]
    ];      
    $author__in = [ 1, 2, 3 ];  

    //---------------------------------------
    // Generate the tax query SQL
    //---------------------------------------
    $qv = [ 'tax_query' => $tax_query, 'cat' => null, 'tag' => null ];
    $q->parse_tax_query( $qv );
    $tc = $q->tax_query->get_sql( $wpdb->posts, 'ID' );

    //---------------------------------------
    // Generate the author query SQL
    //---------------------------------------
    $csv     = join( ',', wp_parse_id_list( (array) $author__in ) );
    $authors = " {$wpdb->posts}.post_author IN ( $csv ) ";

    //---------------------------------------
    // Make sure the authors are set and 
    // the tax query is valid (doesn't contain 0 = 1)
    //---------------------------------------
    if( ! empty( $author__in ) && false === strpos ( $tc['where' ], ' 0 = 1' ) ) 
    {
        // Append to the current join/where parts
        $clauses['join']  .= $tc['join'];
        $clauses['where'] .= sprintf( 
            ' AND ( %s OR ( 1=1 %s ) ) ', // The tax query SQL comes prepended with AND
            $authors,
            $tc['where']
        );
    }

    return $clauses;   

}, PHP_INT_MAX, 2 );

where we modify the $tax_query and $author__in to our needs.

This generates the following main SQL query:

SELECT SQL_CALC_FOUND_ROWS wp_posts.ID 
FROM wp_posts  
INNER JOIN wp_term_relationships 
    ON (wp_posts.ID = wp_term_relationships.object_id) 
WHERE
    1=1  
    AND wp_posts.post_type="post" 
    AND (wp_posts.post_status="publish" OR wp_posts.post_status="private") 
    AND (  wp_posts.post_authors IN (1,2,3 )  OR ( 1=1  AND ( 
        wp_term_relationships.term_taxonomy_id IN (1,43,120)
    ) ) )   
ORDER BY wp_posts.post_date DESC 
LIMIT 0, 10;

With this approach we can still paginate our feed:

example.tld/billyfeed/?paged=2