問題描述

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