问题描述

想像一个 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 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。