Open10

複数条件で絞り込まれたアーカイブページにおける、テンプレートファイルとクエリオブジェクトの優先度について調べてみた

了

アーカイブ系の絞り込み条件が複数ある時、どのテンプレート階層が優先されているか、また、get_queried_object()で取得できるオブジェクトの種類はどれになるのかについて調べてみる。

「アーカイブ系の絞り込み条件が複数ある時」って、どゆこと?

例えば以下のような、複数の条件が指定された場合に表示されるページのこと。

/?cat=1&author=1&year=2022

こういうページでは、is_category()is_author()is_date()も全部trueになる。

では、表示されるテンプレートファイルはcategory.phpauthor.phpdate.phpのどれ?
もしくはarchive.phpsearch.php...?

さらにget_queried_object()で取得できるオブジェクトの種類はWP_TermWP_User...?

みたいな話。

テンプレート階層について
了

search.phpについて

s=パラメータがある時にだけ使用される。(たぶん)
なので冒頭に示したような、キーワード指定のない複数条件での絞り込みには無関係。(たぶん)

/?cat=1&author=1&year=2022&s

みたいにsを付けると、archive.php系ではなくsearch.phpに飛んでくる。

ちなみに上記の条件の場合、クエリオブジェクトはWP_Termになった。

キーワード検索だけの時、クエリオブジェクトは基本的にnull だが、追加でターム系や投稿タイプ、著者の条件指定があれば それに合わせてオブジェクトが渡ってくる。

「キーワード検索&さらにその他の複数条件での絞り込み」時にどのオブジェクトが優先されるかは、ここから先で検証していく、キーワード検索を入れない状態での調査結果とおそらく同じ。

了

前提条件

パーマリンク

パーマリンク設定については、カスタム構造を指定。
{ドメイン}/%post_id%/

ここが変われば結果が変わるかもしれないのであとで余裕があれば別パターンでも調べてみたい。

作成したカスタム投稿タイプ・タクソノミー

投稿タイプ:test_post_type
タクソノミー:test_tax

用意したテンプレートファイル

  • archive.php
  • category.php
  • tag.php
  • taxonomy.php
  • author.php
  • archive-test_post_type.php
了

カテゴリー&著者&年の組み合わせをそれぞれ見てみる

1-A: カテゴリー&著者&年

冒頭で示したURLにアクセスしてみる。

/?cat=1&author=1&year=2022

すると、まずは以下のURLにリダイレクトされた。

/date/2022/?cat=1&author=1

結果

category.phpWP_Term

1-B: カテゴリー&著者

/?cat=1&author=1

→ /author/{ユーザー名}/?cat=1

結果

category.phpWP_Term

1-C: 著者&年

/?author=1&year=2022

→ /date/2022/?author=1

結果

author.phpWP_User

1-D: カテゴリー&年

/?cat=1&year=2022

→ /date/2022/?cat=1

結果

category.phpWP_Term

了

投稿タイプの条件を加えてみる

2-A: 投稿タイプ・ターム・日付・著者を指定

/?test_tax=test-term1&post_type=test_post_type&year=2022&author=1

や、ちょっと順番を変えただけの
/?post_type=test_post_type&year=2022&author=1&test_tax=test-term1

リダイレクト先 → /date/2022/?...その他の条件

結果

archive-test_post_type.phpWP_Term

2-B: ターム指定を外してみる

/?post_type=test_post_type&year=2022&author=1

リダイレクト先 → /date/2022/?...その他の条件

結果

archive-test_post_type.phpWP_Post_Type

2-C: 投稿タイプ&著者

/?post_type=test_post_type&author=1

→ /author/{ユーザー名}/?post_type=test_post_type

結果

archive-test_post_type.phpWP_Post_Type

2-D: ターム&投稿タイプ

/?test_tax=test-term1&post_type=test_post_type

→/test_tax/test-term1/?post_type=test_post_type

結果

archive-test_post_type.phpWP_Term


archive-test_post_type.phpがない時

いずれも、表示されるテンプレートはarchive.phpだった。

了

ターム同士のテンプレート優先度の調査

C-1: カテゴリー&タグ

?cat=1&tag=taga

結果

category.php

C-2: タクソノミー&カテゴリー

/?test_tax=test-term1&cat=1

結果

taxonomy.php

了

分かってきたこと

テンプレートファイル優先度

基本的に、複数の条件で絞り込まれている場合は、優先度順にテンプレートファイルを読み取ろうとしてくれるっぽい。

  • /?cat=1&author=1のページでは、category.phpが優先されるがそれがない場合、author.phpが表示された。
  • /?cat=1&year=2022のページでは、category.phpが優先されるがそれがない場合、date.phpが表示された。
  • /?author=1&year=2022のページでは、author.phpが優先されるがそれがない場合、date.phpが表示された。

ターム系のテンプレート優先度

オブジェクト優先度

URL的な優先度

/date/... > /author/... > /category/...


テンプレートファイルとクエリオブジェクトが合わないケース

2-Aや2-Dのような、投稿タイプとターム系の指定が同時に行われている場合。

これには注意が必要かもしれません。

了

どういう問題が起こり得るか

パンくずリストなんかを生成しようとした時など、それぞれのページ種別に合わせて処理を分岐させたい場合に、条件分岐を書く順序によってはエラーが起きたり正常に情報が取得できない。

例えば、is_author()の分岐を先にしてしまった場合。

$obj = get_queried_object();
if ( is_singel() ) {
    ...
} elseif ( is_author() ) {
    //  ユーザーの表示名を取得しようとする
    $user_name = get_user_meta( $obj->ID, 'nickname', true );

} elseif( is_category() ) {
    ...
} .... 

} elseif ( is_post_type_archive() ){
    ...
}

上記のようなコードがあったとして、$obj->IDみたいなコードを書くならもっと厳密なチェックをしないとエラーを履いてしまう。

/?cat=1&author=1のようなページでは、$objWP_Termオブジェクトなので$obj->IDはタームのidになり正常に動作しないし、/?post_type=hoge&author=1のようなページでは$objWP_Post_Typeなので$obj->IDは存在せず、致命的なエラーになる。

後者のエラーについては、横着せずにできるだけ get_queried_object_id() を使っていれば回避できるっちゃできるはずだが、各ページ種別に対して何か処理を加えたい時、個人的にはget_queried_object()を先に変数に代入してから扱う方がコードがスッキリ見えるのでそうしてしまいがち...。

なんにせよ、意図したidが取得できないケースが出てくるので条件分岐の順番などを考慮することが必要。

または、is_xxx()ではなく、オブジェクトタイプをget_class()などでチェックしておくなどの対策が必要。

了

問題回避方法

エラーを出さないようにするために必要なことと、全テンプレートファイルを考慮して表示の整合性を取るために必要なことは異なるが、正直後者は複雑すぎる上にそこまで対応する必要性もほぼない。

なので、最低限エラーを出さないようにするにはどうすればいいかを考えてみる。

前述した通り、$obj = get_queried_object()から情報を取得する時にエラーが起こり得るので、

  1. 基本的にisset()でしっかり情報が存在するかチェックするか、または現在のオブジェクトのタイプを事前にチェックして、予期せぬデータが渡ってきても問題ないようにすることを忘れないようにする。
  2. クエリオブジェクトから情報の取得をできるだけ行わない。
  3. クエリオブジェクトの取得優先度( WP_Term > WP_Post_Type > WP_User )に合わせて条件分岐する。

の3点くらいが挙げられる。

まず①はできるだけ心がけるようにしたい。

②についての例をあげておくと

  • ポストタイプの表示名を取得する際に、$obj->$label を使用するのではなくpost_type_archive_title()を使用する
  • それと同様に、ターム名の表示にsingle_term_title()を使用する

など。
ただしユーザー名の表示にはこれらのようなコア関数がないため、自分で$objから情報を取る必要があるので注意する。似たような関数を自作しておくといいかもしれない。

③は、コアの変更によって順序が変わる可能性もあるので、それだけで対応するには少し心もとない気がするが、できる範囲で意識しておくといいかもしれない。

※ ただし、コアの<title>生成関数の中身をみると、アーカイブ系の中ではis_post_type_archive()が一番上にきていた。

参考: https://developer.wordpress.org/reference/functions/wp_get_document_title/

了

投稿タイプアーカイブについて

投稿タイプの設定で「アーカイブを持つかどうか」が設定できるようになっているので、これのせいで少し挙動が特殊なのかなと思った。

アーカイブを持たない場合、

/?test_tax=test-term1&post_type=test_post_type

みたいなページでは
is_post_type_archive()はfalseになって、taxonomy.phpが表示される。