1

In category.php, I want to get list of posts in that category.

I found two ways: using get_posts($args) and The Loop.

get_posts() way

$args = array (
  "category" => ID );
$posts = get_posts($args);
// then use foreach to loop the $posts

The Loop way

<?php if (have_posts() ): ?>
  <?php while (have_posts() ): the_post(); ?>
    ...
  <?php endwhile; ?>
<?php endif; ?>

So which one more efficient?

From what I searched, get_posts() is for custom template while The Loop is used inside the template that follows Wordpress naming convention.

I prefer get_posts() but if there's a big overhead compared to The Loop, I should re-think it again.

9
  • 3
    So you are talking about secondary WP_Query() queries (i.e. not main queries)? ps: get_posts() is a WP_Query() wrapper.
    – birgire
    Commented Nov 27, 2013 at 9:55
  • Yes, is using only secondary wp_query significantly slower or negligible? Because I don't like The Loop syntax.
    – hrsetyono
    Commented Nov 27, 2013 at 9:57
  • 1
    I guess it depends on what you are trying to fetch, but I don't think this matters very much for few items. Do you need to use template tags or do you only need the posts ids? What kind of query arguments are you using? There might be some overhead with the_post(). But benchmarking it on your setup might give you the real performance difference. So I think you need to narrow down your question, to get some specific answers.
    – birgire
    Commented Nov 27, 2013 at 10:05
  • 1
    ps: but don't use additional secondary queries if you can use the main query or modify it via the pre_get_posts action.
    – birgire
    Commented Nov 27, 2013 at 10:14
  • 1
    You have that same exact control with the main loop, or with a new loop using WP_Query(). Commented Nov 28, 2013 at 3:12

1 Answer 1

7

When WordPress include the category.php (just like any other template) the main query (i.e. the query triggered by the url) is already ran: it means that a query on database was already fired and this is the reason why in a template you can run the Loop without calling a query.

If you call get_posts you ran another query to database, that is completely unnecessary once is identical to the one already ran by WordPress.

Also note, that get_posts is just a wrapper for WP_Query so the difference from using the Loop and using the foreach syntax, does not regard the query itself, but the way you use the result of the query.

If you prefer foreach over while (the Loop is just a while loop) you can use it also calling directly WP_Query:

$q = new WP_Query( $args ); // or $q = get_posts( $args );

foreach ( $q->posts as $post ) {
  // here your code e.g.
  $title = $post->title;
  $content = $post->content;
}

So why normally people use the Loop instead of this kind of loop? for 2 reason, at least:

  1. Setup of global $post object. When you call the_post() inside the Loop, that function globalize the variable $post containing the current post object. A lot of core functions and plugins rely on that variale, so often is important to setup. However, it can be set up also using a foreach loop, using setup_postdata function, the previous code becomes:

    global $post;
    foreach ( $q->posts as $post ) {
      setup_postdata($post);
      // here your code e.g.
      $title = $post->title;
      $content = $post->content;
    }
    
  2. Second reason regards template tags, over the direct access to post object properties: when you call the_title or the_content (or other template tags) filters are fired before output the property, and if you don't call them, the output can be not the one you expect, again this can be done manually in our foreach loop:

    $q = new WP_Query( $args );
    global $post;
    foreach ( $q->posts as $post ) {
      setup_postdata($post);
      // here your code e.g.
      $title = apply_filters('the_title', $post->title);
      $content = apply_filters('the_content', $post->content);
    }
    

Last code block, can be considered almost identical to the Loop.

I said almost because there's another thing that differe: 2 action hooks, loop_start and loop_end, fired by the Loop. Some plugins can use them, so for better compatibility you should do something like:

$q = new WP_Query( $args );

global $post;
do_action_ref_array('loop_start', array(&$q));
foreach ( $q->posts as $post ) {
  setup_postdata($post);
  // here your code e.g.
  $title = apply_filters('the_title', $post->title);
  $content = apply_filters('the_content', $post->content);
}
do_action_ref_array('loop_end', array(&$q));

As already said, this is better used for custom loops, but regarding the main query, calling another WP_Query is bad for performance and unnecessary. However, the main query is just a WP_Query instance, saved in the global variable $wp_query, so if in your template you do something like:

global $wp_query, $post;
do_action_ref_array('loop_start', array(&$wp_query));
foreach ( $wp_query->posts as $post ) {
  setup_postdata($post);
  // here your code e.g.
  $title = apply_filters('the_title', $post->title);
  $content = apply_filters('the_content', $post->content);
}
do_action_ref_array('loop_end', array(&$wp_query));

you are doing something very similar (pratically identical) to the Loop, just quite more verbose, but if you just hate while loops...

In conclusion, regarding main query, for compatibility reasons is advisable (but not mandatory) use the Loop: if you don't trigger anction and filter hooks there are chance that if you install any plugins some of them will not work...

But for custom loops, sometimes, you don't need (or you don't want) to trigger action and filter hooks, and the foreach syntax can be preferable in that cases.

1
  • Thanks, it does clear up a lot of Wordpress thing for me. Cheers!
    – hrsetyono
    Commented Nov 28, 2013 at 3:51

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.