こんにちわ、PHPエンジニアのエンジニア婦人(@naho_osada)です。
PHPエンジニアとして9年~の経験があります。
この記事では2020年11月に改修、実装した、本サイトのWordPressを用いた「関連記事」の作り方の考え方について掲載します。
改修前の関連記事の仕様(本サイトの場合)
本サイトでは各記事はすべてカテゴリーを振って公開する運用をしています。
よって、表示中の記事に対して関連があるものはすべて同カテゴリーのもの、となります。
例えばWordPressの記事の場合は「WordPress」のカテゴリの記事を投稿日順に上位3件を取得します。
複数のカテゴリを含む記事の場合はその複数のカテゴリを条件に、記事をOR条件で検索し、投稿日順に上位3件を取得します。
改修前の問題点
この条件のままでも各カテゴリに記事数がある程度あれば特別気になるほどの偏りは出ませんが、特に記事数が2件以下のカテゴリを含む記事がある場合、一見無関係ではないかと思われる記事が上位に来ることがありました。
例えばSQLの記事は2件(※2020年11月時点)ですが、片方のSQLの記事を表示中の関連記事はこの通りのルールだともう一つのSQLの記事がリストに出てきません。
これは両方の記事で複数のカテゴリを含んでいることと、双方の記事に公開日の開きがあることが原因です。1つは公開日が2020年2月27日、もう1つは2020年6月25日となっています。
この場合は「複数カテゴリと投稿日降順で検索した結果、他のカテゴリの記事があるのでそちらが上位に並ぶ」ことになるので、2つ目の記事が表示順位が4件以降になって結果にあがってこない、ということになります。
本サイトの場合、各記事は何かしらのカテゴリを持っていますが、例えば「言語(PHP他)」の場合はその下に「PHP」「javascript/jQuery」「HTML/CSS」「nodejs」「SQL」を持っています。
子カテゴリの記事は必ず親カテゴリの記事を持つ運用にしているので、例えばPHPの記事なら必ず「言語(PHP他)」の親カテゴリを持つことになります。そうすると、表示中記事のカテゴリで記事を検索すると「言語(PHP他)」を持った結果が日付降順で返ってくることになります。
その結果、子カテゴリはPHP以外のものが上位に挙がってくることがありました。
改修後の関連記事の仕様(本サイトの場合)
少ないカテゴリを持つ記事の場合、どうしたら同じカテゴリの記事を関連記事にあげる方法を考えます。
関連記事が3件以上返ってきた場合の処理
表示中記事のカテゴリで記事を検索し、その結果3件以上返ってきた場合はデータを精査します。
- 表示中記事のカテゴリで、親カテゴリがどれかを判別しておく
- 表示中記事の同カテゴリを持つ記事を投稿日降順で検索する
- 同カテゴリを持つ記事が3件以上(データの並びは日付降順)でループへ
- 記事のカテゴリ配列から親カテゴリを除外する
- 関連記事のカテゴリが表示中の記事と一致しない場合は除外、全て一致した場合は取得
- 3件以下だった場合は3件表示になるように関連記事を追加する
ソースで書くと以下のようになります。
// 表示中記事のカテゴリを取得
$category = get_the_category($id);
if($category) {
$catAry = [];
$parentCat = [];
// カテゴリIDを取得
foreach($category as $cat) {
$catAry[] = $cat->term_id;
if($cat->parent == 0) $parentCat[] = $cat->term_id;
}
$articles = getPostDatas('-1', $post->post_type, 1, implode(',', $catAry), $post->ID);
// 3件以上の記事があった場合、親カテゴリを除いて3件にする
if(count($articles) > 3) {
$cnt = 0;
$datas = [];
// 記事のカテゴリ配列から親カテゴリを除外する
$catAry = array_diff($catAry, $parentCat);
foreach($articles as $key=>$data) {
// 親カテゴリの物だったら除外
$pFlg = false;
$checkCats = get_the_category($data->ID);
foreach($checkCats as $cat) {
// 関連記事のカテゴリが表示中の記事のカテゴリと一致しない場合は除外
if(!in_array($cat->term_id, $catAry)) continue;
$pFlg = true;
break;
}
if(!$pFlg) continue;
$datas[] = $data;
// 格納したデータは削除しておく
unset($articles[$key]);
$cnt++;
if($cnt == 3) break;
}
// 3件以下だった場合は3件表示になるように調整する
if(count($datas) < 3) {
$num = 3 - count($datas);
for($i=1; $i <=$num; $i++) {
if(!empty($articles)) $datas[] = array_shift($articles);
}
}
} else {
$datas = $articles;
}
if(!empty($datas)) {
// 関連記事を表示する
}
}
function getPostDatas($num=10, $postType='post', $pageNum=1, $category='', $exId='', $order='') {
// 開始件数
$offset = 0;
if($pageNum != 1) {
$offset = ($pageNum-1) * $num;
}
$args = array(
'numberposts' => $num,
'offset' => $offset,
'orderby' => ($order == '') ? 'date' : $order,
'order' => 'DESC',
'post_type' => $postType,
'post_status' => 'publish',
'suppress_filters' => true,
'category' => $category,
'exclude' => $exId,
);
$dataAry = get_posts($args);
return $dataAry;
}
これにより、投稿数が少ないカテゴリを持つ記事の表示順位をあげることができ、より関連した記事が表示されるようになります。
上記のSQLカテゴリの記事の例では、SQLカテゴリを持つ記事そのものが2件(2020年11月時点)ですが、改修前の条件では親カテゴリを持つ記事が日付降順で上位に来ていて、もう一つのSQLの記事は表示されていませんでした。
そこで本改修を入れ、SQLの記事が上位に来るようになりました。投稿日を見るとSQLの記事は後半2つと比べて過去にも関わらず表示されています。
ここでは同じ子カテゴリ「SQL」を持つ記事は1つしかありません。ですが、同じ親カテゴリを持つ記事は存在するため、残り2件を入れて表示するようにしています。
まとめ
関連記事の精度をあげることで、各記事により関連した記事を表示することができるようになりました。
自分でロジックを考えて実装し、それがうまくいったときはとても嬉しいですね。