each_connected

scribu edited this page Feb 13, 2013 · 4 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();

		echo '<p>Connection ID: ' . $post->p2p_id . '</p>';

		...
	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();

		echo '<p>Connection ID: ' . $post->p2p_id . '</p>';

		...
	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' );

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;

You might also want to read about p2p_distribute_connected().