WordPress: Combine multiple WP_Query() queries into a single one, through the custom 'combined_query' attribute of the WP_Query class.
Branch: master
Clone or download
Latest commit 51973b5 Jun 27, 2018
Permalink
Type Name Latest commit message Commit time
Failed to load latest commit information.
includes Simplify the namespace May 8, 2016
.gitattributes Update Jun 27, 2018
LICENCE Version 1.0.0 May 10, 2015
README.md Update Jun 27, 2018
combined-query.php Fixed comment typo May 9, 2016
composer.json Update Jun 27, 2018
composer.lock Update Jun 27, 2018

README.md

Combined Query

WordPress plugin - Combined Query

Description

This experimental plugin allows you to combine multiple WP_Query queries into a single one, using the combined_query attribute.

This started as an answer on Stackoverflow, see here and here.

The idea behind this plugin is to combine the SQL queries for each WP_Query() query with UNION or UNION ALL.

I first noticed this technique in a great answer on WordPress Development by Mike Schinkel.

I use the trick mentioned here to preserve the order of UNION sub queries.

This implementation supports combining N sub-queries.

Notice about the new 1.0.0 version

This version is a total rewrite of the plugin.

The WP_Combine_Query class has been removed in favour of simply using the combined_query attribute of the WP_Query class.

Now the plugin only supports PHP versions 5.4+.

Default Settings

The default setup for the combined_query attribute:

'combined_query' => [        
    'args'   => [],         // [ $args1, $args2, ... ]
    'union'  => 'UNION',    // Possible values are UNION or UNION ALL
 ]

If you want to remove duplicated posts use UNION, else use UNION ALL.

Custom filters

There are two custom filters currently available:

// Modify combined ordering:
add_filter( 'cq_orderby', function( $orderby ) {
    return $orderby;
});

// Modify sub fields:
add_filter( 'cq_sub_fields', function( $fields ) {
    return $fields;
});

Installation

Upload the plugin to the plugin folder and activate it.

To install dependencies with Composer (not required):

composer install

or

php composer.phar install

within our folder. See here for more information on how to install Composer.

Then play with the examples below, in your theme or in a plugin.

Have fun ;-)

Example 1a:

Here we want to display the first published page in an alphabetical order and then the three oldest published posts:

//-----------------
// Sub query #1:
//-----------------
$args1 = [
   'post_type'      => 'page',
   'posts_per_page' => 1,
   'orderby'        => 'title',
   'order'          => 'asc',
];

//-----------------
// Sub query #2:
//-----------------
$args2 = [
   'post_type'      => 'post',
   'posts_per_page' => 3,
   'orderby'        => 'date',
   'order'          => 'asc',
];

//---------------------------
// Combined queries #1 + #2:
//---------------------------
$args = [
    'posts_per_page' => 4,
    'combined_query' => [        
        'args'   => [ $args1, $args2 ],
        'union'  => 'UNION',
    ]
];

//---------
// Output:
//---------
$q = new WP_Query( $args );
if( $q->have_posts() ):
?><ul><?php
    while( $q->have_posts() ): $q->the_post();
        ?><li><a href="<?php the_permalink();?>"><?php the_title();?></a></li><?php
    endwhile;
?></ul><?php
    wp_reset_postdata();
else:
    _e( 'Sorry no posts found!' );
endif;       

Example 1b:

If we want to order the combined query in example 1a, we can use for example:

//---------------------------
// Combined queries #1 + #2:
//---------------------------
$args = [
    'posts_per_page' => 4,
    'orderby'        => [ 'date' => 'asc', 'title' => 'desc' ]
    'combined_query' => [        
        'args'   => [ $args1, $args2 ],
    ]
];

Example 2:

Here we want to display all posts published today, sorted by comment count and after that all posts (excluding today's post) sorted by comment count. This example was provided by Robert Hue.

//-----------------
// Sub query #1:
//-----------------
$args1 = [ 
    'post_type'           => 'post',
    'orderby'             => 'comment_count',
    'posts_per_page'      => 100, // adjust to your needs
    'date_query'          => [
        [
            'after' => date('Y-m-d'),
        ],
        'inclusive'  => true,
     ]
];

//-----------------   
// Sub query #2:
//-----------------
$args2 = [
    'post_type'           => 'post',
    'orderby'             => 'comment_count',
    'posts_per_page'      => 100, // adjust to your needs
    'date_query'          => [
        [
           'before' => date('Y-m-d'),
        ],
        'inclusive'  => false,
     ]
];

//--------------------------- 
// Combined queries #1 + #2:
//---------------------------
$args = [
    'posts_per_page'      => 5,
    'ignore_sticky_posts' => 1,
    'paged'               => get_query_var( 'page', 1 ),
    'combined_query' => [        
        'args'   => [ $args1, $args2 ],
    ]
);

//---------
// Output:
//---------
// See example 1a

Example 3:

Let's combine two meta queries and order by a common meta value:

//-----------------
// Sub query #1:
//-----------------
$args1 = [
   'post_type'      => 'cars',
   'posts_per_page' => 10,
   'orderby'        => 'title',
   'order'          => 'asc',
   'meta_query'     => [
        [
            'key'      => 'doors',
            'value'    => 0,
            'compare'  => '>=',
            'type'     => 'UNSIGNED'
        ],
    ],
];

//-----------------
// Sub query #2:
//-----------------
$args2 = [
   'post_type'      => 'post',
   'posts_per_page' => 10,
   'orderby'        => 'date',
   'order'          => 'desc',
   'tax_query' => [
        [
            'taxonomy' => 'category',
            'field'    => 'slug',
            'terms'    => 'cars',
        ],
    ],
    'meta_query'     => [
        [
            'key'      => 'doors',
            'value'    => 0,
            'compare'  => '>=',
            'type'     => 'UNSIGNED'
        ],
    ],  
];


//------------------------------
// Order by a common meta value
//------------------------------

// Modify combined ordering:
add_filter( 'cq_orderby', function( $orderby ) {
    return 'meta_value ASC';
});

// Modify sub fields:
add_filter( 'cq_sub_fields', function( $fields ) {
    return $fields . ', meta_value';
});

//---------------------------
// Combined queries #1 + #2:
//---------------------------
$args = [
    'posts_per_page' => 5,
    'orderby'        => 'meta_value',
    'order'          => 'DESC',
    'combined_query' => [        
        'args'   => [ $args1, $args2 ],
    ]
];

//---------
// Output:
//---------
// See example 1a

Example 4:

We could also combine more than two sub queries, here's an example of four sub-queries:

 $args = [ 
     'posts_per_page' => 10,
     'paged'          => 1,
     'combined_query' => [        
         'args'   => [ $args1, $args2, $args3, $args4 ],
     ]
  ];

//---------
// Output:
//---------
// See example 1a

Example 5:

The above examples are all for secondary queries. So let's apply Example #1a to the main home query.

add_action( 'pre_get_posts', function( \WP_Query $q )
{   
    if( $q->is_home() && $q->is_main_query() )
    {
        //-----------------
        // Sub query #1:
        //-----------------
        $args1 = [
           'post_type'	=> 'page',
           'posts_per_page' => 1,
           'orderby'        => 'title',
           'order'          => 'asc',
        ];

        //-----------------
        // Sub query #2:
        //-----------------
        $args2 = [
           'post_type'      => 'post',
           'posts_per_page' => 3,
           'orderby'        => 'date',
           'order'          => 'asc',
        ];
    
        //---------------------------
        // Combined queries #1 + #2:
        //---------------------------
        $args = [
            'posts_per_page' => 4,
            'combined_query' => [
                'args'   => [ $args1, $args2 ],
                'union'  => 'UNION',
            ]
        ];

        //-----------------------
        // Modify the Main query:
        //-----------------------
        $q->set( 'combined_query',	$args['combined_query'] );
        $q->set( 'posts_per_page',  $args['posts_per_page'] );
    }
} );

Changelog

1.0.5 (2016-05-08)

  • Fixed: Ticket #8 - Fallback for those who don't use Composer.
  • Improved: Removed an explicit call to $GLOBALS['wpdb'] through the use keyword.
  • Improved: Simplified the namespace to only CombinedQuery.

1.0.4 (2016-04-21)

  • Fixed: Adjusted the paged bug that sneaked in with verion 1.0.2 yesterday.
  • Improved: Simplified the example that uses get_query_var() that can now handle default as an input parameter.

1.0.3 (2016-04-21)

  • Fixed: Ticket #7 - Not able to set the "UNION ALL" union option
  • Improved: Inline docs

1.0.2 (2016-04-20)

  • Fixed: Ticket #6 - Escape % in the Generator class. (Props: @DArcMattr)
  • Improved: Inline docs

1.0.1 (2015-11-09)

  • Fixed: Remove vendor dependency and let the user install it via 'composer install' (Props: @pdufour)
  • Fixed: Ignore sticky posts in the EmptyQuery class

1.0.0 (2015-05-10)

  • ** Total Plugin Rewrite **
  • Closed: Ticket #3
  • Added: New classes Main, EmptyQuery and Generator.
  • Added: Support for 'combined_query' attribute of the WP_Query class.
  • Added: Support only for PHP 5.4+
  • Added: Autoload via Composer.
  • Added: New filter 'cq_sub_fields' instead of 'cq_sub_fields'
  • Added: New filter 'cq_orderby' instead of 'cq_orderby'

0.1.3 (2015-05-09)

  • Added: Support for ignory_sticky_posts.
  • Fixed: Minor

0.1.2 (2015-05-08)

  • Added: Support for the GitHub Updater.
  • Added: New filter 'wcq_sub_fields'
  • Added: New filter 'wcq_orderby'
  • Added: New example for meta value ordering
  • Fixed: Ordering didn't work correctly.

0.1.1

  • Changed: Coding style and autoloading (Props: @egill)

0.1 Various plugin improvements, for example:

  • Added: orderby in the combined query.
  • Added: posts_per_page in the sub queries.
  • Added: offset in the sub queries.
  • Added: paged in the sub queries.
  • Removed: sublimit in the combined query, use posts_per_page instead in sub queries.
  • Fixed: Issue #1 related to max_num_pages (Props: @hellofantastic).

0.0.4

  • Added: support for offset in the combined query

0.0.3

  • Added: GPL2+ License part
  • Removed: Dropped namespace + anonymous function for wider PHP support.

0.0.2

  • Added: Input parameter 'union' with possible values UNION and UNION ALL.
  • Fixed: Empty paged resulted in a sql error. (Props: Robert Hue)