はじめに
【WordPress×PHP】フィルターフックの仕組みと使い方では、表示される値を書き換えるフィルターフックを解説しました。
今回は、WP_Queryを使って、好きな条件で記事一覧を自作する方法を解説します。「このカテゴリーの記事だけ」「古い順に」「この記事以外」といった一覧を、ページの好きな場所に表示できるようになります。第6回のループと、第8回のオブジェクトの知識を使うので、あわせて読んでみてください。
このシリーズの全15回の構成は、第1回の記事の冒頭にまとめています。
メインクエリとサブクエリ
WordPressは、URLを見て「このページで表示する記事」を自動で取り出します。これが第6回で解説したメインクエリで、have_posts()とthe_post()のループはこのメインクエリを使っていました。
それとは別に、自分で条件を決めて記事を取り出すのがサブクエリです。サブクエリは、new WP_Query( 条件 )で、WP_Queryクラスのオブジェクトを作って使います。

SWELLのテーマフォルダをnew WP_Queryで検索すると、22か所で使われていました。記事の下に表示される関連記事(parts/single/related_post_list.php)も、その1つです。
WP_Queryの基本の書き方
カテゴリー「wp-php」の記事を新しい順に3件取り出して、タイトルを一覧にするコードです。
$query = new WP_Query( [
'category_name' => 'wp-php',
'posts_per_page' => 3,
] );
if ( $query->have_posts() ) {
echo '<ul>';
while ( $query->have_posts() ) {
$query->the_post();
echo '<li>' . esc_html( get_the_title() ) . '</li>';
}
echo '</ul>';
}
wp_reset_postdata();
第6回のループとの違いは、have_posts()とthe_post()の前に$query->が付くことです。$query->the_post()を実行すると、取り出した記事が「現在の記事」になるので、ループの中ではget_the_title()やget_permalink()がそのまま使えます。
ローカル環境で試すと、記事が5件あるカテゴリーから新しい順に3件が表示されました。$query->found_posts(条件に合う記事の総数)は5、$query->post_count(実際に取り出した数)は3でした。
主な引数
new WP_Query()には、条件を配列で渡します。よく使うものは次のとおりです。
| 引数 | 意味 | 例 |
|---|---|---|
post_type | 投稿の種類 | 'post'(投稿)、'page'(固定ページ) |
posts_per_page | 取り出す件数 | 5、-1(すべて) |
category_name | カテゴリーのスラッグ | 'wp-php' |
tag | タグのスラッグ | 'swell' |
orderby | 並べる基準 | 'date'(日付)、'title'(タイトル)、'rand'(ランダム) |
order | 並べる向き | 'DESC'(新しい順・初期値)、'ASC'(古い順) |
post__not_in | 除外する投稿IDの配列 | [ get_the_ID() ] |
ignore_sticky_posts | 先頭に固定表示の記事を特別扱いしない | true |
no_found_rows | 総数を数えない(少し速くなる) | true |
posts_per_pageを指定しないと、管理画面の「設定」→「表示設定」の「1ページに表示する最大投稿数」(初期値10)が使われます。また、no_found_rowsをtrueにすると、found_postsは0になりました。ページ送りを作らない一覧なら、trueにしておくと無駄な処理を省けます。
SWELLの関連記事でも、post__not_in(表示中の記事を除外)、no_found_rows、ignore_sticky_postsを指定していました。
get_posts()という関数でも、同じ条件で記事を取り出せます。こちらは記事のオブジェクト(WP_Post)の配列を返すので、foreachで回して使います。指定しないときの件数は5件でした。
wp_reset_postdata()を忘れるとどうなるか
$query->the_post()は、「現在の記事」を次々に切り替えます。ループが終わったら、wp_reset_postdata()で元の記事に戻す必要があります。
これを忘れるとどうなるか、第1回の記事の本文の後ろにサブクエリで一覧(3件)を表示して試しました。

リセットしなかった場合、ループのあとのget_the_ID()は、最後に取り出した第3回のIDのままでした。その結果、本文の後ろに表示されるSWELLの関連記事が「第3回の記事」を基準に作られ、表示中の第1回の記事自身が関連記事に出てしまいました。エラーは出ないので、気付きにくい不具合です。
よくある書き間違い
have_posts()に「$query->」を付け忘れる
$query = new WP_Query( [ 'category_name' => 'wp-php' ] );
while ( have_posts() ) { // $query-> を付け忘れた
$query->the_post();
// …
}ローカル環境で試すと、一覧は表示されず、ページがなかなか表示されないまま、約80秒後に次のエラーになりました。
Fatal error: Allowed memory size of 268435456 bytes exhausted
「使えるメモリの上限を超えました」という意味です。have_posts()はメインクエリの関数なので、投稿ページの中で呼ぶと、メインクエリのループが最初からやり直しになります。そのため、記事の本文が何度も繰り返し処理され、メモリを使い切ってしまいました。サブクエリでは、have_posts()にもthe_post()にも、必ず$query->を付けましょう。
存在しないカテゴリーを指定する
category_nameに存在しないスラッグを書いてもエラーにはならず、$query->have_posts()がfalseになって何も表示されません。一覧が出ないときは、スラッグのつづりを管理画面の「投稿」→「カテゴリー」で確認しましょう。
古い解説記事では、query_posts()という関数で一覧を作る方法が紹介されていることがあります。これはメインクエリそのものを置き換えてしまう関数で、WordPressの公式リファレンスでも使わないよう案内されています。一覧を自作するときは、WP_Queryを使いましょう。
実務サンプル:記事の最後に「このシリーズの記事一覧」を表示する
このシリーズのような連載記事では、記事の最後に「同じシリーズの記事一覧」があると便利です。シリーズ用のカテゴリーに入っている記事を、公開順(古い順)に並べて表示してみましょう。
子テーマのfunctions.phpに、次のコードを追加します。$seriesの部分は、シリーズに使っているカテゴリーのスラッグに書き換えてください。
/**
* シリーズ記事の本文の最後に、同じシリーズの記事一覧(公開順)を表示する
*/
add_filter( 'the_content', function( $content ) {
$series = 'wp-php'; // シリーズに使っているカテゴリーのスラッグ
if ( ! is_singular( 'post' ) || ! in_the_loop() || ! is_main_query() ) {
return $content;
}
if ( ! in_category( $series ) ) {
return $content;
}
$current_id = get_the_ID();
$query = new WP_Query( [
'category_name' => $series,
'posts_per_page' => -1,
'orderby' => 'date',
'order' => 'ASC',
'no_found_rows' => true,
'ignore_sticky_posts' => true,
] );
if ( ! $query->have_posts() ) {
return $content;
}
$list = '<div class="kurumico-series"><p class="kurumico-series__title">このシリーズの記事一覧</p><ol>';
while ( $query->have_posts() ) {
$query->the_post();
if ( get_the_ID() === $current_id ) {
$list .= '<li class="is-current">' . esc_html( get_the_title() ) . '(この記事)</li>';
} else {
$list .= '<li><a href="' . esc_url( get_permalink() ) . '">' . esc_html( get_the_title() ) . '</a></li>';
}
}
$list .= '</ol></div>';
wp_reset_postdata();
return $content . $list;
} );子テーマのstyle.cssには、次のCSSを追加します。
.kurumico-series {
margin-top: 3em;
padding: 1.5em;
background: #f7f9fb;
border: 1px solid #dde3ea;
border-radius: 6px;
}
.kurumico-series__title {
margin-bottom: .5em;
font-weight: bold;
}
.kurumico-series .is-current {
font-weight: bold;
color: #04384c;
}カテゴリー「wp-php」に5件の記事を用意して、第3回の記事を開くと、本文の最後に次のように表示されました。

コードのポイントは次のとおりです。
- 第10回の
the_contentフィルターと3つの条件を使い、投稿ページの本文の最後に一覧を追加している in_category()(第7回)で、シリーズのカテゴリーに入っている記事のときだけ表示している- サブクエリを始める前に、表示中の記事のIDを
$current_idに入れておき、ループの中で比べて「(この記事)」を付けている 'posts_per_page' => -1ですべての記事を、'order' => 'ASC'で古い順に取り出している- フィルターの中なので
echoせず、文字列をつなげてreturnしている。ループのあとはwp_reset_postdata()で元の記事に戻している
雑記カテゴリーの記事、固定ページ、RSSフィードには、一覧は表示されませんでした。また、SWELLの関連記事も、表示中の記事を正しく除外したまま表示されました。
よくある質問・エラー対処
Q. メインクエリの表示件数や並び順を変えたいときも、WP_Queryを使いますか?
→ いいえ。トップページの記事一覧などメインクエリを変えたいときは、pre_get_postsというアクションフック(第9回)で条件を変えます。SWELLも、カスタマイザーの「除外したいカテゴリーのID」(新着記事一覧から除外する設定)を、lib/hooks.phpのpre_get_postsで反映しています。WP_Queryは、メインとは別に一覧を追加したいときに使います。
Q. カテゴリーをIDで指定したいです
→ 'cat' => 5のようにcatを使います。複数のカテゴリーをまとめて指定するときは、SWELLの関連記事のように'category__in' => [ 5, 8 ]と配列で渡します。
おわりに(まとめ)
WP_Queryを使えば、好きな条件の記事一覧を、ページの好きな場所に追加できます。
- ✅ URLで決まるのがメインクエリ、自分で作るのがサブクエリ
- ✅ new WP_Query()に条件を配列で渡して、記事を取り出す
- ✅ have_posts()とthe_post()には必ず「$query->」を付ける
- ✅ ループのあとはwp_reset_postdata()で元の記事に戻す
- ✅ 表示中の記事のIDは、サブクエリを始める前に変数に入れておく
- ✅ メインクエリを変えたいときは、pre_get_postsを使う
次回は、ここまでのコードでも使ってきたesc_html()などの「エスケープ」と、WordPressのセキュリティの基本について解説します。
次回は、エスケープとセキュリティについて解説予定です。
この記事が、少しでも誰かのお役に立てれば幸いです。
関連記事
※1回買えば複数サイトで使い回しOK
当サイトの記事で使用したVBAなどのサンプルをDLできます
この記事のサンプルはありません!
ダウンロードページへは下のカードをクリックすればジャンプできます。
よろしければご利用ください!


