【WordPress】アーカイブページにソート機能追加(PHP)

タグページやカテゴリページ、投稿者ページなどのアーカイブページに、ソート機能(人気記事や最新記事順に並び変え)を追加する方法について説明します。基本的にはテーマ開発者向けですが、子テーマで archive.php 等を作成すれば、既存テーマにも適用することができます。
当サイトは海外旅ブログまとめサイトですが、タグページやカテゴリページにソート機能を実装しており、例えばこちらをご覧になると、完成イメージが湧くかと思います。
中身は、GET でソート条件(例えば最新記事順や7日間の人気記事順)を取得し、取得した条件のサブループを WP_Query() で回して、記事一覧を出力していく感じです。POST を使う方法もありますが、ページをリロードしたりブラウザバックする度に「フォームを再送信しますか?」と聞かれて面倒くさいのでおすすめしないです。そのかわり、GET だと検索エンジンにインデックスされる URL が増えますが(パラメータ付きもインデックスされる)、canonical タグでどうにか対処しましょう。
サブループ:WordPress 側が用意している自動で記事一覧を出力する機能(メインループ)に対して、自分で設定をカスタマイズして記事一覧を出力する機能のこと。
まずは、記事のソート条件を設定するプルダウンメニューを設置します。プルダウンメニューの onchange 属性に、”location.href=value;” としておくことで、選択されると value に書かれた URL (パラメータ付き)にリダイレクトされるようにします。
<?php
if(empty($_GET['order'])){
$post_order = '7days';
}else{
$post_order = $_GET['order'];
}
?>
<select class="popular-posts-order" onchange="location.href=value;">
<option value="<?php echo esc_url(strtok(esc_url((empty($_SERVER["HTTPS"])?"http://":"https://").$_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"]), '?')); ?>" <?php if($post_order == '7days') echo 'selected';?>>7日間の人気記事順</option>
<option value="<?php echo esc_url(strtok(esc_url((empty($_SERVER["HTTPS"])?"http://":"https://").$_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"]), '?').'?order=30days'); ?>" <?php if($post_order == '30days') echo 'selected';?>>30日間の人気記事順</option>
<option value="<?php echo esc_url(strtok(esc_url((empty($_SERVER["HTTPS"])?"http://":"https://").$_SERVER["HTTP_HOST"].$_SERVER["REQUEST_URI"]), '?').'?order=newest'); ?>" <?php if($post_order == 'newest') echo 'selected';?>>最新記事順</option>
</select>
GET の結果($post_order)をもとに、サブループをまわします。ここでは、tag.php の記事一覧にソート機能を付ける想定です。monthly_view、weekly_view という名前の post meta の値が大きい順、あるいは最新記事順にソートします。ソートの基準となる post meta は適宜準備してください。(update_post_meta() 等で準備できます。)meta_query の relation が少し複雑ですが、これは post meta の値が大きいものから、値が空のもの、存在しないものの順に並べ替えている感じです。
わけわからないパラメータつけられた場合、強制的に初期値にするような if 文にしておくのが良いかと思います。
<?php
$query_tag = get_query_var('tag');
$paged = get_query_var('paged') ? get_query_var('paged') : 1;
if($post_order == '30days'){
$args = [
'tag' => $query_tag,
'paged' => $paged,
'orderby' => 'meta_value_num',
'order' => 'DESC',
'meta_query' => [
'relation' => 'OR',
['key' => 'monthly_view', 'compare' => 'NOT EXISTS'],
['key' => 'monthly_view', 'compare' => '=', 'value' => ''],
['key' => 'monthly_view', 'compare' => '!=', 'value' => ''],
]
];
}elseif($post_order == 'newest'){
$args = [
'tag' => $query_tag,
'paged' => $paged,
];
}else{
$args = [
'tag' => $query_tag,
'paged' => $paged,
'orderby' => 'meta_value_num',
'order' => 'DESC',
'meta_query' => [
'relation' => 'OR',
['key' => 'weekly_view', 'compare' => 'NOT EXISTS'],
['key' => 'weekly_view', 'compare' => '=', 'value' => ''],
['key' => 'weekly_view', 'compare' => '!=', 'value' => ''],
]
];
}
$the_query = new WP_Query($args);
if($the_query->have_posts()):
while($the_query->have_posts()):
$the_query->the_post(); ?>
<!-- ここに各記事の紹介を書く -->
<?php
endwhile;
endif;
$GLOBALS['wp_query']->max_num_pages = $the_query->max_num_pages; // 重要!
the_posts_pagination(['prev_text' => '<<span>前</span>', 'next_text' => '<span>後</span>>']);
?>
<!-- 以下フッター -->
以上のようにして、アーカイブページにソート機能を追加することが出来ました。
なおブラウザバックした時にプルダウンメニューが元に戻らないので、以下のような JavaScript で selected がついた option 要素を強制的に選択状態に戻すのもありだと思います。
const target = document.querySelector(".popular-posts-order");
window.addEventListener('pageshow', function() {
target.value = target.querySelector('.popular-posts-order option[selected]').value;
});
ちょっと宣伝
当サイトは WordPress 自作テーマを使った海外旅ブログまとめサイトとなっています。トップページに地図がありますが、地名を押すと画像が開き、画像を押すと関連記事一覧(クリック数順)が開きます。記事数が多い国ほど赤く、地名もその国で記事が多い都市の文字が大きくなるようにしています。ぜひ覗いてみてください。
当サイトで海外旅ブログを執筆することも可能です(もちろん無料です)! また既にブログをお持ちの方も、当サイトからリンクを貼ることができるようになっています。パントレ開発部までお気軽にお問い合わせください。
パントレ開発部