wp-combine-queries
                                
                                 wp-combine-queries copied to clipboard
                                
                                    wp-combine-queries copied to clipboard
                            
                            
                            
                        WordPress: Combine multiple WP_Query() queries into a single one, through the custom 'combined_query' attribute of the WP_Query class.
Combine Query
WordPress plugin - Combine 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+.
Settings
The supported settings for the combined_query attribute:
'combined_query' => [        
	'args'           => [ $args1, $args2, ... ], // Array (default [])
	'union'          => 'UNION',                 // String Possible values are UNION or UNION ALL (default UNION)
	'posts_per_page' => 10,                      // Integer 1,2,...
	'offset'         => 0,                       // Integer 0,1,...
	'orderby'        => 'meta_value_num',        // String (post_name, ..., name, ..., none, meta_value, meta_value_num )
	'order'          => 'DESC',                  // String (ASC,DESC)
]
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;
});
To keep the order by arguments arg1, arg2, ... use:
'combined_query' => [
    ...
    'orderby' => 'none',
    ...
]
or
add_filter( 'cq_orderby', '__return_empty_string' );
$query = new WP_Query( $args );
remove_filter( 'cq_orderby', '__return_empty_string' );
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 1:
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 = [
	'combined_query' => [        
		'args'           => [ $args1, $args2 ],
		'union'          => 'UNION',
		'posts_per_page' => 4,
		'orderby'        => 'none',
	]
];
//---------
// 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 2:
Here we want to display all foo posts with a date query, sorted by comment count and after that all bar posts sorted by comment count. Then we sort all by decreasing comment count.
//-----------------
// Sub query #1:
//-----------------
$args1 = [ 
	'post_type'           => 'foo',
	'orderby'             => 'comment_count',
	'order'               => 'desc',
	'posts_per_page'      => 100, // adjust to your needs
	'date_query'          => [
		[
			'after' => date('Y-m-d'),
		],
		'inclusive'  => true,
	]
];
//-----------------   
// Sub query #2:
//-----------------
$args2 = [
	'post_type'           => 'bar',
	'orderby'             => 'comment_count',
	'order'               => 'desc',
	'posts_per_page'      => 100, // adjust to your needs
];
//--------------------------- 
// Combined queries #1 + #2:
//---------------------------
$args = [
	'combined_query' => [        
		'args'                => [ $args1, $args2 ],
		'posts_per_page'      => 5,
		'paged'               => 2,
		'orderby'             => 'comment_count',
		'order'               => 'desc',
	]
];
//---------
// Output:
//---------
// See example 1
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 sub fields:
add_filter( 'cq_sub_fields', $callback = function( $fields ) {
    return $fields . ', meta_value';
});
//---------------------------
// Combined queries #1 + #2:
//---------------------------
$args = [
    'combined_query' => [        
	'args'           => [ $args1, $args2 ],
	'posts_per_page' => 5,
	'orderby'        => 'meta_value_num',
	'order'          => 'DESC',
    ]
];
//---------
// Output:
//---------
// See example 1
remove_filter( 'cq_sub_fields', $callback );
Example 4:
We could also combine more than two sub queries, here's an example of four sub-queries:
 $args = [ 
    'combined_query' => [        
	'args' => [ $args1, $args2, $args3, $args4 ],
	...
    ]
  ];
//---------
// Output:
//---------
// See example 1
Example 5:
The above examples are all for secondary queries. So let's add a query 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',
	];
	//-----------------
	// Original query #2:
	//-----------------    
	$args2 = $q->query;
	//---------------------------
	// Combined queries #1 + #2:
	//---------------------------
	$args = [
		'combined_query' => [
			'args'           => [ $args1, $args2 ],
			'union'          => 'UNION',
			'posts_per_page' => 4,
			'orderby'        => 'none',
		]
	];
    
	//-----------------------
	// Modify the Main query:
	//-----------------------
	$q->set( 'combined_query', $args['combined_query'] );
    }
} );
Changelog
(2022-03-03)
- Fixed: Wrong plugin URI in header (Props: therealgilles)
1.2.2 (2021-02-20)
- Fixed: Fixes #14 regarding subsequent queries. (Props: @Suranex)
1.2.1 (2020-09-14)
- Fixed: Readme.
1.2.0 (2020-09-14)
- Added: Support for ordering by 'none'.
- Added: Test cases.
- Added: Support for adding parameters (posts_per_page, offset, orderby, order) inside combined_query args.
- Fixed: Hooks handling for pre_get_posts example.
- Adjusted: Examples.
1.1.1 (2020-09-04)
- Added: Example how to keep the order by arguments arg1, arg2, ...
- Adjusted: UNION ALL test
1.1.0 (2020-09-04)
- Added: Ticket #19 - Add test cases for argument order workaround. (Props: therealgilles)
- Cleanup: phpcs
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)