万が一、当サイトで重大な問題を発見した際などは、フォーラムや WordSlack #docs チャンネルでお知らせください。</p>
ループ
目次
「ループ」は、WordPress の投稿を表示するために使われるPHPコードです。このループを使えば、現在のページに表示される各投稿を処理したり、ループタグ内で指定された条件に沿って投稿の形式を整えたりできます。ループの開始部分と終了部分の間に書きこんだ HTML や PHP のコードは、各投稿の表示に使われます。
テンプレートタグやプラグインの説明内に「このタグ(プラグイン)はループ内で使います」とある場合、タグが各投稿で繰り返して表示されます。例として、以下が各投稿にデフォルトでループ内に含まれます。
- 投稿のタイトル - the_title()
- 投稿の公開日時 - the_time()
- 属するカテゴリー - the_category()
さらに他のテンプレートタグを使ったり、詳しい人ならば $post 変数にアクセスしたりして、投稿に関する様々な情報を表示することもできます。
ループに始めて触れる人は、ループの使用例/en ページも参照してください。
ループの使い方
ループは index.php などのテンプレートファイルに含め、投稿の情報を表示するために使います。
テーマのテンプレートの最初で、ヘッダーテンプレートを必ず呼び出すようにしてください。もしループをテンプレート以外の自分のデザインで使っている場合、WP_USE_THEMES を false に設定します。
<?php define( 'WP_USE_THEMES', false ); get_header(); ?>
ループはここから始まり、
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
ここで終わります。
<?php endwhile; else : ?> <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p> <?php endif; ?>
PHPの制御構造を用いることで、このような表現も可能です。
<?php if ( have_posts() ) { while ( have_posts() ) { the_post(); // // 投稿がここに表示される // } // end while } // end if ?>
ループの例
特定のカテゴリーにある投稿のスタイルを変更する
以下のコードは各投稿をのタイトル (パーマリンクとして用いられるもの)、カテゴリー、内容を表示するものです。これはまた、in_category() テンプレートタグを使ってカテゴリー番号が3の投稿に他とは別のCSSクラス名を割り当てています。CSS(style.css)内でこのクラスの宣言を追加・編集することにより、カテゴリー3の投稿にのみ特別なスタイルを加えることができます。
<!-- --> というHTMLコメントで囲まれた部分は、実際にブラウザでは表示されません。ここではコードを分かりやすくするためにHTMLコメントで囲んだ説明を挿入していますが、実際にはテンプレート内に書き込まなくてもかまいません。
<!-- ループ開始 --> <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); ?> <!-- 以下で、投稿がカテゴリー3に属しているかテスト --> <!-- もし属している場合、"post-cat-three"というCSSクラスのdivボックスを表示 --> <!-- それ以外の場合、"post"というCSSクラスのdivボックスを表示 --> <?php if ( in_category( '3' ) ) : ?> <div class="post-cat-three"> <?php else : ?> <div class="post"> <?php endif; ?> <!-- 投稿のタイトルとパーマリンクを表示 --> <h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2> <!-- 日時を表示 --> <small><?php the_time('F jS, Y'); ?></small> <!-- 投稿の本文をdiv内に表示 --> <div class="entry"> <?php the_content(); ?> </div> <!-- 投稿のカテゴリーをコンマ区切りで表示 --> <p class="postmetadata">Posted in <?php the_category(', '); ?></p> </div> <!-- 最初の div ボックスを閉じる --> <!-- “else”部分を除いたループ終了 --> <?php endwhile; else: ?> <!-- 最初の“if”にて表示する投稿があるかどうかをテストしたため、“else”では投稿がない場合に実行 --> <!-- つまり、投稿がなければ以下を表示 --> <p>Sorry, no posts matched your criteria.</p> <!-- ループを「完全に」終了 --> <?php endif; ?>
注: HTMLコードをテンプレート内に書く場合は、必ず <?php ?> というPHP開始・終了タグの外側に書かなければなりません。逆に、PHPコードは、必ず <?php ?> タグの内側に書きます。上記のように、if や else ステートメント内でもPHPコードを一時的に閉じ、HTMLコードを書くことができます。
特定カテゴリーの投稿を除外する
この例は特定のカテゴリーを表示させない場合を示します。この場合、カテゴリー3と8の投稿を除外します。この例は上記の例とは異なり、クエリー自身を変更しています。
<?php $query = new WP_Query( 'cat=-3,-8' ); ?> <?php if ( $query->have_posts() ) : while ( $query->have_posts() ) : $query->the_post(); ?> <div class="post"> <!-- Display the Title as a link to the Post's permalink. --> <h2><a href="<?php the_permalink() ?>" rel="bookmark" title="Permanent Link to <?php the_title_attribute(); ?>"><?php the_title(); ?></a></h2> <!-- Display the date (November 16th, 2009 format) and a link to other posts by this posts author. --> <small><?php the_time( 'F jS, Y' ); ?> by <?php the_author_posts_link(); ?></small> <div class="entry"> <?php the_content(); ?> </div> <p class="postmetadata"><?php _e( 'Posted in' ); ?> <?php the_category( ', ' ); ?></p> </div> <!-- closes the first div box --> <?php endwhile; wp_reset_postdata(); else : ?> <p><?php _e( 'Sorry, no posts matched your criteria.' ); ?></p> <?php endif; ?>
注: この例をメインページに用いる場合、違うテンプレートをカテゴリーページに適用させる必要があります。さもなくば、WordPressは、カテゴリーアーカイブを含めた全てのページから、カテゴリー3と8の投稿を除外してしまいます。しかし、同じテンプレートファイルを使いたい場合、is_home() タグを用いることで、カテゴリー3と8の投稿がメインページからのみ除外されるようにすることができます。
... <?php if ( is_home() ) { $query = new WP_Query( 'cat=-3,-8' ); if ( $query->have_posts() ) : while ( $query->have_posts() ) : $query->the_post(); } else { ... ?> ...
他にも条件分岐タグを用いることで、ページに対する特定の条件がtrueであるか否かで出力をコントロールすることができます。
複数のループ
この章ではループの発展的用途について解説します。少々技術的な内容ですが、怖がらずに読んでください。簡単なところから徐々に発展した内容に入っていきます。ほんの少しの常識や我慢、そして熱意があれば、あなたも複数のループを使うことが出来るでしょう。
はじめに、「なぜ複数のループが必要なのか?」という点について考えてみましょう。おそらくあなたは何かをある投稿のグループに対して行い、別の何かを他の投稿のグループに対して行ったものの、同じページが両方のグループに表示されてしまったのでしょう。「何か」というのは「何にでも」という事になり得ます。あなたは今、PHPのスキルと想像に縛られています。
では、例に入りましょう。まずは普通のループから。
<?php if ( have_posts() ) : ?> <?php while ( have_posts() ) : the_post(); ?> <!-- なにかの処理 ... --> <?php endwhile; ?> <?php endif; ?>
(ソースコードを読むのに慣れている人は飛ばして下さい)。
これを日本語で言えば、以下の様になります。
- もし投稿を表示できれば、次へと進む。
- そして一覧に存在している投稿ごとそれぞれに、
- <!-- なにかの処理 ... -->を行う。
- 最後の投稿にたどり着いたら、終了する。
ここでの <!-- なにかの処理 ... --> はテンプレートに依存します。
なにかの処理について: この例ではページの各投稿をどうフォーマットし、どう表示させるかというコード一式を表しています。ここに入るコードはWordPressでどう表現したいかに依って異なります。例えば、Kubrickのテーマの index.php を見た場合、
<?php while ( have_posts() ) : the_post(); ?>
に続いて、この行が入ります。
<?php comments_popup_link( 'No Comments »', '1 Comment »', '% Comments »' ); ?>
コーダー向けの説明: have_posts() と the_post() は、全てのアクションであるグローバルな $wp_query オブジェクトを用いるのに便利なラッパーです。$wp_query はブログのヘッダーで呼び出され、GET と PATH_INFO を通して来たクエリー引数が入力されます。$wp_query は引数を受け、ビルドし、データベースクエリーを実行し、投稿の配列を作成します。この配列はオブジェクト内に格納され、またグローバルの$posts 配列の中に (古い投稿ループとの後方互換性のために) 入っているブログのヘッダーに返されます。
WordPressがブログのヘッダーを読み込み終わり、テンプレートまでたどり着くと、投稿のループに到達します。ここで have_posts() は $wp_query->have_posts()を呼び出し、ループカウンターと投稿配列に残った投稿の有無をチェックします。そしてthe_post() は $wp_query->the_post() を呼びだし、ループカウンターを進め、グローバルの $post 変数と、全てのグローバル投稿データをセットアップします。ループが完了したら、have_posts() がfalseを返し、終了します。
ループの例
以下に複数ループの使用例を2つ示します。複数ループを使うための鍵は $wp_query が一度しか呼び出されないところにあります。これを回避するために、 rewind_posts() を呼び出してクエリーを再利用するか、新しいクエリーオブジェクトを生成します。これは複数ループの例 1の内容です。複数ループの例 2ではクエリーの結果を格納する変数を使うケースがを挙げています。最後に複数ループの使い方では、ブログのホームページに特定のカテゴリーの投稿を優先的に載せるために様々なアイディアを駆使した複数ループの使用方法の1つを記載しています。
複数ループの例 1
二度目のループでも同じ様に処理を行うにはrewind_posts()を呼び出して下さい。 この関数はループのカウンタをリセットし、別のループを実行出来るようにします。
<?php rewind_posts(); ?> <?php while ( have_posts() ) : the_post(); ?> <!-- なにかの処理... --> <?php endwhile; ?>
もし既に標準のクエリに関するループを終了したあと、別のクエリを使いたくなったら、$wp_queryオブジェクトをquery_posts()から呼び出すことが可能です。query_posts()は新しいクエリ、投稿リストを作り、ループをリセットします。
// special_cat カテゴリーから10件の投稿を取り出します。 <?php query_posts( 'category_name=special_cat&posts_per_page=10' ); ?> <?php while ( have_posts() ) : the_post(); ?> <!-- special_cat についての処理... --> <?php endwhile; ?>
もし標準のクエリを保持しておきたければ、新しいクエリオブジェクトを生成するという方法があります。
<?php $my_query = new WP_Query( 'category_name=special_cat&posts_per_page=10' ); ?> <?php while ( $my_query->have_posts() ) : $my_query->the_post(); ?> <!-- special_cat についての処理.... --> <?php endwhile; ?>
$wp_query用のhave_posts() と the_post() が使えないので、新たなオブジェクト my_query を生成します。
(訳注: ただの have_posts() や the_posts() ではなく、事前に生成した $my_query のメソッドであることに注意して下さい。)
複数ループの例 2
複数ループを使うもう一つの方法は、have_posts() と the_post() が使えないことに対する別の回避策を必要とします。これを解決するために、オリジナルのクエリー結果を変数に格納し、もう一方のループで再度割り当てる必要があります。これにより、全てのグローバルに依存する全ての標準機能を使うことができます。
例:
// ここで脇にそれる <?php $temp_query = $wp_query; ?> <!-- なにかの処理... --> <?php query_posts( 'category_name=special_cat&posts_per_page=10' ); ?> <?php while ( have_posts() ) : the_post(); ?> <!-- special_cat についての処理... --> <?php endwhile; ?>
// ここで通常のプログラミングに戻る <?php $wp_query = $temp_query; ?>
注: PHP 4では "=" オペレーターでオブジェクトが参照されていましたが、PHP 5では "= clone" オペレーターで参照されます。例 2をPHP 5で動くようにするためには、以下のコードを用いる必要があります。
// ここで脇にそれる <?php $temp_query = clone $wp_query; ?> <!-- Do stuff... --> <?php query_posts( 'category_name=special_cat&posts_per_page=10' ); ?> <?php while ( have_posts() ) : the_post(); ?> <!-- special_cat についての処理... --> <?php endwhile; ?> <?php endif; ?> // ここで通常のプログラミングに戻る <?php $wp_query = clone $temp_query; ?>
しかし、2つ目の例はWordPress 2.1では使うことはできません。
複数ループの使い方
複数ループをどのように使うかを理解するための最適な方法は、その使用例を見せることです。最も一般的な複数ループの使い方は、2つ以上の投稿リストを1つのページに表示させることです。最新の投稿だけでなく、特定のカテゴリーからの投稿も注目させたい時にしばしば使われ方法です。
フォーマットとCSSに関する全ての問題は一旦脇において、2つの投稿リストを表示させたい場合を考えましょう。1つは最新の投稿 (標準的な最新10件の投稿)、もう1つは 'featured' カテゴリーからの最新投稿を1件だけを表示させるとします。'featured' カテゴリーからの投稿を一番最初に表示し、次に投稿のリスト (標準のもの) という順番で考えます。重要なのは、両方のカテゴリーに表示させる投稿はあってはならないというところです。
ステップ 1. ‘featured’ カテゴリーから1つの投稿だけを取得する。
<?php $my_query = new WP_Query( 'category_name=featured&posts_per_page=1' ); while ( $my_query->have_posts() ) : $my_query->the_post(); $do_not_duplicate = $post->ID; ?> <!-- なにかの処理... --> <?php endwhile; ?>
日本語ではこのコードはこのように読めます。
- $my_query は 'featured' と名付けられたカテゴリーからの全ての投稿を検索します。ただし、最新の投稿1件分だけ貰います。
- そして一覧に存在している投稿ごとそれぞれに (この場合は1件ですが)
- $do_not_duplicate という変数を、1行目で返された投稿のIDで設定します。
- なにかの処理というのは、表示される投稿に関する全てのフォーマットオプションの処理です。
- ループを終了します (この場合は1件ですが)
この時大事なのは、同じ投稿が2つのリストに出ないようにするために、 $do_not_duplicate の値が次のステップで必要とされることです。
ステップ 2. 2つ目のループで、X個の最新投稿を取得する (但し、1つを除く)
以下のコードでは、WordPress初期設定で定義されたX個の最新投稿を取得し、最初のループで保存された表示済みの投稿をセーブし、なにかの処理に従って表示させます。
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); if ( $post->ID == $do_not_duplicate ) continue;?> <!-- なにかの処理... --> <?php endwhile; endif; ?>
日本語ではこのコードはこのように読めます。
- 表示すべき投稿を取得します。
- 投稿が $do_not_duplicate と同じであった場合、何もしません (continue)。
- さもなくば、なにかの処理にて投稿を表示させます。
- ループの終端処理です。必要に応じて最初に戻るまたは処理を終了します。
この処理は、さらにキャッシュをアップデートすることで、タグとキーワードプラグインが良い働きをするように仕向けます。$do_not_duplicate 変数はすでに表示済みな投稿のIDを含むものです。
最終的な結果
これがフォーマットを含まないコードの最終的な形です。
<?php $my_query = new WP_Query( 'category_name=featured&posts_per_page=1' ); while ( $my_query->have_posts() ) : $my_query->the_post(); $do_not_duplicate = $post->ID; ?> <!-- なにかの処理... --> <?php endwhile; ?> <!-- 他の処理... --> <?php if ( have_posts() ) : while ( have_posts() ) : the_post(); if ( $post->ID == $do_not_duplicate ) continue; ?> <!-- なにかの処理... --> <?php endwhile; endif; ?>
この結果は、2つのリストが1つのページに表示されます。最初のリストは 'feature' カテゴリーからの投稿を1つだけ表示し、2つ目のリストは、最初のリストで表示されたものを除いて、WordPress初期設定で定義されたX個の最新投稿が表示されます。そのため、'feature' カテゴリーの投稿が新しいものと置き換えられると、それまで 'feature' カテゴリーから表示されていたものが、通常の投稿リストに表示されます。 (もちろん何個の投稿を表示するかの設定と投稿の頻度に依ります)。この手法は、テンプレート階層の知識を組み合わせ、異なった見た目の home.php や index.php を作るために、数多く使われてきています。本ページ末尾にある関連する情報を参照してください。
最初のカテゴリーの複数投稿に関する注意
もし、posts_per_page=2 以上の設定がある場合、コードを若干修正する必要があります。変数 $do_not_duplicate を単独の値から配列に変更する必要があります。さもなくば、最初のループが終わった時点で、$do_not_duplicate は最新投稿のIDと同じとなります。その結果、2つ目のループで重複した投稿が表示されることになります。この問題を修正するためには、
<?php $my_query = new WP_Query( 'category_name=featured&posts_per_page=1' ); while ( $my_query->have_posts() ) : $my_query->the_post(); $do_not_duplicate = $post->ID; ?>
を
<?php $my_query = new WP_Query( 'category_name=featured&posts_per_page=2' ); while ( $my_query->have_posts() ) : $my_query->the_post(); $do_not_duplicate[] = $post->ID; ?>
と置き換えてください。
この時、"posts_per_page" はどんな数字にもなり得ます。 $do_not_duplicate を配列に置き換えた後は、
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); if ( $post->ID == $do_not_duplicate ) continue; ?>
を
<?php if ( have_posts() ) : while ( have_posts() ) : the_post(); if ( in_array( $post->ID, $do_not_duplicate ) ) continue; ?>
としてください。これで、"posts_per_page" がどんな値でも (この場合は2ですが) このパターンを続けることが出来ます。
一方で、$do_not_duplicate の配列をまるごと $wp_query に渡し、条件を満たしたもののみ返すというやり方もできます。
<?php query_posts( array( 'post__not_in' => $do_not_duplicate ) ); if ( have_posts() ) : while ( have_posts() ) : the_post(); ?>
文字列の代わりに、 post__not_in オプションを用いることで、クエリーのパラメーターが配列となります。
入れ子のループ
入れ子のループとは、最初のループが終わる前に2つ目のループを実行することを意味します。これはショートコードを用いた投稿リストを作成する時に役立ちます。例えば:
$my_query = new WP_Query( 'cat=3' ); if ( $my_query->have_posts() ) { while ( $my_query->have_posts() ) { $my_query->the_post(); the_content(); } } wp_reset_postdata();
入れ子のループが終わった後、いくつかのグローバル変数が正しい値を再度取得することが出来るようにするために、メインループのデータをリセットする必要があります。
ソース
複数ループの章で取り上げられた内容は、Ryan Boren と Alex King's が 開発者メーリングリストで行った、ループに関する議論の組み合わせです。 入れ子ループの例は、メーリングリストでの別の議論から着想し、 Nicolas Kuttler が投稿したものです。
リソース
英語
- WordPress WP_Query Generator
- Getting Into The Loop - (slides) an introduction to how plugins and themes can modify the Loop
- WordPress custom loop
日本語
- WordPress テーマの詳説:第四章 (tekapo.com)
- WordPressループの使用方法 (tech.ludicmind.net)
- ループ : WordPress (FindxFine)
関連
WordPressループについて、またループ内で使える様々なテンプレートタグについて更に詳しくは、下記のリソースを参照してください。
ループについての詳細
英語
- Matt Read Loop Article
- MaxPower Dynamic Sticky Tutorial
- IfElse Query_post Redux
- The Loop and Adding Content Outside of It
- oBeattie : The Loop
- 1001 WordPression Loops
記事
- 記事: ループ - WordPress ループ内でのクエリの使い方に関する基本的な概要。
- 記事: クエリ概要 - どのクエリが WordPress を生成するのかが決定される方法についての説明。
- 記事: フックを使ったクエリのカスタマイズ
- 記事: カスタムセレクトクエリを使った投稿の表示 /en
- 記事: 高度なタクソノミークエリの生成 /en
- 記事: オフセットとページネーションを活用したカスタムクエリ /en
コード・ドキュメンテーション
- クラス: WP_Query - WP_Query クラスの詳細な全容
- クラス: WP_Comment_Query - コメント関連のクエリのためのクラス
- クラス: WP_User_Query - ユーザー関連のクエリのためのクラス
- オブジェクト: $wpdb - $wpdb オブジェクトの使い方全容
- 関数: set_query_var()
- 関数: get_query_var()
- 関数: query_posts() - 追加のカスタムクエリを作成
- 関数: get_post() - 項目の ID を取得しデータベース内にあるその投稿のレコードを返す
- 関数: get_posts() - 投稿の配列を返すことに特化した関数
- 関数: get_pages() - ページの配列を返すことに特化した関数
- 関数: have posts() - クエリが投稿を返すか否かを判断する条件関数
- 関数: the_post() - クエリ後に自動的にループを設定する
- 関数: rewind_posts() - 現状のループをリセットする
- 関数: setup_postdata() - ループ内で個別の結果を得るためのクエリデータを設定する
- 関数: wp_reset_postdata() - 直前のクエリを復元する (通常はループ内の別のループの後に用いられる)
- 関数: wp_reset_query()
- 関数: is_main_query() - 変更されるクエリがメインのクエリであることを確認する
- アクションフック: pre_get_posts - WordPressクエリが実行される前に変更する
- アクションフック: the_post - post クエリの後で post オブジェクトを変更する
- フィルターフック: found_posts - WP_Query オブジェクトの found_posts 値を変更する
最新英語版: WordPress Codex » The Loop (最新版との差分)