WordPressの起動3 – WP_Queryクラスのカスタマイズ

WPクラスとWP_Queryクラス

$wp、$wp_the_query、$wp_queryなどのクラスのオブジェクトは、wp-settings.phpの中で実体化され、スーパーグローバル変数$GLOBALSに格納されています。

  1. wp()
  2. $wp->main()
  3. —-$wp->parse_request():①URLをパース(文法に従って分析)しクエリを特定
  4. —-$wp->query_posts()
  5. ——–$wp_query->query()
  6. ————$wp_query->get_posts():③投稿データを$posts配列に格納
  7. —————-$wp_query->parse_query():②条件分岐タグで使用されるすべてのis_変数を設定

URLからクエリを特定し、条件分岐タグを使えるための値をセットして、wp_postsテーブルなどから必要な投稿データをグローバル変数$postsに格納する3つの処理は、コンテンツを表示するループの中で使われる処理です。

<?php if( is_single()):  //①③URLから分岐条件にtureかfalseがセットされている?>
<?php 
if(have_posts()):
    while(have_posts()): the_post();  //②必要なデータが格納される
?>
	<h2><?php the_title(); ?></h2>
     <?php the_content(); ?>
<?php 
    endwhile; 
endif;  
?>
<?php endif; //is_single()?>

query_posts関数とget_posts関数

上記のようにquery_posts関数はメインクエリを取得するための関数でありwp-includes/query.php内に以下のように定義されていますが、Codexには「この関数はプラグインまたはテーマの中で使われることを想定されていません。」と書いてあります。

function query_posts($query) {
	$GLOBALS['wp_query'] = new WP_Query();
	return $GLOBALS['wp_query']->query($query);
}

つまりquery_posts関数はテンプレートタグ(テンプレートの中で使う関数)ではないわけで、処理の実態はWP_Queryクラス内のメインクエリ取得のための関数です。

一方でget_posts関数はwp-includes/post.phpで以下のように定義されています。

function get_posts( $args = null ) {
	$defaults = array(
		'numberposts' => 5,
		'category' => 0, 'orderby' => 'date',
		'order' => 'DESC', 'include' => array(),
		'exclude' => array(), 'meta_key' => '',
		'meta_value' =>'', 'post_type' => 'post',
		'suppress_filters' => true
	);

	$r = wp_parse_args( $args, $defaults );
	if ( empty( $r['post_status'] ) )
		$r['post_status'] = ( 'attachment' == $r['post_type'] ) ? 'inherit' : 'publish';
	if ( ! empty($r['numberposts']) && empty($r['posts_per_page']) )
		$r['posts_per_page'] = $r['numberposts'];
	if ( ! empty($r['category']) )
		$r['cat'] = $r['category'];
	if ( ! empty($r['include']) ) {
		$incposts = wp_parse_id_list( $r['include'] );
		$r['posts_per_page'] = count($incposts);  // only the number of posts included
		$r['post__in'] = $incposts;
	} elseif ( ! empty($r['exclude']) )
		$r['post__not_in'] = wp_parse_id_list( $r['exclude'] );

	$r['ignore_sticky_posts'] = true;
	$r['no_found_rows'] = true;

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

}

ややこしいですがWP_Queryクラス内でグローバル変数$postsに投稿データを格納するメソッドもget_posts()ですが、別モノですから要注意。

このテンプレートタグであるget_posts()のほうが、WP_Queryからメインクエリとは別のクエリを生成していることから、カスタムクエリでのデータ取得にはget_query()テンプレートタグが使われてきましたが、最近は以下のようにWP_Queryクラスからカスタムループ用クエリを使うためのインスタンスを生成して使うのが主流です。

カスタムループ作成はWP_Queryクラスで

WordPressのメインループは、WP_Queryクラスの$wp_queryインスタンスからメソッドを実行し、適切な投稿データを取得しますが、メインループ以外で独自の条件でクエリを実行する場合は、$wp_query2みたいに別途インスタンスを生成する必要があります。

<?php
//WP_Queryクラスの引数を連想配列$argsにセット
$args=array(
    'post_type' => 'post',   //投稿
    'post_per_page'=>3  //取得件数3
    );

//WP_Queryクラスからインスタンス$wp_query2を生成
$wp_query2=new WP_Query($args);
?>

いつものWordPressループを$wp_query2のメソッドとして実行します。

<?php
if ( $wp_query2->have_posts() ) {
    while ( $wp_query2->have_posts() ) {
        $wp_query2->the_post();
    }
}
?>
<?php
    endwhile;
endif;
?>

メインループの変更はpre_get_postsフックで

投稿を取得するクエリのコアであるget_postsメソッドの中で、$this->parse_query()を実行した後に、pre_get_postsというアクションフックが仕込んであり、WP_Queryのパラメータを配列引数array( &$this )でプラグイン側に送りますので、プラグイン関数側で$queryという配列型オブジェクトとして受け取り、パラメータを書き換えることで、メインクエリの動きを調整します。

public function get_posts() {
        global $wpdb;
        $this->parse_query();  //条件分岐タグで使用するis_変数への値をセット
        do_action_ref_array( 'pre_get_posts', array( &$this ) ); //アクションフック
                
        //省略
        return $this->posts;
}

functions.php内で、プラグイン関数とpre_get_postsフックをadd_action関数でマッピングしてあげれば、メインクエリを操作できます。

add_action('pre_get_posts', 'pre_get_posts_map');

function pre_get_posts_map($query){
    $query->set('posts_per_page', 3);
}

プラグイン関数ではWP_Queryのパラメータを配列オブジェクト$queryとして受け取り、setメソッドで値を書き換えます。

$query->set(‘パラメータ名’, ‘値’);