問題描述

有一個 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。