Advanced WordPress loop techniques

How to get posts only from a specific custom post type, or how to show posts only from a custom taxonomy. How to keep the original query around, how to deal with posts that have post formats enabled and how to enable pagination on a custom loop.

2015.04.20 I have a new post that is more up to date on the current trends of looping: “An in depth look into the WP_Query and a WordPress loop”.
2012.10.06 added the loop pagination technique.

Recourses:

Basically there is three ways to loop.

  • query_posts() method is good to use when modifying the normal loop
  • WP_Query() method is good for multiple loops
  • get_posts() method is good for additional “miniloops”

On this post, for the sake on simplicity, I’m using only the query_posts() method.

This article wont go to the basics of loopping, or to the details of building multipel loops. You should read this great article to get to know the basics of a loop: 4 Ways to Loop with WordPress.

Basic loop

As a reminder here is the normal bare bones loop.

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
  <!-- Do stuff -->
<?php endwhile; else: ?>
  <p>Sorry, no posts to display</p>
<?php endif; ?>

Get posts from a specific custom post type

Custom post types have changed WP to from a blogging platform to a real CMS. Getting posts from specific custom post types could not be easier.

<?php 
query_posts('post_type=music'); //get all music post types
if (have_posts()) : while (have_posts()) : the_post(); 
?>
  <!-- Do stuff -->
<?php endwhile; endif; ?>

That’s it. Btw, here’s all the parameter the loop can be edited with.

Get posts from a specific custom taxonomy

2015.04.20 Don’t use query_posts anymore, it’s not recommended, use pre_get_posts hook. See my new post on looping: “An in depth look into the WP_Query and a WordPress loop”.

Custom taxonomies are super handy, but looping them is a bit different from what we’re used to.

We’ll use array method rather than the query strings.

Query string method looks like this:


query_posts('post_per_page=10&cat=7');

And this is the array method:

$args = array(
  'post_per_page' => 10,
  'cat'           => 7 
);
query_posts($args);

So, here’s the loop, it uses tax_query which is array of arrays.

This example gets 10 posts from a taxonomy “music” in “mix-tapes” term. See the comments in the code.

<?php
$args = array( 
  'posts_per_page' => 10, 
  //Using tax_query 
  'tax_query' => array(
    array(
      'taxonomy' => 'music', //taxonomy
      'field' => 'slug', //this can be also id
      'terms' => array( 'mix-tapes' ) //the term in the taxonomy
    )
  )
);
query_posts($args);
if (have_posts()) : while (have_posts()) : the_post(); 
?>
<!--Do stuff-->
<?php endwhile; endif; ?>

Easy.

If you want more complex loops you can just add more arrays. This example requires the posts also to have a “thing” as a tag. You can go really specific!

$args = array( 
  'posts_per_page' => 10, 
  //Using tax_query 
  'tax_query' => array(
    //start the array for the first condition    
    array(
      'taxonomy' => 'music', //taxonomy
      'field'    => 'slug', //this can be also id
      'terms'    => array( 'mix-tapes' ) //the term in the taxonomy
    ),
    //start the arrays for the second condition
    array(
      'taxonomy' => 'post_tag', //taxonomy
      'terms'    => array('thing'), //tag
      'field'    => 'slug'
    )
  )
);
query_posts($args);

What if you want all posts in “mix-tapes” and all post with a tag “thing”. The above got only the ones with both.

You can use the relation parameter.

$args = array( 
  'posts_per_page' => 10, 
  'relation' => 'OR', //relations parameter, get both terms
  'tax_query' => array(
    //star the array for the first condition    
    array(
      'taxonomy' => 'music', //taxonomy
      'field' => 'slug', //this can be also id
      'terms' => array( 'mix-tapes' ) //the term in the taxonomy
    ),
    //start the arrays for the second condition
    array(
      'taxonomy' => 'post_tag', //taxonomy
      'terms'    => array('thing'), //tag
       'field'   => 'slug'
    )
  )
);
query_posts($args);

Dealing with posts that have post format applied

If you don’t know post formats see the codex.

Very handy little things those post formats. There is a conditional tag for them.

if ( has_post_format( 'link' )) {
  //do stuff specific to link format
}

Sometimes you may want to weed the links out from the main loop and display them in the sidebar. has_post_format() doesn’t really help you in that. The following code is bad and not really working.

if (have_posts()) : while (have_posts()) : the_post(); 
  if ( has_post_format( 'link' )) {
    //Do nothing
  } else {
    //Normal loop stuff
  } 
endwhile; endif; wp_reset_query();

Yes, that removes the links from the loop but it counts the links as it counts the normal posts. Meaning, if you have four links, and you want to show 10 posts in your home page, it only shows six posts, couse it couts the links as well.

Here’s how to do that properly using the same method as for taxonomies. Post formats are nothing but term in a post_format taxonomy. Here we take use of the operator parameter on line 8.

$args = array( 
  'posts_per_page' => 10, 
  'tax_query' => array(
    array(
      'taxonomy' => 'post_format', 
      'field' => 'slug',
      'terms' => array( 'post-format-link' ),
      'operator' => 'NOT IN' //this tells the loop to skip the links
    )
  )
);
query_posts($args);

So, that skipped all links. If you want only the links, just remove the operator line. Read more about the operators in codex.

How to keep the original query around

What does that even mean? It means that WordPress is smart (of course), on home page when you have the normal loop with no special added to it, WP gets all the posts, but when you’re viewing a category archive, WP only gets posts for that category, right? But, what if you want, on the category page, reduce the amount of posts from the preset 10? Then you say query_posts('posts_per_page=7') before the loop. But this “breaks” the loop by ignoring that you are on a category page and it gets posts from all categories. Not what you wanted.

Here’s how you can customize the loop without “breaking” it.

<?php 
global $query_string; //the magic line
query_posts($query_string.'&posts_per_page=7');
if (have_posts()) : while (have_posts()) : the_post();
?>
  <!--Do stuff-->
<?php endwhile; endif; ?>

If you’re using the array method for getting the parameters, it can be a little confusing. This will not work.

global $query_string; //the magic line
$args = array(
  'posts_per_page'=> 7,
  'order' => 'ASC'
)
query_posts($query_string . $args);
if (have_posts()) : while (have_posts()) : the_post();

You need to merge the query array to the parameter array.

global $query_string; //the magic line
$args = array_merge(
  $wp_query->query,
  array(
    'posts_per_page'=> 7,
    'order' => 'ASC'
  )
);
query_posts($args);
if (have_posts()) : while (have_posts()) : the_post();

Enable pagination on a custom loop

Another thing that is easy to break when building a custom loop, is the pagination. Your loop shows the right amount of posts, but the “next” and “previous” buttons won’t appear. There’s an easy fix.

<?php
$paged = get_query_var('paged') ? get_query_var('paged') : 1; //The magic, ternary if statement
$args  = array(
    'posts_per_page' => 5, //Show 5 posts per page
    'post_type'      => 'projects', //From projects post type 
    'paged'          => $paged //Enable the pagination
);
query_posts($args);
if ( have_posts() ) : while ( have_posts() ) : the_post();
?>

    <!-- Do stuff -->

<?php endwhile; endif; ?>

That’s about it for today.

Comments

Club-Mate, the beverage → club-mate.fi