何时使用WP_query(),query_posts()和pre_get_posts
-
-
[何时应该使用WP \ _Query vs query \ _posts()与get \ _posts()?]的可能重复项] [http://wordpress.stackexchange.com/questions/1753/when-should-you-use-wp-查询-vs-查询-帖子-vs-获取帖子)Possible duplicate of [When should you use WP\_Query vs query\_posts() vs get\_posts()?](http://wordpress.stackexchange.com/questions/1753/when-should-you-use-wp-query-vs-query-posts-vs-get-posts)
- 4
- 2016-01-03
- dotancohen
-
@saltcod,现在有所不同,WordPress不断发展,与可接受的答案相比,我添加了一些评论[此处](http://wordpress.stackexchange.com/questions/50761/when-to-use-wp-query-query-posts-and-pre-get-posts/250523#250523).@saltcod, now is different, WordPress evolved, I added few comments in comparison to the accepted answer [here](http://wordpress.stackexchange.com/questions/50761/when-to-use-wp-query-query-posts-and-pre-get-posts/250523#250523).
- 0
- 2016-12-28
- prosti
-
5 个回答
- 投票数
-
- 2012-05-01
您说对了
不再使用
query_posts
pre_get_posts
pre_get_posts
是一个过滤器,用于更改任何 >查询.它最常用于仅更改"主查询":add_action('pre_get_posts','wpse50761_alter_query'); function wpse50761_alter_query($query){ if( $query->is_main_query() ){ //Do something to main query } }
(我还会检查
is_admin()
是否返回false -尽管这可能是多余的.)主要查询在您的模板中显示为:if( have_posts() ): while( have_posts() ): the_post(); //The loop endwhile; endif;
如果您需要编辑此循环,请使用
pre_get_posts
.即,如果您很想使用query_posts()
-请改用pre_get_posts
.WP_Query
主要查询是
WP_Query object
的重要实例. WordPress使用它来决定要使用的模板,例如,传递给url的所有参数(例如,分页)都将导入到WP_Query
对象的实例中.对于辅助循环(例如,在侧边栏或"相关文章"列表中),您需要创建自己的
WP_Query
对象的单独实例.例如.$my_secondary_loop = new WP_Query(...); if( $my_secondary_loop->have_posts() ): while( $my_secondary_loop->have_posts() ): $my_secondary_loop->the_post(); //The secondary loop endwhile; endif; wp_reset_postdata();
注意
wp_reset_postdata();
-这是因为辅助循环将覆盖标识<当前帖子>的全局$post
变量.这实际上会将其重置为我们所在的$post
.get_posts()
这实际上是
WP_Query
对象的单独实例的包装.这将返回一个post对象数组.上面循环中使用的方法不再对您可用.这不是一个"循环",仅仅是一个post对象数组.<ul> <?php global $post; $args = array( 'numberposts' => 5, 'offset'=> 1, 'category' => 1 ); $myposts = get_posts( $args ); foreach( $myposts as $post ) : setup_postdata($post); ?> <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li> <?php endforeach; wp_reset_postdata(); ?> </ul>
回答您的问题
- 使用
pre_get_posts
更改您的主要查询.模板页面中的辅助循环使用单独的WP_Query
对象(方法2). - 如果要更改主循环的查询,请使用
pre_get_posts
.
You are right to say:
Never use
query_posts
anymorepre_get_posts
pre_get_posts
is a filter, for altering any query. It is most often used to alter only the 'main query':add_action('pre_get_posts','wpse50761_alter_query'); function wpse50761_alter_query($query){ if( $query->is_main_query() ){ //Do something to main query } }
(I would also check that
is_admin()
returns false - though this may be redundant.). The main query appears in your templates as:if( have_posts() ): while( have_posts() ): the_post(); //The loop endwhile; endif;
If you ever feel the need to edit this loop - use
pre_get_posts
. i.e. If you are tempted to usequery_posts()
- usepre_get_posts
instead.WP_Query
The main query is an important instance of a
WP_Query object
. WordPress uses it to decide which template to use, for example, and any arguments passed into the url (e.g. pagination) are all channelled into that instance of theWP_Query
object.For secondary loops (e.g. in side-bars, or 'related posts' lists) you'll want to create your own separate instance of the
WP_Query
object. E.g.$my_secondary_loop = new WP_Query(...); if( $my_secondary_loop->have_posts() ): while( $my_secondary_loop->have_posts() ): $my_secondary_loop->the_post(); //The secondary loop endwhile; endif; wp_reset_postdata();
Notice
wp_reset_postdata();
- this is because the secondary loop will override the global$post
variable which identifies the 'current post'. This essentially resets that to the$post
we are on.get_posts()
This is essentially a wrapper for a separate instance of a
WP_Query
object. This returns an array of post objects. The methods used in the loop above are no longer available to you. This isn't a 'Loop', simply an array of post object.<ul> <?php global $post; $args = array( 'numberposts' => 5, 'offset'=> 1, 'category' => 1 ); $myposts = get_posts( $args ); foreach( $myposts as $post ) : setup_postdata($post); ?> <li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li> <?php endforeach; wp_reset_postdata(); ?> </ul>
In response to your questions
- Use
pre_get_posts
to alter your main query. Use a separateWP_Query
object (method 2) for secondary loops in the template pages. - If you want to alter the query of the main loop, use
pre_get_posts
.
-
那么,有什么情况会直接使用get_posts()而不是WP_Query吗?So is there any scenario when one would go straight to get_posts() rather than WP_Query?
- 0
- 2012-08-25
- urok93
-
@drtanz-是的.举例来说,您不需要分页,也不需要顶部的粘性帖子-在这种情况下,`get_posts()`效率更高.@drtanz - yes. Say for instance you don't need pagination, or sticky posts at the top - in these instances `get_posts()` is more efficient.
- 0
- 2012-08-25
- Stephen Harris
-
但这是否会增加额外的查询,我们只需修改pre_get_posts即可修改主查询?But wouldn't that add an extra query where we could just modify pre_get_posts to modify the main query?
- 1
- 2012-08-26
- urok93
-
@drtanz-您不会在主查询中使用`get_posts()`-用于辅助查询.@drtanz - you wouldn't be using `get_posts()` for the main query - its for secondary queries.
- 0
- 2012-08-26
- Stephen Harris
-
在WP_Query示例中,如果您更改$my_secondary_loop->the_post();到$my_post=$my_secondary_loop->next_post();您可以避免记住使用wp_reset_postdata(),只要使用$my_post即可完成所需的操作.In your WP_Query example, if you change $my_secondary_loop->the_post(); to $my_post = $my_secondary_loop->next_post(); you can avoid having to remember to use wp_reset_postdata() as long as you use $my_post to do what you need to do.
- 0
- 2015-09-18
- Privateer
-
@Privateer并非如此,`WP_Query ::get_posts()`设置为`global $post;`.@Privateer Not so, `WP_Query::get_posts()` sets `global $post;`
- 0
- 2015-09-19
- Stephen Harris
-
@StephenHarris我只是浏览了查询类,但没有看到它.我进行检查主要是因为我从不使用wp_reset_postdata,因为我总是以这种方式进行查询.您正在创建一个新对象,所有结果都包含在其中.@StephenHarris I just looked through the query class and don't see it. I checked mostly because I never use wp_reset_postdata because I always do queries this way. You are creating a new object and all of the results are contained within it.
- 0
- 2015-09-19
- Privateer
-
@Privateer-对不起,错字,`WP_Query ::the_post()`,请参阅:https://github.com/WordPress/WordPress/blob/759f3d894ce7d364cf8bfc755e483ac2a6d85653/wp-includes/query.php#L3732@Privateer - sorry, typo, `WP_Query::the_post()`, see: https://github.com/WordPress/WordPress/blob/759f3d894ce7d364cf8bfc755e483ac2a6d85653/wp-includes/query.php#L3732
- 0
- 2015-09-19
- Stephen Harris
-
@StephenHarris Right=)如果在对象上使用next_post()而不是使用the_post,则无需执行全局查询,也不必记住以后要使用wp_reset_postdata.@StephenHarris Right =) If you use next_post() on the object instead of using the_post, you don't step on the global query and don't need to remember to use wp_reset_postdata afterwards.
- 2
- 2015-09-19
- Privateer
-
@私人啊,我的道歉,似乎让我感到困惑.您是正确的(但是您将无法使用任何引用全局`$post`的函数,例如`the_title()`,`the_content()`).@Privateer Ah, my apologies, seemed to have confused myself. You are right (but you would not be able to use any functions which refer to the global `$post` e.g. `the_title()`, `the_content()`).
- 0
- 2015-09-23
- Stephen Harris
-
是的=)我从不使用任何一个,所以我不会错过他们.True =) I never use any of those so I don't miss them.
- 0
- 2015-09-24
- Privateer
-
@ urok93当我需要获取与ACF相关的帖子时,有时是`get_posts()`,尤其是在只有一个帖子的情况下.考虑到要标准化我的模板,我正在考虑将它们重写为WP_Query实例.@urok93 I sometimes `get_posts()` when I need to get ACF related posts, especially if there's only one. Thought to standardize my templates I'm considering rewriting them as WP_Query instances.
- 0
- 2018-02-14
- Slam
-
- 2012-05-01
循环有两种不同的上下文:
- 主循环,该循环根据URL请求而发生,并在加载模板之前进行处理
- 次要循环以任何其他方式发生,从模板文件或其他方式调用
query_posts()的问题是它是次要循环,试图成为主要循环,但失败了.因此,忘记它的存在.
修改主循环
- 不要使用
query_posts()
- 将
pre_get_posts
过滤器与$ query-&gt;is_main_query()
检查一起使用 - 或者使用
request
过滤器(有点太粗糙了,所以上面的更好)
运行辅助循环
使用可互换性很大的
new WP_Query
或get_posts()
(后者是前者的薄包装).要清理
如果您使用
query_posts()
或直接与全局$ wp_query
混淆,请使用wp_reset_query()
-因此您几乎永远不需要.如果使用
the_post()
或setup_postdata()
或与全局$post
wp_reset_postdata() >,并且需要恢复与帖子相关的事物的初始状态.There are two different contexts for loops:
- main loop that happens based on URL request and is processed before templates are loaded
- secondary loops that happen in any other way, called from template files or otherwise
Problem with
query_posts()
is that it is secondary loop that tries to be main one and fails miserably. Thus forget it exists.To modify main loop
- don't use
query_posts()
- use
pre_get_posts
filter with$query->is_main_query()
check - alternately use
request
filter (a little too rough so above is better)
To run secondary loop
Use
new WP_Query
orget_posts()
which are pretty much interchangeable (latter is thin wrapper for former).To cleanup
Use
wp_reset_query()
if you usedquery_posts()
or messed with global$wp_query
directly - so you will almost never need to.Use
wp_reset_postdata()
if you usedthe_post()
orsetup_postdata()
or messed with global$post
and need to restore initial state of post-related things.-
Rarst的意思是wp_reset_postdata()Rarst meant `wp_reset_postdata()`
- 4
- 2012-06-01
- Gregory
-
- 2012-09-16
有使用
query_posts($query)
的合法方案,例如:-
您要在页面上显示帖子列表或自定义帖子类型的帖子(使用页面模板)
-
您希望对这些帖子进行分页
现在为什么要在页面上显示它而不使用存档模板?
-
对于管理员(您的客户?)来说更直观-他们可以在"页面"中查看页面
-
最好将其添加到菜单中(没有页面,他们必须直接添加url)
-
如果要在模板上显示其他内容(文本,缩略图,或任何自定义元内容),则可以轻松地从页面上获取它(这对客户也更有意义).看看您是否使用了存档模板,您是否需要对其他内容进行硬编码或使用例如主题/插件选项(这会使客户不太直观)
这是一个简化的示例代码(将在您的页面模板上-例如page-page-of-posts.php):
/** * Template Name: Page of Posts */ while(have_posts()) { // original main loop - page content the_post(); the_title(); // title of the page the_content(); // content of the page // etc... } // now we display list of our custom-post-type posts // first obtain pagination parametres $paged = 1; if(get_query_var('paged')) { $paged = get_query_var('paged'); } elseif(get_query_var('page')) { $paged = get_query_var('page'); } // query posts and replace the main query (page) with this one (so the pagination works) query_posts(array('post_type' => 'my_post_type', 'post_status' => 'publish', 'paged' => $paged)); // pagination next_posts_link(); previous_posts_link(); // loop while(have_posts()) { the_post(); the_title(); // your custom-post-type post's title the_content(); // // your custom-post-type post's content } wp_reset_query(); // sets the main query (global $wp_query) to the original page query (it obtains it from global $wp_the_query variable) and resets the post data // So, now we can display the page-related content again (if we wish so) while(have_posts()) { // original main loop - page content the_post(); the_title(); // title of the page the_content(); // content of the page // etc... }
现在,非常清楚,我们可以避免在此处也使用
query_posts()
,而应使用WP_Query
-像这样:// ... global $wp_query; $wp_query = new WP_Query(array('your query vars here')); // sets the new custom query as a main query // your custom-post-type loop here wp_reset_query(); // ...
但是,当我们有这么好的小功能可用时,为什么要这么做呢?
There are legitimate scenarios for using
query_posts($query)
, for example:You want to display a list of posts or custom-post-type posts on a page (using a page template)
You want to make pagination of those posts work
Now why would you want to display it on a page instead of using an archive template?
It's more intuitive for an administrator (your customer?) - they can see the page in the 'Pages'
It's better for adding it to menus (without the page, they'd have to add the url directly)
If you want to display additional content (text, post thumbnail, or any custom meta content) on the template, you can easily get it from the page (and it all makes more sense for the customer too). See if you used an archive template, you'd either need to hardcode the additional content or use for example theme/plugin options (which makes it less intuitive for the customer)
Here's a simplified example code (which would be on your page template - e.g. page-page-of-posts.php):
/** * Template Name: Page of Posts */ while(have_posts()) { // original main loop - page content the_post(); the_title(); // title of the page the_content(); // content of the page // etc... } // now we display list of our custom-post-type posts // first obtain pagination parametres $paged = 1; if(get_query_var('paged')) { $paged = get_query_var('paged'); } elseif(get_query_var('page')) { $paged = get_query_var('page'); } // query posts and replace the main query (page) with this one (so the pagination works) query_posts(array('post_type' => 'my_post_type', 'post_status' => 'publish', 'paged' => $paged)); // pagination next_posts_link(); previous_posts_link(); // loop while(have_posts()) { the_post(); the_title(); // your custom-post-type post's title the_content(); // // your custom-post-type post's content } wp_reset_query(); // sets the main query (global $wp_query) to the original page query (it obtains it from global $wp_the_query variable) and resets the post data // So, now we can display the page-related content again (if we wish so) while(have_posts()) { // original main loop - page content the_post(); the_title(); // title of the page the_content(); // content of the page // etc... }
Now, to be perfectly clear, we could avoid using
query_posts()
here too and useWP_Query
instead - like so:// ... global $wp_query; $wp_query = new WP_Query(array('your query vars here')); // sets the new custom query as a main query // your custom-post-type loop here wp_reset_query(); // ...
But, why would we do that when we have such a nice little function available for it?
-
布莱恩,谢谢你.我一直在努力使pre_get_posts可以在您描述的情况下的页面上正常工作:客户端需要将自定义字段/内容添加到原本为存档的页面中,因此需要创建"页面";客户需要查看一些要添加到导航菜单的内容,因为添加自定义链接会使其脱离;等我+1!Brian, thanks for that. I've been struggling to get pre_get_posts to work on a page in EXACTLY the scenario you describe: client needs to add custom fields/content to what otherwise would be an archive page, so a "page" needs to be created; client needs to see something to add to nav menu, as adding a custom link escapes them; etc. +1 from me!
- 2
- 2012-12-13
- Will Lanni
-
也可以使用"pre_get_posts"来完成.我这样做是为了让"静态首页"以自定义顺序和自定义过滤器列出我的自定义帖子类型.此页面也是分页的.查看此问题以了解其工作原理:http://wordpress.stackexchange.com/questions/30851/how-to-use-a-custom-post-type-archive-as-front-page/30854 简而言之,仍然没有更多使用query_posts的合法方案了;)That can also be done using "pre_get_posts". I did that to have a "static front page" listing my custom post types in a custom order and with a custom filter. This page is also paginated. Check out this question to see how it works: http://wordpress.stackexchange.com/questions/30851/how-to-use-a-custom-post-type-archive-as-front-page/30854 So in short, there is still no more legitimate scenario for using query_posts ;)
- 3
- 2015-01-12
- 2ndkauboy
-
因为"应该注意,用它代替页面上的主查询会增加页面加载时间,在最坏的情况下,所需的工作量增加一倍以上或更多.虽然易于使用,但该功能也容易造成混乱和以后的问题."来源http://codex.wordpress.org/Function_Reference/query_postsBecause "It should be noted that using this to replace the main query on a page can increase page loading times, in worst case scenarios more than doubling the amount of work needed or more. While easy to use, the function is also prone to confusion and problems later on." Source http://codex.wordpress.org/Function_Reference/query_posts
- 2
- 2015-03-09
- Claudiu Creanga
-
答案是各种各样的错误.您可以在WP中使用与自定义帖子类型相同的URL创建一个"页面".例如,如果您的CPT是Bananas,则可以获取一个具有相同URL的名为Bananas的页面.然后,您将获得siteurl.com/bananas.只要您的主题文件夹中有archive-bananas.php,它就会使用模板并"覆盖"该页面.如其他评论之一所述,使用此"方法"会为WP造成两倍的工作负载,因此永远不要使用.THis answer is all kinds of wrong. You can create a "Page" in WP with the same URL as the Custom post type. EG if your CPT is Bananas, you can get a page named Bananas with the same URL. Then you'd end up with siteurl.com/bananas. As long as you have archive-bananas.php in your theme folder, then it will use the template and "override" that page instead. As stated in one of the other comments, using this "method" creates twice the workload for WP, thus should NOT ever be used.
- 0
- 2015-06-19
- Hybrid Web Dev
-
- 2015-01-23
我从functions.php修改WordPress查询:
//unfortunately, "IS_PAGE" condition doesn't work in pre_get_posts (it's WORDPRESS behaviour) //so you can use `add_filter('posts_where', ....);` OR modify "PAGE" query directly into template file add_action( 'pre_get_posts', 'myFunction' ); function myFunction($query) { if ( ! is_admin() && $query->is_main_query() ) { if ( $query->is_category ) { $query->set( 'post_type', array( 'post', 'page', 'my_postType' ) ); add_filter( 'posts_where' , 'MyFilterFunction_1' ) && $GLOBALS['call_ok']=1; } } } function MyFilterFunction_1($where) { return (empty($GLOBALS['call_ok']) || !($GLOBALS['call_ok']=false) ? $where : $where . " AND ({$GLOBALS['wpdb']->posts}.post_name NOT LIKE 'Journal%')"; }
I modify WordPress query from functions.php:
//unfortunately, "IS_PAGE" condition doesn't work in pre_get_posts (it's WORDPRESS behaviour) //so you can use `add_filter('posts_where', ....);` OR modify "PAGE" query directly into template file add_action( 'pre_get_posts', 'myFunction' ); function myFunction($query) { if ( ! is_admin() && $query->is_main_query() ) { if ( $query->is_category ) { $query->set( 'post_type', array( 'post', 'page', 'my_postType' ) ); add_filter( 'posts_where' , 'MyFilterFunction_1' ) && $GLOBALS['call_ok']=1; } } } function MyFilterFunction_1($where) { return (empty($GLOBALS['call_ok']) || !($GLOBALS['call_ok']=false) ? $where : $where . " AND ({$GLOBALS['wpdb']->posts}.post_name NOT LIKE 'Journal%')"; }
-
希望看到此示例,但where子句位于自定义元上.would be interested to see this example but where clause is on custom meta.
- 0
- 2017-03-03
- Andrew Welch
-
- 2016-12-28
仅概述由于WordPress随时间演变而对公认答案的一些改进,而现在(五年后)有些不同:
pre_get_posts
是一个过滤器,用于更改任何查询.它最常用于仅更改"主查询":实际上是一个动作挂钩.不是过滤器,它将影响任何查询.
主查询在您的模板中显示为:
if( have_posts() ): while( have_posts() ): the_post(); //The loop endwhile; endif;
实际上,这也不是事实.函数
have_posts
迭代与主查询不唯一的global $wp_query
对象.global $wp_query;
可能还会与辅助查询一起更改.function have_posts() { global $wp_query; return $wp_query->have_posts(); }
get_posts()
这实际上是WP_Query对象的单独实例的包装.
实际上,如今,
WP_Query
是一个类,因此我们有一个类的实例.
总结:当时@StephenHarris编写的所有内容很可能都是真实的,但随着时间的流逝,WordPress中的情况已经发生了变化.
Just to outline some improvements to the accepted answer since WordPress evolved over the time and some things are different now (five years later):
pre_get_posts
is a filter, for altering any query. It is most often used to alter only the 'main query':Actually is an action hook. Not a filter, and it will affect any query.
The main query appears in your templates as:
if( have_posts() ): while( have_posts() ): the_post(); //The loop endwhile; endif;
Actually, this is also not true. The function
have_posts
iterates theglobal $wp_query
object that is not related only to the main query.global $wp_query;
may be altered with the secondary queries also.function have_posts() { global $wp_query; return $wp_query->have_posts(); }
get_posts()
This is essentially a wrapper for a separate instance of a WP_Query object.
Actually, nowadays
WP_Query
is a class, so we have an instance of a class.
To conclude: At the time @StephenHarris wrote most likely all this was true, but over the time things in WordPress have been changed.
-
从技术上讲,所有过滤器都在幕后,动作只是一个简单的过滤器.但是您在这里是对的,它是通过引用传递参数的操作,这与更简单的操作有所不同.Technically, it's all filters under the hood, actions are just a simple filter. But you are correct here, it's an action that passes an argument by reference, which is how it differs from more simple actions.
- 0
- 2016-12-28
- Milo
-
get_posts返回一个post对象数组,而不是一个WP_Query对象,因此这仍然是正确的.而WP_Query一直是一个类,是class=object的实例.`get_posts` returns an array of post objects, not a `WP_Query` object, so that is indeed still correct. and `WP_Query` has always been a class, instance of a class = object.
- 0
- 2016-12-28
- Milo
-
谢谢@Milo,由于某种原因我的脑子里简化了模型,这是正确的.Thanks, @Milo, correct from some reason I had oversimplified model in my head.
- 0
- 2016-12-28
- prosti
我阅读了 @nacin's 您昨天不认识,并被送往一个正在查询的兔子洞.昨天之前,我(错误地)使用了
query_posts()
我的查询需求.现在,我对使用WP_Query()
有点明智,但仍然有一些灰色区域.我想我肯定知道的信息:
如果我要在页面上的任何地方(在边栏中,页脚,任何类型的"相关帖子"等)中进行附加循环,我想使用
WP_Query()
.我可以在单个页面上重复使用它而不会造成任何伤害. (对?).我不确定的地方
pre_get_posts
与WP_Query()
?我现在应该对所有内容都使用pre_get_posts
吗?if have_posts : while have_posts : the_post
部分并编写自己的< href=" http://codex.wordpress.org/Function_Reference/WP_Query" rel="noreferrer">WP_Query()
?还是在函数中使用pre_get_posts
修改输出. PHP文件?tl; dr
我想从中得出的tl; dr规则是:
query_posts
WP_Query()
感谢任何智慧
特里
ps:我看过并读过:什么时候应该使用WP_Query vs query_posts()vsget_posts()?这又增加了另一个维度-
get_posts
.但根本不处理pre_get_posts
.