複数ループ
index.php などで
while( have_posts() ) : the_post();
/* do stuff */
endwhile;
のように WordPress ループ(メインループ)の処理をいきなり書けるのは、テンプレートファイルが実行される時点ですでに「サイトにアクセスされた URL からクエリ変数を抽出し、それらの条件に一致する記事オブジェクトを取得している」からです。
そして、例えばホーム(トップページ)やサイドバーなどで、メインループとは別の条件で記事を取得(,出力)するループ処理がある場合、それらを複数ループ(Multiple Loops)と言います。
ここでよくある不具合が
- サイドバーやフッターで条件分岐タグが正しく判定されない。
- ページネーションが正しくできない。
- $post を使う関数の結果が正しくない。
などなど。。。エラーにならないけど値が変わっている、動作がおかしい、WordPress は不安定?と、謎現象として長らく恐れられています。
グローバル変数の扱いを理解しよう
複数ループは不安定、危険などと言われる原因は、ループ&クエリーでのグローバル変数の扱いが十分に理解されていないことにあります。
以下は、ループ&クエリーに関係のある主なグローバル変数です。
$wp_the_query ..... WP_Query オブジェクト。URL のクエリーバックアップ用。
$wp_query ......... WP_Query オブジェクト。カレントのクエリー。
$posts ............ クエリーを実行して取得した記事オブジェクトの配列。
$post ............. カレントの記事オブジェクト。
そして、これらのグローバル変数を上書きする関数は以下の通り。
query_posts() ........................ $wp_query を上書き。
the_post(), WP_Query::the_post() ..... $post を上書き。
テンプレートファイル内では $wp_query, $posts, $post
はグローバル変数として宣言されているので、これらも何らかの値を代入すれば上書きされます。
さらに、上書きされたグローバル変数を復元する関数があります。
wp_reset_query() ........ $wp_query を $wp_the_query, $post を $wp_query->post で復元。
wp_reset_postdata() ..... $post を $wp_query->post で復元。
例えば is_category()
などの条件分岐タグや posts_nav_link()
などはグローバル変数の $wp_query
を元にしています。「メインループの状態が欲しいのにグローバル変数を上書きしたまま復元していない」「グローバル変数を復元した後に複数ループ側の状態を取得している」といった現在のグローバル変数の状態を正しく認識していないことが、複数ループにまつわる問題の原因です。
正しい書き方
以下は、query_posts(), get_posts(), WP_Query()
各々についての、現在のところ副作用が無く不具合を作り込み難い書き方です。なお、いずれの関数/クラスも内部では最終的に WP_Query::get_posts()
を実行しています。
query_posts()
$arg = array(
/* 省略 */
'paged' => get_query_var( 'paged' ), // ページネーションするなら必須
);
query_posts( $args );
while ( have_posts() ) :
the_post();
/* do stuff
the_title(), the_permalink() 等使用可
*/
endwhile;
/* 必要なら endwhile 〜 wp_reset_query() の間でページネーション */
wp_reset_query();
グローバル変数の $wp_query, $post
を使うテンプレートタグ/関数がそのまま利用できるので便利です。ちなみに
query_posts();
/* */
query_posts();
/* */
wp_reset_query();
のように複数の query_posts()
の最後に wp_reset_query()
しても問題ありませんが、気持ち悪いのでやめましょう。
get_posts()
$arg = array(
/* 省略 */
);
$my_posts = get_posts( $args ); // $posts = とは書かない
global $post; // テンプレートファイル内なら書かなくても良い
foreach ( $my_posts as $post ) :
setup_postdata( $post );
/* do stuff
the_title(), the_permalink() 等使用可
*/
endforeach;
wp_reset_postdata();
$posts =
と書かないのは、$posts
を復元する関数が用意されていないからです。他で使用されるフシが無く、
$GLOBALS['posts'] = & $wp_query->posts;
で復元すれば良さそうですが、余計なことはしない方が得策です。また、グローバル変数の $post
を使用せず $my_post
のようにも書けますが、the_title()
などのテンプレートタグはグローバル $post
専用のため、echo get_the_title( $my_post )
のように書く必要があります。
ついでに get_posts(), get_children()
は query_posts(), WP_Query()
の WordPress ループの内側で入れ子ループとしても利用できます。そういえばメインループから見るとここで挙げている3つの関数/クラスはどれも二重ループの内側になりますね。
ただし query_posts(), get_posts(), WP_Query()
の同じ関数/クラスでループを入れ子にするのはやめましょう。その必要はまずありませんし、労多くして得るもの少なしです。
WP_Query()
$arg = array(
/* 省略 */
);
$my_query = new WP_Query( $args );
while ( $my_query->have_posts() ) :
$my_query->the_post(); // global の $post を上書きする
/* do stuff
the_title(), the_permalink() 等使用可
*/
endwhile;
wp_reset_postdata();
$my_query->the_post()
がグローバル変数の $post
を上書きすることに注意しましょう。これよりは query_posts()
の方が素直で使い易いかな、と思います。
クエリーの改変
もしメインループとは別のループ、ではなく、メインループそのものを変更したいだけなら pre_get_posts
アクションを使うと良いでしょう。WordPress はメインループ用に URL からクエリ変数を抽出し、条件に一致する記事オブジェクトを取得していますが、これにも WP_Query::get_posts()
が使用されています。
pre_get_posts
アクションは、WP_Query::get_posts()
の内部でオリジナルのクエリーからクエリ変数や条件分岐タグ用の変数を設定した後かつ記事オブジェクトを取得する前に実行されるため、元のクエリーを判定して新たなクエリーを与えることができます。複数ループにする必要がなくメインループだけで完結するので、クエリ実行のコストが節約できますね。
例)カテゴリーアーカイブは1ページ20件で日付の昇順にする場合
テーマフォルダの functions.php に以下を追加します。
add_action( 'pre_get_posts', 'my_pre_get_posts' );
function my_pre_get_posts( $query ) {
if ( ! is_admin() && is_category() ) {
$query->set( 'posts_per_page', 20 );
$query->set( 'order', 'ASC' );
}
}
! is_admin()
の条件が無いと、管理画面でも有効になります。
CONCLUSION
謎めいた現象に見えても、プログラムは書いた通りにしか動いていないのでした。仕組みがわかりさえすればとてもシンプルなことでしたね。
明日は @shinichiN さんです!お楽しみに!
動作確認バージョン
- WordPress 3.2.1
【ブログ】WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/84EXs67i #WordPressAdvent2011 @wordpress_fan #wordpressjp
「グローバル変数の上書き」が平然と行われている状況(さらにplugin開発者が上書きすることを見越して「上書きされたグローバル変数を復元する関数」が用意されてる状況)って果たしてどうなの? と正直思わんでもない
WordPress Advent Calendar http://t.co/7DTa3gCt 12日目! WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/TqboZLen via @kzxtreme さん。明日は @shinichiN さん!
WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/5nlQtgBX via @kzxtreme
【ブログ】WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/84EXs67i #WordPressAdvent2011 @wordpress_fan #wordpressjp
WordPress Advent Calendar 12日目 – WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/9r3VWJG6 #wordpressjp
WordPress Advent Calendar 12日目 – WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/9r3VWJG6 #wordpressjp
【ブログ】WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/84EXs67i #WordPressAdvent2011 @wordpress_fan #wordpressjp
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/kyDCTzdr
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/kyDCTzdr
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/6b7DmvLT
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/6b7DmvLT
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme 12月25日まで毎日ブログをつないでいく WordPress Advent Calendar、12日目担当 福山カズヒデ (@kzxtrem… http://t.co/7xnJ749P
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/XsDgM4MM
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/h96qM4lE
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/xG0UrjWH #wordpress
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/kyDCTzdr
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme: 12月25日まで毎日ブログをつないでいく WordPress Advent Calendar、12日目担当 福山カズヒデ (@kzxt… http://t.co/zkXNBKaQ
[*wordpress] / “WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/fqc7QBXN
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/pcvKwaUV #PHP
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/XsDgM4MM
WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/oyZTP1iY via @kzxtreme
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/RCAEYF22
最近 get_posts() が怪しいとか思ってたんだけど、この記事に書いてあるとおりにして試してみる。 ”WordPress ループ&クエリーのモヤモヤを解消しよう!” http://t.co/vchiJ8KX @kzxtremeさんから
ラブ&ピースなクリスマスを迎える為のTips> WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/32dEKoX2
ラブ&ピースなクリスマスを迎える為のTips>
queryに対しての挙動など 複数ループするときなどに確認する
ラブ&ピースなクリスマスを迎える為のTips> WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/32dEKoX2
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/CYOZs2qe
WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/WdTOtGJM @kzxtremeさんから
WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/zBxy2IHw
「 query_posts() は危険」デマに見事に踊らされていたんだぜ!\(^o^)/
「 query_posts() は危険」デマに見事に踊らされていたんだぜ!\(^o^)/ http://t.co/hsLTFu3s
WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/PI5iWsvj
WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/nscXlqO5 @kzxtremeさんから WP扱うなら読んどくべき
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/NLec3YDS
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/4WC7I97h
http://t.co/pXIBiCXp%E3%81%82%E3%81%A8%E3%81%A7%E3%82%88%E3%82%80
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/mLAjZGQP
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/U9HlfnVP
@HissyNC いえいえ。個人にこの記事と @jim0912 さんの記事 http://t.co/2m1HJxj4 と @kzxtreme さんの記事 http://t.co/RoOCStYD でクエリ周りは何とかなりそうです。
extreme wp_reset_postdata() でググって出てきましたm(__)m。 http://t.co/g0dNQ3Mn RT @jim0912: @shinichiN ループの中でループするときに使える。カズさんがブログ書いてたよ。
wordpress ループ 必読
query モヤモヤ で検索! → 「WordPress ループ&クエリーのモヤモヤを解消しよう!」 http://t.co/g0dNQ3Mn #wordpressjp
query モヤモヤ で検索! → 「WordPress ループ&クエリーのモヤモヤを解消しよう!」 http://t.co/g0dNQ3Mn #wordpressjp
query モヤモヤ で検索! → 「WordPress ループ&クエリーのモヤモヤを解消しよう!」 http://t.co/g0dNQ3Mn #wordpressjp
WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/hChwB992 #wordpress #wp
[wordpress] / “WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/Md1eoBPj
かずさん、こんにちは!いつもありがとうございます。
正しい書き方>query_posts のところなのですが、3行目の
‘paged’ => get_query_var( ‘paged’ );
の最後のコロンがエラーになるようです。
いつも「query_posts モヤモヤ」でググってここにやってくる私の為に直してくださいと言っているわけではありませんw
しんいちさん、こんにちは!
そこはコンマでしたね、ぎゃふん(><)
ご指摘ありがとうございました、こっそり直しておきました◎
[自動はてブ棚卸し] WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme http://t.co/C9my4GNH
“WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme” http://t.co/vloTfQH3
結局どれ使えばいいんだうぼあー。
WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/7Pjl3VfP @kzxtremeさんから
結局どれ使えばいいんだうぼあー。 WordPress ループ&クエリーのモヤモヤを解消しよう! http://t.co/7Pjl3VfP @kzxtremeさんから
wordpressループ(WP_Query / query_posts / get_posts)処理に困ったときに読む!めっちゃわかりやすい
query_posts get_posts WP_QUERY
ループ用のパラメータを排除して、グローバル変数に格納するWordPressの方針がすごく嫌なんだよね…。
前も見た記事だが今の方が意味わかった。 $wp_queryとWP_Queryは名前が似てるがWP_Queryは$postと紐づいてるのか。 WordPress ループ&クエリーのモヤモヤを解消しよう! | wpxtreme