WordPress 記事をランダムに表示してページングする方法

February 3, 2015

例えばカテゴリ ページの記事をランダム順に表示したい場合、クエリを呼び出す際に orderby に rand を指定します。検索すると沢山でてくる。でも、この方法には欠陥があります。なんてことだ!

WordPress How to Posts Orderby Random and Paging

2ページ目以降も全記事からランダムなので、確率的に 1ページ目と同じ記事が表示されるのです。こうした気持ち悪い実装は、検証せずに公開しているのか、「ランダムには違いないだろう?」の確信犯なのか分かりませんが、どちらにせよ良くないですね。よくないよ。本稿では、ランダムの理に適った実装を目指します。

特定の条件下でランダムのシードを固定する

細かい話はあとにして、解決策を載せます。

function category_rand_orderby( $orderby, $query ){
	if ( !is_admin() && $query->is_main_query() ) {
		if ( $query->is_category() ) {
			$seed = strtotime( date( 'Y-m-d H:00:00' ) );
			mt_srand( $seed );
			$orderby = 'RAND(' . mt_rand() . ')';
		}
	}
	remove_filter( current_filter(), __FUNCTION__ );
	return $orderby;
}

add_filter( 'posts_orderby', 'category_rand_orderby', 10, 2 );

上記のコードを functions.php に追加すると、カテゴリ ページの記事がランダム順で並び、なおかつ、ページを切り替えても記事が重複して表示されることはありません。

3行目の if 文が条件になります。例えば、「hoge」というスラッグのカテゴリだけをランダムにしたい場合は、

if ( $query->is_category( 'hoge' ) )

とすれば OK です。もちろん他の条件も指定できます。詳しくは公式 Codex をご参照ください。

プチ解説

posts_orderby フィルターは、WP_Query クラスで定義されています。DB(MySQL)に渡すクエリの ORDER BY 句を制御できるフィルターです。

また、乱数のシードとして現在の時刻(ただし分と秒はゼロで固定し 1時間毎に変わる)を使っています。こうすることで、例えば 10:23 にアクセスしたときも、10:45 にアクセスしたときも同じ乱数が発生し、結果的に全記事一覧の並び順が一定のランダム順で固定されるので、1ページ目も 2ページ目も、何ページ目でも記事が重複して表示されることはありません。そして 11時以降になると異なるランダム順になります。

お気付きかも知れませんが、閲覧者が 10:58 に 1ページ目を表示し、11:02 に 2ページ目を表示した場合には、2ページ目に 1ページ目と同じ記事が表示される可能性があります。これは制限事項になります。

この制限を回避するには、別のシードで固定する必要があります。例えば以下のフォーラムの質問者さんはセッション ID で固定しています。

筆者としては、記事をランダムに表示するためだけにセッション管理するのはオーバーかな、と思い時刻で固定することにしました。ちなみに、このフォーラム回答にある query_posts の使用は現在は推奨されていません。ご注意を。

(参考)

(応用)orderby rand の場合にシードを固定する

orderby に rand を指定した場合には、常に同様に固定したい場合、さきほどのコードに or 条件を付け足すより もう少しスマートな書き方があります。

function fix_rand_orderby( $orderby, $query ){
	if ( 'rand' == $query->get( 'orderby' ) ) {
		$seed = strtotime( date( 'Y-m-d H:00:00' ) );
		mt_srand( $seed );
		$orderby = 'RAND(' . mt_rand() . ')';
	}
	return $orderby;
}

add_filter( 'posts_orderby', 'fix_rand_orderby', 10, 2 );

これで get_posts や new WP_Query で orderby rand を指定するとシードが固定されるようになります。サンプルとして、さらに以下のコードを追加すると、カテゴリ ページがシード固定のランダム順表示になります。

function category_rand_pre_get_posts( $query ) {
	if ( is_admin() || !$query->is_main_query() )
return;

	if ( $query->is_category() )
		$query->set( 'orderby', 'rand' );
}

add_action( 'pre_get_posts', 'category_rand_pre_get_posts' );

(おまけ)ランダム順表示は SEO 的にどうなのか

サイト運営者側におけるカテゴリ ページをランダムで表示するメリットは、過去記事が閲覧されやすくなる、という点でしょう。時間が経っても劣化しにくい情報を掲載しているなら特に有効です。でもこれ、SEO 的にはどうなんでしょうね。クローラーが巡回してくるたびに、記事の並び順(=コンテンツの内容)がコロコロ変わるとしたら?

筆者の結論は、問題なし、です。理由は簡単。「読書」「恋愛」など、単一のビッグ キーワード検索で上位(ほぼトップ)に表示される NAVER まとめ さんのカテゴリ ページは、トピックの並び順が実質的にランダム(=コロコロ変わる)だからです。

オレオレ SEO が跋扈する このインターネッツにおいて、これほど説得力のある SEO 理論も珍しい。自画自賛できたところで また次回。

One thought on “WordPress 記事をランダムに表示してページングする方法

  1. なべさん

    こちらの記事は、丁度悩んでいた箇所でした。とても救われました。
    素晴らしい記事をありがとうございます!

    Reply

コメントを残す

メールアドレスが公開されることはありません。