カスタム投稿タイプの記事のURLにカスタム分類のタームを含める

WordPress で若干複雑なパーマリンクに変えようとしたらとても大変だったのでその記録です。

ちなみにカスタム投稿タイプとカスタム分類の登録には「Custom Post Type UI」プラグインを使用しています。

 

概要

今回やりたかったのは次のようなURLの変更です。

カスタム投稿アーカイブ   -> /news/
カスタム分類アーカイブ   -> /news/カスタム分類ターム名/
カスタム投稿記事ページ   -> /news/カスタム分類ターム名/投稿名/

http://example.com/news/店舗a/リニューアルオープン!/ みたいなURLにしたいわけです。

 

とりあえず作成

とりあえずカスタム投稿タイプを「news」、カスタム分類を「newsstore」として作成するとデフォルトでは以下のようなURLになります。

カスタム投稿アーカイブ   -> /news/
カスタム分類アーカイブ   -> /newsstore/カスタム分類ターム名/
カスタム投稿記事ページ   -> /news/投稿名/

カスタム投稿のアーカイブページはこのままでよいので、カスタム分類のアーカイブページとカスタム投稿記事ページのURLを変更します。

 

まずはカスタム分類アーカイブページを変える

カスタム分類のアーカイブページのスラッグは rewrite_slug オプションで変更することができます。「Custom Post Type UI」プラグインでは以下の「カスタムリライトスラッグ」から変更が可能です。ここをまず「news」に変更しましょう。

ただこれだけだと投稿タイプ「news」の記事ページと被ってしまいうまく動作しないためリライトルールにルールを追加します。

add_action( 'init', function() {
	global $wp_rewrite;
	
	add_rewrite_rule( 'news/([^/]+)(/page/([0-9]+))?/?', 'index.php?newsstore=$matches[1]&paged=$matches[3]', 'top');
	$wp_rewrite->flush_rules( false );
} );

このルールによって正規表現 news/([^/]+)(/page/([0-9]+))?/? に該当するURLを index.php?newsstore=$matches[1]&paged=$matches[3] にリライトします。第3引数の 'top' で他のルールより先にチェックされるようになります。

これでカスタム分類のアーカイブページのURLが /news/カスタム分類名/ になりました。

 

カスタム投稿記事ページも変える

/news/カスタム分類名/ にはアクセスできるようになりましたがそれぞれの記事へのリンクを踏んでも404に飛ばされます。先ほど追加したnews/([^/]+)(/page/([0-9]+))?/? に該当してしまっているからですね。

記事ページ用のリライトルールを追加したいところですが、まずさきにリンクのURLを変更しましょう。post_type_link フィルタを使用することで記事ページへのリンクURLを変更できます。

add_filter( 'post_type_link', function( $permalink, $post, $leavename ) {
	if ( $post->post_type == 'news' ) {
		$term = wp_get_post_terms( $post->ID, 'newsstore' )[0]->slug;
		return "/news/" . $term . "/" . $post->post_name . "/";
	}
}, 10, 4 );

投稿タイプが news の時は /news/ターム名/投稿名/ となるようにしています。

リンクURLが変わったところでリライトルールを追加します。上で追加したリライトルールの上部に書き加えます。

add_action( 'init', function() {
	global $wp_rewrite;
	
	add_rewrite_rule( 'news/([^/]+)/([^/]+)/?$', 'index.php?news=$matches[2]', 'top' );
	add_rewrite_rule( 'news/([^/]+)(/page/([0-9]+))?/?', 'index.php?newsstore=$matches[1]&paged=$matches[3]', 'top');
	$wp_rewrite->flush_rules( false );
} );

news/([^/]+)/([^/]+)/?$ に一致するURLを index.php?news=$matches[2] にリライトします。

これで記事ページにもアクセスできるようになりました。

 

カスタム分類が違う場合はリダイレクト

ただ今のままでは1つ問題があります。

例えばカスタム分類のタームが「店舗a」で投稿名が「リニューアルオープン!」の場合、以下のURLでアクセスできますが、

http://example.com/news/店舗a/リニューアルオープン!/

以下のURLでもアクセスできてしまいます。

http://example.com/news/あああああああ/リニューアルオープン!/

これはリライトルールではカスタム分類の判別まで行えないためです。

これを防ぐためにリダイレクト処理を加えます。WordPressにはカノニカルURLという機能がありますが、それに似た機能を実装します。

add_action( 'template_redirect', function() {
	global $post;

	if ( ! is_singular( "news" ) ) return;

	$term = get_the_terms( $post->ID, "newsstore" )[0];

	$term_slug = $term->slug;
	$post_slug = $post->post_name;

	$request_url  = is_ssl() ? 'https://' : 'http://';
	$request_url .= $_SERVER['HTTP_HOST'];
	$request_url .= $_SERVER['REQUEST_URI'];

	$redirect_url  = is_ssl() ? 'https://' : 'http://';
	$redirect_url .= $_SERVER['HTTP_HOST'];
	$redirect_url .= "/news/{$term_slug}/{$post_slug}/";

	if ( strcasecmp( $request_url, $redirect_url )) {
		wp_redirect( $redirect_url, 301 );
		die();
	}
} );

カスタム分類が異なる場合は正しいタームに変更したURLにリダイレクトしています。

これで目的達成です。おめでとう!!

 

おわり

動作が確認できたら以下の行は削除しましょう。

$wp_rewrite->flush_rules( false );

一度でも実行されればデータベースにリライトルールが書き込まれるためこの行は必要なくなります。むしろ負荷が大きいため削除した方が良いでしょう。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です