临时覆盖$wp_Query是个坏主意吗?

时间:2016-07-12 作者:Cai

我正在重写我的许多代码,并重新构造我的许多主题文件,以使用模板部件(即使用get_template_part()). 我遇到了一种情况,无论是使用主查询还是辅助自定义查询(使用WP_Query). 模板部分还使用依赖于主查询的核心函数(例如条件)。

我可以通过覆盖主查询(或覆盖$wp_query 保存主查询的全局,主查询仍然存在),并在完成后重置主查询。这也意味着我可以对主查询和自定义查询使用相同的循环。例如:

// Query
if ( $i_need_a_custom_query ) {
    $wp_query = new WP_Query($custom_query_args);
}

// The Loop
if ( have_posts() ) : while ( have_posts() ) : the_post();

    // Do some loop stuff
    // and call some functions that rely on the main query

// End the loop
endwhile; endif;

// I\'m done so reset the query
wp_reset_query();
这是可行的。没问题,但我觉得这有点像黑客。所以我的问题是:

  • 我厌倦了这样覆盖主查询,对吗wp_reset_query() 是否成本不高(即它实际上不是重新运行查询,而是简单地重置仍然存在的全局变量)

    我只是将所讨论的自定义查询用作辅助查询,这就是问题的关键所在。无论是使用自定义辅助查询还是使用主查询,我都要使用相同的模板。我知道我所做的与使用query_posts(), 这是个坏主意。就我所知,从广义上讲,使用query_posts() 是1。性能,这不是问题,因为我只是在运行二级自定义查询时才这样做。更改全局$wp\\u查询的意外后果,这正是我所希望发生的(正如我所说的,实际上工作得很好)。

3 个回复
最合适的回答,由SO网友:bosco 整理而成

总的来说,我同意Howdy\\u McGee的观点,即除非绝对必要,否则应该避免覆盖主查询-通常情况下,使用类似\'pre_get_posts\' 钩子是解决这种情况的更好的方法。

手动覆盖全局$wp_query 如果不非常小心,可能会导致各种意外行为,包括破坏分页等。从本质上讲,这是一种与经常受到批评的query_posts() 函数,其中the WordPress Code Reference notes:

此函数将完全覆盖主查询,不供插件或主题使用。它修改主查询的过于简单的方法可能会有问题,应该尽可能避免。在大多数情况下,有更好、更高性能的选项来修改主查询,例如通过WP\\u查询中的“pre\\u get\\u posts”操作。

然而,在这种情况下,如果您需要:

同时访问主查询和辅助查询的不同内容和/或conditional tags

  • 一种通用实现,类似于可重用的模板部分,可应用于主查询或任何自定义查询,然后一种解决方案是存储“上下文”WP_Query 实例,并调用WP_Query\'s在该变量上的条件方法,而不是使用其全局可用的条件标记函数对应项(它们总是显式引用主查询,global $wp_query).

    例如,如果您希望“上下文查询变量”引用自定义帖子类型的单一模板和存档模板中的自定义二次查询,my-custom-post-type, 但在其他情况下,请参考主查询,您可以执行以下操作:

    Theme\'s functions.php file, or a plugin file:

    function wpse_232115_get_contextual_query() {
      global $wp_query;
      static $contextual_query;
    
      // A convenient means to check numerous conditionals without a bunch of \'||\' operations,
      // i.e "if( $i_need_a_custom_query )"
      if( in_array( true,
        [
          is_singular( \'my-custom-post-type\' ),
          is_post_type_archive( \'my-custom-post-type\' )
        ]
      ) ) {
        // Create the contextual query instance, if it doesn\'t yet exist
        if( ! isset( $contextual_query ) ) {
          $query_args = [
            //...
          ];
    
          $contextual_query = new WP_Query( $query_args );
        }
    
        return $contextual_query;
      }
    
      return $wp_query;
    }
    

    "Generic" template-part files:

    $query = wpse_232115_get_contextual_query();
    
    // Contextual Query loop (loops through your custom query when \'my-custom-post-type\'s
    // are being displayed, the main $wp_query otherwise.
    if ( $query->have_posts() ) : while ( $query->have_posts() ) : $query->the_post();
      // Tags dependent on The Loop will refer to the contextual query if The Loop
      // was set up with the "$query->" prefix, as above. Without it, they always
      // refer to the main query.
      the_title();
    
      // The global conditional tags always refer to the main query
      if( is_singular() ) {
        //... Do stuff is the main query result is_singular();
      }
    
      // Conditional methods on the $query object reference describe either
      // the main query, or a custom query depending on the context.
      if( $query->is_archive() ) {
        //... Do stuff if the $query query result is an archive
      }
    // End the loop
    endwhile; endif;
    
    我不确定这是否是最好的解决方案,但这是我解决问题的方式。

  • SO网友:Howdy_McGee

    我觉得你应该get_template_part() 用于标记。假设您有一个自定义模板和使用相同模板的博客get_template_part(). 您可以在循环中调用template\\u part,而不是在部分中调用循环。例如:

    Your Custom Template

    $custom_query = new WP_Query( $args );
    
    if ( $custom_query->have_posts() ) {
        while ( $custom_query->have_posts() ) { 
            $custom_query->the_post();
            get_template_part( $path );
        }
    
        wp_reset_postdata();
    }
    

    Your Blog File

    if ( have_posts() ) {
        while ( have_posts() ) { 
            the_post();
            get_template_part( $path );
        }
    }
    
    这将使您的自定义模板和日志文件都可以访问以下变量the_title()the_permamlink() 无需覆盖主查询。最后,以上内容将为您提供更大的灵活性。

    覆盖主查询几乎总是一个坏主意,以后会成为一个头痛的问题。这个wp_reset_query() 函数并不是最大的开销,但在处理二次查询时,它所做的仍然超出了实际需要。如果我们看看function itself 它几乎重置了更多的全局变量,并调用wp_reset_postdata() 我们可以称之为我们自己的函数。

    SO网友:Pieter Goosen

    你所做的正是为了什么query_posts 是的,这真的是个坏主意。为了证明我的观点,这里是query_posts()

    function query_posts($query) {
        $GLOBALS[\'wp_query\'] = new WP_Query();
        return $GLOBALS[\'wp_query\']->query($query);
    }
    
    记住,$GLOBALS[\'wp_query\'] === $wp_query. 现在,看看你的代码,你会发现它是一样的。

    关于到底有多糟糕的文章已经足够多了query_posts 是的,所以慢慢来,努力解决它们。

    任何页面上都没有替换主查询的有效理由。替换主查询将ALWAYS 导致一些问题,它可能不会立即可见,但您肯定会看到它的效果。

    你还必须记住,如果你追求的是搜索引擎优化和性能,那么真正的坏消息是你当时肯定做错了。主查询总是在任何页面加载时正常运行,即它将查询数据库并返回页面的相关帖子。只需删除循环即可NOT 停止主查询。如果将循环替换为自定义循环(如示例中所示),那么您将再次查询db,这意味着您将执行两倍的查询,并因此降低页面速度。这会对SEO产生负面影响,因此有一些事情需要您考虑。

    总之,如果您需要从主查询中获取其他内容,ALWAYS 使用pre_get_posts 改变它。这样,您就不会运行任何额外的查询或中断全局查询

    相关推荐

    将wp_Query替换为wp_User_Query

    我在插件中制作了一些订阅者档案和单曲(个人资料页),其中我还包括了一个“用户单曲”模板,通过template_include. 不过,我正在尝试从插件中删除一些模板,以使其使用主题模板。我用了locate_template( \'single.php\' ) 从活动主题中选择单个模板。我没有使用全局wp_query 在本例中显示我的内容,但页面显示了基于查询默认值的循环(十篇帖子)。我想知道的是,我是否可以完全放弃默认查询,用wp_user_query 我可以将查询到的用户ID输入其中。然后我想筛选the