ワードプレスのテーマを見ていると必ずと言っていいほど出てくるのが、have_posts 関数とthe_post 関数を用いたループ文です。
<?php
if ( have_posts() ) {
while ( have_posts() ) {
the_post();
}
}
テーマ開発入門者にとっては最初につまづく部分だと思います。実際何をやっているのか分からないけどおまじないのように書いている人も多いのではないでしょうか?正直プログラミングをやったことのある人でも分かりにくいと思います。
このページでは、このループ文の中で何が行われているかについて分かりやすく解説していきたいと思います。
Contents
ループ処理の前に行われる準備
この「have_posts・the_postループ」はテーマを構成するファイルの「page.php」や「archive.php」で実行されることが多いと思いますが、実はこのテーマが動き出す前にページ表示を行うための準備が行われます。まずこの準備がどのようなものであるかを知っておくと「have_posts・the_postループ」の動きが理解しやすくなると思います。
何の準備が行われるのか?
この準備では、要求された URL(クエリ)で表示すべき投稿データの準備が行われます。
例えば URL がカテゴリ表示を行うものであれば「表示すべき投稿」はそのカテゴリに設定されている複数の投稿になります。投稿単位で表示を行うものであれば、「表示すべき投稿」はその投稿のみ(1つだけ)になります。
この表示すべき投稿の「投稿数や各投稿のタイトル・記事のコンテンツ」などのデータを収集し、have_posts 関数や the_posts 関数が動作するための準備が行われます。
スポンサーリンク
どのようにして準備が行われるのか?
ではこの準備はどのようにして行われるのでしょうか?この点ついても解説しておきたいと思います。
ワードプレスでは、テーマが動作する前に wp-blog-header.php という PHP ファイルが実行され、その中で wp 関数が実行されます。
if ( ! isset( $wp_did_header ) ) {
$wp_did_header = true;
// Load the WordPress library.
require_once( dirname( __FILE__ ) . '/wp-load.php' );
// Set up the WordPress query.
wp();
// Load the theme template.
require_once( ABSPATH . WPINC . '/template-loader.php' );
}
さらに wp 関数の中で main 関数が実行されます。
function wp( $query_vars = '' ) {
global $wp, $wp_query, $wp_the_query;
$wp->main( $query_vars );
if ( ! isset( $wp_the_query ) ) {
$wp_the_query = $wp_query;
}
}
その中で表示を要求されたURL(クエリ)に対応する投稿のデータが $wp_query 変数に格納されます。実際に main 関数実行直後の $wp_query の中身を見ると下のようになっています(黄色の部分は main 関数実行により変更された部分になります)。
例えば $wp_query->post_count は要求された URL で表示すべき投稿が格納されています。上の図だと “4” が格納されていますので、要求された URL で表示すべき投稿が4つあるということになります。
また $wp_query->posts に表示すべき投稿4つ全てのタイトルや記事のコンテンツが格納されています。$wp_query->posts は配列になっており、要素0から新しい順に記事のデータが格納されています。
他にも様々なデータが格納されています。
このように、テーマが動作する前にページ表示するために必要なデータが準備されます。図で表すと下のような感じになりますね。
テーマの中では、事前に収集されたこの投稿のデータを用いて、ページ表示のための処理が実行されます。このデータを利用する関数の一つが have_posts 関数であり、もう一つが the_post 関数なのです(他にもたくさんの関数が用意されています)。
ループ処理の動き
それではここまでの準備の内容も踏まえて「have_posts・the_postループ」の動きについて解説していきます。まずは各関数について説明します。
have_posts 関数
要求されているURL(クエリ)に対して表示すべき次の投稿が存在するかどうかを判断する関数です。存在する場合は「true」を返し、存在しない場合は「false」を返却します。
もう少し詳細に説明します。まず $wp_query に格納されるデータの画像を再掲します。
また、次の図は $wp_query に格納されるデータの他の部分です。
$wp_query->current_comment の値に注目してください。”-1″ になってますね。have_posts 関数では、「この $wp_query->current_comment に +1 した値が、投稿数である $wp_query->post_count の値よりも小さいかどうか」を判断します。小さい場合は、まだ取得していない投稿があるということなので true を返します。小さくない場合は false を返します。
スポンサーリンク
the_post 関数
次の投稿のデータを取得する関数です。
こちらももう少し詳細に解説していきます。再度 $wp_query に格納されるデータの画像を再掲します。
$wp_query->current_post には今現在テーマが取得している投稿データの要素番号が格納されています。$wp_query->current_post が “-1” というのは、まだ何も投稿データを取得していない状態です。the_post 関数を一度も実行していないとこの状態になります。
the_post 関数を実行すると、この $wp_query->current_post が +1 され、その要素番号の $wp_query->posts 配列 のデータが取得されます。取得したデータは $wp_query->post に格納されます。
前述の通り、$wp_query->posts 配列 には要素0から投稿が新しい順に投稿データが格納されています。ですので、the_post 関数を実行するたびに古い投稿のデータを取得することができます。
したがって、have_posts を条件としたループ文の中で the_post 関数を実行することで、要求されたページのURLに対応する投稿データを順々に取得することが可能です。the_post 関数により投稿データを取得した状態で the_title 関数や the_content 関数を実行すれば、取得中のデータのタイトルやコンテンツを出力することができます。また、もう他に対応する投稿データがない場合は have_posts が false を返してループを抜け、次の処理に移ることが可能です。
実例を用いた have_posts・the_post ループの動き
ここまでの解説を読んでくださった方なら、「have_posts・the_post ループ」の動きはすんなり理解できるのではないかと思います。
下の図のように「要求された URL が caregoryX のカテゴリページで、このカテゴリには投稿Aと投稿Bの2つの投稿がある」場合を想定し、この時に have_posts・the_post ループが実際にどのように動作するのかを確認していきたいと思います。
カテゴリー表示が要求されていますので category.php がある場合は category.php が動作します。
この category.php の一部に下記のプログラムが記述されている場合に、このプログラムがどのように動くのかを解説します(the_title と the_content は have_posts・the_post ループには直接関係ないですが、説明が分かりやすくなると思いますので追加しています)。
<?php
while ( have_posts() ) {
the_post();
the_title();
the_content();
}
?>
このプログラム実行前の、$wp_query->current_post は “-1” で $wp_query->post_count は “2” となっています。また $wp_query->post には初期状態では $wp_query->posts[0] のものが格納されています。
プログラムの while( have_posts() ) は、have_posts() が true を返却する限り、そのあとの “{ }” の中を繰り返し実行するループ制御文になります。
最初の1回目の have_posts 関数を実行すると、$wp_query->cunnrent_post + 1 < $wp_query->post_count が成立しますので true が返却されます。
have_posts 関数が true を返却するため、ループの中の処理が実行されます。最初に実行されるのは the_post 関数です。この関数が実行されることで $wp_query->current_post が “0” となり、$wp_query->posts[0] のデータが取得され $wp_query->post に格納されます。
続いて実行する the_title 関数と the_content 関数ですが、これは現在取得している投稿データ(つまり $wp_query->post に格納されているデータ)のタイトルと記事のコンテンツを取得して HTML として出力する関数になっています。
これでループ内の一通りの処理が完了しましたので、ループの最初に戻ってまた have_posts 関数が実行されます。$wp_query->current_post が “0” で $wp_query->post_count は “2” なので、$wp_query->cunnrent_post + 1 < $wp_query->post_count が成立し、 have_posts 関数は true を返却します。
have_posts 関数が true を返却するため、再びループの中の処理が実行されます。the_post 関数が実行され、 $wp_query->current_post が “1” となり、$wp_query->posts[1] のデータが取得され $wp_query->post に格納されます。
さらに the_title 関数と the_content 関数により $wp_query->post に格納されているデータのタイトルと記事のコンテンツを取得して HTML として出力されます。
ループ内の処理が終わったので、またループの最初に戻ります。今度は $wp_query->current_post が “1” で $wp_query->post_count は “2” なので、$wp_query->cunnrent_post + 1 < $wp_query->post_count が成立しません(”=” の関係になるので不成立)。
ですので have_posts 関数は false を返却し、これによりループが終了し、上記のプログラムが終了します。
以上が実例を用いた have_posts・the_post ループの動きの解説になります。なんとなくこのループがどのように動くかを理解していた方も、実際にどのように動くかが分かるとテーマ開発がさらにやりやすくなると思います。
他の例での have_posts・the_post ループ の動き
先ほどは複数の投稿があるカテゴリーを表示することを想定して解説しましたが、他のケースだとどのように動作するのかを解説していきます。
投稿ページの表示
投稿ページを表示する URL で表示すべき投稿は1つなので、 have_posts・the_post ループの while 文の中のループ処理は1度だけ実行されることになります。
投稿のないカテゴリの表示
投稿がないカテゴリの場合は、最初に実行される have_posts が false を返却することになりますので、ループの中の処理は一度も行われません。ですので、ループの中で the_title 関数や the_content 関数を実行している場合は、何も表示されないことになります。
投稿がない時にエラー表示等を行い場合は、下のプログラムのように have_posts・the_post ループ の前に if ( have_posts() ) を用いて条件分岐すると良いです(この形式のループ文もよく見かけますね)。
<?php
if ( have_posts() ) {
while ( have_posts() ) {
the_post();
the_title();
the_content();
}
} else {
printf( "表示する投稿がありません!\n" );
}
?>
投稿がない場合は「表示する投稿がありません!」と表示されるようになります。
ループ終了後の投稿データの出力
ループ終了後に the_title 関数や the_content 関数を実行すると、最後に the_post 関数で取得した投稿データのタイトルやコンテンツが表示されることになります。
ループ終了後に再度ループを実行
have_posts・the_post ループ を抜けた後に再度 have_posts・the_post ループ を実行した場合、一回目のループと同様に動作します。つまり、表示すべき投稿を最初から最後まで順々に取得することができます。
ループを抜ける時に実行される have_posts 関数の中で $wp_query->current_post の値が “-1” に初期化されるため、2回目の have_posts・the_post ループ も最初の投稿から順に取得されていくことになります。
the_post 実行前の投稿データの出力
the_post 実行前に the_title 関数や the_content 関数等を実行して投稿データを出力しようとするとどうなるでしょうか?どうやらコレは関数によって動作が異なるようです。the_title 関数はタイトルを出力してくれますが、the_content 関数は何も出力してくれません….。関数の作りが違うので動きも異なるようです。
もしかしたらワードプレスの更新によりこの辺りの動きは変わるかもしれませんが、いずれにせよ the_title 関数や the_content 関数で投稿データを出力する場合は、事前に the_post 関数により明示的に投稿データを取得してから実行する方が良いと思います。
スポンサーリンク
まとめ
このページでは have_posts・the_post ループについて解説しました。まずこのループが行われる前にクエリに対応した投稿のデータが収集され、have_posts・the_post のループでそれを利用して収集された投稿データを順々に取得しながら処理を行うことが可能になります。
ちなみにこのページは Eclipse でデバッガを用いて動きを確認しながら作成しました。Eclipse でデバッガーを使用するとプログラムを止めながら動きを確認できるので便利です。興味がある方は下のページで Eclipse のインストールの仕方とデバッガーの有効化の仕方を解説していますので参考にしてください。
ワードプレスのテーマ開発環境にEclipseを導入 Eclipse にデバッガー(Xdebug)を導入