OXY NOTES

WordPressで複数のループを使ってカスタム投稿一覧を自在に表示する方法

サブループを使えば、多様な情報を表示するポータルサイトも簡単に作れます

複数ループの制御までくるとワンタッチで使えるプラグインは用意されていません。カスタマイズするには基本的なPHPの知識が必要になります。
そもそも記事の表示だけならメインループだけで実現できます。おそらくこのページの情報が必要になるのはWeb業界の人だと思います。

Codexを何十回も読み返しながら書く、解説の解説みたいな記事なので専門知識のある方にはくどいかもしれません。
ご了承下さい。

2015年3月追記:この記事は古い内容を含んでいます。「新しいループについての投稿はこちら」をご覧ください。


ループについて更に詳しく解説

前回に続き、ループでどんな動作をしているかを更に詳しく見てきます。この仕組を理解しておくと後の説明もすぐに飲み込めるようになります。

ループ処理の順序

    1.WordPressはまず動作するのに必要な部品が揃っているかどうか確認する。
    (テンプレートファイル、ループファイルなど)

    2.部品が揃っている場合、管理者の定義した設定を取得。
    (管理画面で設定した1ページの表示件数5件などの情報)

    3.ユーザーがどんなデータを必要としているか確認。ここまでは前回解説しました。
    (各ループファイルで指定する投稿、カテゴリ、ページなどの情報)

ここまでは前回の解説と同じです。この先をもう少し掘り下げていきます。

    4.指定されたデータをデータベースから取り出して変数に保存。
    ($wp_the_query、$wp_query、$posts、$postの中に保存)

    5.ループ中で取得したいデータを指定する。
    (the_ID();〈記事ID〉、the_title();〈タイトル〉、the_content();〈本文〉など)

    6.変数から値を取り出して書き出す。
    (3、魚辞典、本文など)

    7.指定された条件に合う投稿があれば4に戻って同じ処理を繰り返す。

このようにして投稿を表示しています。


「変数」と「グローバル変数」

新しい言葉が出たので解説しておきます。

「変数」とは

変数」とは数値を入れる箱のようなもので「$post」といった具合に$を先頭に付けて定義します。この中にさまざまな値を入れておいて必要なときに呼び出します。
$post」で言えばページIDだったり、日付などのデータが入っています。

「グローバル変数」とは

グローバル変数」とはプログラム全体に対して定義される変数で、どこからでも参照できるのが特徴です。
グローバルに対してローカルがあるように「ローカル変数」というものもあります。「ローカル変数」は関数の内部でだけ使われる変数で、別の関数で使うことはできません。


グローバル変数とリセット

ループの仕組みを解説したのには理由があります。「ループ処理の順序4」に「変数に保存」とあります。この際にグローバル変数を上書きしてしまうと、次のループの時もその値が参照されてしまいます。

図にするとわかりやすいと思います。

これが実に厄介で、単体のループだと問題なく動作するので「何が原因で動作しないのかわからない」といった状態に陥ります。
そんなエラーを避けるために複数のループをする時はグローバル変数の値をリセットする必要があります。

    query_posts()に対してwp_reset_query()
    get_posts()に対してwp_reset_postdata()
    WP_Query()に対してwp_reset_postdata()

セットで覚えちゃいましょう。


ループを変更・追加する際に使うタグとクラス

ループ処理の4」でユーザーが必要としてるデータを伝えますが、その際に使うのが
query_posts()、get_posts()」というタグと、「WP_Query()」というクラスです。
それぞれ()の中にクエリ(問い合わせる内容)を指定します。

クエリの書式はどれも似ていますが、微妙に異なることもあるため、
WordPress Codexで確認して下さい。

    WordPress Codex query posts()のページ
    WordPress Codex get posts()のページ
    WordPress Codex WP_Query()のページ

こちらに詳しい書式が載っています。

では上にある3つのタグのどれを使うのが適切でしょうか。

query_posts()」は前回の記事で使ったように、メインループを変更するのに使います。ページネーション(次へなど)を使うような、メインの要素を表示するのに使うのがメインループです。
query_posts()は基本的に1度しか使えません。Codexにも「メインループの他で query_posts() を使用すると、メインループが不正な状態になり期待する結果が得られません。」とあります。


サブループの追加には「get_posts()」「WP_Query()」を使おう

前置きが長すぎた感がありますが、新たなループを追加するには「get_posts()」、または「WP_Query()」を使います。現状(WordPress3.3)どちらもできることは同じらしいので、どちらか片方を覚えればいいと思います。
これらを使って追加されたループのことをサブループといいます。

ちなみにループファイル内で2つのループを使うことをマルチループといいます。


書式

基本的に書式のようなものなので、逐一分解して理解するよりも、まるごとセットとして覚えたほうが早いと思います。(DOCTYPE宣言文みたいなもんです)

今回は見やすくするために「配列($args)」を使って書いてますが、引数によっては配列で書けないものがあります。

配列($args)」を使わないでクエリを指定する書式

<?php query_posts( 'post_type=fish&posts_per_page=2&paged='.$paged ); ?>

配列(=>)と違い「=(イコール)」で値を代入して、複数指定する際は「&(アンド)」で繋ぎます。


メインループ用query_posts()の書き方

前回もやりましたが、メインループもグローバル変数のリセットが必要なので書いておきます。
7行目「query_posts( $args );」で定義したグローバル変数を、12行目「wp_reset_query();」でをリセットしています。

<?php
$args = array(/* 配列に複数の引数を追加 */
	'post_type' => 'fish', /* 投稿タイプを指定 */
	'posts_per_page' => 2, /* 表示するページ数 */
	'paged' => $paged, /* WP-PageNaviプラグイン用 */
); ?>
<?php query_posts( $args ); ?><!-- 上で指定したクエリ(問い合わせ内容)の指定 -->
<?php if ( have_posts() ) while ( have_posts() ) : the_post(); ?><!-- お決まりのループ開始処理 -->
<!-- ここに表示するタイトルやコンテンツなどを指定 -->
<?php endwhile; // end of the loop. ?><!-- ここまでサブループ。投稿がまだある場合は◯行目に戻る -->
<?php wp_pagenavi_dropdown(); ?><!-- #WP-PageNaviプラグイン用タグ -->
<?php wp_reset_query(); ?><!-- 忘れずにリセットする必要がある -->

サブループ用get_posts()の書き方

6行目で「get_posts()」を記述。
12行目の「wp_reset_postdata();」でリセットしている。

get_posts自体はグローバル変数を上書きしない。グローバル変数である$postを使用するテンプレートタグがあるので、$postへ変数を代入している。そのためリセットする必要がある。

また「setup_postdata( $post );」がないと取得できないデータがあるので常にセットにしておく。

11行目ループを終了させるのが「endforeach」な点も注意が必要です。

<?php
$args = array(/* 配列に複数の引数を追加 */
	'category' => 1, /* get_posts()の場合categoryと入力する他はcat */
	'posts_per_page' => 2, /* 表示するページ数 */
); ?>
<?php $my_posts = get_posts( $args ); ?><!-- クエリの指定 -->
<?php global $post;/* グローバル変数から値を取得 */
	foreach ( $my_posts as $post ) :/* $my_postsを$postへ代入 */
	setup_postdata( $post ); ?><!-- ここからループ開始処理 -->
<!-- ここに表示するタイトルやコンテンツなどを指定 -->
<?php endforeach; // end of the loop. ?><!-- ここまでサブループ。投稿がまだある場合は◯行目に戻る。endforeachに注意 -->
<?php wp_reset_postdata(); ?><!-- 忘れずにリセットする必要がある -->

サブループ用WP_Query()の書き方

6行目「new WP_Query( $args )」でサブループを追加。
WP_Query::the_post()はグローバル変数である$postを上書きするため10行目「wp_reset_postdata();」でリセットする必要がある。

<?php
$args = array(/* 配列に複数の引数を追加 */
	'cat' => 1, /* 表示するカテゴリーを指定 */
	'posts_per_page' => 2, /* 表示するページ数 */
); ?>
<?php $my_query = new WP_Query( $args ); ?><!-- クエリの指定 -->
<?php while ( $my_query->have_posts() ) : $my_query->the_post(); ?><!--他と違い$my_queryはクラスなので、「->(アロー演算子)」を使って値を取得。ここからループ開始 -->
<!-- ここに表示するタイトルやコンテンツなどを指定 -->
<?php endwhile; // end of the loop. ?><!-- ここまでサブループ。投稿がまだある場合は◯行目に戻る -->
<?php wp_reset_postdata(); ?><!-- 忘れずにリセットする必要がある -->

それでは実際にメインループとサブループを書いていきます。

前回の記事で作った「魚図鑑ページ」を使って、メインループにカスタム投稿(fish)の一覧を、サブループに投稿(post)のカテゴリーの一覧(今回はWordPressカテゴリ)を表示してみます。

魚図鑑ページ」は「fishguide.php」と「loop-fishguide.php」で表示していました。前回「loop-fishguide.php」の中で、カスタム投稿の一覧を「query_posts()」を使って表示していました。(これがメインループになります)

このファイルにサブループを追加します。上で解説した「WP_Query()」を使って「WordPressカテゴリー」を「3件」表示するサブループを追加します。

すると以下のようになります。

<?php
/**
 * 魚図鑑用ループ
 */
?>

<!-- ここからサブループに関する記述です。新しく追加しました。 -->
<p>ここからサブループ。投稿(post)のカテゴリー「WordPress」を表示します</p>
<?php
$args = array(/* 配列に複数の引数を追加 */
	'cat' => 3, /* 表示するカテゴリーを指定 */
	'posts_per_page' => 3, /* 表示するページ数 */
); ?>
<?php $my_query = new WP_Query( $args ); ?><!-- クエリの指定 -->
<?php while ( $my_query->have_posts() ) : $my_query->the_post(); ?><!-- 他と違い$my_queryはクラスなので、「->(アロー演算子)」を使って値を取得。ここからループ開始 -->
				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
						<h1 class="entry-title"><?php the_title(); ?></h1>
					<div class="entry-content">
						<?php the_excerpt(); ?>
					</div><!-- .entry-content -->
				</div><!-- #post-## -->
<?php endwhile; // end of the loop. ?><!-- ここまでサブループ。投稿がまだある場合は◯行目に戻る -->
<?php wp_reset_postdata(); ?><!-- 忘れずにリセットする必要がある -->
<hr />
<!-- ここまでサブループ -->

<p>ここからメインループ。カスタム投稿のfishを表示します</p>
<!-- ここからメインループ。ここら下は前回作った部分で、今回は変更してません。 -->
<?php
$args = array(/* 配列($args)に複数の引数を追加 */
	'post_type' => 'fish', /* 表示するカテゴリーを指定 */
	'posts_per_page' => 2, /* 表示するページ数 */
	'paged' => get_query_var( 'paged' ),/* ページネーションする場合は必須 */
); ?>
<?php query_posts( $args ); ?><!-- メインの WordPress ループを変更するタグ -->
<?php if ( have_posts() ) while ( have_posts() ) : the_post(); ?><!-- ループの開始 -->

				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
						<h1 class="entry-title"><?php the_title(); ?></h1>
					<div class="entry-content">
						<?php the_excerpt(); ?>
					</div><!-- .entry-content -->
				</div><!-- #post-## -->
				<?php comments_template( '', true ); ?>

<?php endwhile; // end of the loop. ?>
<!-- ここまでメインループ -->

<?php next_posts_link('次のページ') ?><!-- ページネーション用タグ -->
<?php previous_posts_link('前のページ') ?><!-- ページネーション用タグ -->
<?php wp_reset_query(); ?><!-- 忘れずにリセットする必要がある -->

メインループはいじってないので、追加したサブループについて軽く説明します。
基本的には前回作ったメインループと変わりません。
同じように配列「$args」で表示する投稿を定義します。
11行目「‘cat’ => 3,」でカテゴリーIDを指定、
12行目「‘posts_per_page’ => 3,」で表示する件数を指定しています。今回はサブループなので、ページ送りをしません。そのためページネーションに関する記述はありません。

14行目にサブループ用のタグ「new WP_Query( $args );」を追加。$argsで指定したデータをデータベースから取得して、グローバル変数を書き換えてます。
23行目の「wp_reset_postdata();」でグローバル変数をリセットしています。上でも解説しましたが、こうしないと後でグローバル変数「the_post()」の値を取得するときに、不具合が出ます。

実際の表示は以下のようになります。


せっかくなのでサブループを「get_posts()」でも書いてみます

<!-- ここからサブループに関する記述です。新しく追加しました。 -->
<p>ここからサブループ。投稿(post)のカテゴリー「WordPress」を表示します</p>
<?php
$args = array(/* 配列に複数の引数を追加 */
	'category' => 3, /* 表示するカテゴリーを指定 */
	'posts_per_page' => 3, /* 表示するページ数 */
); ?>
<?php $my_posts = get_posts( $args ); ?><!-- クエリの指定 -->
<?php global $post;/* グローバル変数から値を取得 */
	foreach ( $my_posts as $post ) :/* $my_postsを$postへ代入 */
	setup_postdata( $post ); ?><!-- get_postsで記事IDなどを取得するための記述。ここからループ開始処理 -->
				<div id="post-<?php the_ID(); ?>" <?php post_class(); ?>>
						<h1 class="entry-title"><?php the_title(); ?></h1>
					<div class="entry-content">
						<?php the_excerpt(); ?>
					</div><!-- .entry-content -->
				</div><!-- #post-## -->
<?php endforeach; // end of the loop. ?><!-- ここまでサブループ。投稿がまだある場合は◯行目に戻る。endforeachに注意 -->
<?php wp_reset_postdata(); ?><!-- 忘れずにリセットする必要がある -->

new WP_Query()」と基本的には同じです。
5行目、カテゴリーは「cat」から「category」へと変えてあります。「get_posts()」では「cat」は使えません。注意して下さい。

あとは8行目からのループ開始処理が他と違います。そして18行目、ループの終わりを宣言するタグも合わせて「endforeach;」へと変更してあります。

見た目は以下のようになります。

当たり前ですが、「new WP_Query()」を使った時と全く同じですねw


サブループを使ってできること

サブループは繰り返し使えます。今回は一種類のカスタム投稿一覧を表示しました。組み合わせれば複数の一覧を表示できます。
この方法を応用すれば、トップページで編集者ごとの記事の一覧を表示したり、カテゴリ別の新規更新ページを配置できます。
ポータルサイトのトップページも簡単に作れますね。

カスタム投稿やサブループの仕様は発展途上で、便利なプラグインの登場か、WordPressのバージョンアップで簡略化されていくと思います。しかし現状は手作業でカスタマイズするしかないので、幾つかのタグと引数を覚えてしまいましょう。

WordPress ループ&クエリーのモヤモヤを解消しよう!
記事を書くにあたって参考にさせていただきました。有益な情報を提供してくださってます。

今回でカスタム投稿に関する記事を終了しようかと思いましたが、せっかくCustom Post Type UIに触れたので、次回は「Custom Post Type UIでカスタムタクソノミーをとことん使いこなす方法」について解説します。