Spring Sale! Save 30% on all books w/ code: PLANET24
Web Dev + WordPress + Security

Triple Loop for WordPress

[ WordPress Triple Loop ] Two of the themes developed by Perishable Press, Apathy and Information, depend on three WordPress loops to operate as intended. For each of these themes, the three loops consist of two “side” loops and one main loop. The side loops each display posts from one specific category, while the main loop displays posts from every category not specified in the side loops.

Update: It’s now recommended to use WP_Query and other “looping” techniques to customize the WordPress loop, create multiple loops, and so forth. You can learn more about these techniques in my tutorial, 4 Ways to Loop with WordPress. The basic principles of this article still apply, but you should be using WP_Query instead of query_posts whenever possible.

Intro

There are many different multi-loop configurations currently available for WordPress users. Needless to say, despite a wide variety of available loop setups, implementing a customized multiple loop frequently requires a great deal of time of energy. Certain loop sets accomplish one task, but fail at another, while others refuse to provide enough flexibility in general. Indeed, after countless rounds of trial and error establishing multiple loops, we finally developed the almost-perfect triple-loop configuration.

The Upside

The Perishable Press triple loop for WordPress provides several specifically designed features:

  • Each side loop displays posts from any unique category.
  • Each side loop displays any unique number of posts.
  • The main loop may exclude posts from any category.
  • The main loop displays no posts included in either side loop.
  • The main loop displays any unique number of posts.
  • Post views function properly for any post in any loop.

The Downside

The Perishable Press triple loop for WordPress currently encompasses these specific caveats:

  • Archive navigation is only possible for the main loop.
  • The main loop must precede both of the side loops.
  • The “side loops” technically are neither “side” nor “loops”.

Additionally, the main loop will look at the first “x” number of posts, and display only those posts which are not included in either side-loop category. Thus, if the most recent “x” number of posts all belong to either of the side categories, there will be no posts to display in the main loop. Fortunately, there are several relatively simple solutions for this situation, which will be discussed at the end of this article.

The Main Loop

As mentioned, the main loop must appear before the two side loops. Fortunately, the magic of CSS nullifies the restriction imposed by this requirement. Another important point to mention involves the lack of archive navigation (e.g., << previous | next >>) for either of the side posts. Although this may be possible, I have yet to determine an optimal method.

Nonetheless, complete navigation comes standard with the main loop. The side loops work perfectly for displaying the latest “x” number of posts from their respective categories. An elegant solution would be to provide a nice “Read more posts from this category »” link for each side loop display.

Moving along, our first task is to exclude our two chosen side categories from the main loop. Here, we are instructing WordPress to process posts as usual, with the exception that all posts from either side-loop category are ignored. Here are the first three lines of the main loop:

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<?php if ( in_category('7') && is_home() ) continue; ?>
<?php if ( in_category('8') && is_home() ) continue; ?>

Above, the loop starts with the usual if():while() statement, and then proceeds into a key pair of conditional if statements. The first conditional statement filters out all posts from category 7, while the second does the same for category 8. The remainder of the main loop is expressed as usual, as generalized here, replete with several key elements:

// the first loop
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<?php if ( in_category('7') && is_home() ) continue; ?>
<?php if ( in_category('8') && is_home() ) continue; ?>
<?php the_title(); ?>
<?php the_content(); ?>
<?php comments_template(); ?>
<?php endwhile; ?>
<?php posts_nav_link('','','« Previous') ?><?php previous_post('« %', '', 'yes'); ?>
<?php posts_nav_link('','Next »','') ?><?php next_post('% »', '', 'yes'); ?>
<?php else : ?>
<p>Sorry..</p>
<?php endif; ?>

The previous loop example includes title, contents, comments, and navigational elements, as well as an else statement issuing a nice “Sorry” message for those awkward, missing-post moments. For more information regarding the WordPress loop, check out the WordPress Codex and 4 Ways to Loop with WordPress.

The Side Loops

As previously noted, the term “side loops” is a bit of a misnomer. Instead of the standard if():while() loop intro, the two secondary loops each begin with get_posts() and foreach():start_wp() statements. With such, the secondary loops function similar to standard loops, but employ a different functional method. As far as calling them “side” loops, well that is just a convenient name, as they may display posts anywhere, not just on the side of a web page.

Now, with the main loop in place, it is time to add the two side loops. Here is the basic setup:

// the second loop
<?php query_posts('cat=7&showposts=3'); ?>
<?php $posts = get_posts('category=7&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>

// the third loop
<?php query_posts('cat=8&showposts=3'); ?>
<?php $posts = get_posts('category=8&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>

Okay, in the previous example, the second loop is querying all posts in category 7 and displaying the first three, beginning with the most recent posts. The second post acts similarly, with the exception that it queries posts from category 8. Each loop also contains a title and excerpt element, included for the sake of demonstrative clarity.

Several important points should be mentioned here. For each of the two side loops, there are two parameters indicating category number, two parameters indicating the number of displayed posts, and one parameter indicating an offset value. For this triple-loop configuration to function properly, it is essential to provide matching parameters for both category and post display number. That is, the values cat=8&showposts=3 must correspond precisely to the values category=8&numberposts=3. Same category, same number of posts to display. Meanwhile, the offset parameter should remain at zero.

Thus, to customize the two secondary loops, simply choose two unique category ID’s and corresponding values specifying how many posts from either category you wish to display.

All Together Now

Now that I have bored you nearly to death with all of the gruesome details, here is the complete code for the Perishable Press triple loop for WordPress:

// the first loop
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<?php if ( in_category('7') && is_home() ) continue; ?>
<?php if ( in_category('8') && is_home() ) continue; ?>
<?php the_title(); ?>
<?php the_content(); ?>
<?php comments_template(); ?>
<?php endwhile; ?>
<?php posts_nav_link('','','« Previous') ?><?php previous_post('« %', '', 'yes'); ?>
<?php posts_nav_link('','Next »','') ?><?php next_post('% »', '', 'yes'); ?>
<?php else : ?>
<p>Sorry..</p>
<?php endif; ?>

// the second loop
<?php query_posts('cat=7&showposts=3'); ?>
<?php $posts = get_posts('category=7&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>

// the third loop
<?php query_posts('cat=8&showposts=3'); ?>
<?php $posts = get_posts('category=8&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>

That’s all there is to it — copy, paste, and tweak to taste!

Caveat

As previously discussed, the main loop will look at the first “x” number of posts, and display only those posts which are not included in either side-loop category. Thus, if the most recent “x” number of posts all belong to either of the side categories, there will be no posts to display in the main loop. Fortunately, there are several relatively simple solutions for this situation:

  1. Increase the number of posts that are displayed in the main loop via the Admin > Options > Reading panel.
  2. Decrease the number of posts that are displayed in the side loops via step 5a and 5b in the Installation instructions above.
  3. Simply add a few more (or however many it takes) posts to categories appearing in the main loop (i.e., non-side-loop categories).

A Million Loops

With a little imagination, it is easy to envision a case where more than three loops might be required. Fortunately, our method for dishing three WordPress loops may be extrapolated into four WordPress loops, five WordPress, loops, six WordPress loops, …even a million WordPress loops. Well, okay, maybe not. Nonetheless, for any reasonable number of loops, if you have the categories, we have the code!

Here is a hypothetical, generalized example demonstrating “n” number of WordPress loops:

// the first loop
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<?php if ( in_category('2') && is_home() ) continue; ?>
<?php if ( in_category('3') && is_home() ) continue; ?>
<?php if ( in_category('4') && is_home() ) continue; ?>
<?php if ( in_category('5') && is_home() ) continue; ?>
.                .      .         .            .
.                .      .         .            .
.                .      .         .            .
<?php if ( in_category('n') && is_home() ) continue; ?>
<?php the_title(); ?>
<?php the_content(); ?>
<?php endwhile; ?>
<?php else : ?>
<?php endif; ?>

// the second loop
<?php query_posts('cat=2&showposts=3'); ?>
<?php $posts = get_posts('category=2&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>

// the third loop
<?php query_posts('cat=3&showposts=3'); ?>
<?php $posts = get_posts('category=3&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>

// the fourth loop
<?php query_posts('cat=4&showposts=3'); ?>
<?php $posts = get_posts('category=4&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>

// the fifth loop
<?php query_posts('cat=5&showposts=3'); ?>
<?php $posts = get_posts('category=5&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>
.     .     .     
.     .     . 
.     .     . 
// the nth loop
<?php query_posts('cat=n&showposts=3'); ?>
<?php $posts = get_posts('category=n&numberposts=3&offset=0'); 
foreach ($posts as $post) : start_wp(); ?>
<?php the_title(); ?>
<?php the_excerpt(); ?>
<?php endforeach; ?>

Well, surely I have worn out my welcome here. Perhaps you will be so kind as to drop a comment to share your particular experience with multiple WordPress loops. Cheers!

About the Author
Jeff Starr = Web Developer. Security Specialist. WordPress Buff.
Banhammer: Protect your WordPress site against threats.

50 responses to “Triple Loop for WordPress”

  1. Lauren Grinberg 2007/10/16 7:27 pm

    My set up, still in local-test-run, is: sidebar1.php then index.php then sidebar.php.

    i want to have the sideloops in sidebar1.php and the post content in index.php.

    So far, I have got the sideloops working but the post content in index.php which shouldbe showing all other posts instead shows the “Sorry” message no matter how many posts I have in the other categories (not the sideloop cats). Any ideas?

    p.s. thanks for taking the time to publish your tips.

  2. Perishable 2007/10/17 8:03 am

    Lauren,
    Have you tried combining the contents of the three files into one (i.e., the index.php)? That would eliminate the placement of the secondary loops in the sidebars as a potential cause of the issue.

  3. Lauren Grinberg 2007/10/17 9:14 am

    I somewhat fixed the problem after reviewing the procedure. I think the problem was that index.php was calling for sidebar1.php before the content. Once I moved the call everything fell into place, except the main content is now in the lefthand sidebar. I can live with that with a few design tweeks.

    Thanks again.

  4. Perishable 2007/10/17 9:40 am

    No problem — glad to hear things are working! ;)

  5. I’m trying to implement the hiding of posts in the main section if they show up on a sidebar via your “if ( in_category(”)” statements and everything does display correctly per category.

    However, the next and previous links don’t work as expected. The home page shows posts in the main section, but if you click on “previous” the resulting page doesn’t show any posts in the main area, although the second and third loops do work showing their posts.

    If I comment out the lines in the index file to hide posts in the categories showing posts in other loops, all is good. This is on a server on my workstation so I can’t show it to you live as yet. Any thoughts?

    Thanks in advance.

  6. Perishable 2007/11/06 9:16 pm

    Arthur,
    Keep in mind that the side loops “steal” x number of posts from the primary loop. Thus, if there are only 10 posts total, and the side loops each show three (3+3=6), that only leaves four posts to display in the main loop. Therefore, depending on the number of posts you have set to display in the primary loop, say five, there may be no additional posts to show on the next archive page. The solution, of course, is to populate the database with more posts, or throw down some conditional navigational links to avoid the confusion. I realize I am assuming a lot here, but without more information it is difficult to advise. I hope this helps ;)

  7. >>[T]here may be no additional posts to show on the next archive page. The solution, of course, is to populate the database with more posts[.]<<

    Absolutely correct! Thanks so much for the timely response. All is working fine now.

    What a great resource, this site!!!

  8. I am so bewildered.

    I have an old blog (running WP 2.0.5) and I can get this to work fine. Actually, I only want the code from the side loops for a project on a particular page. My tests there work fine.

    But on the new blog (running WP 2.1.2) where I actually want the code to work, I cannot get it to work to save my life.

    I feel it will be something silly.

    I have the EXEC-PHP (v4.0) plugin installed in both blogs. PHP seems to be working ok; I get the expected result when I try the test…

    <?php echo "This is the Exec-PHP 'Hello World'"; ?>

    …so no problems there.

    The option “WordPress should correct invalidly nested XHTML automatically” is not checked.

    I am not using the WYSIWYG editor. The ‘unfiltered html’ and ‘exec php’ capabilities are new to me, but I don’t believe this to be a problem since I’m posting from the admin account and since the Exec-php test is working correctly.

    But I just cannot get it to work. Here is the code that works in the old (v2.0.5) blog but not in the new (v2.1.2) blog.

    <div style="background-color:#CCCCCC; float:left; width:46%; padding: 10px">
    <?php query_posts('cat=7&showposts=2'); ?>
    <?php $posts = get_posts('category=7&numberposts=2&offset=0'); foreach ($posts as $post) : start_wp(); ?>
    <?php the_title(); ?>
    <?php the_content(); ?>
    <?php endforeach; ?>
    </div>

    The category is populated with posts.

    The code stands alone on a WP page (for now). When I save the page and try to load it, it usually churns for a while and times out. I get one line of the background color defined in the DIV statement.

    I really hope someone can save my sanity and help me understand why it works one place and not the other.

    Thank you very much.

  9. Lauren Grinberg 2007/11/15 10:00 am

    Danny,

    I compared your code to mine (I use WP2.3) and it looks the same. One thing that tripped me up, and was very silly, was the actual category number. I had been working on my localhost which used a different number as the actual online blog (I hadn’t made a fresh copy of the DB and installed it locally). I’m a novice and DID get this Triple Loop working. Forgive me if my suggestion is BEYOND obvious.

    : )

  10. Lauren,

    No suggestion is too obvious including making sure that I have the monitor on.

    I did get tripped up early with the category #, and especially making sure that both parameters (category & number of posts) were the same both places, but I am quite sure that it is correct.

    I can’t figure what the problem is, and it’s quite vexing. I would be glad for any help or other suggestions. (The problem is outlined in comment #20.)

    Danny

  11. Perishable 2007/11/18 9:33 am

    Danny,

    Are you running the additional loops through EXEC-PHP on both blogs?

    Jeff

  12. Hi Jeff,

    Not sure exactly what you are asking…

    Both blogs have EXEC-PHP installed. The old blog doesn’t really need/use the loops, but I tested the code there first, and it worked fine.

    The new blog is where I would like to use the loops. EXEC-PHP is installed there also, and as I said, this php test code worked fine on the new blog:

    <?php echo "This is the Exec-PHP 'Hello World'"; ?>

    So at least some php is executing okay on the new blog.

    Thanks. I hope I answered what you were asking.

    Danny

Comments are closed for this post. Something to add? Let me know.
Welcome
Perishable Press is operated by Jeff Starr, a professional web developer and book author with two decades of experience. Here you will find posts about web development, WordPress, security, and more »
BBQ Pro: The fastest firewall to protect your WordPress.
Thoughts
I live right next door to the absolute loudest car in town. And the owner loves to drive it.
8G Firewall now out of beta testing, ready for use on production sites.
It's all about that ad revenue baby.
Note to self: encrypting 500 GB of data on my iMac takes around 8 hours.
Getting back into things after a bit of a break. Currently 7° F outside. Chillz.
2024 is going to make 2020 look like a vacation. Prepare accordingly.
First snow of the year :)
Newsletter
Get news, updates, deals & tips via email.
Email kept private. Easy unsubscribe anytime.