Web事業部実績紹介
Web事業部実績紹介
2017.04.07

ここまで違う!WordPressの[WP_Query]と[get_posts()]の特性を暴いてみる

エリカ

こんにちは、エリカです。

WordPress を理解していくうえで重要な概念のひとつに「ループ」というものがあります。トップページも、一覧ページも、詳細ページも、固定ページも、検索結果ページも、すべてループとして処理されています。

 

if ( have_posts() ) {
    while( have_posts() ) {

 
テンプレートファイルでは、404 を除くほとんどのページで上記のようなコードを見かけると思います。一覧ページだけでなく、詳細ページや固定ページなど投稿データをひとつしか扱わないと思われるテンプレートでも、同様に処理します。

これが メインループ と呼ばれるもので、リクエストされた URL の情報と、その内容に従ってデータベースから取得したデータが格納されています。

 
対して、メインループ以外の部分で、投稿データを処理するために作成されるループが サブループ複数ループ などと呼ばれるループになります。

今回はこのサブループについて掘り下げたいと思います。

 

サブループとしての[WP_Query]と[get_posts()]

さて、サブループを作成する場合に利用されるものには、WP_Queryget_posts() があります。まずは、それぞれの使い方についてご説明しましょう。条件に従った投稿データのサブループを作成し、その投稿のタイトルを表示していきます。

 

WP_Queryの場合

 

$args = array(
  // 取得したい投稿の条件
);
$the_query = new WP_Query( $args );
if ( $the_query->have_posts() ) {
    while( $the_query->have_posts() ) {
        $the_query->the_post();        
                the_title();
    }
    wp_reset_postdata();
}

 

get_posts()の場合

 

$args = array(
  // 取得したい投稿の条件
);
$my_posts = get_posts( $args );
if ( $my_posts ) {
        global $post;
    foreach( $my_posts as $post ) {
        setup_postdata( $post );
        the_title();
    }
    wp_reset_postdata();
}

 
両方のコードを見ると、ほとんど一緒です。大きな違いは 配列として扱う か、オブジェクトとして扱うか というところかと思いますが、処理の流れはほとんど同じで、ここだけ見ると違いはまだわかりません。

 
では、それぞれの中身はどうなっているのでしょうか。

 

WP_Queryを直接扱う

まずはインスタンスを作成し、投稿の取得条件を渡します。

ここで、ちょっとメインループの処理について思い出してください。メインループはリクエストされた URL の情報と、その内容に従ってデータベースから取得したデータを保持していました。実は、それも WP_Query インスタンスが行っています。

つまり、条件は違えどメインループも WP_Query を利用するサブループも同じく、投稿の取得条件とその結果を保持することになるのです。

 
前述したとおり get_posts() との決定的な違いはこの部分です。取得結果しか扱えない、get_posts() に加え、WP_Query を直接利用する場合は、取得条件や、それに付随する機能をメインループと同じように利用できることになるわけです。

それには、例えばループの途中であるかどうかを判定できる[ in_the_loop ]というプロパティを使えば、「ループにバナーなどを差し込みたい」と言った場合に、それがループの途中なのか、ループが終了しているかといった判定ができます。

逆に、その様な使い方をしなければ、get_posts()で十分、となるかもしれません。

 

get_posts()を扱う

実は、get_posts() は WP_Query のラッパーです。以下をご覧ください。

 

function get_posts( $args = null ) {

    // 中略

    $get_posts = new WP_Query;
    return $get_posts->query($r);

}

 
関数の最後で、WP_Query オブジェクトを作成し、WP_Query::query メソッドの実行結果だけを受け取ることによりサブループを作成しています。結局は WP_Query を利用していますが、この実行結果だけを受け取るという部分が、両者の大きな違いになります。

 

両者のパラメータの違い

扱い方という大きな違いに加えてもうひとつ、投稿取得条件のパラメータにも違いがあります。WP_Query で使えるパラメータがベースなのですが、get_posts() 専用の、また get_posts() では指定できないパラメータがあります。それらは主に以下のようなものです。

 

パラメータ名 差異
post_type 初期値がpostとしてセットされている
numberposts posts_per_pageに変換される
category catに変換される
include post__inに変換される
exclude post__not_inに変換される
ignore_sticky_posts trueから変更不可
no_found_rows trueから変更不可

 

$GLOBALS[‘post’]の変更

get_posts() の場合も、WP_Query を直接扱う場合も、setup_postdata($post) と WP_Query::the_post() を利用することが多いです。これはどちらも $GLOBALS[‘post’] を変更し、the_title() や the_content() などの、$GLOBALS[‘post’] を参照する関数群を使えるようにしてくれます。

 

wp_reset_postdata()の必要性

また、どちらも場合もループ終了時に、wp_reset_postdata() を実行しています。サブループによって置き換えられてしまった $GLOBALS[‘post’] をメインループを戻すのがこの関数です。

 
ここで「 WP_Query を直接扱った場合には、なんとなく下記のようになるんじゃないか」と思いませんでしたか?

 

endwhile;
$the_query->reset_postdata();

 
reset_postdata() は、自身が保持している post プロパティを $GLOBALS[‘post’] に戻しています。

つまり、上記のようにしてしまうと、サブループの post プロパティを戻すことになってしまうのです。

対して wp_reset_postdata() は、$GLOBALS[‘wp_query’] が所有している post プロパティを $GLOBALS[‘post’] に戻します。

 
そうです、$GLOBALS[‘wp_query’] というのがメインループとなる WP_Query インスタンスを保持している のです。これで、メインループの情報に戻せました。

 
なお、サブループを作るうえで query_posts() を利用する方法もありましたが、この関数は、メインループとなる $GLOBALS[‘wp_query’] にも変更を加えます。従って、サブループ終了時に メインループとなる $GLOBALS[‘wp_query’] についても復元しなければなりません。なので、最近ではサブループを作る上では、あまり好ましい方法ではないとされています。

 

まとめ

これで、利用したい機能によって使い分けられるようになったのではないでしょうか。

個人的な好みですが、サブループを使う場面では get_posts() だけでも必要な要件を満たせる場合が多いので、get_posts() を使うことが多いと思います。

 

<?php foreach( get_posts( $args ) as $post ) : setup_postdata( $post ); ?>

<?php endforeach; wp_reset_postdata(); ?>

 
こんな風に簡単に書けちゃうのが嬉しいです。