【WordPress】URLとメインクエリ、リライトルールや仕組みなど

WordPress

大仰なタイトルがついてますが、よくわからないのでなんとなくのメモでしかありません。

WordPressでオリジナルテーマを作るときに、投稿ページの投稿タイプを複数作るときなどに、URLをわかりやすいようにつけたいと思い、いろいろ実験していたときの記録です。難しいですが、なんとなくの方向性はわかったような…。

wpの仕組み

簡単に言うとwordpressはURLのパスを見て、どのテンプレートで、どのページを表示すべきか決めています。

例)ドメイン/category/感想 → archive.phpで、感想カテゴリーのアーカイブページを表示

例)ドメイン/page/about(固定ページスラッグ) → page-about.phpでaboutページを表示

URLさえWPが理解できるようになっているものなら、きちんと表示され、メインクエリも適切に設定され、ページネーションもちゃんと機能します。

じゃあなぜきちんと表示されない、機能しないときがあるのか?

自分が思うに、
1.パーマリンク設定がカスタム投稿タイプのURLに影響を与えている
2.カスタム投稿タイプだとwp関数で出るリンクが崩れる
3.カスタム投稿タイプを登録するとき、リライトで変なプレフィックスをいれている

からだと思っています。これらが原因でURLが崩れます。

リライトルールとは

そこで、WPはリライトルールというものを作成します。パーマリンクを設定したとき、それでも正しい設定が読み込めるようWPはリライトルールというURLの読み方ルールのようなものを作成し、多少変形したURLでもどのページを表示すべきか理解するよう努めています。

例)/%postname%/ とパーマリンク設定すると、投稿ページのスラッグがpostnameになり、WordPressは投稿名(スラッグ)に基づいて、その投稿を表示します。
例)/%post_id%/とするとURLには記事のIDが表示され、それを元にWPは表示ページを決定します。

このようなリライトルールがあるからWPはきちんと表示するページを判別することができるわけです。

他にも、リライトルールは自分で設定できたり、関数として作ることもできます。(パーマリンク設定以外でリライトルールをいじる場合、URLも書き換えることになるので、表示が崩れることがあります。理解できた上で、最後の手段として変更した方がいいです。)

  //カスタム投稿タイプ
  function register_custom_post_type(){
    $labels = [
        'name' => 'doodle',
        'all_items' => 'doodle一覧',
        'add_new' => '新しいdoodleを追加',
    ];
    $args = [
        "label" => "doodle",
        "public" => true,
        "has_archive" => true,
        "delete_with_user" => false,
        "rewrite" => ["slug" => "doodle", "with_front" => false],
    ];
    register_post_type("doodle", $args);
}
add_action("init", "register_custom_post_type");

例えば、以上のように

“rewrite” => [“slug” => “doodle”, “with_front” => false],

カスタム投稿タイプを設定するときにURLを書き換える、リライトルールの項目があります。ここでいう”slug”はカスタム投稿タイプのベースURLを決めるもので、一般的なスラッグではないため”slug”という項目名は混乱を招きます。また、”with_front”はパーマリンク設定などでプレフィックスをつけた場合、それをつけるかどうかという設定なので、パーマリンク設定で/*post_id*/だけの場合などはtrueでもfalseでも同じです。

カスタム投稿タイプdoodleのリライト設定例

例)パーマリンク/*post_id*/のとき
“rewrite” => [“slug” => “doodle”, “with_front” => false],だと個別ページは
ドメイン/doodle/{post-slug} になります。

例)パーマリンク/blog/*post_id*/のとき
“rewrite” => [“slug” => “”, “with_front” => true],だと個別ページは
ドメイン/blog/{post-slug} になります。
ドメイン/doodle/{post-slug}になります。(空にするとカスタム投稿タイプ名が自動で入ります、プレフィックスはアーカイブページにつきますが個別ページにつきません)

例)パーマリンク/blog/*post_id*/のとき
“rewrite” => [“slug” => “dd”, “with_front” => true],だと個別ページは
ドメイン/dd/{post-slug} になります。(ここでカスタム投稿タイプのプレフィックスを決めることができるような感じです)

基本的にリライトルールの方が優先度が高いので、パーマリンク設定よりも優先されます。
他にも、add_rewrite_rule()関数などでURLを書き換えることが可能になります。

このようにいろいろ設定していくうちにURLがWPの理解できないものになり、表示が崩れていく一因にもなります。

そもそもどのようなURLならWPがきちんとページを表示してくれるのか

※パーマリンク設定によって変動します
パーマリンク設定 /%post_id%/ の場合

1. デフォルトの投稿ページ(個別ページ)

  • URL形式: ドメイン/投稿ID/
    • : https://example.com/65/
      (この場合、65 は投稿IDです)

2. デフォルトの投稿ページ(アーカイブページ)

年別アーカイブページ

  • URL形式: ドメイン/年/
    • : https://example.com/2024/

カテゴリー別アーカイブページ

  • URL形式: ドメイン/category/カテゴリースラッグ/
    • : https://example.com/category/news/

タグ別アーカイブページ

  • URL形式: ドメイン/tag/タグスラッグ/
    • : https://example.com/tag/technology/

3. カスタム投稿タイプの個別ページ

  • URL形式: ドメイン/カスタム投稿タイプのスラッグ/投稿スラッグ/
    • : https://example.com/doodle/sample-doodle/ページ

4. カスタム投稿タイプのアーカイブページ

  • URL形式: ドメイン/カスタム投稿タイプのスラッグ/
    • : https://example.com/doodle/
  • URL形式: ドメイン/カスタムタクソノミーのスラッグ/(カスタムタクソノミー別アーカイブ)
    • : https://example.com/doodle-category/sketches/

5. 固定ページ

  • URL形式: ドメイン/固定ページのスラッグ/
    • : https://example.com/about/

6. アーカイブページのページネーション

カテゴリーアーカイブページ

  • URL形式: ドメイン/taxonomy/term/page/{page-number}
    • : https://example.com/category/repair/page/2/

リライトルールによって変わったりもしますが、このような一般的なURLならばきちんとページが表示されるはずです。ページがうまく表示されない、ページネーションがきちんと機能しないときは、URLを見て、何かいらないプレフィックスやパスが入ってないかチェックすることが大事だと思います

そして、どうしてもうまくURLが整理できないとき、add_rewrite_rule()関数でリライトルールを自作し、URLを書き換えるべきです。

パーマリンク設定はどうすればいいのか

パーマリンク設定でよく使われるのはカスタム構造で/%postname%/や/%post_id%/や/%category%/などを追加していく方法だと思いますが、これが影響するのはほぼ投稿タイプのページのみです。

パーマリンク設定をしても、固定ページやフロントページなどには影響がなく、そもそも投稿タイプのリンクをどうするかの設定でしかありません。そして、パーマリンク設定をユーザーがいろいろ変更しても、デフォルト投稿タイプならwordpressはうまくリライトルールを作ってちゃんとページを表示してくれます。メインクエリやページネーションもちゃんと機能させてくれます。

だから、デフォルト投稿タイプのみしか使わない場合は、何も気にすることなく自由にパーマリンク設定を変更すればいいだけになります(URLが崩れるので日本語のタイトルが出るものはやめた方がいいと思いますが)

問題はカスタム投稿タイプです。カスタム投稿タイプの場合、このパーマリンク設定が影響を与え、WPが判断できないURLを生成することがあります。

①パーマリンクが
/%post_id%/の場合、カスタム投稿タイプはそもそもpost_idをURLに出せないので、こう設定していても、投稿タイトルが出力されます。(日本語だと長ったらしいし読みづらいURLが出てよくありません。)

// カスタム投稿タイプを保存するときに、デフォルトURLは投稿タイトルが
// 出てしまうのでそれをIDに変えます
add_action('save_post', 'custom_price_slug_to_post_id');
function custom_price_slug_to_post_id($post_id) {
    $post = get_post($post_id);

    if ($post->post_type === 'price') {
        // すでに post_name が ID なら更新しない(無限ループ防止)
        if ($post->post_name != $post_id) {
            remove_action('save_post', 'custom_price_slug_to_post_id'); // 一時的に外す

            wp_update_post(array(
                'ID' => $post_id,
                'post_name' => $post_id
            ));

            add_action('save_post', 'custom_price_slug_to_post_id'); // 再登録
        }
    }
}

②パーマリンクが
/%post_id%/だけの場合、ワードプレスはそれをデフォルトの投稿タイプだと判断しますが、カスタム投稿タイプだと判断できないので404がでます。リライトルールで/custompost/123のように投稿タイプのslugをURLにつけないといけません。

"rewrite" => ["slug" => "price", "with_front" => false],

例)投稿タイプもURLにいれたいと思い、
カスタム構造 /blog/%year%/%category%/%post_id%/ とした場合
〇デフォルト投稿タイプ
アーカイブページでは
domain/blog 
個別ページでは
domain/blog/2024/aboutpet(term)/63(post_id)/
となって、詳細でわかりやすいURLが生成できます。
〇カスタム投稿タイプ(worksとします)
アーカイブページでは
domain/blog/works/65
となって違う投稿タイプのURLプレフィックスがついてしまい、うまくページが表示されません
個別ページも同じです。

この、パーマリンク設定で設定した/%year%/%category%/%post_id%/は、ただのそのページの情報ですのでカスタム投稿タイプでもあまり影響はないのですが、/blog というふうに決まったプレフィックスをつけてしまうと、それがカスタム投稿タイプに関するページにも表示されてしまい、wpが混乱します。

ではこのようなプレフィックスを入れつつカスタム投稿タイプに影響させないようにするにはどうしたらいいでしょうか。それは上段でも説明しましたように、カスタム投稿タイプを登録するときの”rewrite”で“with_front”をfalseにすれば良いだけです

この設定をきちんとしていれば、パーマリンクでどう設定していようが、カスタム投稿タイプに影響はなく、正しいURLが出てくるのでwpがきちんとページを表示し、メインクエリを設定してくれます。

メインクエリを使うべき

カスタム投稿タイプのアーカイブページでもメインクエリを使うべきです。URLがきちんとできていれば、wpはカスタム投稿タイプのメインクエリもちゃんとループさせてくれます。

サブクエリで件数や条件を変えることができても、functions.phpでメインクエリの設定を変える関数を作ることができるのでそれを使うべきです。pre_get_postsというフックできちんと機能します

//doodleのアーカイブ一覧ページでメインクエリの設定を変更
function customize_doodle_archive_query($query) {
  if ($query->is_main_query() && !is_admin() && is_post_type_archive('doodle')) {
      $query->set('posts_per_page', 5);
      $query->set('post_status', 'publish');
  }
}
add_action('pre_get_posts', 'customize_doodle_archive_query');

サブクエリを使うのは、同一ページ内で違うクエリのループを使いたい場合や、条件がかなり複雑な場合にしましょう。

つまづきやすいポイント

wp_get_archives()やwp_list_categories()は、カスタム投稿タイプで使おうとしてpost_typeにカスタム投稿タイプスラッグを設定すると、URLの最後尾にpost_type=slugのように余計なものがついて、ページがうまく表示できなかったりページネーションがうまく機能しなくなります。

具体的に言うと、年別アーカイブへのリンクを作ろうとして、以下のようにリンクを出すよう書いたとします。

<?php echo esc_url(home_url('/doodle/' . $year . '/')); ?>

ですが、post_typeを設定したためか、urlに?post_type=doodleのようないらないクエリ部分がついてしまいます。

するとURLが
ドメイン/doodle/2024/?post_type=doodle
になってしまい、これだとwpが読み取れなくなり、ページが表示されません。

これを、ドメイン/doodle/2024/ という、wpが判別できるURLに変えるためにリライトルールを設定する関数を作成します。

function custom_doodle_rewrite_rules() {
  // 年別アーカイブに対応(ページ番号なし)
  add_rewrite_rule(
      '^doodle/([0-9]{4})/?$', 
      'index.php?post_type=doodle&year=$matches[1]',
      'top'
  );
}
add_action('init', 'custom_doodle_rewrite_rules');

ここで、マッチする正規表現部分にはクエリ部分は含みません
だから ドメイン/doodle/2024/ にマッチする正規表現を書き、
‘index.php?post_type=doodle&year=$matches[1]’,
このような形に新しくURLを書き換えると命令します。

このように書くと、ドメイン/doodle/2024/ というURLになり、年別アーカイブページがきちんと表示されます。

※ちなみに、/%post_id%/のパーマリンク設定のときは、2024のような年月表現の数字がURLとかぶって、年月アーカイブだと認識されないため、wpが/page/というプレフィックスを勝手につける場合があります。

まとめ

1.パーマリンクは(日本語を含まないなら)好きなようにしてもわりと大丈夫
 (投稿タイプのベースURLを設定したいときはカスタム構造で/blogなどのようにプレフィックスをつける)

2.カスタム投稿タイプを登録するときは”with_front”=>falseにする
 (投稿タイプのベースURLを設定したいときはrewiteのslugで設定する)

3.URLがwpの判断できるような形式かどうか気を付ける

4.なるべくメインクエリを使い、条件を変えたいときはfunctions.phpで変える

5.URLがどうしても直らないときはリライトルールを作る

これらを気を付ければ大体ちゃんとページは表示できるはずだと思います。