Skip to content

Instantly share code, notes, and snippets.

@franz-josef-kaiser
Last active January 18, 2021 19:15
Show Gist options
  • Star 7 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save franz-josef-kaiser/8464919 to your computer and use it in GitHub Desktop.
Save franz-josef-kaiser/8464919 to your computer and use it in GitHub Desktop.
An example plugin to show the use of the PHP SPL and subsidiary loops with a FilterIterator.
<?php
// Reduced to the minimum
class ThumbnailFilter extends FilterIterator
{
private $wp_query;
public function __construct( Iterator $iterator, WP_Query $wp_query )
{
NULL === $this->wp_query AND $this->wp_query = $wp_query;
parent::__construct( $iterator );
}
public function accept()
{
$this->wp_query->the_post();
$this->wp_query->current_post === $this->wp_query->query_vars['posts_per_page'] // -1
AND $this->wp_query->rewind_posts();
return $this->wp_query->have_posts() AND $this->deny();
}
public function deny()
{
return ! has_post_thumbnail( $this->current()->ID );
}
}
<?php
/**
* Plugin Name: (#130009) Merge Two Queries
* Description: "Merges" two queries by using a <code>RecursiveFilterIterator</code> to divide one main query into two queries
* Plugin URl: http://wordpress.stackexchange.com/questions/130009/how-to-merge-two-queries-together
*/
class ThumbnailFilter extends FilterIterator implements Countable
{
private $wp_query;
private $allowed;
private $counter = 0;
public function __construct( Iterator $iterator, WP_Query $wp_query )
{
NULL === $this->wp_query AND $this->wp_query = $wp_query;
// Save some processing time by saving it once
NULL === $this->allowed
AND $this->allowed = $this->wp_query->have_posts();
parent::__construct( $iterator );
}
public function accept()
{
if (
! $this->allowed
OR ! $this->current() instanceof WP_Post
)
return FALSE;
// Switch index, Setup post data, etc.
$this->wp_query->the_post();
// Last WP_Post reached: Setup WP_Query for next loop
$this->wp_query->current_post === $this->wp_query->query_vars['posts_per_page'] // -1
AND $this->wp_query->rewind_posts();
// Doesn't meet criteria? Abort.
if ( $this->deny() )
return FALSE;
$this->counter++;
return TRUE;
}
public function deny()
{
return ! has_post_thumbnail( $this->current()->ID );
}
public function count()
{
return $this->counter;
}
}
class NoThumbnailFilter extends ThumbnailFilter
{
public function deny()
{
return has_post_thumbnail( $this->current()->ID );
}
}
add_action( 'loop_start', 'wpse130009Query' );
function wpse130009Query()
{
// Only need to remove this callback for the current test
// Else we'd infinitely nest this callback as WP_Query::the_post()
// calls the 'loop_start' filter.
remove_action( current_filter(), __FUNCTION__ );
global $wp_query;
echo number_format_i18n( $wp_query->found_posts );
$arrayObj = new ArrayObject( $wp_query->get_posts() );
$primaryQuery = new ThumbnailFilter( $arrayObj->getIterator(), $wp_query );
$secondaryQuery = new NoThumbnailFilter( $arrayObj->getIterator(), $wp_query );
foreach ( $primaryQuery as $post )
{
var_dump( get_the_ID() );
}
foreach ( $secondaryQuery as $post )
{
var_dump( get_the_ID() );
}
}
@franz-josef-kaiser
Copy link
Author

@samjco You can find plenty of examples on StackExchange sites and StackOverflow themselves as well as various blogs. For the reasoning, read for example this SO answer. The filters explain themselves if you look at their code above. They wrap basic WP functions that you can look up at WPs dev resources. Hope that helps.

@samjco
Copy link

samjco commented Jan 17, 2021

Ok, so the main goal is to first define one query with all args. Then from there, you can remove args using deny(), thus making the main query into 2 queries. First (the main) query results, of all desired args, and the other query results are with less-than all args as - we take things out using the function: deny()... Thus splitting the query into two.
Is this correct?

@franz-josef-kaiser
Copy link
Author

@samjco Yes, you got it. Save DB-queries and just split the the data set into multiple loops, easy and readable.

@samjco
Copy link

samjco commented Jan 17, 2021

So then the class "ThumbnailFilter" and "NoThumbnailFilter" can be named "FirstQueryResults (or MainQueryResults)" AND "SecondQueryResults"...
I will also attempt to make a real-life example for others who were stumped like me...

@samjco
Copy link

samjco commented Jan 17, 2021

Another question...
Doesn't this show each query results in one after another instead of mixed in together?
So in order words... The $primaryQuery results all shows first and then $secondaryQuery's results:

	foreach ( $primaryQuery as $post )
	{
		var_dump( get_the_ID() );
	}

	foreach ( $secondaryQuery as $post )
	{
		var_dump( get_the_ID() );
	}

How can we mix together? For Instance. What if I want two different results mixed together forming one result that order-by "date" of each post?

@franz-josef-kaiser
Copy link
Author

I am not sure what you are up to. The WP.SE answer explains whats's happening here. If you got a different question, please ask a follow up question on WP.SE directly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment