WordPressループ生成まとめ – メインクエリとサブクエリ

メインクエリ – main query

WordPressのコアAPIでは、URLリクエストに応じて必要な記事データを取得するクエリが発行されて、取得されたデータが $wp_query というグローバル変数に格納されます。ここまでを自動的に行ってくれます。
この自動的に発行されるクエリを、メインクエリと呼びます。

素直にメインクエリを出力する場合は、すべてのテンプレートで共通のループ構文を記述すればいいことになります。

<?php if (have_posts() ) : ?>
	<?php while (have_posts()) : the_post(); ?>
		<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
	<?php endwhile; ?>
<?php else : ?>
	Not Found.
<?php endif; ?>

advanced

$wp_query オブジェクトは以下のような構造になっています。

WP_Query Object
(
[query] => Array
[query_vars] => Array
[tax_query] => WP_Tax_Query Object
[meta_query] => WP_Meta_Query Object
[request] => SELECT SQL_CALC_FOUND_ROWS wp_posts.ID FROM wp_posts WHERE 1=1 AND wp_posts.post_type = ‘post’ AND (wp_posts.post_status = ‘publish’ OR wp_posts.post_status = ‘private’) ORDER BY wp_posts.menu_order ASC LIMIT 0, 10
[posts] => Array(
[0] => WP_Post Object (
)

)

[is_home] =>
[is_paged] =>
[is_admin] =>
[is_singular] =>
[is_post_type_archive] =>

)

request: メインクエリのSQL文
posts: 記事データ

is_home からの部分にも注目。
これは is_home() などの条件分岐タグに対するフラグです。
つまり条件分岐タグは $wp_query オブジェクトの値をみているということがわかります。

サブクエリ – sub query

メインクエリで取得される記事データとは別のデータを取得したい場合、自前でクエリを発行します。
この自前で発行するクエリを、サブクエリと呼びます。

サイドバーに記事一覧を表示したり、トップページに post_type=news の記事一覧を表示したいときなどに使います。

サブクエリの発行方法は2種類あります。
いずれも引数はほぼ同じなので、単純に記事データ(メタデータも含む)だけが必要であればどちらを使っても問題はないでしょう。

1. WP_Queryクラスを使う


<?php $args = array(
	'post_type' => 'hoge',
); ?>

<?php $query = new WP_Query( $args ); ?>
<?php if( $query->have_posts() ) : ?>
	<?php while ( $query->have_posts() ) : $query->the_post(); ?>
		<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
	<?php endwhile; wp_reset_postdata(); ?>
<?php else : ?>
	Not Found.
<?php endif; ?>


2. get_posts関数を使う

<?php $args = array(
	'post_type' => 'hoge',
); ?>

<?php $hoge_posts = get_posts( $args ); ?>
<?php if( !empty( $hoge_posts ) ) : ?>
	<?php foreach( $hoge_posts as $post ) : setup_postdata( $post ); ?>
		<h2><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h2>
	<?php endforeach; wp_reset_postdata(); ?>
<?php else : ?>
	Not Found.
<?php endif; ?>

Advanced

1 と 2 の違い

WP_Queryクラスを使う場合は、その名の通り WP_Queryクラスでクエリを発行するわけですが、これはメインクエリを発行する時に使われるクラスと同じです。
取得されたデータを格納した $query 変数を出力してみると、$wp_query と同じ構造のオブジェクトであることがわかります。
一方 get_postsで取得されるデータは、記事データ $wp_query->posts 限定になっています。

以上のことから、記事データのみを扱う場合は WP_Queryクラスでも get_posts でもどちらを使ってもいいですが、条件分岐タグや、その他のパラメーターを扱う場合は WP_Query を使った方がいろいろ高度なことができるということです。

メインクエリの改変

query_posts() はもう使うべきではありません。
その理由について WordPress Codex ではパフォーマンスの問題によって説明されおり、今後は query_posts() ではなく pre_get_posts にフックすべし、と言っています。

pre_get_posts に移行するメリットは他にもあります。
メインクエリの改変はテンプレート側ではなく、関数ファイル(functions.php)で一元的に行う、ということなので、MVCモデルに基づくビューテンプレートとコントローラーの分離が明確になります。

また、テンプレート数の削減にもつながるかもしれません。

news と evnet という2つのカスタム投稿タイプのアーカイブページがあったとして、いずれもループ出力の内容は同じ、唯一取得件数だけ違う場合、query_posts() を使うと、archive-news.php と archive-event.php という2つのテンプレートを用意しなければなりません。
一方、pre_get_posts を使うと、archive.php の1つのテンプレートだけでまかなえます。

つまり、メンテナブルなテーマ作成になるということです。

> functions.php

function custom_main_query( $query ) {
	if ( is_admin() || !$query->is_main_query() ) return;
	
	if ( $query->is_home() ) {
		$query->set( 'posts_per_page' , '1' );
	}
	if ( $query->is_post_type_archive( 'hoge' ) ) {
		$query->set( 'order' , 'ASC' );		
	}
}
add_action( 'pre_get_posts', 'custom_main_query' );