问题描述

有一个 long-standing WordPress 核心错误 (#16373),其中如果自定义查询变量被注册并存在于查询字符串中,查询将不会设置 is_front_page()为真,即使'page' == get_option( 'show_on_front' )。完整的详细信息在 trac 门票中,但最终结果是首页返回无效页面而不是 get_option( 'page_on_front' )

我有一些用例,我需要将自定义查询变量传递到首页上的查询字符串 (使用表单传递注册的查询变量,这些变量反过来用于过滤主题选项 – front-end 主题选项演示) 。 above-mentioned trac 车票被关闭为无效,所以我需要一个 work-around 。

我可以轻松地强制前头模板,就像这样:

function themeslug_force_front_page_template( $template ) {
    if ( '' != get_query_var( 'foobar' ) ) { // Registered custom query var
        return get_front_page_template();
    }
    return $template;
}
add_filter( 'template_include', 'themeslug_force_front_page_template' );

但是,基础查询条件不受影响。因此,任何依赖于 is_front_page()的代码都是 true(例如,仅在站点首页上显示的滑块) 仍然无法正常呈现。

我试图在 pre_get_posts 修改 $query,但这似乎没有工作:

function themeslug_force_static_front_page( $query ) {
    if ( $query->is_main_query() ) {
        if ( 'page' == get_option( 'show_on_front' ) ) {
            if ( '' != get_query_var( 'foobar' ) ) { // Registered custom query var
                $query->set( 'page_id', get_option( 'page_on_front' ) );
                $query->set( 'is_home', false );
                $query->set( 'is_page', true );
                $query->set( 'is_front_page', true );
            }
        }
    }
}
add_action( 'pre_get_posts', 'themeslug_force_static_front_page' );

查询条件不被修改。我是否在我的回调中错误地尝试?是否有不同/更好的方式强制查询条件,特别是 is_front_page()

Edit

请注意,设置 page_id 可以在 pre_get_posts 回调中工作:

$query->set( 'page_id', get_option( 'page_on_front' ) );

如果省略 template_include 过滤器,则显示的页面确实是分配给首页的页面; 然而,使用 get_page_template(),而不是 get_front_page_template()。因此,在 pre_get_posts 中设置 page_id 确实会导致 is_page()为真。但是上述的 bug 阻止了 is_front_page()被设置为 true

编辑 2

Per @ toscho 的请求,这里有一些查询 var 代码,用于上下文。

注册查询变量:

/**
 * Add options-related query variables
 */
function themslug_add_theme_demo_query_vars( $qvars ) {
    $qvars[] = 'demo_foo';
    $qvars[] = 'demo_bar';
    $qvars[] = 'demo_baz';
    return $qvars;
}
add_filter( 'query_vars', 'themeslug_add_theme_demo_query_vars' );

(foobarbaz 是主题选项 – 背景,各种颜色等)

如何使用它们只是为主题选项添加一个过滤器 – 该部分超出了问题的范围,因此我将在此省略。

它们通过自定义 Widget 在前端输出。小工具输出只是一个表单。以下是一个表单域的示例:

<h3>Foo</h3>
<input name="demo_foo" id="demo_foo" class="pickcolor"  type="text" value="<?php echo $foo_setting; ?>" data-default-color="<?php echo $foo_setting; ?>" />

生成的查询字符串 on-submit:

www.example.com/?foo=some_value

最佳解决方案

你检查了 is_front_page()的哪一部分导致它返回 false?

我可以通过跟踪从 trac 机票的设置重现问题。在我的这个函数内,调用 is_page()返回 false

我猜这是因为 $wp_query->set()用于 page_id,而 is_page 只是导致 query_vars 被改变,你不会改变 「查询状态」 本身。

我可以通过将这两行添加到您的代码来创建一个解决方法:

$query->is_page = true;
$query->queried_object = get_post(get_option('page_on_front') );

在页面正确显示 (在我的设置中) 导致的页面 (尽管 URL 中存在 query_var),还有 is_front_page()返回 true

我希望这有助于你进一步。


如果您查看 WP_Query 类的定义,您将看到有一个变量 $query_vars 和一个变量 $is_page

您使用的 set 功能 ($query->set( 'is_page', true );) 仅设置 query var

function set($query_var, $value) {
    $this->query_vars[$query_var] = $value;
}

它不会更改保存在 $query->is_page = true 中的状态信息,而是设置查询 var $query->query_vars['is_page'] = true

如果您来自 OOP-approach 并将 WP_Query::set()理解为经典的 class-setter 功能,那么这是有点误导的 – 它不是。

次佳解决方案

我也在那张 Trac 票里回答,但是我认为这里的基本问题比这更简单。

你正在挂钩在 demo_foo,demo_bar,demo_baz 作为”Query variables”,但他们不是; 至少不是在意图上。

虽然 URL 后的 foo = bar 字符串通常称为查询字符串,但”query variables” 实际上只是影响主查询的变量。如果你不挂钩一个变量,它不会消失,它不会被主要的 WP_Query 对象读取。

在你的情况下,这似乎正是你想要的。您的变量是主题选项,它们不是主要查询 (它从数据库获取帖子) 的一部分。您没有使用它们来选择要显示的帖子,您正在使用它们来设置主题选项等。

如果你不把它们挂接成 query_vars,那么它们根本不会影响主 WP_Query 对象。您将不得不从主要的 $ _GET 超级全局获取它们,而不是使用 get_query_var,您将不得不对其进行适当的清理,以防止反射性 XSS 攻击 (使用 esc_attr 或任何适当的),但这应该通过消除它来解决您的主要问题该代码首先引起问题,而不是添加额外的解决方法。

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。