WordPressのアーカイブページを人気順などで並び替えるボタンをつける
カテゴリやタグの記事一覧(アーカイブ)ページは、デフォルトでは公開日が新しい順に並んでいますが、公開日の古い順や、WP-PostViewsというプラグインを利用した閲覧数順などの並べ替えボタンをつけてみよう!という内容です。
完成図
カテゴリとタグのアーカイブページに4種類の並び替えボタン(CSSでテキストリンクっぽくしてます)をつけてみました。
スマホ版はこんな感じになっています。
きっかけ
記事終わりにカテゴリ別の人気記事を置くために一時期使っていたこちらのプラグインなのですが、WordPress.comの統計情報を使うほうが理想に近かったため、途中で切り替えてしまいました。
しかしながら、ダッシュボードの投稿一覧に閲覧数が出たり、並び替えられたりと管理面で気に入っていたので、稼働させたままでした。
そんな折、この記事を拝見しまして。
なっ…!(( ゚д゚)ガタッ
インストールして有効化するだけで、あとはget_posts()
で自由に…!? そ、そんなことが出来るのかッ…! 一生懸命ダッシュボードのプラグイン編集からサムネイルが出るようにカスタマイズとかしてた…! こっちとこっちで違う画像の大きさにしたいから片方はCSSで画像サイズ整えたりとかしてた…w
などと恥ずかしい思い出が噴出するなか調べてみたところ、このプラグインは wp_postmeta というテーブル内の meta_key に views というフィールドを作り、そこの meta_value にカウント数を格納していってくれるのだそう。ほうほう。
phpMyAdminで、wp_postmeta テーブルを ‘meta_key’=’views’ で絞ってみた結果のキャプチャ。こんな感じで格納されているわけですね!これを使ってWP_Query()
やget_posts()
などで、
array( 'meta_key' => 'views', 'orderby' => 'meta_value_num' );
こんな感じのパラメータを指定してあげれば閲覧数の多い順に取り出せる、と…。なんて便利なんだ。並び順はデフォルトで降順なので、「’order’ => ‘DESC’」は省略できるんですね。
ところで、フィールド名は meta_value なのに、meta_value_num というのはどこから出てきたのだろう、と疑問に思ったのですが。
こちらに書いてありました。英語だったのでざっくり解釈するに、meta_value_num と書くことで、meta_value を数値として扱えるようになるみたいです。確かにテーブル構造では meta_value は LONGTEXT 型でした。
などなど、かなり使い勝手が良さそうな WP-PostViews の metaデータ。これを使ってアーカイブを並び替える方法を紹介しているこちらの記事も拝見し、
これ良いな…!こういうの、やってみたかった…!わたしはこれを、
pre_get_posts()
でやってみたーい!!!\\└(‘ω’)┘//
と思ったという、ここまでが前置きです。すごい長くてすみません。
事前準備
パーマリンクはデフォルト以外で
今回はGET変数を使った方法を紹介しますので、http://xxx.com/?p=1
という形のパーマリンクではうまく動きません。「?」の入ったパーマリンクを使いたい方(若しくは、並び替えたときURLに「?sort=newer」などとつくのが嫌な方)はPOST変数を使うと実装できると思います。
追記:タイトルタグの重複エラーにご注意
アーカイブURLの末尾に「?sort=newer」などをつけて並び替えるだけなので、違うURLでタイトルタグや内容の重複するコンテンツが存在することになります。link rel=”canonical” で正規URLを指定することが望ましいですが、プラグインが手っ取り早いです。詳細はコメント欄をご参照ください。
構成を決める
まずは Codex を見て、どのページに並び替えボタンを適用させたいのかということを決定します。今回はこのブログに実装したものを例にして
- カテゴリアーカイブ …
is_category()
- タグアーカイブ …
is_tag()
このふたつだけで紹介しますが、他にも日付や作成者のアーカイブにも実装が可能です。
追記:検索ページは「?s=○○&sort=newer」という形にしなければいけません。検索ページにも対応した形を新たに書きましたので、こちらの記事と合わせてどうぞ。
アーカイブページのカスタマイズ
並び替えからはちょっと逸れるのですが、ついでに特定のアーカイブページを表示したときに「○件」と表示されるようにしちゃいます。
こちらを参考にさせていただいて、アーカイブページで読み込まれるテンプレート(一般的にはindex.php
か、header.php
あたりでしょうか)の、メインループ外にこのように書いてみました。
<?php if ( is_archive() || is_search() ) { //アーカイブか検索ページだったら global $wp_query; $total_results = $wp_query->found_posts; //件数を取得しておく } ?> <?php if ( is_archive() ): //アーカイブページ ?> <?php if ( is_category() || is_tag() ): //カテゴリ,タグアーカイブ ?> <div class="result"> <?php if( is_category() ): //カテゴリアーカイブのとき ?> カテゴリ "<?php single_cat_title(); ?>":<?php echo $total_results; ?>件<br /> <?php elseif( is_tag() ): //タグアーカイブのとき ?> タグ "<?php single_tag_title(); ?>":<?php echo $total_results; ?>件<br /> <?php endif; ?> //並び替えボタンの実装 </div> <?php elseif( is_date() ): //日付アーカイブ ?> <div class="result"> <?php if( $year ): echo $year ?>年<?php endif; ?> <?php if( $monthnum ): echo $monthnum ?>月<?php endif; ?> <?php if( $day ): echo $day ?>日<?php endif; ?> :<?php echo $total_results; ?>件 </div> <?php endif; ?> <?php endif; ?> <?php if ( is_search() ): //検索 ?> <div class="result">"<?php the_search_query(); ?>" で検索した結果:<?php echo $total_results; ?>件</div> <?php endif; ?>
カテゴリ・タグ・日付アーカイブと、検索結果ページに件数が出るようにしています。ハイライト部分に後で並び替え用のボタンを実装する予定ですが、ボタン無しならとりあえずこのままでも動くと思います。
ちなみにis_archive()
にはカテゴリ、タグ、日付の他に作成者のページが含まれますが、このブログでは作成者はひとりしかいないので、上記コードでは作成者ページには特に何もしていません
並び替えボタンの実装
GET変数の動き
まずは仕組みの説明のために、例として上記コードのハイライト部分に
<form method="get" action=""> <input type="hidden" name="sort" value="sample" /> <input type="submit" value="サンプル" /> </form>
こう書いて、試しにカテゴリアーカイブを見てみると、ボタンができてます。
このボタンを押すと、URLがこのように遷移します。
https://ateitexe.com/category/xxx/
↓
https://ateitexe.com/category/xxx/?sort=sample
3行目のvalue
がボタン内の文字列で、1行目のaction
で指定した URL(空欄の場合は同じページ)に遷移します。このとき、2行目の内容が変数としてURLの最後にくっつきます。
<form method="get" action=""> <input type="submit" name="sort" value="サンプル" /> </form>
ちなみに、これだけでも https://ateitexe.com/category/xxx/?sort=サンプル という形で使えるのですが、URLに日本語が入るのが個人的に気持ち悪くて、hidden要素を持たせています。お好みでどうぞ。ボタン内の文字列を英語にしてもいいかも。
$sortset = $_GET['sort'];
URL末尾の「?sort=○○○」はこのように書くことで取得できますが、信頼できない変数なので、
$sortset = (string)filter_input(INPUT_GET, 'sort');
いろいろなチェック処理をしてから取得する形にします。上記の例だとsample
という文字列が入ることになります。
こんな要領で、最終的には並び替えたい条件の数だけボタンを作り、そのボタンごとに違う文字列を持たせて、それを使って条件分岐させていきます。
- 日付が新しい順 … newer
- 日付が古い順 … older
- 閲覧数が多い順 … popular
- 閲覧数が少ない順 … unpopular
今回はこんな想定で実装しようと思います!(不人気記事は需要ないかもしれませんが、一応w)
遷移先を指定
このブログの場合、カテゴリとタグのアーカイブページのURLは、それぞれ
https://ateitexe.com/category/xxx/
https://ateitexe.com/tag/xxx/
で、これが2ページ目以降になると
https://ateitexe.com/category/xxx/page/2/
このようになります。form の遷移先を空欄にしたまま、この場所から並び替えボタンを押すと、
https://ateitexe.com/category/xxx/page/2/?sort=older
のように、同じURLに遷移してしまうので、違う並び順を選んでも、いきなり2ページ目に行ってしまうんです。これは気持ち悪いよなーと思ったので、2ページ目以降でボタンを押されても、1ページ目に飛ぶようにURLを指定します。
<?php $url = explode('/', $_SERVER["HTTP_HOST"] . $_SERVER["REQUEST_URI"]); $url_str ='http://'.$url[0].'/'.$url[1].'/'.$url[2].'/'; ?> <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="newer" /> <input type="submit" value="新しい順" /> </form>
こう書いてみます。2行目で( http:// を抜いた)現在のURLを「/」で分割して$url
という配列に格納しています。
$url[0]
→ ateitexe.com$url[1]
→ category(tag)$url[2]
→ xxx(スラッグ)$url[3]
→ (あれば)page$url[4]
→ (あれば)2など
すると配列の中身はこのようになるので、$url[2]
までを使ってURLを組み立てて$url_str
という変数に入れておき(3行目)、そいつをaction
の遷移先に指定(5行目)してやれば、現在地が1ページ目でも2ページ目以降でも、アーカイブのトップに遷移できます。
コメントで教えていただきました!
get_pagenum_link(1)
を使えばもっとスマートにそのアーカイブの先頭ページのリンクが取得できます!
<?php $url_str = get_pagenum_link(1); ?> <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="newer" /> <input type="submit" value="新しい順" /> </form>
しょぼーんさん、ありがとうございました!
CSSでテキストリンク風に
ボタンのままでも良いのですが、テキストリンクっぽくしてみました。
<?php $url_str = get_pagenum_link(1); $sortset = (string)filter_input(INPUT_GET, 'sort'); ?> <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="newer" /> <?php if ( $sortset === 'newer' ): ?> <input type="submit" class="sort_current" value="新しい順" /> <?php else: ?> <input type="submit" value="新しい順" /> <?php endif; ?>
3行目でGET変数を取得して、7行目にて現在地かどうかを判別します。一致したときだけsort_current
というクラスを付加する形で、ボタンを出し分けます。
style.css
.result form{display: inline;} .result input[type="submit"]{ color: #aa9a85; padding: 0; margin: 0; box-shadow: none; background: none; border: none; font-size: 0.85em; text-decoration: underline; cursor: pointer; } .sort_current{ font-weight: bold; text-decoration: none; }
全体を div class=”result” で括っているので、その中に入っている form に適用する、という形で書いてみます。元のボタンのCSSを打ち消す形になるのでテーマによって違うとは思いますが、わたしの環境ではこんな感じで。現在位置は太字で下線なしにしました。
4つ並べる
ボタンを4つつけて、それぞれの区切りに「|」を入れてみます。
<?php $url_str = get_pagenum_link(1); $sortset = (string)filter_input(INPUT_GET, 'sort'); //取得 ?> 並び替え: <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="newer" /> <?php if ( $sortset !== 'older' && $sortset !== 'popular' && $sortset !== 'unpopular' ): ?> <input type="submit" class="sort_crt" value="新しい" /> <?php else: ?> <input type="submit" value="新しい" /> <?php endif; ?> </form> | <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="older" /> <?php if ( $sortset === 'older' ): ?> <input type="submit" class="sort_crt" value="古い" /> <?php else: ?> <input type="submit" value="古い" /> <?php endif; ?> </form> | <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="popular" /> <?php if ( $sortset === 'popular' ): ?> <input type="submit" class="sort_crt" value="閲覧多" /> <?php else: ?> <input type="submit" value="閲覧多" /> <?php endif; ?> </form> | <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="unpopular" /> <?php if ( $sortset === 'unpopular' ): ?> <input type="submit" class="sort_crt" value="閲覧少" /> <?php else: ?> <input type="submit" value="閲覧少" /> <?php endif; ?> </form>
ハイライト部分は「日付が新しい順」にするか判別する部分ですが、さっきは
<?php if ( $sortset === 'newer' ): ?>
でしたが、ここでは変えています。GET変数は性質上ユーザーが容易に変更できてしまうものですし、ボタン以外からアーカイブページに遷移してきたときは空白になるなど、指定した4種類以外の値をとる可能性が十分考えられるため、「older, popular, unpopular ボタンのどれでもないとき」という条件にしています。「日付が新しい順」をデフォルトとする、という感じでしょうか。
省コード
<?php $url_str = get_pagenum_link(1); //URL $sortset = (string)filter_input(INPUT_GET, 'sort'); //取得 $crt = ' class="sort_current"'; ?> 並び替え: <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="newer" /> <input type="submit"<?php if( $sortset !== 'older' && $sortset !== 'popular' && $sortset !== 'unpopular' ){ echo $crt; } ?> value="新しい" /> </form> | <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="older" /> <input type="submit"<?php if( $sortset === 'older' ){ echo $crt; } ?> value="古い" /> </form> | <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="popular" /> <input type="submit"<?php if( $sortset === 'popular' ){ echo $crt; } ?> value="閲覧多" /> </form> | <form method="get" action="<?php echo $url_str ?>"> <input type="hidden" name="sort" value="unpopular" /> <input type="submit"<?php if( $sortset === 'unpopular' ){ echo $crt; } ?> value="閲覧少" /> </form>
カレントのクラスの付加をもうちょっと効率的にするために、4行目で$crt
という変数に class="sort_current"
という文字列(頭にスペース含む)を入れておいて、該当する時だけecho
してやる、という書き方にしてみました。こっちのほうが記述が少なくなりますね。
pre_get_posts() で並び替え
ここまでで、やっとボタンができました! 後はボタンを押したときの変数に合わせて並び替えをしてあげるだけです。
こちらを参考にさせていただいています。このページもう何回読んでることか…!いつもありがとうございます!
functions.php
function SortArchive( $query ) { if ( is_admin() || ! $query->is_main_query() ) return; if ( $query->is_tag() || $query->is_category() ) { $sortset = (string)filter_input(INPUT_GET, 'sort'); if ( $sortset === 'older' ) { //古い $query->set( 'orderby', 'date' ); $query->set( 'order', 'ASC' ); } elseif ( $sortset === 'popular' ) { //閲覧多 $query->set( 'meta_key', 'views' ); $query->set( 'orderby', 'meta_value_num' ); } elseif ( $sortset === 'unpopular' ) { //閲覧少 $query->set( 'meta_key', 'views' ); $query->set( 'orderby', 'meta_value_num' ); $query->set( 'order', 'ASC' ); } else { //それ以外(新しい) $query->set( 'orderby', 'date' ); } return; } } add_action( 'pre_get_posts', 'SortArchive' );
このように書いてみました。(記述がおかしかったらご指摘くださると有難いです!)
5行目はカテゴリとタグのアーカイブページに適用するという意味で、6行目で変数を取得、ハイライト部分でそれぞれ変数の中身に従って並び替える条件を変えてあげる、という形になっています。
以上です! いかがでしたでしょうか。もうちょっと手を加えれば、コメントの多い順とか少ない順とか、かなり色々できると思いますので興味のある方は試してみてくださいー!
追記
ショッピングカートのカスタマイズへの参考にしていただけました! こんな使い方もできるんですねー!(*´∀`*)
1件のピンバック
[…] WordPressのアーカイブページを人気順などで並び替えるボタンをつける – Ateitexe […]
8件のコメント
参考にさせていただきソートは出来ましたが
しばらくしてgoogleウエブマスターツールを見ると
HTML の改善→タイトルタグの重複にエラーが表示されるようになりました
https://ateitexe.com/category/notes/
https://ateitexe.com/category/notes/?sort=older
https://ateitexe.com/category/notes/?sort=popular
https://ateitexe.com/category/notes/?sort=unpopular
たとえば上記のページのタイトルはすべて「雑記」ということになって
タイトルタグの重複エラーになりませんか?
shuさん、コメントありがとうございます。
わたしのブログではタイトルタグの重複エラーが出なかったため、問題に気がつかずに申し訳ありません。原因というか、エラー回避してくれていたのはプラグインのようでした。
このプラグインが rel=”canonical” で正規のURL( ?sort=* 無しのもの)を指定してくれていたため、ソート用のURLはインデックス登録されていなかったようです。
詳細についてはこちらのサイトが参考になると思います。
プラグインを使わない場合は、rel=”canonical” の記述を別に設定するか、アーカイブページを noindex にしてしまうなど、なにかしらの対策をしたほうが良さそうです。ご指摘、ありがとうございました。
丁度、探していた内容でとても参考になりました。ありがとう御座います。
それで、1カ所こうすれば良いのではないかと思ったのでコメントしておきます。
URL生成ですが、WordPress関数を使って
$url_str = get_pagenum_link(1);
で、子カテゴリの問題も解決すると思います。
今の所、問題も無いのでお試し頂ければ。
しょぼーんさん、コメントありがとうございます。
get_pagenum_link()を使えばよかったのですね!! ページングで見るやつ!! うわー! 全然気づかなかったです、ありがとうございます!! 記事修正させていただきました(*`・ω・)ゞ
こんにちは。不躾な話で申し訳ありません。
ブログの記事、カテゴリ別に抽出するウェジットは存在しますが、それを読者が任意にソートする機能を付けたいと考えています。
プラグインなどを探しましたが、どうしても見つかりません。
そこで、このページに行きついたという次第です。
大変興味深い記事なのですが、自分はプログラムの知識が全くなく、これをどのようにしたらいいのか?
どこに、どう配置したらいいのか全く分からなく途方に暮れています。
もしよろしければ、設置法、設置場所などご教授いただけたらと思った次第です。
ソートしたい項目は、投稿日、閲覧数(多)、タイトルのアイウエオ順、ランダム表示になります。
これが出来たら、カテゴリ抽出と組み合わせたらいろいろ便利がいいと考えています。
お手数とは存じますが、ご教授いただければ幸いです。
よろしくお願い致します。
高橋さん、コメントありがとうございます。
記事を読んでいただき、ご検討いただけたこと、私としてもとても嬉しいことなのですが、残念ながら「プログラムの知識が全くない」となると、いきなりこの実装は難しいのではないかと思います。
この記事の内容を書く場所はテーマによって異なり、お使いのWordPressテーマの構造がどのようになっているのかは私には遠隔で理解することができないので、アドバイスも大変難しいのです。
WordPressに興味を持ち、カスタマイズを自分でやったみたい!というお気持ちがあるならば、勉強しながら自作テーマを作ってみることをお勧めします。
既存テーマのカスタマイズから入ってもよいと思いますが、私の経験上、既存テーマは機能が豊富すぎるので初心者から見るとコードが多すぎて必要最低限の骨組みを見つけるのが難しいと感じています。
検索すればシンプルな自作テーマの作り方も学べるので、まずはそうして必要最低限の骨組みを理解することができれば、カスタマイズするにはどのあたりに書けばいいのかが分かってくると思います。
ご期待に添えずに申し訳ありません。ですが、ご興味があればぜひチャレンジしてみてください。
はじめまして。
今wordpressでソート機能の実装に取り組んでおり、
おかげさまで並べ替えボタンをつくることができました。ありがとうございます。
しかし、肝心の機能の実装に苦難しています。
具体的にはfunction.phpに記述した$sortsetがうまく機能していないようです。
自分はショートコードにボタンの記述をしたため、変数を読み込めていないのかと考えました。
お手数ですが、何かアドバイスいただければ幸いです。
よろしくお願い致します。
こんにちは、コメントありがとうございます。
この記事ではショートコードを考慮して書いておりませんので、まずは使わずに書いて問題なく動くか確認されたのち、必要な部分を少しずつカスタマイズされるのが良いかと思います。
コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)
YouTubeでQ&Aコンテンツを企画しています
運営しているYouTubeチャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。