FB Pixel

Inserting custom posts into the loop

Today on the WordPress subreddit, someone asked if it was possible to have posts from a custom post type (recipes, in their example) show up in the main blog at a set frequency (ie. every 5th post). My initial thought was, probably not. But I decided to do a little digging anyway. Turns out there’s a handy filter called the_posts that I didn’t know existed.

The the_posts filter is run at the end of WP_Query->get_posts() [trac] – after the results of the query have been retrieved from the database, but before The Loop begins. We can use this filter to modify the results, including adding our own. Our filter function will receive two arguments:

  1. $posts – the results of the query as an array of post objects
  2. $query – the WP_Query instance
add_filter( 'the_posts', 'gowp_insert_recipes', 10, 2 ); function gowp_insert_recipes( $posts, $query ) { 	return $posts; } 

We don’t want our filter to affect post lists in the admin dashboard, so we’ll start with:

	if ( is_admin() ) return $posts;

We also only want our filter to affect the main query, so we’ll add this:

	if ( ! is_main_query() ) return $posts;

We’ll set a variable for the frequency of the insertion which we’ll use in some math later on (if we’re developing this as a public plugin we could set/get this value as an option/setting):

	$freq = 5;

Now we’ll retrieve our recipes. We’ll calculate our posts_per_page on the fly, based on the value from the main query and our frequency, and use the same paged value as the main query – this ensures that the inserted posts stay in sync regardless of pagination settings:

	$args = array( 		'post_type' => 'gowp_recipe', 		'posts_per_page' => floor( $query->query_vars['posts_per_page'] / $freq ), 		'paged' => ( $query->query_vars['paged'] ) ? $query->query_vars['paged'] : 1, 	); 

We’ll then loop through the recipes and insert them into the $posts array using array_slice() [documentation]. We’ll do a little math to ensure that our recipes are inserted correctly (I’m sure there’s a more elegant way to do this – if you know one, please let me know in the comments):

	if ( $recipes = get_posts( $args ) ) { 		$insert = -1; 		foreach ( $recipes as $recipe ) { 			$insert =  $insert + ( $freq + 1 ); 			array_splice( $posts, $insert, 0, array( $recipe ) ); 		} 	} 

Here is the final code all together:

add_filter( 'the_posts', 'gowp_insert_recipes', 10, 2 ); function gowp_insert_recipes( $posts, $query ) {  	if ( is_admin() ) return $posts;  	if ( ! is_main_query() ) return $posts;  	$freq = 5;  	$args = array( 		'post_type' => 'gowp_recipe', 		'posts_per_page' => floor( $query->query_vars['posts_per_page'] / $freq ), 		'paged' => ( $query->query_vars['paged'] ) ? $query->query_vars['paged'] : 1, 	);  	if ( $recipes = get_posts( $args ) ) { 		$insert = -1; 		foreach ( $recipes as $recipe ) { 			$insert =  $insert + ( $freq + 1 ); 			array_splice( $posts, $insert, 0, array( $recipe ) ); 		} 	}  	return $posts;  }

The Landing Page Builds service includes:

  • Unlimited page builds for one monthly rate
  • Dedicated account manager
  • Dedicated WordPress developer
  • At least 2 hours of daily dev time
  • Daily progress reports
  • Find out more here

The Content Edits Plan includes:

  • Unlimited content edits
  • White label help desk
  • Support ticket dashboard
  • 24/7 team of WordPress experts
Plus, everything in our Maintenance Plan:
  • Visual Validator WordPress updates
  • 90 days of off-site backups
  • Daily security scans and malware cleanup
  • Maintenance dashboard
  • Find out more here

The Maintenance Plan includes:

  • Visual Validator WordPress updates
  • 90 days of off-site backups
  • Daily security scans and malware cleanup
  • Maintenance dashboard
  • Find out more here