こんにちは!コーダーのゆうしです。
WordPressでWP_Queryを使ってサブループを書いたあと「なぜか他の場所で the_title() や the_permalink() が正しく出力されない」という経験はありませんか?原因のほとんどは wp_reset_postdata() の書き忘れです。この記事では wp_reset_postdata() が必要な理由と正しい使い方を解説します。
そもそもメインループとサブループとは?
メインループ
WordPressが自動で生成するクエリをもとにしたループです。have_posts() と the_post() を使った以下の構文がメインループです。
if ( have_posts() ) :
while ( have_posts() ) : the_post();
// 投稿の内容を出力
endwhile;
endif;メインループは現在のURLに応じたクエリを自動で実行します。
サブループ
WP_Queryを使って独自のクエリを実行するループです。メインループとは別に追加で投稿を取得したいときに使います。
$args = [
'post_type' => 'news',
'posts_per_page' => 3,
];
$my_query = new WP_Query( $args );
if ( $my_query->have_posts() ) :
while ( $my_query->have_posts() ) : $my_query->the_post();
// 投稿の内容を出力
endwhile;
endif;なぜメインループが壊れるのか?
サブループで $my_query->the_post() を呼び出すと、WordPressのグローバル変数 $post がサブループの投稿で上書きされます。
この状態のままサブループが終わると $post がサブループの最後の投稿を指したままになります。その後にメインループの the_title() や the_permalink() などを呼び出しても、サブループの最後の投稿の情報が返ってしまいます。
// サブループを実行
$my_query = new WP_Query( $args );
while ( $my_query->have_posts() ) : $my_query->the_post();
echo get_the_title(); // サブループの投稿タイトルが出力される
endwhile;
// wp_reset_postdata()を書き忘れたまま...
// メインループのつもりで書いているが...
echo get_the_title(); // サブループの最後の投稿タイトルが出力されてしまう!wp_reset_postdata()の役割
wp_reset_postdata() はサブループで上書きされた $post をメインループの投稿に戻す関数です。サブループが終わった後に呼び出すことで、その後の the_title() などが正しくメインループの投稿を参照できるようになります。
$my_query = new WP_Query( $args );
if ( $my_query->have_posts() ) :
while ( $my_query->have_posts() ) : $my_query->the_post();
echo get_the_title();
endwhile;
endif;
wp_reset_postdata(); // ← これで$postをメインループに戻す正しい書き方
基本パターン
<?php
$args = [
'post_type' => 'news',
'posts_per_page' => 3,
'orderby' => 'date',
'order' => 'DESC',
];
$my_query = new WP_Query( $args );
?>
<?php if ( $my_query->have_posts() ) : ?>
<?php while ( $my_query->have_posts() ) : $my_query->the_post(); ?>
<article>
<h2><?php the_title(); ?></h2>
<a href="<?php the_permalink(); ?>">続きを読む</a>
</article>
<?php endwhile; ?>
<?php endif; ?>
<?php wp_reset_postdata(); // ← ループの外・endifの後に書く ?>wp_reset_postdata() はループの外・endif の後に書くのが正しい位置です。
サブループが複数ある場合
サブループが複数ある場合はそれぞれのループの後に書いてください。
// 1つ目のサブループ
$query1 = new WP_Query( $args1 );
while ( $query1->have_posts() ) : $query1->the_post();
// 出力
endwhile;
wp_reset_postdata(); // 1つ目のループ後にリセット
// 2つ目のサブループ
$query2 = new WP_Query( $args2 );
while ( $query2->have_posts() ) : $query2->the_post();
// 出力
endwhile;
wp_reset_postdata(); // 2つ目のループ後にリセットwp_reset_query()との違い
wp_reset_postdata() と似た関数に wp_reset_query() があります。混同しやすいので整理しておきます。
| 関数 | 用途 |
|---|---|
| wp_reset_postdata() | WP_Queryのサブループ後に使う |
| wp_reset_query() | query_posts() を使った後に使う |
query_posts() はメインクエリ自体を書き換える関数でパフォーマンス的に推奨されていません。基本的には WP_Query を使って wp_reset_postdata() で戻すのが正しいやり方です。
get_posts()を使う場合は不要
get_posts() を使ったループでは the_post() を使わないため wp_reset_postdata() は不要です。
$posts = get_posts([
'post_type' => 'news',
'posts_per_page' => 3,
]);
foreach ( $posts as $post ) :
setup_postdata( $post ); // これを使う場合はwp_reset_postdata()が必要
echo get_the_title();
endforeach;
wp_reset_postdata(); // setup_postdata()を使った場合は必要setup_postdata() を使った場合は wp_reset_postdata() が必要ですが、$post->post_title のように直接プロパティにアクセスする場合は不要です。
躓きやすいポイント
サブループの後のメインループでタイトルが違う
サブループの後に the_title() が意図しない投稿のタイトルを返している場合は wp_reset_postdata() の書き忘れが原因のほとんどです。サブループの endwhile や endif の直後に追加してみてください。
wp_reset_postdata()をループの中に書いてしまう
// NG:ループの中に書いてしまっている
while ( $my_query->have_posts() ) : $my_query->the_post();
echo get_the_title();
wp_reset_postdata(); // ループ中に書くと毎回リセットされてしまう
endwhile;
// OK:ループの外に書く
while ( $my_query->have_posts() ) : $my_query->the_post();
echo get_the_title();
endwhile;
wp_reset_postdata(); // ループが終わってから呼び出すループの中に書いてしまうとループのたびにグローバル変数がリセットされて、意図しない動作になることがあります。
まとめ
WP_Queryのサブループを使ったら wp_reset_postdata() をループの後に必ず書く、これを習慣にしておくと予期しないバグを防げます。書き忘れても見た目上は問題ないように見えることもありますが、メインループの後にサブループを使っている場合や複数のループがある場合に症状が出やすいので注意してください。
ご不明な点やコーディングのご依頼はお問い合わせからお気軽にどうぞ。
