こんにちは!WordPressの裏側の仕組みを紐解いていくと、開発が何倍も楽しく、そして強固なものになりますよね。
他のプログラミング言語やフレームワークを少し知っている方なら、「データを取得する関数がいくつもあるけど、結局どれを使えばいいの?」と疑問に思ったことがあるはずです。WordPressの代表的なデータ取得手段である `WP_Query` と `get_posts()` も、まさにその代表格ですね。
「初心者向けに優しく、でも本質は妥協せず」に、データベース負荷の観点からこの2つの違いをマスターしていきましょう。ここをクリアすれば、あなたのWordPress開発スキルは一段と洗練されますよ!
—
1. そもそも、この2つの「裏側」はどうなっているの?
まず大前提として知っておいてほしいことがあります。
それは、「`get_posts()` の中身は、実は `WP_Query` そのものである」 という事実です。
[ あなたの書いたコード ]
│
├─► WP_Query クラスを直接召喚する ──► 最も生々しいクエリの制御が可能
│
└─► get_posts() 関数を使う ─────► 中身で WP_Query をこっそり呼び出している(ラッパー関数)
「あれ、じゃあ同じなの?」と思いますよね。
実は、「料理人(`WP_Query`)が自分でフルコースを作るのか、それともデリバリー(`get_posts()`)で手軽に定食を受け取るのか」 という決定的な違いがあります。
この違いが、データベース(MySQL)への負荷や、コードの書きやすさに大きく影響してくるんです。
—
2. それぞれの基本的な使い方とコードの読み方
まずは、それぞれの書き方を見てみましょう。他の言語から来た方にも馴染みやすいように、具体的なコードとコメントを用意しました。
パターンA:`WP_Query`(本格派シェフのフルコース)
自分でクラスをインスタンス化し、取得したデータのループ処理だけでなく、グローバルな投稿データ( `$post` )の巻き戻し(後述します)まで自分で責任を持つスタイルです。
‘post’,
‘posts_per_page’ => 5,
‘category_name’ => ‘news’,
);
$my_query = new WP_Query( $args );
// 2. データベースから持ってきたデータがあるかチェックしてループ開始
if ( $my_query->have_posts() ) :
while ( $my_query->have_posts() ) : $my_query->have_posts(); // ※正しくは $my_query->the_post() ですね!うっかりミスに注意!
// 正しいループの書き方:
// $my_query->the_post();
?>
パターンB:`get_posts()`(手軽なデリバリー定食)
こちらは配列を返すだけのシンプルな関数です。オブジェクト指向の面倒な作法を意識せず、サクッと配列データを取得したいときに使われます。
‘post’,
‘posts_per_page’ => 5,
‘category_name’ => ‘news’,
);
$recent_posts = get_posts( $args );
// 2. 返ってきたのは普通の配列なので、foreachで回すだけ!
if ( ! empty( $recent_posts ) ) {
echo ‘
- ‘;
- ‘ . get_the_title( $post->ID ) . ‘
foreach ( $recent_posts as $post ) {
// 注意:setup_postdata() をしないとテンプレートタグ(the_title()など)が使えない場合があります
setup_postdata( $post );
echo ‘
‘;
}
echo ‘
‘;
// 3. こちらも必ずリセットが必要
wp_reset_postdata();
}
—
3. データベース負荷とパフォーマンスの観点からの比較
さて、ここからがエンジニアとしての腕の見せ所です。
「どちらを使っても同じ記事が取れるなら、短いコードのほうがいいや」と思っていませんか?
実は、データベース(MySQL)やメモリ効率の観点から見ると、それぞれに適材適所の使い分けが存在します。
① 発行されるSQLクエリの差はある?
結論から言うと、指定する引数(`$args`)が同じであれば、生成されるSQL文自体は基本的に同じです。どちらも内部で `WP_Query` が動いているため、`SELECT FROM wp_posts …` のような重いクエリが発行されます。
では、何が違うのでしょうか?
② メモリ消費と「余計な機能」の有無
- `WP_Query`(特にページネーションを使う場合)
- `’no_found_rows’ => false` (デフォルト)の場合、WordPressは「この条件に一致する全データの総数(MAX件数)」を計算するために、`SQL_CALC_FOUND_ROWS` という重い処理をSQLに含めます。これにより、データベースサーバーへの負荷が跳ね上がることがあります。
- `get_posts()`
- デフォルトで `’suppress_filters’ => true` や `’no_found_rows’ => true` が内部的に考慮されているケースが多く、「総件数(ページネーション用の全体数)を計算しない」軽量な設定で動きやすいという特徴があります。ページネーションを必要としない「新着情報リスト」などを表示する場合、データベースへの無駄な負荷を抑えられます。
—
4. 陥りがちな文法エラーとトラップ
初学者が必ずと言っていいほどハマるポイントが2つあります。ここを知っておくだけで、現場でのデバッグ時間が何時間も削減されますよ。
トラップ1:`the_post()` と `setup_postdata()` の違いによるバグ
WordPressの便利なタグ(`the_title()` や `the_content()` など)は、裏側で「グローバル変数 `$post`」を頼りに動いています。
- `WP_Query` のループ内では、`$my_query->the_post()` を使えば自動でこれが設定されます。
- 一方、`get_posts()` は単なる配列を返すだけなので、ループ内で明示的に `setup_postdata( $post );` を呼ばないと、タイトルのリンクがおかしくなったり、同じ記事のタイトルがループし続けたりする怪奇現象が起きます。
トラップ2:メインクエリの汚染と `wp_reset_postdata()` の忘れ
カスタムクエリを書いた後、`wp_reset_postdata()` を書き忘れるとどうなるでしょう?
ページ本来のメインクエリ(そのページが本来持っている記事情報)が、あなたの書いたカスタムクエリのデータに書き換わったままになってしまいます。その結果、サイドバーのウィジェットがおかしくなったり、コメント欄が消えたりという不可解なバグを生む原因になります。「クエリを書いたら、必ずリセットする」 はエンジニアの絶対のルールです。
—
5. 結局、どちらを使うべきなのか?(選択の基準)
ここまでの知見をまとめると、シンプルに次のように使い分けるのがベストプラクティスです。
| 状況・目的 | 推奨する関数 | 理由 |
| :— | :— | :— |
| アーカイブページやメインのブログ一覧
(ページネーション(1, 2, 3…)が必要) | `WP_Query`
(またはメインクエリをそのまま利用) | ページネーションに必要な全件数を正確に算出するため。 |
| トップページの「新着5件」やサイドバーの「おすすめ記事」
(ページネーション不要) | `get_posts()`
(または `WP_Query` で `no_found_rows => true` 指定) | ページネーション用の重いカウント処理を省き、データベースの負荷を最小限に抑えるため。 |
—
まとめ
いかがでしたか? `get_posts` と `WP_Query` は、単なる「書き方の好み」ではなく、データベースの裏側の挙動やパフォーマンスに直結する重要な選択です。
「とりあえず動くからいいや」ではなく、「なぜこの関数を選ぶのか」を意識できるようになると、WordPressはただのブログツールではなく、強力でスケーラブルなWebアプリケーション基盤へと姿を変えます。
ここをクリアしたあなたなら、どんな複雑な要件の案件が来ても怖くありませんよ。自信を持って次のステップへ進んでいきましょう!