WordPress plugin - Combined Query
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.
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+.
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
.
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;
});
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 ;-)
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;
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 ],
]
];
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
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
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
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'] );
}
} );
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)