情報科学屋さんを目指す人のメモ

方法・手順・解説を書き残すブログ。私と同じことを繰り返さずに済むように。

WordPress:記事IDの配列からループを作成して記事一覧を表示する方法

PHP (25) WordPress (80) WordPressテーマ (7)

WordPressは、テンプレートファイルの「ループ」と呼ばれる範囲からループ内部専用の関数を呼び出して、記事や記事の一覧を表示します。これは、個別ページでもトップページでもカテゴリアーカイブでも同じ仕組みです。

今回は、記事ID(post id)の配列から、オリジナルのループを作成する方法を紹介します。

いきなり需要無しにこの記事を読んだ人にとっては「そんな配列はいつ手に入るのか」と思うかもしれませんが、私の場合は、プラグインの提供する情報を使おうと思ったとき、「プラグインのデータベースからIDの配列までは取り出せたけどここからどうしよう」と思ったのがきっかけです。

「ループ」の優位性

記事IDの配列があれば、foreachでIDを順番に取り出して、IDから記事タイトルや本文などを取り出せばよい、と考えがちですが、投稿IDから簡単に取得できる情報はかなり限られているとともに、関数の利用方法に統一感が無く、「ループ」に比べてかなり扱いにくいという問題があります

ですから、「記事IDの配列」から「ループ」を作り出してしまうのがとても便利です

しかし、ループを一発で作る方法は(おそらく)なく、また、記事ID配列の順番を維持して表示するのにもちょっと工夫が必要です。

今回はこれらを満たすループの作り方を紹介していきます。

ループの基本

とある条件式(MySQLのクエリ)で記事全体のデータベースを検索した結果を順番に取り出すのがWordPressのループと呼ばれる仕組みです。

トップページなら、「最新の投稿を投稿日時順に取り出す」というクエリが標準で設定されており、タグページなら「このタグのついた投稿を投稿日時順に取り出す」というクエリが設定されています。このようにクエリが標準で設定されているので、始めから次のようなループの記述を書くだけで、思った通りの記事が表示できるのです。

<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
(ループ内部)
<?php endwhile; else: ?>
(該当記事がなかった場合)
<?php endif; ?>

したがって、その「クエリ」を、記事IDの配列によって指定してあげればいい、というのが基本方針となります。

記事IDの配列からループを作る方法

とりあえず$post_idsという配列に記事IDが入っていると思ってください。

クエリの作成

この$post_idsから、次のようにして「クエリ」、実体としてはWP_Queryオブジェクトを作ります。

<?php $args = array( 'post__in' => $post_ids );
$post_ids_query = new WP_Query ( $args );

ここでは、「post__in」というパラメータにID配列を指定します。ここが最重要ポイントです。

ループの呼び出し

この準備ができたら、先ほど紹介したループに似た書き方で、ループを作成することができます。

<?php if ( $post_ids_query->have_posts() ) : while ( $post_ids_query->have_posts() ) : $post_ids_query->the_post(); ?>
(ループ内部)
<?php endwhile; wp_reset_postdata(); else: ?>
(該当記事がなかった場合)
<?php endif; ?>

「$post_ids_query->」という、具体的なクエリの指定部分が余分にくっつくところだけがまず違います。そして、「wp_reset_postdata()」という部分が追加されている部分が追加されています。これは、独自のループを作ったことによって、先ほど言った「標準クエリ」に伴うグローバル変数が上書きされた状態になってしまうのを、もとの状態に戻す役割があります。

投稿ID配列の順番を維持する

これでほとんど完成のように見えるのですが、実はこのままでは、記事の順番が配列の順番では無くなってしまいます

そこで、「posts_orderby」という場所にフィルタを追加します。このフィルタでは、SQL文の「ORDER BY」を直接指定することができます。

今回は、ORDER BYに「FIELD(ID, 10, 30, 50)」のような文字列を指定できれば、「ID:10の記事、ID:30の記事、ID:50の記事」という順番で記事が表示できるようになるので、この文字列を返すフィルタを作成します。クロージャの使えないPHP 5.2以前に対応するためにglobalキーワードを使って書くと次のようになります(これは、先ほどのループより前に書くことになります)。

function post_ids_orderby ( $orderby ) {
	global $post_ids;
	return 'FIELD(ID, ' . join( ", " , $post_ids ) . ')';
}
add_filter( 'posts_orderby' , 'post_ids_orderby' );

join関数を利用して、post_idの配列から、「FIELD(ID, ...)」の部分を作成しています。

PHP 5.3以降の場合は無名関数およびクロージャが利用できるため、add_filterをこのようにすっきりと書くことができるはずです。

add_filter( 'posts_orderby' , function ( $orderby ) use ( $post_ids ) {
	return 'FIELD(ID, ' . join( ", " , $post_ids ) . ')'; }
);

最終的なループ用のコード

以上をまとめると次のようになります。

<?php function post_ids_orderby ( $orderby ) {
	global $post_ids;
	return 'FIELD(ID, ' . join( ", " , $post_ids ) . ')';
}
add_filter( 'posts_orderby' , 'post_ids_orderby' );

if ( $post_ids_query->have_posts() ) : while ( $post_ids_query->have_posts() ) : $post_ids_query->the_post(); ?>
(ループ内部)
<?php endwhile; wp_reset_postdata(); else: ?>
(該当記事がなかった場合)
<?php endif; ?>

こうすることで、事前に取得した$post_idsというpost idの配列から、WordPressのループを作成し、the_timeやthe_titleなどを自由に利用したり、既存の記事一覧に利用しているテンプレート(.php)を転用することもできるようになります。

「post__in」と「posts_orderby」がわかりにくいポイントでした。

参考文献

コメント(0)

新しいコメントを投稿