Manually Curated Related Posts

There’s a bunch of plugins to automatically generate related posts. But some people prefer to have more control over their related posts and manually select them. This can be set up pretty easily using the Posts 2 Posts plugin.

There’s three pieces of code we’ll add to our core functionality plugin 1:

  1. Register the connection. This adds the metaboxes for linking posts.
  2. Pre-Loop code. This adds the related posts information to the $wp_query (better performance)
  3. Display Related Posts.

Register the Connection

<?php
/**
* Related Post Connection
* @author Bill Erickson
* @link http://www.billerickson.net/manually-curated-related-posts/
*
* @uses Posts2Posts
* @link https://github.com/scribu/wp-posts-to-posts/wiki
*/
function be_post_type_connections() {
p2p_register_connection_type( array(
'name' => 'related-articles', // unique name
'from' => 'post',
'to' => 'post',
'title' => array( 'to' => 'All Connections', 'from' => 'Related Articles' )
) );
}
add_action( 'p2p_init', 'be_post_type_connections' );
view raw gistfile1.php hosted with ❤ by GitHub

I’m creating this connection from the post post type, to the post post type. So when you’re editing a post, you can select other posts to connect it to. This actually creates two metaboxes: a from metabox that you use to connect the current post to others, and a to metabox that shows what posts have already been connected to this one. I’ve labeled the from metabox “Related Articles” since this is what is displayed as related to the current post, and I labeled to “All Connections”.

Once you add this code to your site, you should see metaboxes like this when editing a post:

Pre-Loop Code

<?php
/**
* Related Posts Before Loop
* Adds connection data to $wp_query. Run before the loop.
*
* @author Bill Erickson
* @link http://www.billerickson.net/manually-curated-related-posts/
*/
function be_related_posts_pre_loop() {
// Make Sure plugin is active
if ( !function_exists( 'p2p_register_connection_type' ) )
return;
global $wp_query;
p2p_type( 'related-articles' )->each_connected( $wp_query );
}
view raw gistfile1.aw hosted with ❤ by GitHub

As described in the Posts 2 Posts wiki, when dealing with archive pages its much better to add the connected posts to the $wp_query before you run the loop.

Display Related Posts

<?php
/**
* Display Related Posts
* @author Bill Erickson
* @link http://www.billerickson.net/manually-curated-related-posts/
*/
function be_related_posts() {
// Make Sure plugin is active
if ( !function_exists( 'p2p_register_connection_type' ) )
return;
global $post;
if( isset( $post->connected ) && !empty( $post->connected ) ):
echo '<div class="related-posts">';
$count = 1;
foreach( $post->connected as $related ):
if( $count < 6 ) {
echo '<div class="related-post">';
echo '<a class="image" href="' . get_permalink( $related->ID ) . '">';
$cat = wp_get_object_terms( $related->ID, 'category', array( 'count' => 1 ) );
echo '<span class="category">' . $cat[0]->name . '</span>';
echo get_the_post_thumbnail( $related->ID, 'be_home_small' );
echo '</a>';
echo '<a class="title" href="' . get_permalink( $related->ID ) . '">' . $related->post_title . '</a>';
echo '</div>';
$count++;
}
endforeach;
echo '</div>';
endif;
}
view raw gistfile1.aw hosted with ❤ by GitHub

You can of course display the related posts however you’d like. For my specific implementation, I wanted to limit it to 5, display the post image at a specific size, float the category name on top of the post image, and then display the title below the image.

The important thing to note is that $post->connected is an array of post objects, so use the foreach() to loop through them and then display the post data however you’d like.

Integrating it with your theme

Once you’ve added this code to your core functionality plugin, you have to actually call it in your theme. Just place be_related_posts_pre_loop() before you call the loop, and be_related_posts() somewhere inside the loop. If you’re using Genesis, it has hooks throughout the theme, so you just need to add the following two lines to your theme’s functions.php file (alter the second line to change the positioning of the related posts):

<?php
// Related Posts (see mu-plugins/lib/functions/related-posts.php
add_action( 'genesis_before_loop', 'be_related_posts_pre_loop' );
add_action( 'genesis_after_post_content', 'be_related_posts', 15 );
view raw gistfile1.aw hosted with ❤ by GitHub

I’d love to hear any thoughts you have about this, especially if you have experience with Posts 2 Posts.

  1. Core Functionality Plugin
    While you could put all this code in your theme’s functions.php file, a core functionality plugin is better for things like this because if you change your theme in the future, you’ll want to easily move this functionality to your new theme. If it’s in a plugin, all you have to do is add two lines to your theme (call the pre-loop code and then the display code). If all the code is in your old theme, you have to dig through all your files, figuring out how you built your related posts section so you can duplicate it in your new theme. In my core functionality plugin, I created /lib/functions/related-posts.php so that all the code was on one location.

Bill Erickson

Bill Erickson is the co-founder and lead developer at CultivateWP, a WordPress agency focusing on high performance sites for web publishers.

About Me
Ready to upgrade your website?

I build custom WordPress websites that look great and are easy to manage.

Let's Talk

Reader Interactions

Comments are closed. Continue the conversation with me on Twitter: @billerickson

Comments

  1. Dorian Speed says

    I look forward to someday staring at this for a long time and understanding how to implement it. You write really good tutorials – not just showing what to do, but explaining why you’re doing it. Am I missing it, or is there not an option on here to subscribe to your blog via email?

    (I see the “Notify me of new posts by email” checkbox…maybe I’ll just do that.)

    (So I guess that was a dumb question.)

  2. Peter says

    Great tutorial Bill,
    Does it retreive only the featured image though for a post? I know that many of the themes out of convenience retreive the first image from the post if there are no featured images – I assume that it would not load anything if this were the case.

    Bookmarked and saved, will implement this once I get the number of posts up on my blog 🙂

    • Bill Erickson says

      Yes, this code retrieves the featured image. If you’re building on top of Genesis, you can use the genesis_get_image() function which grabs the featured image, and if there isn’t one it pulls the first image in the post.

      But for a tutorial like this there’s no need to complicate things by writing the code that does that when the focus is on the related query.

  3. Paul says

    thanks Bill, just had a request for manually curated CPT products. you save the day again!

  4. Jon Brown says

    Thank you! 9/10 times I google for something I need to figure out how to do related to WP or Genesis I find one of your tutorials that EXACTLY covers what I need.

    Today the google query that got me here was: “posts to posts” plugin show featured image. While I knew how to output a mini loop of featured posts I was hoping for an example of how to output the post’s featured image as part of that loop… Bingo.

  5. Alex says

    Is there a method to implement this in a sidebar widget? How would one call this?

  6. Dave Thackeray says

    Hey Bill – this is IMMENSE. Thank you lots!

    Quick question: What kind of CSS would I need to use to have my Related Posts displayed as yours are in the image at the top of this post? Right now the images are the full width of the body content and the titles are unstyled.

    Thanks for a GREAT display of knowledge.

    d

    • Bill Erickson says

      First you’ll need to define the image size. In the code above I’m using ‘be_home_small’, so add this to your theme’s functions.php file: add_image_size( ‘be_home_small’, 150, 100, true );

      Then add some CSS like this (I’m eyeballing all this so you’ll most likely need to tweak it: https://gist.github.com/3899522

  7. Mike Dutton says

    Hi Bill,
    Thanks for your continuing tutorials.
    I have a couple of queries on this;
    Is it possible to display custom fields.
    I have tried “echo get_post_meta( $related->ID, ‘name of custom field’ ) ;”
    which gives the result “array.”
    I notice the post to posts plugin gives us a widget which lists related post titles. Again is it possible to utilise this with your code to include custom fields?
    Thanks for your help.
    Mike.

  8. Dorian Speed says

    This is great, and I’ve almost got it figured out. I’m running into a problem on a site on which I’m using Edit Flow to create custom statuses. At first, I thought it wasn’t displaying related posts I’d selected, but then I realized they were saved as “Up for Grabs” or another status that means “not published,” basically. I am guessing I need to specify somewhere in here that the status needs to be “published,” but I’m not sure where that should go. Would appreciate it if you have a chance to chime in. Thanks!

    • Bill Erickson says

      I’ve never used Edit Flow so I’m not sure what changes are necessary to integrate with it.

      • Dorian Speed says

        Okay, thanks anyway. I hacked away at it several times last night, cried, vowed never to set foot on the Internet again, and decided to take a break. If I do figure it out I’ll come back and post it here in case someone else is searching for similar information.

  9. MeMeMe says

    Is there a trick to getting this working if you have your page code split into two php files, like, say,
    archive.php containing get_template_part(‘loop’).

    I have the be_related_posts_pre_loop() function in archive.php, and be_related_posts() in the loop.php.

    When I put all loop code in the archive.php, this works. When i use a separate loop.php, the connections seem to be empty, though the rest of each post (the_title(), the_[ermalink(), the_content(), etc.) all work fine.