Display taxonomy loop using custom field data

So you got a lot of stuff right, and some things wrong, so I’ll go through it line by line

First:

    wp_reset_query();

This function cleans up after a query_posts call, you should never use either of them. Sadly, a lot of tutorial writers use this instead of what they intended to use, wp_reset_postdata, as it calls that function internally.

To save time, this is what a well formed WP_Query that cleans up after itself looks like:

$args = array();
$q = new WP_Query( $args );
if ( $q->have_posts() ) { // if posts were found
    while( $q->have_posts() { // for each post
        $q->the_post(); // set the current post
        the_title(); // display its title
        the_content(); // display its content
    }
    // after the loop, reset the current post back to before the loop 
    wp_reset_postdata();
} else {
    // no posts were found
}

Notice that wp_reset_postdata is called after the while loop, but inside the if statement. This way the postdata is only reset if there is work to cleanup.

Second:

Your query arguments are close but not quite right:

    $args = array(
        'post_type' => 'blogs',
        'posts_per_page' => 2,
        'tax_query' => array(
            array(
                'taxonomy' => 'show-blogs',
                'field' => $news,
                'terms' => $custom_term->slug,
                'posts_per_page' => 2,
            ),
        ),
    );
    $loop = new WP_Query( $args );

Notes:

  • field identifies what it is you’re searching for. AKA the term withe ID 5, or the term with the slug test, etc, it has a limited number of possible values. The default is term_id, so you should remove this
  • terms is right, but you can make things simpler by passing in the term ID instead, aka 'terms' => $custom_term->term_id,, this might even be a tiny bit faster. Eitherway, always prefer term IDs over term slugs or names, as slugs/names can be edited, but IDs always remain the same
  • The extra posts_per_page doesn’t belong in tax_query

Third:

Your code is pulling 2 posts as you wanted, it’s just doing it multiple times. Instead of 1 query that’s misbehaving, it’s multiple queries doing the same thing, but for different terms.

$custom_terms = get_terms('show-blogs');
foreach ( $custom_terms as $custom_term ) {

Lets say that $custom_terms contains 5 terms. That’s 5 iterations of the loop, and a query for 2 posts in each loop, so 2 posts x 5 iterations = 10 posts.

We can demonstrate this by modifying the start of

$custom_terms = get_terms('show-blogs');
foreach ( $custom_terms as $custom_term ) {
    ?><h3>Show Blog: <?php echo esc_html( $custom_term->name ); ?></h3><?php

Now we should see each term as a header, followed by 2 posts.

Notable mentions:

  • Congratulations on using esc_url, escaping is a greatly under-appreciated security measure that can do wonders for securing a site. Escape late escape often
  • Some CSS grid should make your table layout much simpler/easier to manage
  • Avoid putting things like } etc on the same line, and indent them, and stick to one style e.g. while () {} rather than while(): endwhile;, it avoids an entire class of mistakes and bugs, and it’s much nicer to read and type