Learning to work with advanced custom queries

Open up the single.php file and put the following code at the end of the left sidebar code. To be precise, right underneath the div element with “post-author” class.






<?php $category_term_list = wp_list_pluck( get_the_category(), 'slug' ); ?>
<?php 
    $related_posts_query = new WP_Query(
        array(
            'post_type' => 'post',
            "posts_per_page" => 2,
            'post__not_in' => array( get_the_ID() ),
            'tax_query' => array(
                array(
                    'taxonomy' => 'category',
                    'terms' => $category_term_list,
                    'field' => 'slug',
                )
            )
        )   
    );
?>
<?php if( $related_posts_query->have_posts() ): ?>
    <section class="blog-posts related-articles">
        <h2><?php _e( 'Related Articles', 'nd_dosth' ); ?></h2>
        <?php while( $related_posts_query->have_posts() ): ?>
            <?php $related_posts_query->the_post(); ?>
            <?php get_template_part( 'parts/blog', 'index' ); ?>
        <?php endwhile; ?>
        <?php wp_reset_postdata(); ?>
    </section>
<?php endif;  ?>

You already know what’s happening with the Loop.

But let’s dismantle the custom query piece by piece. Don’t worry. It’s easy!

We are showing related articles of the current post based on the categories of the current post.

For example, if the current post is assigned to the “Advice” category, we only want to display related articles that are assigned to the “Advice” category.

So, first, we are getting all the categories of the current post and extracting the slugs of all the categories from the result using:


<?php $category_term_list = wp_list_pluck( get_the_category(), 'slug' ); ?>

The get_the_category() function returns the categories of the current post as an array of WP_Term objects. 

For example, if a blog post is assigned to the “Advice”, “Friendship” and “Love” categories, the result of get_the_category() function looks like the following array:

array(3) {
    [0]=> object(WP_Term)#1216 (16) {
        ["term_id"]=> int(5)
        ["name"]=> string(6) "Advice"
        ["slug"]=> string(6) "advice"
        ["term_group"]=> int(0)
        ["term_taxonomy_id"]=> int(5)
        ["taxonomy"]=> string(8) "category"
        ["description"]=> string(0) ""
        ["parent"]=> int(0)
        ["count"]=> int(6)
        ["filter"]=> string(3) "raw"
        ["cat_ID"]=> int(5)
        ["category_count"]=> int(6)
        ["category_description"]=> string(0) ""
        ["cat_name"]=> string(6) "Advice"
        ["category_nicename"]=> string(6) "advice"
        ["category_parent"]=> int(0)
    }
[1]=> object(WP_Term)#1216 (16) {
        ["term_id"]=> int(5)
        ["name"]=> string(6) "Friendship"
        ["slug"]=> string(6) "friendship"
        ["term_group"]=> int(0)
        ["term_taxonomy_id"]=> int(7)
        ["taxonomy"]=> string(8) "category"
        ["description"]=> string(0) ""
        ["parent"]=> int(0)
        ["count"]=> int(2)
        ["filter"]=> string(3) "raw"
        ["cat_ID"]=> int(7)
        ["category_count"]=> int(2)
        ["category_description"]=> string(0) ""
        ["cat_name"]=> string(6) "Friendship"
        ["category_nicename"]=> string(6) "friendship"
        ["category_parent"]=> int(0)
    }
[2]=> object(WP_Term)#1216 (16) {
        ["term_id"]=> int(5)
        ["name"]=> string(6) "Love"
        ["slug"]=> string(6) "love"
        ["term_group"]=> int(0)
        ["term_taxonomy_id"]=> int(9)
        ["taxonomy"]=> string(8) "category"
        ["description"]=> string(0) ""
        ["parent"]=> int(0)
        ["count"]=> int(1)
        ["filter"]=> string(3) "raw"
        ["cat_ID"]=> int(9)
        ["category_count"]=> int(1)
        ["category_description"]=> string(0) ""
        ["cat_name"]=> string(6) "Love"
        ["category_nicename"]=> string(6) "love"
        ["category_parent"]=> int(0)
    }
}

In the language of WordPress, the word “Category” is a taxonomy and the individual categories like “Advice”, “Friendship” are the terms of the “Category” taxonomy.  

Since the blog post is assigned to three terms, the array returned by get_the_category() function has three terms in the form of WP_Term object.

If you notice, there is a “slug” property for every term and using the wp_list_pluck() function, we are plucking out that “slug” for every term.

<?php $category_term_list = wp_list_pluck( get_the_category(), 'slug' ); ?>

If you now var_dump the $category_term_list() function, this is what you’ll see:


array(1) {
  [0]=> string(6) "advice"
  [0]=> string(10) "friendship"
  [0]=> string(5) "basic"
}

Anyway, the bottom line is, we will give this array to the Custom Query so that it fetches the posts that are assigned to these terms belonging to “Category” taxonomy. 

Next comes the custom query itself.

$related_posts_query = new WP_Query(
    array(
        'post_type' => 'post',
        "posts_per_page" => 2,
        'post__not_in' => array( get_the_ID() ),
        'tax_query' => array(
            array(
                'taxonomy' => 'category',
                'terms' => $category_term_list,
'field' => 'slug',
            )
        )
    )  
);

You already know what’s happening with the first two arguments of the custom query. 

The “post__not_in” argument is used for excluding a particular post from the query result.

'post__not_in' => array( get_the_ID() )

In our case, we are giving the ID of the current blog post that the visitor is reading. We don’t want to display the current blog post inside the related articles, right?

Remember, we placed the custom query inside the main loop of the single blog post page. So, we can still access all ID of the current blog post using the get_the_ID() function.

In the above screenshot, I omitted some important parts of the single.php file for demonstration purposes. So don’t take it literally!

$related_posts_query = new WP_Query(
    array(
        'post_type' => 'post',
        "posts_per_page" => 2,
        'post__not_in' => array( get_the_ID() ),
        'tax_query' => array(
            array(
                'taxonomy' => 'category',
                'terms' => $category_term_list,

'field' => 'slug',
            )
        )

    )  
);

Next, we are providing the “tax_query” argument instructing custom query to fetch posts only belonging to the taxonomy “category” with the help of:

'taxonomy' => 'category'

Then, we are further filtering down the posts to specific terms from the taxonomy “category” by providing the terms list that we plucked earlier.

'terms' => $category_term_list

The $category_term_list variable contains “slugs” of the categories, right?

We have to tell this to WordPress. So, we are specifying that the category term list we have provided contains slugs of the categories with the help of:

'field' => 'slug'

That’s pretty much how you would craft complex custom queries in WordPress.

If you now visit any single blog post in the frontend, two related articles will show up and both of them belong to the same category of the current blog post.

“Hey, What If I want to display related articles based on Tag taxonomy along with the Category taxonomy?”

Easy! Visit the codex page for WP_Query for more questions and practice. It will answer all your questions and you’ll keep visiting this page throughout your WordPress career!

https://codex.wordpress.org/Class_Reference/WP_Query

Here is a different approach to achieving the same result:

https://www.wpbeginner.com/wp-themes/how-to-add-related-posts-with-a-thumbnail-without-using-plugins/

Here is the final single.php file for this lesson:


<?php
/**
 * The template for displaying all single posts
 *
 * @link https://developer.wordpress.org/themes/basics/template-hierarchy/#single-post
 *
 * @package Dosth
 */
get_header();
?>
<div class="content-container">
    <?php while( have_posts() ): ?>
        <?php the_post(); ?>
        <?php if ( has_post_thumbnail() ) :
            $featured_image = wp_get_attachment_image_src( get_post_thumbnail_id( get_the_ID() ), 'full' ); ?>
            <div class="full-width-featured-image" style="background-image:url(<?php echo $featured_image[0]; ?>);">
                <h1><?php the_title(); ?></h1>
            </div>
        <?php endif; ?>
        <div class="container">
            <div class="row">
                <div class="article-info col-md-3">
                    <?php $categories = get_the_category(); ?>
                    <?php if ( ! empty( $categories ) ) : ?>
                        <div class="posted-in">
                            <h4><?php _e( 'Posted In', 'nd_dosth' ); ?></h4>
                            <?php the_category(); ?>
                        </div>
                    <?php endif; ?>
                    <div class="published-on">
                        <h4><?php _e( 'Published On', 'nd_dosth' ); ?></h4>
                        <?php the_date(); ?>
                    </div>
                    <div class="post-author">
                        <h4><?php _e( 'Author', 'nd_dosth' ); ?></h4>
                        <a class="author-archive" href="<?php echo esc_url( get_author_posts_url( get_the_author_meta( 'ID' ) ) ); ?>">
                            <?php the_author(); ?>
                        </a>
                        <?php echo get_avatar( get_the_author_meta('ID')  , 100 ); ?>
                    </div>
                    <?php //custom query starts here ?>
                    <?php $category_term_list = wp_list_pluck( get_the_category(), 'slug' ); ?>
                    <?php 
                        $related_posts_query = new WP_Query(
                            array(
                                'post_type' => 'post',
                                "posts_per_page" => 2,
                                'post__not_in' => array( get_the_ID() ),
                                'tax_query' => array(
                                    array(
                                        'taxonomy' => 'category',
                                        'terms' => $category_term_list,
                                        'field' => 'slug',
                                    )
                                )
                            )   
                        );
                    ?>
                    <?php if( $related_posts_query->have_posts() ): ?>
                        <section class="blog-posts related-articles">
                            <h2><?php _e( 'Related Articles', 'nd_dosth' ); ?></h2>
                            <?php while( $related_posts_query->have_posts() ): ?>
                                <?php $related_posts_query->the_post(); ?>
                                <?php get_template_part( 'parts/blog', 'index' ); ?>
                            <?php endwhile; ?>
                            <?php wp_reset_postdata(); ?>
                        </section>
                    <?php endif;  ?>
                    <?php //custom query ends here ?>
                </div>
                <div id="actual-article" class="col-md-8">
                    <?php the_content(); ?>
                    <?php get_template_part( 'parts/post', 'navigation' ); ?>
                    <?php comments_template(); ?>
                </div>
            </div>
        </div>
    <?php endwhile; ?>
</div>
<?php get_footer(); ?>

And, here is output in the browser:

Let’s fix this quickly by adding the following CSS to the end of the style.css file:


/*-------------------------------------------------------------------------------
  20.Related Blog List styling
-------------------------------------------------------------------------------*/
.single .related-articles .blog-post{
    width:100%;
}
.single .related-articles h2{
    font-size:25px;
    margin-bottom:15px;
    margin-top:30px;
    color:#1a3794;
}

Here is an updated styles outline for style.css file:

And here is the output that I am proud of:

We are finally done with the blog completely.

In the next lesson, we will start discussing Custom Post Types.