Sometimes you want your blog posts to appear on more pages in your WordPress site than just your blog.
You’ve already got the option of using category and taxonomy archives, as well as tags. This can help you divide up your content and add a variety of sections to your blog or news site.
But sometimes you might have posts that relate to a custom post type. A good example of this is a store. The products in your store are custom post types, and in order to help you sell those products, you might write blog posts about them.
In this tutorial, I’ll show you how to add a list of relevant posts to each page for a custom post type in your site. I’ll do this using a custom taxonomy, whose terms will correspond to the names of those products you want to write posts about. I’ll show you how to assign the custom taxonomy to both products and standard posts, and use that relationship to output a list of posts on the single page for the product.
What You’ll Need
To follow along with this tutorial, you’ll need:
- A development installation of WordPress—don’t add anything to your live site until you have it working!
- A code editor.
- A theme you can edit directly or one with an action hook after the content. If you’re using a third-party theme without hooks, you’ll need to create a child theme and edit that.
Setting Up the Plugin
Start by creating a new plugin and adding the header information to it:
<?php /** * Plugin Name: Tuts+ Add Taxonomy Archive to Custom Post Types * Plugin URI: https://github.com/rachelmccollin/tutsplus-custom-post-type-taxonomy-archive * Description: Uses a custom taxonomy to add relevant blog posts to custom post type pages * Version: 1.0 * Author: Rachel McCollin * Textdomain: tutsplus * Author URI: http://rachelmccollin.com * */
If you’re not familiar with this, check out our course on creating your first plugin. And feel free to edit the header text above, substituting your own name, URI, etc.
Registering the Post Type
If you’re not working with a post type that’s been registered by an existing plugin in your site, you’ll need to start by registering one.
We do this by creating a function and hooking it to the init
hook. In your plugin, add this:
function tutsplus_register_product_post_type() { // book blurbs $labels = array( 'name' => __( 'Products' ), 'singular_name' => __( 'Product' ), 'add_new' => __( 'New Product' ), 'add_new_item' => __( 'Add New Product' ), 'edit_item' => __( 'Edit Product' ), 'new_item' => __( 'New Product' ), 'view_item' => __( 'View Product' ), 'search_items' => __( 'Search Products' ), 'not_found' => __( 'No Products Found' ), 'not_found_in_trash' => __( 'No Products found in Trash' ), ); $args = array( 'labels' => $labels, 'has_archive' => true, 'public' => true, 'hierarchical' => false, 'supports' => array( 'title', 'editor', 'excerpt', 'custom-fields', 'thumbnail', 'page-attributes' ), 'rewrite' => array( 'slug' => 'product' ), ); register_post_type( 'tutsplus_product', $args ); } add_action( 'init', 'tutsplus_register_product_post_type' );
This registers the tutsplus_product
plugin. If you want your post type to have a different name, edit the code above.
Now, when you visit your WordPress admin pages, you’ll see your post type in the menu:
Registering the Taxonomy
The next step is to register the taxonomy. Again, create another function in your plugin:
function tutsplus_register_product_taxonomy() { // product taxonomy $labels = array( 'name' => __( 'Products', 'tutsplus' ), 'singular_name' => __( 'Product', 'tutsplus' ), 'search_items' => __( 'Search Products', 'tutsplus' ), 'all_items' => __( 'All Products', 'tutsplus' ), 'edit_item' => __( 'Edit Product', 'tutsplus' ), 'update_item' => __( 'Update Product', 'tutsplus' ), 'add_new_item' => __( 'Add New Product', 'tutsplus' ), 'new_item_name' => __( 'New Product Name', 'tutsplus' ), 'menu_name' => __( 'Product Taxonomy Term', 'tutsplus' ), ); $args = array( 'labels' => $labels, 'hierarchical' => true, 'sort' => true, 'args' => array( 'orderby' => 'term_order' ), 'rewrite' => array( 'slug' => 'book' ), 'show_admin_column' => true ); register_taxonomy( 'tutsplus_product_tax', array( 'tutsplus_product', 'post', 'page' ), $args); } add_action( 'init', 'tutsplus_register_product_taxonomy' );
My taxonomy is called tutsplus_product_tax
. I’ve deliberately included tax
so I know this is the taxonomy and not the post type. I also made the menu item more specific so anyone using this plugin in the WordPress admin will know when they’re working with the post type and when they’re working with the taxonomy.
Here it is in the WordPress admin:
You can see from the screenshot that I’ve also added some dummy products.
Creating the Query
Now for the fun part. We need to write a function that will fetch the taxonomy terms in our new taxonomy for a product and then output a list of posts that also have that term.
To do this, we’ll use the get_the_terms()
function. We’ll then create an empty array of variables and add the slug of each of the terms fetched to that array. We can then use that array in the arguments for WP_Query
.
Start by opening a new function and running a check to see if we’re on a single post page for our custom post type. If you’ve given your custom post type a different name, or you’re using one registered by a third-party theme, you’ll have to replace 'tutsplus_product'
in the code with your own post type.
function tutsplus_add_posts_to_product_pages() { // check if we're in the product post type if( is_singular( 'tutsplus_product' ) ) { } }
Fetching the List of Terms and Adding Them to an Array
Inside your function (and inside the check for being on a single product page), you now need to fetch a list of taxonomy terms for this post.
Add this:
$productterms = get_the_terms( get_the_ID(), 'tutsplus_product_tax' );
Now you need to loop through all of the terms and add them to an array of variables. But you only want to do this if the previous function has returned a list of terms, so wrap that in a check that $productterms
is populated:
if( $productterms ) { $producttermnames[] = 0; foreach( $productterms as $productterm ) { $producttermnames[] = $productterm->name; } }
This fetches the slug of each term and adds it to the $producttermnames
array.
Running the Query
Now we need to set up the query arguments. This will be inside your if( $productterms )
check, since you don’t want this to run if there aren’t any terms. Add this to your code:
$args = array ( 'post_type' => 'post', 'tax_query' => array( array( 'taxonomy' => 'tutsplus_product_tax', 'field' => 'slug', 'terms' => $producttermnames, ), ), );
Here, we’ve used the $producttermnames
array within the tax_query
arguments. This will fetch any post with any of the terms that this product also has.
Now run a query using those arguments:
if( $query->have_posts() ) { ?> <section class="product-related-posts"> <?php echo '<h2>' . __( 'Related Posts', 'tutsplus' ) . '</h2>'; ?> <ul class="product-posts"> <?php while ( $query->have_posts() ) : $query->the_post(); ?> <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li> <?php endwhile; ?> <?php wp_reset_postdata(); ?> </ul> </section> <?php }
This will output an h2
element with a title, and a list linking to each post. If you wanted, you could output this differently: maybe by adding the excerpt or the featured image.
Avoiding Errors
Normally, you wouldn’t expect a user to add more than one taxonomy term to each product, because the terms are each related to a product. But it’s impossible to say for sure that this would never happen. This is why the function includes that $producttermslist
array and uses that for one instance of WP_Query
, instead of running WP_Query
in a foreach
loop. It also makes for a more efficient page as it’s only running one extra query.
The Full Function
Here’s the function in full, with all the braces in the right place:
function tutsplus_add_posts_to_product_pages() { // check if we're in the product post type if( is_singular( 'tutsplus_product' ) ) { // fetch taxonomy terms for current product $productterms = get_the_terms( get_the_ID(), 'tutsplus_product_tax' ); if( $productterms ) { $producttermnames[] = 0; foreach( $productterms as $productterm ) { $producttermnames[] = $productterm->name; } // set up the query arguments $args = array ( 'post_type' => 'post', 'tax_query' => array( array( 'taxonomy' => 'tutsplus_product_tax', 'field' => 'slug', 'terms' => $producttermnames, ), ), ); // run the query $query = new WP_Query( $args ); if( $query->have_posts() ) { ?> <section class="product-related-posts"> <?php echo '<h2>' . __( 'Related Posts', 'tutsplus' ) . '</h2>'; ?> <ul class="product-posts"> <?php while ( $query->have_posts() ) : $query->the_post(); ?> <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li> <?php endwhile; ?> <?php wp_reset_postdata(); ?> </ul> </section> <?php } } } }
Running the Function
Right now, this function won’t do anything. To get it to fire, you’ll need to do one of three things:
- Hook it to an existing action hook in your theme.
- Add an action hook to your theme using
do_action()
and hook it to that. - Add the function name to the place in your theme where you want the list to be output.
For the second and third options, if you’re working with a third-party theme, you should create a child theme and edit that. You could either create a single-tutsplus_product.php template file and add the hook or function to that, or simply add it to single.php. If you hooked it to the template file for the post type, you could remove the if ( is_singular( 'tutsplus_product' ) )
check.
In my case, I’m using the Suki theme from the WordPress plugin directory. It includes this hook in the single.php file:
do_action( 'suki/frontend/after_main' );
The hook is right below the content, exactly where I want my list to be output. So I’ll hook my function to that action hook:
add_action( 'suki/frontend/after_main', 'tutsplus_add_posts_to_product_pages' );
Once you’ve done that, save your plugin and add some products and taxonomy terms.
The Result
I’ve added the widget term to my Widget product, but I’ve also added another term to demonstrate this:
I’ve added these two terms to a number of posts in my site, and when you view the product page for a Widget, you can see the list of related posts beneath, from both terms:
This list of posts will help site visitors to access blog posts relating to that product and will encourage them to find out more about it and (hopefully) to buy.