When should you use WP_Query vs query_posts() vs get_posts()?

  • It seems like half the tutorials in the Codex and around the blogosphere use query_posts() and half use WP_Query. What's the deal?

  • Rarst

    Rarst Correct answer

    10 years ago
    • query_posts() is overly simplistic and a problematic way to modify the main query of a page by replacing it with new instance of the query. It is inefficient (re-runs SQL queries) and will outright fail in some circumstances (especially often when dealing with posts pagination). Any modern WP code should use more reliable methods, like making use of the pre_get_posts hook, for this purpose. TL;DR don't use query_posts() ever.

    • get_posts() is very similar in usage and accepts the same arguments (with some nuances, like different defaults), but returns an array of posts, doesn't modify global variables and is safe to use anywhere.

    • WP_Query is the class that powers both behind the scenes, but you can also create and work with your own instance of it. A bit more complex, fewer restrictions, also safe to use anywhere.

    (1) "and is safe to use anywhere" --> but do not use this for the MAIN loop. (2) remember to use global $query_string; before the line that has query_posts();

    @scribu then again 'get_posts' will work although not advised: http://core.trac.wordpress.org/ticket/16545

    I believe that `query_posts` is also less efficient in that it will run additional queries, when if you use only `WP_Query` for your main loop, it will only run the query you choose in WP_Query.

    @jjeaton `query_posts()` is tiny wrapper function for `WP_Query`, the only extra thing it does (as per flowchart) is overwriting global `$wp_query`

    @Rarst I was referring to this section in the Codex for query_posts however, I may be mistaken regarding the effect on performance. Unless using WP_Query in your template file will also have the same result (i.e. throwing away your query, and re-executing)

    @jjeaton Replacing `query_posts()` with `WP_Query` will make no difference in performance, original page's query will still run because that is part of core load. Those queries will run even if your template file has no loop at all.

    Can't get rid off the feeling that this the most genious and upvoted post on WPSE. Should be in Codex as well.

    Ok, after looking at it for more than quite a time, I think `query_posts()` is missing a static var that get's set to true after the first use and - if used twice - should trigger `_doing_it_wrong();`. Guess I'm going to bug the wp-hacker or trac guys about this.

    @kaiser well... using `query_posts()` twice is about as bad as once, doesn't matter much as for me. :) btw Andrew Nacin is going to do presentation on queries and he said he might propose some improvements to the flowchart, so version two might be coming some time in the future.

    I'll just add my clearest description of the "performance of query_posts()" issue: Using query_posts() or WP_Query within a template file will have the same performnace cost: the query you just performed. The issue discussed in the codex article is that if you actually want to replace the query you should do so by filtering the original query_posts() with the 'parse_query' filter. That way you only have the one, original, desirable query, rather than doing a second query to awkwardly replace it. query_posts() is NEVER THE WAY!! NEVER!

    This makes no mention of the 'request' filter, which is a great way to modify the main query. The advantage over query_posts is that function wipes out the original query and generates a new one - same as if you used WP_Query. By using the request filter, your modifying the original query before its ever sent. I think thats what @JeremyClarke is getting at above.

    There's a freaking awesome explanation of query_posts written by John James Jacoby on the developer.wordpress.com blog that blows all of these answers out of the water. The main point: `query_posts` doesn't *modify* the main loop at all, it *replaces* it *after* it has already run. The best way to modify the main loop is through a `pre_get_posts` filter. http://developer.wordpress.com/2012/05/14/querying-posts-without-query_posts/

    @Dan you are confusing technical implementation and purpose. `query_posts()` does replace main loop object, but the purpose of it is to modify main loop. Also I am warmly fond of loop filters, but that wasn't what question asked. There is follow up question from other person on that topic.

    The question was "When should you use... query_posts()" and according to the logic presented by that blog post and the comments above, the answer is likely never.

    so, if its so bad, why does `query_posts` exist?

    @Manny Fleurmond conceptually `query_posts()` is an attempt to dumb down main loop concepts to the level of theme template tag (ease of which is one of the strong points for WP popularity). The task simply turned out to be too much for template tag to possibly accomplish. Core developers did voice possibility of it getting deprecated but I don't think there was decision about that yet.

    You actually can't "use anywhere" the WP_Query(), i just tried and it still croaks at $thequery->have_posts(), infinite recursion, see http://wordpress.stackexchange.com/questions/34270

    @NoBugs the loop in that question is wrong and there is answer explaining why.

    Agghh thanks for this. Finally, something makes sense. Seriously, WordPress and their lousy documentation. I don't know how such convoluted software and bad coding standards became so popular.

    Found this speed test between wp_query and get_posts http://www.wpclocked.com/

    I would trust such test... exactly none. :) The function is a _very_ thin wrapper, any difference will be coming from slight difference in arguments and/or hooks.

    No need for emotions, query_posts() is a function with side effects: setting a global. WordPress is staggerd with functions with side effects. This is not a performance issue but a quality of code issue. Look at https://developer.wordpress.org/reference/functions/query_posts/ and see what query_posts does. Use WP_Query unless you want to mess up global variables.

License under CC-BY-SA with attribution

Content dated before 6/26/2020 9:53 AM