問題描述

想像一個 CPT,讓我們稱之為”News” 。本新聞郵件型別主要用於在首頁上顯示短訊息內容。但其中有些新聞將會更長,要求僅在首頁上顯示摘錄,並具有單一檢視。這個規則可能是我把所有的文字都放在首頁的時候,而只是有時候輸入內容。

到目前為止我所想到的

如果沒有需要單獨的頁面,我將停用 publicly_queryable

只要留下一個檢視的一切,做一個重定向回到首頁,如果內容是空的,聽起來像一個髒的駭客。

只是過濾那些沒有內容可能不足夠。重寫規則怎麼辦?外掛生成站點 Map?搜尋?

還有什麼我錯過的嗎?我主要是為了實現這個方向,而不是一般的實現方向。

最佳解決方案

我的想法很簡單:使用'publicly_queryable' => TRUE 註冊 CPT,然後在沒有查詢到沒有內容的單個訊息時,有條件地使釋出型別不可公開查詢。

這意味著在註冊了帖子型別後,我們必須更改'publicly_queryable'引數。一些簡單的事情:所有的 post 型別的物件都儲存在全域性變數 $wp_post_types 中,所以假設 CPT slug 是’news’,只需使用

$GLOBALS['wp_post_types']['news']->publicly_queryable = FALSE;

我們將能夠停用新聞 CPT 的查詢。

第二個問題是有條件地停用。

我們知道所有帖子都有一個 url,即使是 non-queryable,但是當訪問 non-queryable CPT 的單個帖子的 URL 時,WordPress 傳送 404 響應。

這發生在 WP::parse_request()方法內,所以執行我們的條件邏輯的最佳位置就在請求解析發生之前,所以最好的選擇是過濾器鉤子'do_parse_request'(在 WP::parse_request()的第一行觸發) 。

所以我們的工作流應該是:

  1. 'do_parse_request'內部檢查請求是否為單個訊息

  2. 如果#1 為是,請檢查所請求的新聞是否沒有內容

  3. 如果#2 為是,則將訊息 CPT 的 publicly_queryable 引數設定為 FALSE

  4. 主查詢發生後,將 publicly_queryable 引數重置為 TRUE

最困難的部分是#1,因為一旦 WordPress 尚未解析請求,我們就不能使用任何 conditional tags,也就是說 is_singular( 'news' )太早了。

只有可以看看 url,幸運的是 url_to_postid()的功能將幫助我們完成這項任務。

那就是說我們可以寫一個簡單的類來實現我們的工作流程:

class SingleCptEnabler {

  private $id = -1;

  private $cpt_slug;

  function __construct( $cpt_slug ) {
    $this->cpt_slug = $cpt_slug;
  }

  /**
   * Run on 'do_parse_request' filter, and enable publicly_queryable
   * when a single news having content is required
   */
  function setup() {
    if (
      current_filter() === 'do_parse_request'
      && $this->isSingle()
      && ! $this->hasContent()
    ) {
        // when 'wp' hook is fired main query already happen
        add_action( 'wp', array( $this, 'enable' ) );
        $this->disable();
    }
  }

  /**
   * Query DB to get post content of the current queried news
   */
  function hasContent() {
    if ( (int) $this->id <= 0 ) {
      return;
    }
    $post = get_post( $this->id );
    $content = ! empty( $post ) && $post->post_type === $this->cpt_slug
      ? apply_filters( 'the_content', $post->post_content )
      : FALSE;
    return ! empty( $content );
  }

  /**
   * Enable publicly_queryable argument for news CPT
   */
  function enable() {
    $GLOBALS['wp_post_types'][$this->cpt_slug]->publicly_queryable = TRUE;
  }

  /**
   * Disable publicly_queryable argument and reset id
   */
  function disable() {
    $GLOBALS['wp_post_types'][$this->cpt_slug]->publicly_queryable = FALSE;
    $this->id = -1;
  }

  /**
   * Check if the current url is for a singular news
   */
  function isSingle() {
    $this->id = -1;
    if ( ! is_admin() ) {
      $this->id = (int) url_to_postid( add_query_arg( array() ) );
    }
    return (int) $this->id > 0;
  }

}

在這個類在一個活動的外掛或主題 functions.php(或更好的檔案需要從那裡),我們只需要呼叫'do_parse_request'過濾器鉤子上的 SingleCptEnabler::setup(),傳遞給類建構函式 CPT slug:

add_filter( 'do_parse_request', function( $do ) {
  $news_enabler = new SingleCptEnabler( 'news' );
  $news_enabler->setup();
  return $do; // we don't want to affect the filter results
} );

類是可重複使用的,它也可以用於多個 CPT,例如如果我們想要相同的行為’news’ 和’commentary’ CPT 我們可以做:

add_filter( 'do_parse_request', function( $do ) {
  $news_enabler = new SingleCptEnabler( 'news' );
  $commentary_enabler = new SingleCptEnabler( 'commentary' );
  $news_enabler->setup();
  $commentary_enabler->setup();
  return $do; // we don't want to affect the filter results
} );

現在,當你想要一些訊息有一個完整的內容,只需填寫釋出內容 (編輯器),否則只是填寫摘錄。

唯一的缺點是,開啟的單數新聞頁面將會減慢,因為需要額外的工作。

參考文獻

注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。