Skip to content

Looping The Loop

scribu edited this page Sep 17, 2012 · 23 revisions

Continuing from the Basic usage tutorial, let's say that you want to display the connected pages, not just for a single post, but for all posts in an archive.

The slow way

You could just copy the code you used for a single post:

<?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>

	<?php the_title(); ?>

	<?php
		// Find connected pages
		$connected = new WP_Query( array(
			'connected_type' => 'posts_to_pages',
			'connected_items' => $post,
			'nopaging' => true
		) );

		// Display connected pages
		echo '<p>Related pages:</p>';

		while ( $connected->have_posts() ) : $connected->the_post();
			the_title();

			...
		endwhile;

		wp_reset_postdata(); // set $post back to original post
	?>

<?php endwhile; ?>

The only change I made was replacing get_queried_object() with $post.

This will work as expected, but you're making at least one additional SQL query for each post. If you have 10 or 20 posts per page, that's a lot of extra queries!

Using each_connected()

On each request, WordPress automatically runs a query which finds the appropriate posts to display. These posts are stored in the $wp_query global variable. That's where The Loop pulls it's data from.

Since we already have all the posts tucked away in $wp_query, couldn't we find all the connected pages for all the posts in one go?

Here's how that would look like:

<?php
// Find connected pages (for all posts)
p2p_type( 'posts_to_pages' )->each_connected( $wp_query );
?>

<?php while ( $wp_query->have_posts() ) : $wp_query->the_post(); ?>

	<?php the_title(); ?>

	<?php
		// Display connected pages
		echo '<p>Related pages:</p>';

		foreach ( $post->connected as $post ) : setup_postdata( $post );
			the_title();

			...
		endforeach;

		wp_reset_postdata(); // set $post back to original post
	?>

<?php endwhile; ?>

p2p_type( 'posts_to_pages' )->each_connected() runs a single query, which is much more efficient.

The only other difference is that instead of looping over $connected, we loop over $post->connected.

Using each_connected() multiple times

You can use each_connected() on custom queries and multiple times on the same query:

<?php
$my_query = new WP_Query( array(
  'post_type' => 'movie'
) );

p2p_type( 'movies_to_actors' )->each_connected( $my_query, array(), 'actors' );

p2p_type( 'movies_to_locations' )->each_connected( $my_query, array(), 'locations' );

<?php while ( $my_query->have_posts() ) : $my_query->the_post(); ?>

	<?php the_title(); ?>

	<?php
		// Display connected actors
		echo '<p>Actors:</p>';

		foreach ( $post->actors as $post ) : setup_postdata( $post );
			the_title();

			...
		endforeach;

		wp_reset_postdata();

		// Display connected locations
		echo '<p>Filming locations:</p>';
		foreach ( $post->locations as $post ) : setup_postdata( $post );
			the_title();

			...
		endforeach;

		wp_reset_postdata();
	?>

<?php endwhile; ?>

Further nesting

Since P2P 1.3, you can use each_connected() with an array of post objects as well:

<?php
$my_query = new WP_Query( array(
  'post_type' => 'movie'
) );

p2p_type( 'movies_to_actors' )->each_connected( $my_query, array(), 'actors' );

while ( $my_query->have_posts() ) : $my_query->the_post();

	// Another level of nesting
	p2p_type( 'actors_to_producers' )->each_connected( $post->actors, array(), 'producers' );

	foreach ( $post->actors as $post ) : setup_postdata( $post );
		echo '<h3>Connected Producers</h3>';

		foreach ( $post->producers as $post ) : setup_postdata( $post );
			the_title();

			...
		endforeach;
	endforeach;

	wp_reset_postdata();
endwhile;