WordPressにおいて、「同一子カテゴリ限定」で「前後の記事」を表示させる

何度かくじけそうになりました

今回の対応は、タイトルの通り、「同一の子カテゴリだけにしぼって、前後の記事を表示させる」というものです。

この課題、実はしばらく前から悩んでおり、先日どうにか自分自身の想定した通りに動作するようになったところです。諸所、荒も多いと思いますが、備忘録として残しておきたいと思います。

スポンサーリンク

どのような条件で作成を開始したか

想定されるカテゴリ構成(例)

 親カテゴリ1
  ― 子カテゴリA
  ― 子カテゴリB
  ― 子カテゴリC
 親カテゴリ2
  ― 子カテゴリD
 親カテゴリ3
  ― 子カテゴリE
  ― 子カテゴリF

条件1.「一つの親カテゴリに対し、一つ以上の子カテゴリが属する」
条件2.「一つの記事に対し、親・子カテゴリが一つずつ割り振られる」

このような構成において、「同一子カテゴリ限定で前後の記事へのリンクを貼る」という方式にしたいと考えました。「同一親カテゴリに属する」というルールであれば、「previous_post_link」「next_post_link」を使ってすぐに実装できると思いますが、このテンプレートは「IDを除外すること」はできても、「特定のIDを指定すること」はできないようです。

テンプレートタグ/previous post link - WordPress Codex 日本語

といったことから試行錯誤をし、下記のような回りくどい方法を考えました。

[ad_contents]

できたもの


	category_parent == 0){
			$pid = $cat->term_id;
			} else {
			$cid[] = $cat->term_id;
			}
		}
		$allchild = get_term_children( $pid, 'category');
		$excchild = array_diff($allchild, $cid);
		$excchild = implode(",", $excchild);

		$prev_post = get_previous_post( TRUE, $excchild, 'category' );
		$next_post = get_next_post( TRUE, $excchild, 'category' );

		if ( !empty($prev_post) or !empty($next_post) ) :
			echo '

同一シリーズの前後の記事

'; echo '
    '; if ( !empty($prev_post) ) : previous_post_link('
  • %link
  • ', ' %title', TRUE, $excchild, 'category' ); endif; if ( !empty($next_post) ) : next_post_link('
  • %link
  • ', ' %title', TRUE, $excchild, 'category' ); endif; echo '
'; endif; ?>

コードは以上です。以下に詳細を記します。

詳細1.記事を基準としたカテゴリIDの取得

		$category = get_the_category();
		foreach($category as $cat){
			if($cat->category_parent == 0){
			$pid = $cat->term_id;
			} else {
			$cid[] = $cat->term_id;
			}
		}

一行目、「get_the_category」で取得した「表示している記事に関わるカテゴリデータ」の中から、「その記事が属する親カテゴリID・子カテゴリID」を取得します。
そもそもここが、結構つまずいたポイントでした。最初は$category[0]などと指定して値を取得していましたが、どうやっても「parent」のIDが「0」になってしまって先に進まないのです。
下記を拝見すると「category_parentが0を返す時=親カテゴリが存在しない」となっています。しかし、そのような造りにしている覚えはなく、混乱は大きくなるばかりでした。
テンプレートタグ/get the category - WordPress Codex 日本語版

が、時間をかけてようやく、僕は自分の思い込みを疑うことになりました。これはつまり、「親カテゴリが存在しない=そのカテゴリそのものが親」とも言えるのではないか?結果、下記のようなサイトに行きつき、内容を修正することが叶いました。
[wordpress] get_the_categoryを使った親カテゴリと子カテゴリの判定

それを踏まえて、三行目以降で、「cat->category_parent」が「0」時は親カテゴリIDとしてterm_idを取得し、それ以外の場合は子カテゴリID用の配列にterm_idのデータを入れていくことにしました。

詳細2.親カテゴリに割り振られた子カテゴリIDを処理する


		$allchild = get_term_children( $pid, 'category');
		$excchild = array_diff($allchild, $cid);
		$excchild = implode(",", $excchild);

続きましてこちら。
一行目で、「前段で取得した、表示中の記事が属する親カテゴリID($pid)」から、「それ($pid)に属する子カテゴリID」を「$allchild」として取得します。
二行目で、「前段で取得した、表示中の記事が属する子カテゴリID($cid)」と「表示中の記事が属する親カテゴリIDに属する子カテゴリID($allchild)」を比較します。その結果、重複するものを排除して返します。この値が「$excchild」に入ります。

このような経緯を経て、$excchildには「表示している記事が属さないけれども、表示している記事が属する親カテゴリに属する子カテゴリID」が入るわけです(ややこしい)。

例えば、
 親カテゴリ1
  ― 子カテゴリA
  ― 子カテゴリB
  ― 子カテゴリC
という構成において、「親カテゴリ1」の「子カテゴリB」に属する「記事α」があるとします。目的としては、「子カテゴリB」の直近の記事にリンクが貼りたい。ただし「子カテゴリA」と「子カテゴリC」の記事は除外したい。

この時、

  • $pid = 記事αが属する親カテゴリID(つまり、親カテゴリ1のID)
  • $cid = 記事αが属する子カテゴリID(つまり、子カテゴリBのID)
  • $allchild = 記事αが属する親カテゴリに属する全ての子カテゴリID(つまり、親カテゴリ1に属する子カテゴリA・B・CのID)
  • $excchild = allchildからcidを除いた結果(つまり、子カテゴリA・CのID)

という具合で異なる値が入ります。必要なのは$excchildであり、これを得るためにあちらこちらふらふらしていたとも言えます。

三行目で「implode」していますが、これは取得したデータがを次に処理するために、「,」で区切った文字列として再読み込みをさせています。それまでは「 [0]=> int(14) [1]=> int(15)」といった具合のデータとなっており、implodeした後は「14,15」といったstring型に変わります(数字は一例です)。

詳細3.取得した情報を元に表示の準備


		$prev_post = get_previous_post( TRUE, $excchild, 'category' );
		$next_post = get_next_post( TRUE, $excchild, 'category' );

ここまでで必要なデータ取得が終わっているため、記事表示に移ります。
「get_previous_post」「get_next_post」のテンプレートを使った「前後の更新記事の有無確認」は、ごく一般的な手法だと思います。値がemptyになったら無し、empty以外なら記事ありとして、次のif分岐で用いることになります。

しかし、今回はここで「除外するID」を渡してやります。それが先で取得した「$excchild」の文字列です。
ここで渡す値には「文字列で値を渡す」ことと「複数の場合は「,」で区切る」という二つのルールが存在するため、先にimplodeして整形しています。
関数リファレンス/get previous post - WordPress Codex 日本語

詳細4.除外カテゴリIDを指定してリンクを表示させる


		if ( !empty($prev_post) or !empty($next_post) ) :
			echo '

同一シリーズの前後の記事

'; echo '
    '; if ( !empty($prev_post) ) : previous_post_link('
  • %link
  • ', ' %title', TRUE, $excchild, 'category' ); endif; if ( !empty($next_post) ) : next_post_link('
  • %link
  • ', ' %title', TRUE, $excchild, 'category' ); endif; echo '
'; endif;

更に広く使われているであろう、「previous_post_link」「next_post_link」を使った前後記事の表示方法で表示します。
テンプレートタグ/previous post link - WordPress Codex 日本語

一行目、先に取得した「$prev_post」もしくは「$next_post」が両方空でなければ、HTMLタグを表示させます。
以降、「previous_post_link」「next_post_link」の二つに必要な情報を渡します。ここでも「$excchild」を忘れないことで、「表示中の記事が属する子カテゴリ以外の子カテゴリを除外すること」ができます。

以上により、「記事が属する子カテゴリと同一子カテゴリに限定した、前後記事のリンク表示」ができました。
今のところ、目立った不具合などは起きていませんが、見落としなどがあったら申し訳ありません。ただ、この実装をするに当たり、この記事の情報へ辿り着くことができなかったので、どなたかの参考になるのであれば幸いです。