query_posts(), get_posts(), WP_Query()による$wp_queryオブジェクトの違い

*上級者向け*

基本

WordPressループを変更および生成する関数は3つ。

query_posts() メインループを変更する
get_posts() カスタムループを生成する
WP_Query() カスタムループを生成する

メインループ: テンプレート階層に従って生成されるWordPressループ
カスタムループ: テンプレート階層とは無関係に生成するカスタムループ

メインループとカスタムループの違いについてはこちらの記事を参考のこと: WordPressのループの仕組みを深く知る query_posts() と get_posts() の違い

基本構文

query_posts()

<?php query_posts( $args ); ?>
<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
	<p><?php the_title(); ?></p>
<?php endwhile; endif; ?>
<?php wp_reset_query(); ?>

get_posts()

<?php $myposts = get_posts( $args ); ?>
<?php foreach($myposts as $post) : setup_postdata($post); ?>
	<p><?php the_title(); ?></p>
<?php endforeach; ?>

WP_Query()

<?php $the_query = new WP_Query( $args ); ?>
<?php while ( $the_query->have_posts() ) : $the_query->the_post(); ?>
	<p><?php the_title(); ?></p>
<?php endwhile; ?>
<?php wp_reset_postdata(); ?>

パラメーター引数 $args はほぼ同じ。

次が本題

$wp_queryオブジェクトの違い

パラメーター引数($args = array(‘post_type’ => ‘page’) )を指定した場合の$wp_queryオブジェクトを比較する

query_posts()

[query] =  Array
(
     [post_type] =  page
)
 ...

get_posts()

[query] =  Array
    (
        [numberposts] =  10
        [offset] =  0
        [category] =  0
        [orderby] =  post_date
        [order] =  DESC
        [include] =  Array
            (
            )

        [exclude] =  Array
            (
            )

        [meta_key] =  
        [meta_value] =  
        [post_type] =  page
        [suppress_filters] =  1
        [post_status] =  publish
        [posts_per_page] =  10
        [ignore_sticky_posts] =  1
        [no_found_rows] =  1
    )
 ...

WP_Query()

[query] =  Array
(
     [post_type] =  page
)
 ...

このように、get_posts()の場合だけ、指定していないパラメーター引数のディフォルト値も$wp_queryオブジェクトが保持しているのがわかる。
一方、query_posts()とWP_Queryの場合は、明示的に指定したパラメーター引数しか保持していない。
ただし、ディフォルト値はいずれも有効。

よって、pre_get_postsフィルターにフックする時などは、この違いを理解しておくことが重要になってくる。
つまり、get_posts()で生成されたクエリなのか、query_posts()とWP_Query()で生成されたクエリなのかを条件分岐して処理する必要が出てくる為。

さらに、これら3つの関数を使わないWordPressループ、つまり、テンプレート階層に従って自動的に生成される$wp_queryオブジェクトにも注意したい。

<?php if (have_posts()) : while (have_posts()) : the_post(); ?>
	<p><?php the_title(); ?></p>
<?php endwhile; endif; ?>
<?php wp_reset_query(); ?>

対象オブジェクトがpostsになるWordPressループの場合、$wp_query->queryは空配列になり、対象オブジェクトが固定ページやカスタム投稿タイプの場合は、$wp_query->query->post_typeのみが保持される。

*suppress_filtersパラメーター

suppress_filtersはフィルタリングを有効にするパラメータである。suppress_filters=trueの場合フィルタリングが有効にならない。

query_posts()とWP_Query()の場合はディフォルトがfalse(正確にはパラメーター領域は存在しない)なので問題はない。
だが、get_posts()の場合はディフォルトがtrueであり、この値も$wp_queryオブジェクトに保持される。

よって、先ほど指摘したように、pre_get_postsフィルターにフックする時などは、この点を考慮してsuppress_filterパラメーターをfalseにする処理が必要である。

ということは、$wp_queryオブジェクトを生成した関数が、get_posts()なのかそれ以外なのかを判別するには以下の条件分岐が考えられる。

if ( isset($query->query['suppress_filters']) )

query_posts()とWP_Query()にはsuppress_filtersパラメーターは用意されていない。公式ドキュメントにもアナウンスされていない。が、意図的に渡すこともできる。
そうすると、上記の判定文は効力を失うが、そもそもフィルタリング条件中でフィルタリングOFFのパラメーターを指定することは無意味な行為なので、この条件分岐は、完全ではないものの、有効と言えるだろう。

コメントを残す