WordPressは内部でURLをパラメータに読み換えて、それにより該当するページを表示させています。その「URLをパラメータに読み換える規則」をリライトルールと呼び、WP内に管理しています。
例えば、下記のURLの場合
https://notes.sharesl.net/about/
WordPressはリライトルールに従い、下記のように読み換えて処理します。
https://notes.sharesl.net/index.php?pagename=about
パラメータのpagename
というのが固定ページのスラッグ名を意味しています。そのため、このURLからは固定ページのスラッグが「about」のページを表示させます。このように、WordPressはルートディレクトリに配置されたindex.phpに付いたパラメータ(?
以降の文字列)に基づいて、表示させるページを決定しています。
このindex.phpに付くパラメータは「パブリッククエリ変数」と呼ばれ、pagename
の他にも固定ページIDを意味するpage_id
や投稿タイプを意味するpost_type
など、WP内部で定義されています。
つまり、リライトルールは、URLを「パラメータ付きのindex.phpのURL」に変換して適切なページを表示するためのルールのことです。
リライトルールを独自で追加するWordPressの関数です。
主にfunctions.php
でinit
アクションフックを使って定義します。
function custom_rewrite_rule() {
add_rewrite_rule($regex, $redirect, $after);
}
add_action('init', 'custom_rewrite_rule');
引数は下記。
$regex
Rewrite API/add rewrite rule - WordPress Codex 日本語版
(文字列) (必須) リクエストされたURLにマッチする正規表現。オプションで1つ以上のグループを使用できる。
初期値: なし
$redirect
(文字列) (必須) $regexがマッチした場合に、実際にフェッチしたいURL。マッチしたキャプチャグループを挿入するには $matches[] を使用する。
初期値: なし
$after
(文字列) (オプション) 'top' または 'bottom'。'top' の場合、ルールは WordPressのすべての既存ルールに優先する。'bottom' の場合、ルールはすべての既存ルールがマッチしない場合に検査される。
初期値: "bottom"
この関数でどういうことができるか例を示します。
まずhttps://example.com/sample/
というURLにアクセスした時に、WordPressでは基本的には固定ページのスラッグ名「sample」のページを表示します。このルールを、新たなルールを追加することで変えることができます。
function custom_rewrite_rule() {
add_rewrite_rule('sample/?$', 'index.php?pagename=rewrite', 'top');
}
add_action('init', 'custom_rewrite_rule');
上記のコードを追加し、リライトルールを反映するためにWordPressの管理画面から、「設定」->「パーマリンク設定」を選択し、何も変更せずに「変更を保存」をクリックします。その後、https://example.com/sample/
にアクセスすると、固定ページのスラッグ名「rewrite」のページの情報が表示されるようになります。
ポイントは、コードを追記したあとの「WordPressの管理画面から、「設定」->「パーマリンク設定」を選択し、何も変更せずに「変更を保存」をクリック」の作業です。これをしないと設定が反映されません。
リライトルールを反映させる手順は、コード上でflush_rewrite_rules()
関数を書くことでも実現できますが、高負荷な処理のためfunctions.php
でinit
フックなどに書いて毎回実行されるようにするのはやめておきましょう。
今回はリライトルールを追加して問題解決に至った内容をいくつかメモします。
やりたいことがWordPressの仕様でできないかも・・・と思っていたら、リライトルールですんなり解決することがあります。そういう場面に遭遇した時にリライトルールをどのように追加したかを備忘録として残しておきます。
具体的には下記のような内容になります。
/amp/
にして表示させる上記のような不具合の解決やクライアントからの要望は、すべてリライトルールを追加することで解決します。リライトルールの概念や基本的な内容については他にまとめている方がいらっしゃいますのでこの記事ではこれ以上細かくは書きません。より具体的な、実際にリライトルールを追加した場面と内容について簡単にまとめてメモします。
カスタム投稿のアーカイブページを作らずに、固定ページにWP_Query
でサブクエリを使って一覧ページを作る場合に、ページ送りの2ページ目以降が404エラーになってしまう場合があります。
カスタム投稿は下記のような条件。
$args = [
'label' => 'News',
'has_archive' => false,
'public' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'show_ui' => true,
'query_var' => true,
'rewrite' => [
'slug' => 'news',
'with_front' => false
],
'hierarchical' => false,
'menu_position' => null,
'show_in_rest' => true,
'supports' => [
'title',
'slug',
'thumbnail',
'editor'
]
];
register_post_type('news', $args);
has_archive
を false
にしてアーカイブページを作らない。rewrite
設定をwith_front
をfalse
、slug
を任意の固定ページのスラッグ名に合わせる。ここではnews
のアーカイブページは作らず、固定ページnews
を作り、その下層ページにカスタム投稿を配置するという構造を例にします。アーカイブページに他の固定ページと同じくカスタムフィールドやエディターなどを使いたい時は、オプションページを作って別枠で用意しないといけないので、作る方も使う方も面倒です。初めてWPを触るようなクライアントに「固定ページはここ、一覧ページはこっちで設定してねー」とか説明してもなんで分けてるのかよくわからないので、使いづら!って思われるだけです。できるだけ固定ページをそのまま使った方が作る方も圧倒的に作りやすいし、使う方もまとまっている方がわかりやすいので筆者はよくこういった構造にします。
ただ、こういう時に表題のような「ページネーションの2ページ目以降が404になってしまう」といった予期しない不具合が起こる場合があります。
ページ送りは、下記のようなURLになります。
1ページ目 | https://example.com/news/ → 表示される |
2ページ目 | https://example.com/news/page/2/ → 404 |
3ページ目 | https://example.com/news/page/3/ → 404 |
アーカイブページから固定ページにしたことで、/page/
というディレクトリが固定ページの子ページのスラッグとして認識されてしまうためか、うまくテンプレートを表示できなくなってしまいます。
こういった場合は、下記のようなリライトルールを追加すれば解決します。
function custom_rewrite_rule() {
add_rewrite_rule('(news)/page/?([0-9]{1,})/?$', 'index.php?pagename=$matches[1]&paged=$matches[2]', 'top');
}
add_action('init', 'custom_rewrite_rule');
このリライトルールを追記すると、ページスラッグnews
の後ろに/page/ページ数/
が入るURLに一致する場合は、パブリッククエリ変数pagename
に一致したスラッグ名($matches[1]
)、paged
に一致したページ数($matches[2]
)のページが表示されます。
具体的には下記のようになります。
1ページ目 | https://example.com/news/ | https://example.com/index.php?pagename=news |
2ページ目 | https://example.com/news/page/2/ | https://example.com/index.php?pagename=news&paged=2 |
3ページ目 | https://example.com/news/page/3/ | https://example.com/index.php?pagename=news&paged=3 |
/page/ページ数/
が付くとpaged
がパラメータに追加されるようになります。
これでアーカイブページを固定ページにした場合でも期待通りに表示できるようになります。
これはけっこう特殊な内容にはなりますが、特殊な時ほどリライトルールの追加が必要になります。例えば、「一覧ページはいらないけど管理画面上では固定ページとは別にしておきたいページ」の場合などです。
例えば、「商品」のカスタム投稿タイプitem
を作ると、WordPressでは通常下記のようなURLになります。
アーカイブページ | https://example.com/item/ |
「商品」投稿ページ | https://example.com/item/{商品ページのスラッグ名やIDなど}/ |
このitem
という階層そのものは必要ないのでなくしたい。
アーカイブページ | なし |
投稿ページ | https://example.com/{商品ページのスラッグ名やIDなど}/ |
この場合、普通は固定ページに全部突っ込んでしまわないといけないのですが、「商品」などの場合は他の固定ページと違って商品画像を複数カスタムフィールドに登録したり、コンテンツも他の固定ページと全然違うものになったりするので、ページの編集画面が複雑になってしまいます。そうなると「商品」は固定ページの中に置くのではなく別の投稿として管理して編集画面も分けたい、といった要望が出てくる場合があります。
その際は下記のようなリライトルールを書いて対応します。
function custom_rewrite_rule() {
//リダイレクトフラグ
add_rewrite_tag('%redirect_from%', '([^&]+)');
//item一覧
$args = [
'post_type' => 'item',
'posts_per_page' => -1,
'post_status' => 'publish',
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false
];
$query = get_posts( $args );
if ( !empty($query) ) :
foreach ( $query as $q ) :
$slug = $q->post_name;
add_rewrite_rule($slug.'/?$', 'index.php?post_type=item&name='.$slug.'&redirect_from=item', 'top');
add_rewrite_rule($slug.'/([^/]+)/?', 'index.php?post_type=item&name=$matches[1]&redirect_from=item', 'top');
endforeach;
endif;
}
add_action('init', 'custom_rewrite_rule');
まずadd_rewrite_tag
関数を使って独自のパブリッククエリ変数を登録します。add_rewrite_tag
の使い方は下記。
add_rewrite_tag( string $tag, string $regex, string $query = '' )
Parameters
$tag
(string) (Required) Name of the new rewrite tag.
$regex
(string) (Required) Regular expression to substitute the tag for in rewrite rules.
$query
(string) (Optional) String to append to the rewritten query. Must end in '='.
Default value: ''
https://developer.wordpress.org/reference/functions/add_rewrite_tag/
↑英語でわかりにくかったので要約すると、
第一引数$tag
はリライトタグの名前。これがそのままパブリッククエリ変数の名前になります。
第二引数$regex
にリライトルールでリライトタグを置き換える正規表現。
第三引数$query
にリライトされたURLに追加するパラメータの文字列。最後は=
で終わる必要があります。指定がない場合は{リライトタグ}=
になります。
ということです。
これを使って
add_rewrite_tag('%redirect_from%', '([^&]+)');
redirect_from
というパブリッククエリ変数を登録します。
この変数は後ほど使います。
次にカスタム投稿item
の一覧を取得します。
//item一覧
$args = [
'post_type' => 'item',
'posts_per_page' => -1,
'post_status' => 'publish',
'no_found_rows' => true,
'update_post_meta_cache' => false,
'update_post_term_cache' => false
];
$query = get_posts( $args );
取得したitem
一覧をループで回して、各投稿ごとにリライトルールを追加します。
$query = get_posts( $args );
if ( !empty($query) ) :
foreach ( $query as $q ) :
//スラッグ名
$slug = $q->post_name;
//リライトルールを追加
add_rewrite_rule($slug.'/?$', 'index.php?post_type=item&name='.$slug.'&redirect_from=item', 'top');
add_rewrite_rule($slug.'/([^/]+)/?', 'index.php?post_type=item&name=$matches[1]&redirect_from=item', 'top');
endforeach;
endif;
具体的には下記のような内容になります。
商品カスタム投稿に「shoes
」「socks
」ページが公開されている場合
商品カスタム投稿「shoes」ページ | https://example.com/shoes/ | https://example.com/index.php?post_type=item&name=shoes&redirect_from=item |
商品カスタム投稿「socks」ページ | https://example.com/socks/ | https://example.com/index.php?post_type=item&name=socks&redirect_from=item |
固定ページ「contact」ページ | https://example.com/contact/ | https://example.com/index.php?pagename=contact |
わかりやすいように固定ページの場合もcontact
ページを書いておきました。
このようにカスタム投稿item
に投稿されたページは固定ページを上書きするような形にできます。
あとはもとから存在する方の投稿ページをリライトした方にリダイレクトしてあげればOKです。
まずはパーマリンクを変更します。
//パーマリンクの変更
function custom_permalink( $url, $post ) {
if('item' == get_post_type($post)) {
$url = str_replace('/item/', '/', $url);
return $url;
}
return $url;
}
add_filter('pre_post_link', 'custom_permalink', 10, 2);
add_filter('post_link', 'custom_permalink', 10, 2);
add_filter('post_type_link', 'custom_permalink', 10, 2);
これでitem
投稿のループでthe_permalink()
など投稿のURLを取得する関数を使った時に、リライトされた方のURL(/item/
が削除されたURL)が表示されるようになります。
次に元URLからリライトされたURLにリダイレクトします。
ここで先ほどadd_rewrite_tag
関数で追加したredirect_from
変数を使います。
function canonical_item_url(){
$redirect_from = get_query_var('redirect_from');
if(is_singular('item') && empty($redirect_from)){
global $post;
$url = get_the_permalink($post->ID);
wp_safe_redirect($url);
exit;
}
}
add_action('template_redirect', 'canonical_item_url');
パブリッククエリ変数はget_query_var('redirect_from')
のようにして取得できるので、redirect_from
がない場合はリライト前のページが表示されているので、リライト後のページへリダイレクトします。
これで固定ページとカスタム投稿をドメイン直下のディレクトリで表示できるようになりました。
注意点としては、固定ページとカスタム投稿で同じスラッグ名を使わないことです。
これは過去記事でやり方について書いていますのでそちらを参考に。
init
アクションフックを使います。
そして個人的にはこれ以外使わない方が無難だと思っています。
WordPressにはいくつかフックがあるので、ちょっと詳しい方はinit
なんか毎回必要ない時まで最初に読み込まれちゃって邪魔になるんじゃないか?と思うかもしれません。たしかに毎回読み込まれてしまいますし気になる方は気になるかと。管理画面側だけで実行されるような記述に変えた方が効率良いかも、と考えて下記のように書いたりしがちです。
//admin_initフックを使う
function custom_rewrite_rule() {
add_rewrite_rule($regex, $redirect, $after);
}
add_action('admin_init', 'custom_rewrite_rule');
//管理画面だけで読み込むようにする
function custom_rewrite_rule() {
if(is_admin()){
add_rewrite_rule($regex, $redirect, $after);
}
}
add_action('init', 'custom_rewrite_rule');
これらはどちらも動くのですが、確実に動作する保証がないです。admin_init
はどうやらフックの順番としては遅すぎるようで、その前のフックでflush_rewrite_rules()
が実行されてしまう場合があり、動作が不安定になります。
init
フック内でif文を使って管理画面だけ読み込ませるようにした場合についても、前述したようにflush_rewrite_rules()
という関数があるのが厄介なところで、もしも利用しているプラグインのフロント側にflush_rewrite_rules()
が書かれていた場合は追加したリライトルールは読み込まれません。
そのため多少は負荷になるかもしれませんが、リライトルールを独自で追加する場合はその負荷込みでinit
アクションフック内に書いた方が確実です。
もしその負荷がどうしても受け入れられないということであれば、使用しているプラグイン・テーマを全て調べて問題ないことを確認してから別のフックを使えば良いと思います。
一応、他に使えるフックとしてroot_rewrite_rules
フィルターフックがあります。
function custom_rewrite_rule($rules) {
add_rewrite_rule($regex, $redirect, $after);
return $rules;
}
add_filter( 'root_rewrite_rules', 'custom_rewrite_rule', 9999);
「ブログのルート用に生成されたリライトルールをフィルターするフック」なのでアクションフックと違って毎回動作するものではないので負荷は少ないと思います。ただしあまり使っているのを見たことがなく情報が少ないのでどうなのかなーって感じです。やはり確実にいくならinit
アクションフックです。
root_rewrite_rules
の参考記事は下記。
WordPressのrewrite ruleいじるときのhook
https://qiita.com/zuya/items/e4b9afcec84befe98ae2
WordPressのデフォルトのリンク構造でキレイに作れるのが1番良いと思いますが、独自でテーマを作成する場合はそうならないことがよくあります。リライトルールの追加方法を知っていれば、WordPressの構造に捉われずにより自由にサイト構造をカスタマイズできるようになり、仕様でできないと思っていたことも「WordPressの仕様でして・・・」とか言い訳しなくて済むようになります。リライトルールを追加する手順やそういった場面はWordPress特有のものになるので、ニッチすぎて少しとっつきにくい内容ですが、WordPressでサイトを作成することが多いなら覚えておいて損はないです。