在通过挂接过滤器和记录变量值对重写进行了大量检查之后,我设法解决了这个问题!
查询和查询重写当查询发生时,WordPress将使用正确的模板提供内容,只要它有足够的信息来明确确定模板和帖子是什么。对于非定制的post类型,WordPress只需要知道post slug。对于自定义的post类型,它需要知道post slug和post类型;因此,对于播客,查询需要指定post_type=podcast
和例如。name=news-for-august
. 这是因为post段塞对于给定的post类型是唯一的,但不必跨post类型是唯一的,因此段塞本身不足以识别post。此外,必须知道帖子类型,才能选择正确的模板。因此/?post_type=podcast&name=news-for-august
可以解析并正确呈现帖子。
此外,注册post类型时,将注册一个重写标记和一个查询变量,以允许压缩此查询。例如,对于我的podcast
post类型,重写标记为%podcast%
(不是%postname%
与非自定义帖子一样),查询变量为podcast=
; 这是post_type
加name
. 例如,请求/?podcast=news-for-august
内部重写为/?podcast=news-for-august&post_type=podcast&name=news-for-august
, 从而导致该职位的任职。
这解释了以下问题:
奇怪的是,在播客permalinks的背景下%postname%
标签没有像普通博客帖子那样被填充。
此外,关于以下内容。。。
当CPT UI注册自定义帖子类型时podcast
, 它还添加了一个名为podcast
. 因为我的帖子的permastruct(在[设置>Permalinks>自定义结构]中设置)是/articles/%post_id%/%postname%
, the podcast
permastruct is /articles/podcast/%postname%
.
。。。默认的permalink结构实际上是/articles/podcast/%podcast%
.
在查询中指定帖子ID时(通过p=
), 它优先于任何post_type
和/或name
变量如果这些变量与指定的ID不一致,则会发生重定向。实际上,如果指定了ID,例如,如果播客帖子的IDNews for August
是50
, 然后/?p=50
也在内部重写为/?post_type=podcast&name=news-for-august
, 这将导致重定向到该帖子的永久链接。
我们可以利用这种行为来确保对要实现的其他URL格式重定向到永久链接。
调整permastruct和标记替换
我们将调整permastruct以使用
%podcast%
而不是
%postname%
:
function wpse373987_add_tag_and_permastruct() {
/** Define the tag */
add_rewrite_tag( \'%podcast_episode_number%\', \'([0-9]+)\' );
/** Override the default permastruct for the podcast post type */
add_permastruct(
\'podcast\',
\'podcasts/%podcast_episode_number%/%podcast%\', // This line changed
[ \'with_front\' => false ]
);
/** Define podcast shortlinks */
add_rewrite_rule( \'^([0-9]+)/?\', [ \'podcast_episode_number\' => \'$matches[1]\' ], \'top\' );
}
add_action( \'init\', \'wpse373987_add_tag_and_permastruct\' );
因为我们不再使用
%postname%
标签在我们的permastruct中,我们也不再需要替换
%postname%
对于slug;这可以通过
%podcast%
自动标记。正在筛选
post_link
也没有必要,因为
post_type_link
用于自定义帖子类型:
function wpse373987_handle_tag_substitution( $permalink, $post ) {
// Do nothing if the tag isn\'t present
if ( strpos( $permalink, \'%podcast_episode_number%\' ) === false ) {
return $permalink;
}
$fallback = \'_\';
$episode_number = \'\';
if ( function_exists( \'get_field\' ) && $post->post_type === \'podcast\' ) {
$episode_number = get_field( \'episode_number\', $post->ID, true );
}
if ( ! $episode_number ) {
$episode_number = $fallback;
}
$permalink = str_replace( \'%podcast_episode_number%\', $episode_number, $permalink );
// The following line is now not needed.
// $permalink = str_replace( \'%postname%\', $post->post_name, $permalink );
return $permalink;
}
add_filter( \'post_type_link\', \'wpse373987_handle_tag_substitution\', 100, 2 );
// The following line is not needed.
// add_filter( \'post_link\', \'wpse373987_handle_tag_substitution\', 100, 2 );
调整我们的查询重写
在进行了上述两项调整之后,播客的永久链接的形式如下
/podcasts/<episode_number>/<episode_title>
, 而且内容是从该URL正确提供的,因为它在内部解析为查询
/?post_type=podcast&name=<episode_title>&podcast_episode_number=<episode_number>
, 其中包含
post_type
和
name
确定服务哪个帖子和使用哪个模板所需的变量。
但是,对于其他URL格式,即:
/podcasts/<episode_number>/<incorrect_title>
;/podcasts/<episode_number>
; 和/<episode_number>
;
我们仍然需要确定如何解决
<episode_number>
我们通过连接到
request
滤器之前,我们正在将播客的所有查询重写到表单中
/?p=<podcast_post_id>
, <当我们访问permalink URL时,包括在内,这就是导致404错误的原因。这是因为,如果客户端访问永久链接URL,WordPress不会对该表单的查询发出重定向-相反,查询处理会继续,WordPress在意识到查询不包含
post_type
和
name
(因为我们的查询重写删除了这些内容),因此它无法确定要服务哪个帖子,也无法确定要使用哪个模板。
因此,我们应该只将查询重写为表单/?p=<podcast_post_id>
当我们当前访问的URL不是永久链接时。内容已在永久链接URL上正确提供;我们只想将其他URL重定向到permalink,我们可以像以前一样,通过重写查询来获得post ID,但不能在客户端访问permalink URL时这样做。
还有,而不是返回[ \'p\' => \'-1\' ]
要在需要时引发404响应,正确的方法是返回[ \'error\' => 404 ]
.
以下是修改后的过滤器:
function wpse373987_handle_query_var( $query_vars ) {
/** Ignore requests that don\'t concern us. */
if ( ! isset( $query_vars[\'podcast_episode_number\'] ) ) {
return $query_vars;
}
/** Validate the episode number; it must be an unsigned integer. */
if ( preg_match( \'/^[0-9]+$/\', $query_vars[\'podcast_episode_number\'] ) !== 1 ) {
/** The episode number is invalid; respond with a 404 Not Found. */
return [ \'error\' => 404 ];
}
/**
* Episode number, with any leading zeroes stripped;
* they must be stripped for the SQL query to work.
*/
$episode_number = (int)( $query_vars[\'podcast_episode_number\'] );
global $wpdb;
/** Array of IDs of posts that have the given episode number */
$post_ids = $wpdb->get_col(
$wpdb->prepare(
"SELECT post_id FROM {$wpdb->postmeta} WHERE
meta_key = \'episode_number\'
AND meta_value = %d
ORDER BY post_id ASC",
$episode_number
)
);
/** String representing `$post_ids` in SQL syntax */
$sql_post_ids = "(\'" . implode( "\',\'", $post_ids ) . "\')";
// The logic after this point has been adjusted.
/**
* Determine the ID and name of the published podcast with the given episode
* number (and lowest ID, if multiple such podcasts exist).
*/
$podcast = $wpdb->get_row(
"SELECT id, post_name AS name FROM {$wpdb->posts} WHERE
id IN {$sql_post_ids}
AND post_type = \'podcast\'
AND post_status = \'publish\'
ORDER BY id ASC"
);
/**
* If there are no published podcasts with the given episode number,
* respond with 404.
*/
if ( $podcast === null ) {
return [ \'error\' => 404 ];
}
/**
* If the podcast name specified in the query doesn\'t correspond to the
* episode number specified in the query, we need to redirect to the right
* page, based on the episode number (ignoring the specified name). We do
* this by issuing a query for the post ID; that query will then redirect
* to the podcast\'s permalink, where we won\'t take action.
*
* Else, the specified name matches the specified episode number,
* so we are already at the podcast\'s permalink, and thus do nothing.
*/
if ( ! isset( $query_vars[\'name\'] )
|| $query_vars[\'name\'] !== $podcast->name
) {
return [ \'p\' => $podcast->id ];
}
return $query_vars;
}
add_filter( \'request\', \'wpse373987_handle_query_var\', 100 );
结果
太好了,它工作了!
表单的URL/podcasts/<episode_number>
, 然后是错误的slug或无slug,将重定向到具有该集号的播客的永久链接。现在,我们在中添加的重写规则也可以正确处理短链接add_tag_and_permastruct()
; 它解析表单的URL/<episode_number>
到表单的查询/?podcast_episode_number=<episode_number>
. 我们的request
滤器handle_query_var()
, 将其改写为表单/?p=<post_id>
, WordPress然后重定向到相应的播客permalink。全部排序!