カスタム投稿タイプを管理する Custom Post Type Maker を試してみた

管理画面からカスタム投稿タイプやら、カスタム分類やらを設定するために有名なプラグインといえば、Custom Post Type UI だと思いますが、デバッグモードにすると notice エラーが出たり(あまり人のこと言えない。。)、アップデートもしばらくされておらず、3.5 で追加になったパラメーターの指定ができなかったり、UIが今ひとつ使いにくいなど、今後にちょっと心配があり、代替となるプラグインで良いものないかなと探していたところ、Custom Post Type Maker の存在を知り、試してみました。

結論から言うと、現バージョンでは、使用すべきではないというのが私の判断です。

カスタム投稿タイプなどを新しく追加した場合、表示されるURLが新たに加わりますが、パーマリンク使用時にこのURLを表示するためには、リライトルールというマッピングデータを更新してあげる必要があります。

Custom Post Type Maker は、Webページ表示時にこのリライトルールを毎回再生成しているのですね。こうすることによって、新しくカスタム投稿タイプやカスタム分類を追加したときにも、自動的に表示されるようになるのですが、このリライトルールの再生成は、それなりに重い処理で表示の際に毎回やるようなことではありません。これは、Codex の flush_rewrite_rules の説明にもしっかりと注意書きとして載っています。

Important: Flushing the rewrite rules is an expensive operation, there are tutorials and examples that suggest executing it on the ‘init’ hook. This is bad practice. Instead you should flush rewrite rules on the activation hook of a plugin, or when you know that the rewrite rules need to be changed ( e.g. the addition of a new taxonomy or post type in your code ).

WordPress は、3.3 で、リライトルールの改善を行い、この処理も大幅に軽減はされていますが、毎回行うようなことではありません。

私の判断ではありますが、この点が改善されるまで、利用すべきでないプラグインととらえています。

カスタム分類を利用して、先頭に固定表示を実現する

WordPress には、先頭に固定表示の機能もありますが、1ページ目の最初にモリッと差し込まれるので、表示件数が変わってしまったり、ページ送りにも対応できません。

これを、カスタム分類を用いて、

  • 該当分類に属するものを、home 表示の際に優先表示
  • 1ページの表示投稿数は、設定通りにする
  • 先頭固定表示の記事のあとは、それ以外の記事を最新順に
  • ページ送り・ページナビに影響がでないこと

を満たすものを作ってみました。

具体的には、news というカスタム分類で、sticky という分類に属する投稿を先頭表示されるようにしています。

また、些細な工夫として、複数箇所から呼ばれる get_custom_sticky_posts で、wp_cache_set と wp_cache_get を使っています。これを使うと、実行結果がメモリ上にキャッシュされるため、複数回呼ばれるような場面においても、パフォーマンスロスを防ぐことができるようになります。

/*
 * 通常表示されるべき投稿を先頭固定の投稿で差し替える
 */
function insert_custom_sticky_posts( $posts, $wp_query ) {
	if ( ! is_admin() && $wp_query->is_home() ) {
		$stickies = get_custom_sticky_posts();
		if ( $stickies ) {
			$posts_per_page = $wp_query->get( 'posts_per_page' );
			$paged = $wp_query->get( 'paged' ) ? $wp_query->get( 'paged' ) : 1;

			if ( count( $stickies ) >= $posts_per_page * $paged ) { // 全て差し替え
				$posts = array_slice( $stickies, $posts_per_page * ( $paged - 1 ), $posts_per_page );
			} elseif ( count( $stickies ) > $posts_per_page * ( $paged - 1 ) ) {
				$insert_sickies = array_slice( $stickies, $posts_per_page * ( $paged - 1 ), $posts_per_page );
				$posts = array_merge( $insert_sickies, $posts );
				$posts = array_slice( $posts, 0, $posts_per_page );
			}
		}
	}
	return $posts;
}
add_filter( 'the_posts', 'insert_custom_sticky_posts', 10, 2 );

/*
 * 全投稿数に先頭固定の投稿数を足す
 */
function add_custom_sticky_posts_count( $found_posts, $wp_query ) {
	if ( ! is_admin() && $wp_query->is_home() && ! $wp_query->get( 'suppress_filters' ) ) {
		$found_posts = $found_posts + count( get_custom_sticky_posts() );
	}

	return $found_posts;
}
add_filter( 'found_posts', 'add_custom_sticky_posts_count', 10, 2 );

/*
 * SQL文のLIMIT 句を改変し、先頭固定の投稿数文を差し引いた開始位置にする
 */
function replace_custom_sticky_posts_limit_sql( $limits, $wp_query ) {
	if ( ! is_admin() && $wp_query->is_home() ) {
		$stickies = get_custom_sticky_posts();
		if ( $stickies ) {
			$posts_per_page = $wp_query->get( 'posts_per_page' );
			$paged = $wp_query->get( 'paged' ) ? $wp_query->get( 'paged' ) : 1;

			$start = $posts_per_page * ( $paged - 1 ) - count( $stickies );
			if ( $start < 0 ) {
				$start = 0;
			}
			$limits = "LIMIT $start, $posts_per_page";
		}
	}

	return $limits;
}
add_filter( 'post_limits', 'replace_custom_sticky_posts_limit_sql', 10, 2 );

/*
 * 通常表示の記事から、先頭固定表示する投稿を除外する
 */
function ignore_custom_stickies( $wp_query ) {
	if ( ! is_admin() && $wp_query->is_home() && ! $wp_query->get( 'suppress_filters' ) ) {
		$wp_query->set(
			'tax_query',
			array(
				'relation' => 'AND',
				array(
					'taxonomy' => 'news',
					'terms' => array( 'sticky' ),
					'field' => 'slug',
					'operator' => 'NOT IN',
				),
			)
		);
	}
}
add_action( 'pre_get_posts' , 'ignore_custom_stickies' );

/*
 * 先頭固定表示する投稿を全て取得する
 */
function get_custom_sticky_posts() {
	$cache_key = 'custom-sticky-posts';
	$stickies = wp_cache_get( $cache_key );

	if ( $stickies === false ) {
		$stickies = get_posts(
			array(
				'posts_per_page' => -1,
				'tax_query' => array(
					'relation' => 'AND',
					array(
						'taxonomy' => 'news',
						'terms' => array( 'sticky' ),
						'field' => 'slug',
						'operator' => 'IN',
					),
				)
			)
		);
		wp_cache_set( $cache_key, $stickies );
	}
	return $stickies;
}

■ 更新
2013.03.22 suppress_filters による条件分岐を追加しました。

カスタム投稿タイプのアーカイブ表示で、カスタム分類での絞り込み検索を行う

Webサイト上で絞り込み検索ができるようにしたい」というのは比較的よくある要望だと思います。そんな場合どうしてますか?カスタム投稿タイプだったら、アーカイブ表示の際に、少々工夫すれば絞り込みを行うことができてしまうのです。

WordPress では、パーマリンクの設定如何に関わらず、?cat=5 などといったパラメーターを追加すると絞り込みが可能となっています。(なので、カテゴリー内でのテキスト検索といったことも簡単に可能なんですよ?)
このため、フォームの method 属性を get にしておけば、比較的簡単に絞り込みができてしまうのですが、この場合、アドレス欄にパラメーターがずらずら並んでしまってクライアント受けが良くないのと、チェックボックスによる複数項目での検索に対応しづらいのが難点です。

これを post で投げて、アドレスはそのままで絞り込み検索、さらにはページ送りにも対応させてしまいましょう。

“カスタム投稿タイプのアーカイブ表示で、カスタム分類での絞り込み検索を行う” の続きを読む

WordPressのカテゴリー管理画面にキーワード欄を追加してみる

カテゴリーページでキーワードなどの項目を任意に設定したいと思い、ちょっとチャレンジしてみました。
ただし、カテゴリーや投稿タグなどを管理している term、term_taxonomy テーブルには、投稿に対するカスタムフィールドのようなものが存在せず、項目を追加した場合にどこにデータを保存するかが問題になってきます。

では、どのような方法があるかというと

  1. optionsテーブルに配列として保存する
  2. termsテーブルにフィールドを追加する
  3. カスタムフィールドの内容を保存するpostmetaテーブルのようなテーブルを追加する

の3つが考えられます。これらの手法について

1.は、テーブル構造を変更することなく簡易に実装ができますが、検索性はほぼ0です。

2.は、多少の変更で get_category など従来の関数で情報の取得が可能で、検索性も良好ですが、項目毎にフィールドが必要になるため、自由な拡張は期待できません

3.は、拡張性が高く検索性も保持されますが、独自の取得関数が必要になり、実装には一番手間がかかります。

今回は、テストの意味もあり、3の方法でトライしてみることにしました。

“WordPressのカテゴリー管理画面にキーワード欄を追加してみる” の続きを読む

wp_list_pages利用時にカスタム分類での絞り込みを実現してみる

フォーラムのタクソノミーのターム別記事一覧表示にて回答した内容です。
ごく稀なケースで、あまり汎用的なものではないと思いますが、なんかの参考になれば幸いです。

質問の概要はというと、「階層構造を表現しつつ、カスタム分類での絞り込みを行った一覧表示を実現したい」というもの。

階層構造の表現しつつ一覧表示するには、wp_list_pages を利用するのが最も簡単で、私自身もサイトマップのプラグイン(横着者)やサブナビのウィジェット(横着者)で大変お世話になっています。

“wp_list_pages利用時にカスタム分類での絞り込みを実現してみる” の続きを読む