トップページの新着3件、記事下の関連記事、サイドバーのおすすめ——メインのループとは別に投稿を取り出したいときはWP_Queryを使います。型はこれだけです。
<?php
$query = new WP_Query([
'post_type' => 'post',
'posts_per_page' => 3,
]);
?>
<?php if ($query->have_posts()) : ?>
<ul>
<?php while ($query->have_posts()) : $query->the_post(); ?>
<li><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></li>
<?php endwhile; ?>
</ul>
<?php endif; ?>
<?php wp_reset_postdata(); ?>ポイントは$query->を付けることと、最後のwp_reset_postdata()の2つです。
なぜ$query->を付けるのか
WordPressは、そのURLに応じた投稿一覧(メインクエリ)をあらかじめ用意しています。have_posts()やthe_post()を引数なしで呼ぶと、そのメインクエリを操作してしまいます。
自分で作った$queryのループでは、$query->have_posts() / $query->the_post()と対象を明示する——これがサブループの基本ルールです。逆に、ループの中でthe_title()やthe_permalink()のような表示系の関数は、$query->なしでそのまま使えます(the_post()が「今の投稿」を切り替えてくれているため)。
wp_reset_postdata()を忘れるとどうなる?
$query->the_post()は「今の投稿」をサブループ側の投稿に置きかえます。ループを抜けても、その状態が残ります。
そのため呼び忘れると、たとえば記事ページで「関連記事一覧を出したあと、その下の投稿者情報が関連記事の最後の投稿のものになる」といった崩れが起きます。get_field()が別の投稿の値を返す原因にもなります。
サブループを書いたら、閉じたすぐ下にwp_reset_postdata()。セットで手が覚えるまで書きましょう。
よく使う条件(引数)の早見表
| 引数 | 意味 | 例 |
|---|---|---|
post_type | 投稿タイプ | 'post' / 'shohin' / ['post','shohin'] |
posts_per_page | 取得件数(-1で全件) | 5 |
orderby | 並べ替えの基準 | 'date' / 'rand' / 'menu_order' / 'meta_value_num' |
order | 昇順・降順 | 'DESC'(新しい順)/ 'ASC' |
offset | 先頭から何件飛ばすか | 3 |
post__not_in | 除外する投稿ID | [get_the_ID()] |
category_name | カテゴリのスラッグ | 'news' |
tag | タグのスラッグ | 'sale' |
tax_query | カスタムタクソノミーで絞る | 下のリンク参照 |
meta_query | カスタムフィールドで絞る | 下のリンク参照 |
post_status | 公開状態 | 'publish'(既定) |
paged | ページ送りの現在ページ | get_query_var('paged') |
ignore_sticky_posts | 先頭固定の投稿を無視 | true |
実例:カスタム投稿タイプの新着を並べる
<?php
$news = new WP_Query([
'post_type' => 'shohin',
'posts_per_page' => 6,
'orderby' => 'date',
'order' => 'DESC',
]);
?>
<?php if ($news->have_posts()) : ?>
<div class="cards">
<?php while ($news->have_posts()) : $news->the_post(); ?>
<article class="card">
<a href="<?php the_permalink(); ?>">
<?php the_post_thumbnail('medium'); ?>
<h3><?php the_title(); ?></h3>
<time><?php echo esc_html(get_the_date('Y.m.d')); ?></time>
</a>
</article>
<?php endwhile; ?>
</div>
<?php else : ?>
<p>まだ商品がありません。</p>
<?php endif; ?>
<?php wp_reset_postdata(); ?>elseを書いておくと、0件のときにレイアウトが崩れず、メッセージが出せます。納品するサイトでは必ず入れておきたい一手です。
実例:関連記事(自分を除いた同カテゴリ)
<?php
$cats = wp_get_post_categories(get_the_ID());
$related = new WP_Query([
'category__in' => $cats,
'post__not_in' => [get_the_ID()], // 自分自身を除く
'posts_per_page' => 4,
'orderby' => 'rand',
]);
?>べんりワザ関連記事をプラグインなしで出す関連記事の出し方は専用の記事でくわしくget_posts()との違い
get_posts()は投稿の配列を返す関数で、短く書けます。ループを回す必要がない、リストを1つ出すだけの場面で便利です。
<?php
$posts = get_posts([
'post_type' => 'post',
'numberposts' => 5, // ← get_postsはposts_per_pageではなくnumberposts
]);
?>
<ul>
<?php foreach ($posts as $p) : ?>
<li>
<a href="<?php echo esc_url(get_permalink($p)); ?>"><?php echo esc_html(get_the_title($p)); ?></a>
</li>
<?php endforeach; ?>
</ul>使い分けの目安はこうです。
WP_Query——アイキャッチや本文、the_content()など投稿ループの関数を使いたいとき。ページ送りも作れるget_posts()——タイトルとURLだけの簡単なリスト。件数指定はnumberposts
なおget_posts()のあとでsetup_postdata($p)やthe_post()を使った場合は、WP_Queryと同じくwp_reset_postdata()が必要です。
メインの一覧そのものを変えたいときはpre_get_posts
「カテゴリページの表示件数を20件にしたい」「アーカイブの並び順を変えたい」——これはサブループではなくメインクエリの調整です。functions.phpでフックします。
function my_pre_get_posts($query) {
if (is_admin() || !$query->is_main_query()) {
return; // 管理画面とサブループには効かせない
}
if (is_category()) {
$query->set('posts_per_page', 20);
}
}
add_action('pre_get_posts', 'my_pre_get_posts');is_admin()とis_main_query()のガードは必ず入れます。これが無いと管理画面の一覧や他のサブループまで巻きこんで、原因の分かりにくい不具合になります。
件数が多いサイトでの小技
'no_found_rows' => true——ページ送りが不要な一覧で、総件数の計算を省いて軽くする'posts_per_page' => -1は慎重に——記事が増えると一気に重くなる。上限を決めておく'ignore_sticky_posts' => true——先頭固定の投稿が混ざるのを防ぐ
まとめ
- 別の一覧は
new WP_Query([...])。ループでは$query->have_posts()/$query->the_post()と対象を明示する wp_reset_postdata()を必ず呼ぶ。忘れるとページ下部の表示やカスタムフィールドが崩れる- タイトルとURLだけの簡単なリストなら
get_posts()(件数はnumberposts) - メインの一覧を変えるなら
pre_get_posts。query_posts()は使わない
「主役の一覧はフックで調整、おまけの一覧はWP_Queryで追加」と覚えておくと、迷う場面がぐっと減ります。