WordPressで404を取り除く最後の手段

これはずーっと404とにらめっこしてた僕が発掘した「対404」最終手段です。

それは「リライトルールを書き換える」

この手段をとるにあたり思いつく限りの注意点をいくつか。

  • 通常のパーマリンク設定で何とかなるならその方が望ましいです。メンテが半端なくめんどくさくなりそうです。
  • 正規表現についてざっくりでいいから知っている方が望ましいです。
  • 一度書くと後から書き換えるのは大変なので出来るだけ最後の工程でやりましょう

そもそもなぜこんなことをしようかと思ったか。カスタム投稿でタクソノミーをカテゴリとして使おうとした際に

http://hoge.com/カスタム投稿名/ターム名/記事

って感じのURLを実現したかった訳なんですよ。それでカスタム投稿タイプ(スラッグ:cpt)、タクソノミー(スラッグ:cpt_cat)、ターム(スラッグcat1, cat2, …)を設定しました。

ここでプラグイン「Custom Post Type Permalinks」を使って

http://hoge.com/cpt(アーカイブページ)

http://hoge.com/cpt_cat/cat1(taxonomy.phpを使ったアーカイブページ)

http://hoge.com/cpt_cat/cat1/記事 (投稿ページ)

まで持っていくことができました。タクソノミースラッグであるcpt_catをcptにしたかったため、CPT UIの設定にあるカスタムタクソノミースラッグをカスタム投稿のスラッグと同じであるcptに変更すると、

http://hoge.com/cpt/cat1(taxonomy.phpを使ったアーカイブページ)

までは大丈夫なのだが投稿ページを開くと。。。

404 not found

ここで色々試しましたが駄目でした。(検索してるとパーマリンク設定を空更新したらいけるかもよ!って内容の記事が多すぎてブチ切れたのは僕だけじゃないはず)

ここでURLの「cpt_cat」を「cpt」に書き換えたらいいのではないかと閃いた。他にも同じような投稿タイプがあるためタクソノミーは「hogehoge_cpt」という形で統一します。

functions.php

function my_custom_post_type_permalinks_set( $termlink, $term, $taxonomy ) {
	return str_replace( '_cat', '', $termlink );
}
add_filter( 'term_link', 'my_custom_post_type_permalinks_set', 11, 3 );

これでタームのリンクが強制的に書き換えられる。しかしこれだけではURLが整うだけで肝心の中身にはたどり着けない。

ワードプレスでは様々な形のURLでも受け付けてくれるのはそれを正しい形に書き換える(rewrite)機能があるからだ。ここも変更します。

まず、どういう条件で目的の内容を参照しているのか調べます。

functions.php

if ( isset( $_GET[ '_rewrite' ] ) ) {
	/* Produces a dump on the state of WordPress when a not found error occurs */
	/* useful when debugging permalink issues, rewrite rule trouble, place inside functions.php */

	ini_set( 'error_reporting', -1 );
	ini_set( 'display_errors', 'On' );

	echo '<pre>';

	add_action( 'parse_request', 'debug_404_rewrite_dump' );

	function debug_404_rewrite_dump( & $wp ) {
		global $wp_rewrite;

		echo '<h2>rewrite rules</h2>';
		echo var_export( $wp_rewrite->wp_rewrite_rules(), true );

		echo '<h2>permalink structure</h2>';
		echo var_export( $wp_rewrite->permalink_structure, true );

		echo '<h2>page permastruct</h2>';
		echo var_export( $wp_rewrite->get_page_permastruct(), true );

		echo '<h2>matched rule and query</h2>';
		echo var_export( $wp->matched_rule, true );

		echo '<h2>matched query</h2>';
		echo var_export( $wp->matched_query, true );

		echo '<h2>request</h2>';
		echo var_export( $wp->request, true );

		global $wp_the_query;
		echo '<h2>the query</h2>';
		echo var_export( $wp_the_query, true );
	}
	add_action( 'template_redirect', 'debug_404_template_redirect', 99999 );

	function debug_404_template_redirect() {
		global $wp_filter;
		echo '<h2>template redirect filters</h2>';
		echo var_export( $wp_filter[ current_filter() ], true );
	}
	add_filter( 'template_include', 'debug_404_template_dump' );

	function debug_404_template_dump( $template ) {
		echo '<h2>template file selected</h2>';
		echo var_export( $template, true );

		echo '</pre>';
		exit();
	}
}

add_filter( 'rewrite_rules_array', 'wp_insertMyRewriteRules' );
add_filter( 'query_vars', 'wp_insertMyRewriteQueryVars' );
add_filter( 'init', 'flushRules' );

URLの末尾に?_rewriteを付けることでリライト情報が確認できます。作者さんありがとうございます…!(ここの解説に関してはお仕事で制作中のため公開できないのとめんどくさいので自分で見て確かめて頂ければと思います。)

ここで得た情報をもとにリライトを書き換えます。(参考元

functions.php

add_filter('rewrite_rules_array','wp_insertMyRewriteRules');
add_filter('query_vars','wp_insertMyRewriteQueryVars');
add_filter('init','flushRules');

// ルールを追加するときはflush_rules()を忘れないように
function flushRules(){
    global $wp_rewrite;
    $wp_rewrite->flush_rules();
}

// 新しいルールを追加
function wp_insertMyRewriteRules($rules)
{
    $newrules = array();
    $newrules['(project)/([^/]+)/?$'] = 'index.php?post_type=project&project_categoy=$matches[1]';
    return $newrules + $rules;
}

// 変数idを追加して、WordPressが認識できるようにする
function wp_insertMyRewriteQueryVars($vars)
{
    array_push($vars, 'id');
    return $vars;
}

ここで気を付けるのは

  1. 順序。リライトルールは上から当てはまったものが採用?されていくため、詳細なものほど上に書く
  2. このコードは「リライトルールの最上位に追加していく」形になるので1.の過程で上書きしてしまったものがあればこれも書く
  3. ページ送りがある場合、その辺も加味したルールを追加する必要がある。

これで完成です。はっきり言ってもうしたくねぇ。こんなにめんどくさいから最終手段なんです。

えらい長文になったなぁ。参考にしたサイトはたくさんあったのですがメモし忘れました…