Created
December 5, 2016 20:06
-
-
Save jameswburke/d0776d742ab74c469cd8af8dacd916fc to your computer and use it in GitHub Desktop.
Unique_WP_Query.php
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
/** | |
* Wrapper for WP_Query | |
* Interacts directly with WP_Query_Manager to ensure | |
* only unique posts are ever returned. | |
* | |
* Known issues: | |
* Won't work with offset values | |
*/ | |
class Unique_WP_Query extends WP_Query { | |
function __construct( $args ) { | |
// Act as a flag for pre_get_posts | |
$args['unique_wp_query'] = true; | |
// Initialize the WP_Query object like normal | |
parent::__construct( $args ); | |
// Track used posts, and remove duplicates | |
Unique_WP_Query_Manager::process_posts( $this->posts, $this->post_count, $this->get( 'original_posts_per_page' ) ); | |
} | |
} | |
/** | |
* Keeps track of which posts have already appeared and | |
* removes them from future Unique_WP_Query objects. | |
*/ | |
class Unique_WP_Query_Manager { | |
/** | |
* Keep track of which post_ids have already been used | |
* @var array | |
*/ | |
public static $used_post_ids = array(); | |
/** | |
* Keeps track of how many used_posts_ids exist | |
* @var integer | |
*/ | |
public static $used_post_count = 0; | |
/** | |
* Takes the posts and post count of a WP_Query object and | |
* ensures that it removes posts that have already been used, | |
* and then trims it to the necessary size. | |
* | |
* @param array &$posts | |
* @param integer &$post_count | |
*/ | |
public static function process_posts( &$posts, &$post_count, $original_posts_per_page ) { | |
// Keep track of how many posts are acceptable to return | |
$current_accepted_post_count = 0; | |
// Make sure we have posts | |
if ( empty( (array) $posts ) ) { | |
return; | |
} | |
// Loop through all the found posts | |
foreach ( (array) $posts as $key => $post ) { | |
// If we have enough acceptable posts, break the loop | |
// Otherwise, determine if we've already used this post | |
if ( $original_posts_per_page > $current_accepted_post_count ) { | |
// Has this post already been used? | |
if ( in_array( $post->ID, Unique_WP_Query_Manager::$used_post_ids ) ) { | |
// Remove from $posts | |
unset( $posts[ $key ] ); | |
// And update count | |
$post_count--; | |
} else { | |
// If not, add it to the list of used ids | |
Unique_WP_Query_Manager::$used_post_ids[] = $post->ID; | |
// Update how many accepted posts we have | |
$current_accepted_post_count++; | |
} | |
} else { | |
// If we have enough acceptable posts, break the foreach | |
// This prevents extra results from accidently being added to $used_post_ids | |
break; | |
} | |
} | |
// Reindex the array correctly | |
$posts = array_values( $posts ); | |
// Update the $used_post_count | |
Unique_WP_Query_Manager::$used_post_count = count( Unique_WP_Query_Manager::$used_post_ids ); | |
// Trim any excess posts and update $post_count | |
if ( count( $posts ) > $original_posts_per_page ) { | |
// Remove extra posts | |
$posts = array_slice( $posts, 0, $original_posts_per_page ); | |
// Update post count to our new value | |
$post_count = $original_posts_per_page; | |
} | |
} | |
} | |
if ( ! function_exists( 'unique_wp_query_pre_get_posts' ) ) { | |
/** | |
* Increase the posts_per_page value by the number of posts that have | |
* already been used. Executes as the last pre_get_posts action. | |
*/ | |
function unique_wp_query_pre_get_posts( &$query ) { | |
// Only apply to Unique_WP_Query objects | |
if ( true === $query->get( 'unique_wp_query' ) ) { | |
// Check our upward bound of posts_per_page | |
$posts_per_page = $query->get( 'posts_per_page' ); | |
if ( $posts_per_page <= 200 ) { | |
// Increase posts_per_page by the amount of used post_ids | |
$query->set( 'original_posts_per_page', $posts_per_page ); | |
$query->set( 'posts_per_page', $posts_per_page + Unique_WP_Query_Manager::$used_post_count ); | |
} else { | |
// Max out at 200 posts_per_page | |
$query->set( 'original_posts_per_page', 200 ); | |
$query->set( 'posts_per_page', 200 ); | |
} | |
} | |
} | |
// Ensure it's always the last pre_get_posts action | |
add_action( 'pre_get_posts', 'unique_wp_query_pre_get_posts', PHP_INT_MAX ); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment