【WordPress】関連記事をプラグインなしで実装する方法・アルゴリズム

アイキャッチ:植物園

ブログの末尾には、関連記事がよく表示されています。
カテゴリやタグなど、関連性のある記事を提供するものです(Fig1)。

新しいテーマを作るにあたり、このブログでも関連記事を実装しようとしていました。
WordPressはプラグインは充実していて、関連記事の実装も手軽に行えます。
ですが、プラグインを入れすぎると処理が負担になるため「プラグインを使わずに実装したい」のが本音です。

とは言うものの、「プラグインで済むもの」をわざわざ解説しているページも少ない。
あったとしても「コードのコピペ」で済まされていたりします。
今回は、関連記事の表示を実装するアルゴリズムとコードを解説していきます。

Fig1.解説する関連記事のイメージ
Fig1.解説する関連記事のイメージ

期待する動作・仕様

さっそく、実装にはどんな処理が必要か見ていきましょう(Fig2)。

  • 表示した記事と関連する記事を表示する
  • 表示した記事と同じカテゴリ、またはタグを持っている記事を抽出する
  • 投稿(post)の記事を抽出する(固定ページは除外する)
  • (ここでは)抽出する記事数は4件である
  • 抽出した記事は、ランダムで並べる
  • 表示した記事は、関連記事として扱わない

関連する記事を表示する部分は、HTMLとCSSがメインになります。
表示するデータを受け渡すには、PHPとWordPressの関数が必要です。

Fig2.人間が期待する動作をコンピュータが処理できるようにする
Fig2.人間が期待する動作をコンピュータが処理できるようにする

どうやって記事を抽出するか

WordPressでは、WP_Query関数を使うことで条件に合った記事を取り出すことが出来ます。
難しいことをしなければ、おおよその要件を満たしてくれる便利なもの。
今回条件として使うパラメータの一例を見てみましょう。

WordPress Codexには各パラメータが詳しく載っています。

  • post_type (文字列 / 配列) – 投稿を[[投稿タイプ]によって取得する。デフォルト値は ‘post’。
  • posts_per_page (整数) – 1ページに含める投稿数
  • post__not_in (配列) – 投稿の ID を使用する。指定された投稿は取得されない
  • category__in (配列) – カテゴリー ID を使用します。
  • tag__in (配列) – タグ ID を指定。
  • orderby (文字列 | 配列) – パラメータで指定した項目の値で投稿をソートする。

例えば、WordPressのダッシュボードの設定に「1ページあたりの記事の表示数」があります。
5件と設定すれば5件、10件と設定すれば10件表示できます。
これはまさに「posts_per_page」と同じ役割を果たしています。
(固定ページを省いた「投稿タイプ」の集まりである点も、「post_type」のイメージが理解しやすい。)

$wp_query オブジェクトに現在のリクエストを定義する情報を与えることで、$wp_query はどのタイプのクエリを扱っているのか (カテゴリーアーカイブ、年月別アーカイブ、フィード、検索など) を確定し、要求された投稿を取り出します

関数リファレンス_WP Query – WordPress Codex 日本語版

パラメータに渡す値を加工する

パラメータの多くは、用意された単語か、整数を与えれば完成します。
しかし、記事のカテゴリーのIDやスラッグのように、把握するのが難しく指定できない場合があります。
いっそのこと、「この記事のカテゴリ(タグ)と同じやつを持ってきて」と自動でやってほしいですよね。

PHPで記事の持っている情報を加工することで、抽象的な要求を実現することができます。
次の項でアルゴリズムを見ていきます

処理のフローチャート

関連記事を表示するコードをフローチャートに起こしてみました(Fig3)。

フローチャートと異なり、データの加工よりも先にWP_Query関数を解説しました。
これは、目的によってデータの加工は必ずしも行うとは限らないからです。

関連記事を実装するコードはカテゴリ、またはタグをベースに処理しますが、解説はカテゴリIDを使っていきます。

Fig3.関連記事を表示するアルゴリズム
Fig3.関連記事を表示するアルゴリズム

term_idはメンバー変数ではなく、連想配列のキーです。
画像を差し替えるのが面倒だったので注釈しておきます。

最初のif文は必須ではありません。
ただ、カテゴリやタグを持っていない場合に「関連記事」と見出しが出るのは不格好です。
いっそのこと、カテゴリやタグを持っていなければif文で処理をスキップしましょう。
(タグは「持っていない」場合がありますが、カテゴリはデフォルトで割り当てる設定がダッシュボードにあります)
(最後のendifを忘れずに。)
(サブループで「関連記事はありません」と表示するのもアリ)

カテゴリIDを取り出す

WP_Query関数にカテゴリID渡せるようにするには、オブジェクト(配列)の中から取り出す必要があります。
「加工」と言っているのはまさにココです。
get_the_category関数では、直にカテゴリIDを取得するのではなく、様々な情報がついてきます。

オブジェクトから特定のデータを取り出すためには、「foreach」のループ処理で連想配列の値を一つずつ取り出します。

get_the_category関数で取得したオブジェクト

get_the_category関数で取得したオブジェクトは、様々なデータを持っています。
このままではクエリのパラメータに指定することができないので、加工する必要があります。
ほしいカテゴリIDは、term_idの値です。

  • term_id
  • name
  • slug
  • term_group
  • term_taxonomy_id
  • taxonomy
  • descriptionparent
  • count
  • filter

タグを取り出す場合、関数名に注意
このサイトはタグで関連記事を表示しています。
タグを取り出したい場合、WordPress関数はget_the_tagsです。複数形です。

foreach文で配列を反復処理する

カテゴリやタグを持っていれば、その数のぶんだけ配列に要素を持っています。
foreach文でそれぞれを対象に、アロー演算子でterm_idを取り出します(Fig4)。

(「アロー演算子 PHP」でググると「クラスのメンバー変数にアクセスする」ような記述が多かったので、「クラスじゃねぇしな……」という理由で引用はしていません。)

「記事が持っているカテゴリID」は複数持っている可能性があるため、定義した配列に保存しています。

Fig4.オブジェクトの配列を視覚化したもの
Fig4.オブジェクトの配列を視覚化したもの

デバッグについて

実際に加工していく過程で、どのようなデータを持っているのか。デバッグすると確認できます(Fig5)。

Fig5.オブジェクト・配列をprint_r関数で表示したもの
Fig5.オブジェクト・配列をprint_r関数で表示したもの

初学者視点では、PHPではecho関数を使いたがるかもしれません。
ですが、echoでは配列を出力することは出来ません。実際やらかしました★
 配列を出力するには、print_r関数またはvar_dump関数を使いましょう。

preタグで囲んだ中でvar_dumpすれば、コードがそのまま表示されるので改行やインデントが行われた状態で表示できます。

phpでvar_dumpした時の表示を整える3つの方法 – Qiita

WP_Query関数のパラメータに値を渡す

foreach文とアロー演算子で取り出したIDの配列をクエリのパラメータに渡します。
他のパラメータは前述した通りです。

<?php $my_query = new WP_Query( $args ); ?>

クエリを作成したら、最後に忘れずにリセットしましょう。


<?php wp_reset_postdata(); ?>

サブループで該当する記事を出力する

いつもどおり、ifとwhileでループを回します。
WordPressのテーマを自作したことがある人なら、home.phpやindex.phpでの使い方と同じです。

<?php
if( $my_query -> have_posts() ):
    while( $my_query -> have_posts() ):
        $my_query -> the_post();
?>
articleタグなど
<?php endwhile; endif; ?>

まとめ

  • WP_Queryで条件に合う記事を取り出せる
  • 条件の指定にはパラメータと値を渡す
  • カテゴリIDやタグIDなど、PHPで加工する必要な場合がある
  • タグを取り出す関数はget_the_tags(複数形)である
  • foreach文を使うと配列を反復処理できる
  • アロー演算子を使うとオブジェクトの配列から特定のキーにアクセスできる

■タイムスタンプ
誤字脱字などの連絡にどうぞ。

付録:ソースコード

<?php
if( has_category() ):
  $cats_obj = get_the_category();
  $cat_IDs = array();
  foreach( $cats_obj as $cat_obj ){
    $cat_IDs[] = $cat_obj -> term_id;
  }
  //条件(クエリオブジェクト)
  $args = array(
    'tag__in'    =>  $cat_IDs,
    'post_type'       =>  'post',
    'posts_per_page'  =>  4,
    'post__not_in'    =>  array( $post -> ID ),
    'orderby'         =>  'rand',
  );
  $my_query = new WP_Query( $args );
?>

<h3>関連記事</h3>
<?php
  //サブループ
  if( $my_query -> have_posts() ):
    while( $my_query -> have_posts() ):
      $my_query -> the_post();
?>
記事のHTML
  <?php endwhile; endif; ?>
  <?php wp_reset_postdata(); ?>
<?php endif; ?>