问题描述

我有一个关于”the loop” 的问题。

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
<?php endwhile; else: ?>
<p><?php _e('Sorry, no posts matched your criteria.'); ?></p>
<?php endif; ?>

代码取自 The Loop WordPress Codex page

为什么要把 if 放在一起?好像是有 while 循环,工作正常。

在没有 if 语句的情况下会发生什么情况?

Edit

我接受了 @ Chip 的回答。但老实说,我只需要最后一部分。

我现在知道我想知道的问题。

if statement is only when necessary, if you want to put a header or footer that should be write only once. if you don’t use the “wrapper”, then if statement is not necessary.

最佳解决方案

WordPress 模板加载程序将在许多情况下包含适当的上下文模板文件,即使该上下文的查询不返回任何帖子。例如:

  • 主要博客文章索引

  • 类别归档索引 (类别存在,但没有帖子)

  • 标签存档索引 (标签存在,但没有帖子)

  • 作者存档索引 (作者存在,但没有帖子)

  • 搜索结果索引

因此,在这些情况下,将加载适当的模板文件,但不会输出任何帖子,因为查询不返回帖子。

Proof-of-concept 示例:

因此,在这些上下文中,模板文件包含 if ( have_posts() )条件有用。

在其他上下文中,如果查询不返回任何帖子,模板文件将永远不会被加载。例如:

  • 单个博文

  • 静态页面

在这些情况下,if ( have_posts() )可能是不必要的。

Edit

I understand the query is invoked by the_post(), right? And if while(have_posts()) exist, the query is never occurs if there is no post.

要了解发生了什么,you have to look at the order of WordPress actions 。从 wp_loaded 开始 (为了清楚起见,省略一些):

  • wp_loaded

  • parse_request

  • send_headers

  • parse_query

  • pre_get_posts

  • wp

  • template_redirect

  • get_header

  • wp_head

  • the_post

  • wp_footer

那么,发生了什么,以什么顺序呢?

  • 查询被调用:

    • parse_query

    • pre_get_posts

    • wp

  • 选择模板:

    • template_redirect

  • 模板加载/输出。模板触发了以下操作:

    • get_header

    • wp_head

    • the_post

    • dynamic_sidebar

    • get_footer

    • wp_footer

因此,the_post()触发的 the_post 会在查询被解析后发生很长时间,并且将模板加载。

I’m very grateful that you give a lot of information that I didn’t know, but this is not what I asked.

哦,但我相信这正是你所问的。

真正的问题是:什么是有效的查询返回?对于诸如类别归档索引的上下文,查询是有效的,并且如果查询的类别 ID 存在,则加载类别模板,即使没有分配给该类别的帖子。

为什么?因为正在解析的查询是 (IIRC)&cat={ID} – 即使没有分配给该类别的帖子也是一个有效的查询,因此在解析时不会导致 404 。

在这种情况下,您会收到一个有效的查询,并加载了一个模板文件,但没有发布任何帖子。因此,if ( have_posts() ),其实是相关的。再次,这里有一个例子:category 存在,但没有分配任何帖子。加载类别模板文件,if ( have_posts() )返回 false

对于包含一个 post 变量 (&p={ID}) 的查询 (例如单个博客文章和静态页面),这并不适用,因为该帖子实际上不存在,并且在解析时,该查询将不会返回一个有效的对象。

编辑 2

If I rightly understand if there is no if(have_posts()) in a category template and the category have no post, then it return 404.php, even though it should be return category-sample.php without post. Is that right?

不记得:模板是在 template_redirect 中选择的。所以如果查询是否有效,那么加载相应的模板文件。如果查询无效,则加载 404 模板。

所以,一旦模板被加载 – 例如类别模板 – 一旦循环输出,模板不会更改。

再看一下行动的顺序:

  • parse_query

  • pre_get_posts

  • wp

  • template_redirect – 在这里选择并加载模板。这是不退货的模板点。此后,模板无法更改。

  • the_post – postdata 设置在这里,作为循环调用的一部分。这在模板中被调用,并且模板不会根据查询对象中的可用数据进行更改

最终编辑

And I’am claiming that while checks existence of posts, why should I run the same test twice. That is my question from first point I’ve been asking only about that.

而且,我终于明白了:一直以来,你的问题与 WordPress 或 WordPress Loop 无关。您正在询问将任意 PHP while 循环包含在 if 条件中,以检查相同的条件。

这个问题超出了 WPSE 的范围,但我将简要解释一下:

if 条件是一个二进制评估:它是 truefalse,并且该条件内发生的内容将被执行一次。

while 条件是一个循环:基于某种计数器,它在一些离散时间段内保持为真; 并且该条件内发生的事情将被执行多次 – 每次计数器迭代一次。

所以,假设你想输出一个无序列表的东西,如果事情的列表被填充。如果您使用 while 循环,并省略 if 包装器,则您的标记如下所示:

<ul>
<?php while ( list_of_things() ) : ?>
    <li><?php the_list_item(); ?></li>
<?php endwhile; ?>
</ul>

如果 list_of_things()为空,则渲染的输出为:

<ul>
</ul>

其中留下不必要的 (和无效的) 标记。

但是,如果添加 if 条件包装器,您可以这样做:

<?php if ( list_of_things() ) : ?>
    <ul>
    <?php while ( list_of_things() ) : ?>
        <li><?php the_list_item(); ?></li>
    <?php endwhile; ?>
    </ul>
<?php endif; ?>

如果 list_of_things()为空,则不会输出任何标记。

这只是一个例子。 if 条件包装器有很多用途,if 条件包装器与 while 循环完全不同。

次佳解决方案

改进芯片的答案真的是不可能的,只是为了减少追逐:

如果您想在没有帖子时显示不同的东西,请使用 if 部件。这在例如日期或类别归档页面上特别有用。如果有人浏览到没有帖子的页面,很高兴有一个消息说出来,而不是根本没有出现,因为循环永远都不会被执行。

if ( have_posts() ):
  // Yep, we have posts, so let's loop through them.
  while ( have_posts() ) : the_post();
  // do your loop
  endwhile;
else :
  // No, we don't have any posts, so maybe we display a nice message
  echo "<p class='no-posts'>" . __( "Sorry, there are no posts at this time." ) . "</p>";
endif;

参考文献

注:本文内容整合自 google/baidu/bing 翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:gxnotes#qq.com(#替换为 @) 。