PR

WP_Queryでページネーションできない!管理画面や設定ページではget_query_varが使えない?!

WP_Query() で特定の記事一覧を表示する場合、ページネーション(1ページ目、2ページ目など表示しているページ数を示す数字のリンク表示)では、get_query_var() でページ番号を取得してpaginate links() でページリンクの表示をしたりします。

どうやら WP_Query() は投稿や固定ページといった「表側の画面」と、プラグインなどの「管理画面・設定ページ」とでは内部的なデータの持ち方が異なるようです。

具体的には、管理画面では WP_Query が認識するはずのクエリ変数が自動でセットされないため、get_query_var() が空っぽになってしまうのが原因でした。

この記事では、管理画面でも確実にページ番号を取得してページネーションを動かす2つの解決法をご紹介します。

管理画面ではページネーションができない?

WP_Queryを使ってページネーション表示をする場合、
基本形は以下みたいになると思いますが...

Code
<?php
	//------------------------------------
	//ページ番号を取得:最初は1ページ目
	//------------------------------------
	$paged = get_query_var( 'paged' ) ?: 1;

	//------------------------------------
	//WP_Queryの検索条件
	//------------------------------------
	$args_query	= array(
		'post_type' 	=> array( 'post', 'page' ),		//投稿、固定ページ
		'post_status' 	=> array( 'publish', 'draft' ),	//公開済、下書き
		'paged' 		=> $paged,	//表示するページ番号
	);

	//------------------------------------
	//一覧の取得と表示
	//------------------------------------
	$my_query = new WP_Query($args_query);
	
	if ( $my_query->have_posts() ) :
		echo '<ul>';
		while ( $my_query->have_posts() ) : $my_query->the_post();
			//何を表示するか
			echo '<li>' . get_the_title() . '</li>';
		endwhile;
		echo '</ul>';
		wp_reset_postdata();	//WP_Query のリセット

	else:
		echo '<p>no data</p>';
	endif;

	//------------------------------------
	//ページペーション
	//------------------------------------
	$big = 999999999;

	$args_paginate	= 		array(
			'base' => str_replace($big, '%#%', esc_url(get_pagenum_link($big))),
			'current' => max(1, get_query_var('paged')),
			'total' => $my_query->max_num_pages,
		);
?>
Jin Simple Code Block

※管理画面のURL構造によっては、
admin_url() をベースに組み立てる方が安定する場合もあります。

関数リファレンス/WP_Query
関数リファレンス/get_query_var
関数リファレンス/paginate_links

これでプラグイン化して管理画面で表示させてみると、
以下のような感じで良さそうには見えます。

期待通り WP_Query で記事の一覧が表示され、
ページネーション(ページのリンク)も表示されますね。

でも② のページネーションの数字をクリックしても、
2ページ目以降が表示されず、表示されるのは常に1ページ目。

こんな感じで、ページリンクの「2」とか「次へ」をクリックしてもURLは「paged=2」とか変わりますが、実際の表示は1ページ目しか表示されないんですね。
(ショートコード化して記事で表示すると2ページ目とかも表示されて問題ないんですけど)

WP_Queryは動作しているしページネーションの数字のリンク自体は問題なさそうですが、管理ページで表示すると以下がうまく動作しないようです。

  • 1)WP_Queryに渡す引数の paged (表示するページ番号)が正しくない
  • 2)paginate_links に渡す引数の current (現在のページ番号)が正しくない
  • どちらのページ番号も get_query_var で取得しているので、管理ページでは get_query_var が期待通りに動作しないのかも

get_query_var の関数リファレンスを改めて見てみると、
get_query_var は固定ページでは get_query_var( 'paged' ) ではなく、get_query_var( 'page' ) にする、という仕様なため、管理ページもそうなのかな?と変えてみても結果は同じ。

また get_query_var は「WP_Queryで認識する”パブリッククエリ変数のみ”を取得する」とも書かれてます。

この場合の変数は「paged=2」となるので、これが管理画面で認識されていないのかと思い、以下のようなコードで変数を明示的に追加してみましたが、やはり結果は同じ。2ページ目は表示されませんでした。

Code
// 新しい独自クエリ変数をWP_Query に含める(これだけでは管理画面では不十分)
function add_query_vars_filter( $vars ){
$vars[] = "paged";
return $vars;
}
add_filter( 'query_vars', 'add_query_vars_filter' );
Jin Simple Code Block

どうやら管理画面では、URLにある「paged」というパラメータを WordPress がメインクエリの変数として自動で拾ってくれない仕組みになっているようです。

そもそも関数リファレンスをよく読むと「paged」は標準でパブリッククエリ変数に含まれています。つまり、登録し直しても意味がなかったというわけですね(笑)。

どうやら管理画面のプログラム(wp-admin配下)は、フロント側のページを表示する仕組みとは独立して動いているため、標準のクエリ変数がそのままでは適用されないようです。

管理画面という「特殊な環境」において、
ページ番号を確実にキャッチするには別の方法が必要だということが分かりました。

ということで解決策を色々調べてみてもこのあたりの情報はなさそうでしたが、英語で調べてみると(WP_Query pagination admin とかで調べてみると)2つ解決法がありました。

解決法その1)$_GET['paged'] を使う

ページ番号を取得するのを `get_query_var` ではなく、URLに「paged=2」などの数値があるので、直接 `$_GET['paged']` を使います。

最初、何かワードプレスのルールに従ってないかもと不安になってましたが、英語文化圏ではこの解決策が出ていて一安心(笑)。

ただ、最初の1ページ目の表示で「paged=」がURLにない場合にはエラー(pagedなんて知らないよエラー)が出てしまいます。 ということで、以下のようにすると良さそうですね。

Code
$paged = (int)($_GET['paged'] ?? 1);
Jin Simple Code Block

これは「??(Null合体演算子)」という書き方で、$_GET['paged'] があればその値を、なければ 1 にするという意味です。

さらに (int) をつけて数値として扱うことで、エラーを未然に防ぎます。

これを使って、ページ番号( $paged)の取得箇所を置き換えた全体コードがこちら。

Code
<?php
	//------------------------------------
	// ページ番号を取得:最初は1ページ目
	//------------------------------------
	// $paged = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;
	// ↓ 管理画面では get_query_var が効かないので $_GET から直接取得!
	$paged = max(1, (int)($_GET['paged'] ?? 1));
	
	//------------------------------------
	// WP_Queryの検索条件
	//------------------------------------
	$args_query	= array(
		'post_type' 	=> array( 'post', 'page' ),		// 投稿、固定ページ
		'post_status' 	=> array( 'publish', 'draft' ),	// 公開済、下書き
		'paged' 		=> $paged,	// 表示するページ番号
		'posts_per_page' => 5,		// 一度に表示する件数
	);
	
	//------------------------------------
	// 一覧の取得と表示
	//------------------------------------
	$my_query = new WP_Query($args_query);
	
	if ( $my_query->have_posts() ) :
		echo '<ul>';
		while ( $my_query->have_posts() ) : $my_query->the_post();
			// 何を表示するか
			echo '<li>' . get_the_title() . '</li>';
		endwhile;
		echo '</ul>';
		wp_reset_postdata();	// WP_Query のリセット

	else:
		echo '<p>no data</p>';
	endif;

	//------------------------------------
	// ページネーション
	//------------------------------------
	$big = 999999999;

	$args_paginate = array(
			'base' => str_replace($big, '%#%', esc_url(get_pagenum_link($big))),
			// 'current' => max(1, get_query_var('paged')),
			'current' => $paged, // 事前に1以上であることを保証しているので、これだけでOK!
			'total' => $my_query->max_num_pages,
		);
	echo paginate_links( $args_paginate );
?>
Jin Simple Code Block

置き換えた箇所は 6-7行目と44-45行目あたり。

※ 6-7行目では、(int)で数値にし、max(1, …)で1未満にならないように制限することで、不正な値によるエラーを未然に防ぎます

これで、URLにパラメータがない時もエラーにならず、2ページ目以降も正しく動作するようになりました。(やったね!^-^)

※ 今回は数値への変換(int)で対策していますが、WordPressには absint() という「絶対値を整数で返す」便利な関数もあります。より厳格に作りたい時はそちらもおすすめになるようです。

解決法その2)get_query_var が使える?

もう1つ解決策があるようなので参考までに。

get_query_var の英語版の関数リファレンスをチェックしてみると、下の方の「User Contributed Notes」(ユーザーメモ)に以下が書かれてます。

Because get_query_var() uses the WP_Query class, which only operates within The Loop, this function cannot be used to get a url variable outside of The Loop (e.g., a WordPress admin page).

Instead use $_GET[‘var_name’] as in typical PHP.

  • get_query_var()はループ内でのみ動作するWP_Queryクラスを使用することから、WordPress管理ページなどループ外のurl変数を取得することはできない。
  • 代わりに$ _GET [‘var_name’] を使用する。

最初にここをチェックすればよかった(日本語版の関数リファレンスにも書いておいてほしいぞよ)...と思いつつ、これに対してのフィードバック(以下)に、「get_query_var はループ外でも使用できる」と書かれてるんですね。

We actually can use get_query_var() outside The Loop, this function only requires the main $wp_query to be already intialized. See in the source code that the $wp_query->get() method gets the value from the $this->query_vars field, which is intialized on constructor and on call of init method. — By jorovipe — 1 year ago

  • get_query_var はループ外でも使用でる
  • この関数では、メインの$wp_queryが初期化されている必要がある。
  • $wp_query-> get()メソッドが$ this-> query_varsフィールドから値を取得することをソースコードで確認してみてよ。
  • このフィールドは、コンストラクターとinitメソッドの呼び出しで初期化されるんだ。

この「初期化されていれば使える」という説を信じて、試しにコードの最初に `wp_reset_postdata()` などを入れて初期化を試みてみましたが、やはり `get_query_var` でページ番号は取得できませんでした。

実は wp_reset_postdata() は表示中の投稿データをリセットするだけなので、管理画面のメインクエリがそもそも「paged」を持っていないという根本的な問題は解決できないんですね。

管理画面の場合、そもそも WordPress が「URLにある `paged` という文字を、クエリ変数として読み込む準備」をしてくれていないという仕様上の壁があるようです。

管理画面で get_query_var を通して値を取得するには、フックを使って「この変数を許可するよ!」と事前に登録するような、かなり回りくどい記述が必要となります。

これはWordPressがセキュリティのために、許可された変数以外は受け付けない「ホワイトリスト制」をとっているからですが、今回のようなケースでは、わざわざコードを複雑にしてまで使うメリットは少なそうですね。

ここでの結論としては、
解決法その1の「URLから直接 $_GET で取得する」という方法が、シンプルで確実な対応法と言えそうです。

今回のポイント

  • 投稿や固定ページ(表側): `get_query_var('paged')` でOK!
  • 管理画面(プラグインや設定画面):`$_GET['paged']` を使うのが正解!

解決策を見つけるまでに物凄く時間がかかってしまいましたが、ひとまず `$_GET['paged']` を使えば解決できると分かってスッキリしました(笑)。

「もっとスマートな書き方があるよ!」という方や、別の方法で解決された方がいれば、ぜひコメント欄で教えていただけると嬉しいです。

ネットビジネス、ブログでの収益化については、以下のメルマガからジャンジャン情報取ってみてくださいね。

早期退職して海外で奮闘する JIN のメールマガジン

時間や場所に縛られず稼いだJINが教える

~ 最短で月収10万円稼げるようになる方法 ~

お名前/ニックネーム

隣のあの人にも、思わず教えたくなる秘密

配信停止は、いつでもできます

迷惑メールは一切配信されませんので、ご安心くださいね

自らの手で未来を変える力を手に入れる!

コメント