WordPressで前後の固定ページの投稿オブジェクトを取得する方法

2012.09.13
コードを修正しました(カスタム投稿タイプでも動作するようにpageの縛りを削除。条件式の不具合修正)。

下記の関数とクラスをfunctions.phpに貼り付けてください。「get_next_page()」で次の固定ページの投稿オブジェクト($post)が、get_previous_page()で前の固定ページが取得できます。

注意点として、グローバル変数の$postを読みますので、WordPressループ内でしか使用できないこと、標準的な固定ページの並び順ルール(menu_order、投稿名順)で取得されること、があります。

<?php
function get_next_page() {
	$get_adjacent_page = new get_adjacent_page();
	return $get_adjacent_page->get_next_page();
}
function get_previous_page() {
	$get_adjacent_page = new get_adjacent_page();
	return $get_adjacent_page->get_previous_page();
}

/**
 * get_adjacent_page
 * 前後の固定ページ(同階層)を取得するクラス
 */
class get_adjacent_page {
	private $post = null;
	private $wpdb = null;
	private $op = '';
	private $orderby = '';

	/**
	 * __construct
	 */
	public function __construct() {
		global $post;
		global $wpdb;
		$this->post = $post;
		$this->wpdb = $wpdb;
	}

	/**
	 * get_next_page
	 * 後の固定ページを取得する
	 * @return	null or $post
	 */
	public function get_next_page() {
		$this->op = '>=';
		$this->orderby = 'ASC';
		return $this->get_adjacent_post( 'next' );
	}

	/**
	 * get_previous_page
	 * 前の固定ページを取得する
	 * @return	null or $post
	 */
	public function get_previous_page() {
		$this->op = '<=';
		$this->orderby = 'DESC';
		return $this->get_adjacent_post( 'previous' );
	}

	/**
	 * get_adjacent_post
	 * get_adjacent_post関数を実行
	 * @params	next or previous
	 * @return	null or $post
	 */
	private function get_adjacent_post( $adjacent ) {
		if ( !in_array( $adjacent, array( 'previous', 'next' ) ) ) return;
		$previous = ( $adjacent === 'previous' ) ? true : false;
		add_filter( 'get_'.$adjacent.'_post_join', array( $this, 'get_page_join' ), 10, 3 );
		add_filter( 'get_'.$adjacent.'_post_where', array( $this, 'get_page_where' ), 10, 3 );
		add_filter( 'get_'.$adjacent.'_post_sort', array( $this, 'get_page_sort' ) );
		return get_adjacent_post( false, '', $previous );
	}

	/**
	 * get_page_join
	 * get_{$adjacent}_post_joinにフックさせる関数
	 * @return	null
	 */
	public function get_page_join( $join, $in_same_cat, $excluded_categories ) {
		return;
	}

	/**
	 * get_page_where
	 * get_{$adjacent}_post_whereにフックさせる関数
	 * @return	String	where文
	 */
	public function get_page_where( $where, $in_same_cat, $excluded_categories ) {
		$where = $this->wpdb->prepare( "WHERE
			p.ID != %s AND
			p.post_parent = %s AND
			( p.menu_order $this->op %s OR ( p.menu_order = %s AND p.post_title $this->op %s ) ) AND
			p.post_type = %s AND
			p.post_status = 'publish'",
			$this->post->ID, $this->post->post_parent, $this->post->menu_order, $this->post->menu_order, $this->post->post_title, $this->post->post_type
		);
		return $where;
	}

	/**
	 * get_page_sort
	 * get_{$adjacent}_post_sortにフックさせる関数
	 * @return	String	order by, limit文
	 */
	public function get_page_sort( $sort ) {
		return "ORDER BY p.menu_order $this->orderby, p.post_title $this->orderby LIMIT 1";
	}
}

解説など

WordPressには、get_next_post関数や、get_previous_post関数など、前後の「投稿」を取得する関数はあるのですが、固定ページを取得する関数がありません(厳密にはget_next_post関数や、get_previous_post関数でも取得できるはずですが、投稿時間順になってしまう)。

そこで、方法がないか調べたところ、フォーラムで同様の質問を発見。

ここでは、SQLを直接発行して前後の固定ページを取得する方法が挙げられていました。

function get_next_page( $post ) {
	global $wpdb;

	if ( $post->post_type != 'page' ) { retrun; }
	$next_page = $wpdb->get_row( $wpdb->prepare( "
SELECT	*
FROM	$wpdb->posts
WHERE	post_type = 'page'
AND		post_status = 'publish'
AND		ID != %s
AND		post_parent = %s
AND		menu_order >= %s
AND		post_title >= %s
ORDER BY	menu_order ASC, post_title ASC
LIMIT	1", $post->ID, $post->post_parent, $post->menu_order, $post->post_title ) );
	return $next_page;
}

はじめはこのコードを利用させてもらおうかなーと思っていましたが、get_next_post関数、get_previous_post関数のコードを見てみると内部では「get_adjacent_post関数」を利用していることを発見。さらにこの関数のコードを見てみると、複数のフィルターフックが用意されており、投稿オブジェクトを取得する条件を変更することができるということがわかりました。

このフックに引っ掛けて条件を変更することで、固定ページのデータ取得を実現しました。ただ、恐らく速度的にはSQL書いたほうが速いと思います…。

  • ブックマーク
  • Feedly

この記事を書いた人

キタジマタカシ

長崎在住、フリーランスのWordPress テーマ / プラグインデベロッパー。 多数のプロダクトをオープンソースで開発・公開しています。現在は WordPress 有料テーマ Snow Monkey を開発・販売しています。