WordPressでの記事表示とかメインクエリーとかもう一度かんがえてみた

WordPressが記事を表示する仕組みだとか、メインループとサブループとか、WP_Queryやpre_get_postsとか、なんども調べたり読んだり、話を聞いたりしてきたけど、わかっているようでわかってない、自分の中にまだしっかり落とし込めてない感じがするので、やってみたメモ。


前に、なんでループ無しでthe_title()とthe_content() を書くとthe_title()の結果は表示されけど、the_content() の結果は表示されないの?という疑問からWordPressの表示フローとLoopとthe_title()〜いろいろ考えたメモ〜でループのこととか考えてみていたんですが、またちょっと違う角度から。

メインクエリーとメインループとindex.php

WordPressのテーマってstyle.cssとindex.phpさえあればトップページでも、アーカイブでも、固定ページでも、投稿の個別ページでもちゃんと表示できますよね。

ここからいつもはテンプレート階層の話になるんだけど、メインクエリーとからめて、index.phpだけでどうしてなんでも表示できるのかっていう部分もいっしょにみてみました。

$wp_queryを表示させてみる

つくってみたのは、style.cssとindex.phpだけのテーマ。index.phpには

<?php var_dump($wp_query); ?>

これだけを記述。このテーマを有効化してアクセスしてみると。。

20131124-01

これはほんの一部で、まだまだ続くよどこまでも…という感じで見ることができます。
トップページなら最新記事10件、固定ページならそのページ、と言った感じでちゃんとURLによって取って来ているものが変わっているのもわかります。

余談ですが、表示されてる中に

["is_home"]=>
  bool(true)

とかがあって、条件分岐タグ、なるほどーなんて思いました。はい、やっぱり実感として色々わかってないので、またちゃんと表示させてじっくり見て見てよかったです。

関数リファレンス/WP QueryWP_Queryのメソッドとプロパティのとこだって何度も読んでし、この前の記事書いた時にもやってみてるのにね..この前はループの方を気にしてたからかな。やっぱり何度でもやってみないとと、思いました。一回でもっといろいろ理解できたらいいんだろうけど。

メインクエリーかどうかみてみる

テンプレートに

<h2><?php var_dump(is_main_query()); ?></h2>

を付け加えてみます。これで今表示してるのがメインクエリーかどうかわかりますね。結果は

20131124-02

こんな感じ。trueでちゃんとメインクエリーだということがわかります。ふむふむ。

have_posts()

次にWordPressループの始まりであるhave_posts()。

<h2>記事あるかな?<?php var_dump($wp_query->have_posts()) ;?></h2>

結果は

20131124-03

ということでWordPressループの登場。

<?php if ( have_posts() ) : while ( have_posts() ) : the_post();  ?>
				
			
//表示する内容
					
				
<?php endwhile; endif; ?>

こういう仕組みがあるから、index.phpだけでメインクエリーで取得したものを表示するメインループで、それぞれのURLの条件にあった内容が表示できると言うわけですね。(という理解でいいのかな)

query_posts()

query_posts()はメインクエリーを上書きする、というのもせっかくなので確かめてみました。

<h2>メインクエリーか?<?php var_dump(is_main_query()); ?></h2>

<?php query_posts( 'posts_per_page=1' );

if ( have_posts() ) : while ( have_posts() ) : the_post();  ?>
				
<h2>query_postsの後はメインクエリーか?<?php var_dump(is_main_query()); ?></h2>	

<?php endwhile; endif; ?>

結果はこれ。メインクエリーじゃなくなっています。

20131124-04

このままじゃクエリーが変わったまんまになるから、リセットしないといけないわけですよね。

忘れちゃいけない wp_reset_query()

ということでquery_postsとループの後に

<?php wp_reset_query(); ?>

<h2>wp_reset_queryしたらメインクエリーか?<?php var_dump(is_main_query()); ?></h2>

と追記。結果は

20131124-05

当たり前と言っちゃ当たり前なんですが、ちゃんと戻ってました。

wp_reset_query()はwp-includes/query.php にあって

function wp_reset_query() {
	$GLOBALS['wp_query'] = $GLOBALS['wp_the_query'];
	wp_reset_postdata();
}

となってました。うぬ?$wp_the_query?

調べてみると WordPress 私的マニュアル is_main_query

内部ではグローバル変数の$wp_the_queryと$wp_queryを比較している。$wp_the_queryにはWordPressがリクエストされたURLに基づいて最初に検索した結果などが格納されている。

にこんな文章が。query.phpのこれかしら。

function is_main_query() {
   global $wp_the_query;
   return $wp_the_query === $this;
	}
}

これは宿題に。

pre_get_posts

次にようやくpre_get_postsについて。

pre_get_posts はデータベースへのリクエストの前、SQL文を生成する直前のアクションフック。
メインクエリーそのものを変えます。ということで、これも確かめてみます。先ほどのテーマにfunctions.phpを追加して以下を記述。

function one_post_per_page( $query ) {
	if ( is_admin() || ! $query->is_main_query() ) 
	return; //①

	if ( $query->is_home() ) {
		$query->set( 'posts_per_page', 1 );
		return;
	}
}
add_action( 'pre_get_posts', 'one_post_per_page');

pre_get_postsにフックしてトップページのとき(is_homeがtrue)表示する件数を1件にします。
①のところはis_adminで管理画面だったとき、! $query->is_main_query() でメインクエリーじゃなかった時にはそこで処理が終わるので、影響が無いようになっています。

結果はこんな感じ。

20131124-06

$wp_queryで表示していたものもあんなにたくさんあったのに1件分になりました。そして、メインクエリーはtrueなので、メインクエリーそのものの内容が変わっていることがわかります。

同じトップページの表示を1件に、というのでもquery_posts()を使うのとpre_get_postsフックを使うのとでは違うんだよ、ということも目で見て、再確認できました。

WP_Queryでのサブループについてもやってみたいけど、力尽きたのでとりあえずここまで。
ほんとに、メモ書きみたいで、誰の役に立つんだ?という記事になっちゃいました。。。

最近WordPressの仕組みもっとちゃんとわかってないとできないな、解決しないな、と思うことに出会うことが多くなってきたり、ちゃんと説明しようと思うとあれ?ってなったりすることが多かったりするので、いろんな角度から考えて、少しずつでも知識や理解を深めて、問題解決の基礎力をつけていきたいです。あと実践も..

ではでは

WordPressでの記事表示とかメインクエリーとかもう一度かんがえてみた」への3件のフィードバック

  1. hissync

    WP_Query->is_main_query() は、自分自身(WP_Queryのインスタンス)と、グローバル変数 $wp_the_query を比較しています。
    グローバル変数 $wp_the_query には wp-settings.php のなかで WP_Query クラスのインスタンスが代入されています。

    $wp_the_query = new WP_Query();

    その直後で $wp_the_query は $wp_query に代入されています。

    $wp_query = $wp_the_query;

    ここで、この代入が複製ではなく参照であることに注意してください。なぜなら、PHPではオブジェクトの代入は複製ではなく参照となる仕様だからです。参照とは、ひとことで言えば同じ存在、ということです。

    これ以降、WordPressのコード内でグローバル変数 $wp_the_query は使用されず $wp_query に対して操作が行なわれますが、それは同じ存在の別名である $wp_the_query に対しても反映されます。

    ところが、query_postsなどを使いグローバル変数 $wp_query に対して新しい WP_Query のインスタンスを代入( $wp_query = new WP_Query; )した場合はどうなるでしょう。この場合、$wp_query と $wp_the_query の参照は切り離され、別のインスタンス(オブジェクト)になります。従って、is_main_query()メソッドの返り値はfalseとなります。
    is_main_query()内の比較演算子が === であることに注意してください。 == だと別のインスタンスであっても同等であれば true になります。=== だと同等のクエリーであっても、参照が切れた時点で false となります。

    返信
  2. jim912

    WP_Query クラスの is_main_query メソッドなのか、一般関数の is_main_query なのか、ちゃんと分けて考えるべし。

    返信

コメントをどうぞ♪