2011.05.17 一部修正しました。

もくじ

また、タイトルいは「カスタム投稿タイプで」なんて書いてありますが、どちらかというとメタデータで日付の取り扱い的なことがメインとなっています、ベーシックなカスタム投稿タイプの例は「WordPressのカスタム投稿タイプで作るよくある質問(Q&A)ページ」 をご覧ください。

カスタム投稿タイプの設定

管理画面で入力するところから作っていきます。最初はカスタム投稿タイプです。

function.php

 
	function event_postype() {
		$labels = array(
			'name' => 'イベント',
			'singular_name' => 'イベント',
			'add_new' => '新規追加',
			'add_new_item' => '新規イベントを追加',
			'edit_item' => 'イベントを編集',
			'new_item' => '新規イベント',
			'view_item' => 'イベントを表示',
			'search_items' => 'イベントを検索',
			'not_found' =>  '投稿されたイベントはありません',
			'not_found_in_trash' => 'ゴミ箱にイベントはありません。',
			'parent_item_colon' => '',
		);
		$args = array(
			'label' => __('Events'),
			'labels' => $labels,
			'public' => true,
			'menu_position' => 5,
			'supports'=> array('title', 'thumbnail', 'excerpt', 'editor') ,
			'taxonomies' => array( 'eventcategory', 'post_tag'),
			'has_archive' => 'event'
		);
		register_post_type( 'events', $args);
	}
	add_action( 'init', 'event_postype' );
	

この辺りはほぼ設定するのは決まっていると思いますのでコピペな感じですね。

カスタム分類(タクソノミー)の設定

たとえば同じイベントでもセミナーだったり展示会だったりでカテゴリー別けしたい場合はカスタム分類(タクソノミー)を使用します。

function.php

 
	function eventcategory_taxonomy() {
		$labels = array(
			'name' => 'イベントカテゴリー',
			'singular_name' => 'イベントカテゴリー',
			'search_items' =>  'イベントを検索',
			'popular_items' => 'よく使われているイベント',
			'all_items' => 'すべてのイベント',
			'parent_item' => null,
			'parent_item_colon' => null,
			'edit_item' => 'イベントカテゴリーの編集',
			'update_item' => '更新',
			'add_new_item' => '新規イベントカテゴリー',
			'new_item_name' => '新しいイベントカテゴリー'
		);
		register_taxonomy('eventcategory','events', array(
			'label' => 'イベントカテゴリー',
			'labels' => $labels,
			'hierarchical' => true,
			'show_ui' => true,
			'query_var' => true,
			'rewrite' => array( 'slug' => 'event-category' ),
		));
	}
	add_action( 'init', 'eventcategory_taxonomy', 0 );
	

これで管理画面にイベントという項目(カスタム投稿タイプ)が作成され。サブメニューにはイベントカテゴリー(カスタム分類)も入っています。

追加されたイベントメニュー

カスタムフィールドで日付を入力

Wordpressのデフォルトの時間を利用してイベントの日付を管理するのも手ですが、投稿時間などはそれはそれで残しておいた方が良いこともあるので、ここでは新たにカスタムフィールドを作成し、日付と時間の入力欄を作成します。

今回はイベントの「日付」「開始時間」「終了時間」を入力できるようにします。
日付はソートなども行うため、フォーマットを統一する必要があります。
Wordpressの投稿時間は「2011-04-28 14:03:17(Y-m-d H:i:s)」という形式なので、この形で保存できるようにしましょう。
なので、入力箇所は3つありますが、保存されるメタキーは「開始日時」と「終了日時」の2つになります。

function.php

 
	add_action( 'admin_init', 'event_create' );
	function event_create() {
		add_meta_box('events_meta', 'イベント日時', 'events_meta_html', 'events');
	}
	

カスタムフィールドを作成するには「add_meta_box」という関数を実行します。

events_meta このカスタムフィールドのid
イベント日時 カスタムフィールドのタイトル
events_meta_html カスタムフィールドに表示するhtml
events ポストタイプが「events」のときだけ表示

「events_meta_html」という関数を作成して、その中に日付を入力するフィールドを作成していくわけですね。

function.php

	function events_meta_html () {
		global $post;
		$custom = get_post_custom($post->ID);
		//メタキーがあったら
		if(!empty($custom)) {
			$event_start = $custom["event_start"][0];
			$event_end = $custom["event_end"][0];
			//日付だけ表示
			$event_date = date("Y-m-d", strtotime($event_start));
			//開始時間
			$start_time =  date("H:i", strtotime($event_start));
			//終了時間
			$end_time =  date("H:i", strtotime($event_end));
		}
		echo '<input type="hidden" name="events-nonce" id="events-nonce" value="' . wp_create_nonce( 'events-nonce' ) . '" />';
		
		//入力フィールドの表示
		?>
		<style type="text/css">
		#event-meta table th {
				text-align: left;
				font-weight: normal;
				padding-right: 10px;
		}
		</style>
		<div id="event-meta">
		<table>
			<tr>
				<th>日付</th>
				<td><input name="event_date" class="event_date" id="datepicker" value="<?php if(isset ( $event_date)) echo $event_date; ?>" /></td>
			</tr>
			<tr>
				<th>時間</th>
				<td>
					<input name="start_time" class="start_time" value="<?php if(isset ( $start_time)) echo $start_time; ?>" /> ~
					<input name="end_time" class="end_time" value="<?php if(isset ( $end_time)) echo $end_time; ?>" />
				</td>
			</tr>
		</table>
		</div>
	<?php
	}
	

表示するだけなら28~42行目だけですが、カスタムフィールドは投稿を編集する画面でも同じものが表示されますので、メタキーに値が入っていたときの処理もしなくてはいけません。
前半ではメタキーがあった場合「event_start(開始日時)」「event_end(終了日時)」を「event_date(日付)」「start_time(開始時間)」「end_time(終了時間)」に置き換えています。
メタキーはすべて文字列になっているので、「strtotime」でタイムスタンプにしてそれを「date」で適切なフォーマットに変換してます。

日付のテキストフィールドのidに「datepicker」としているのはあとでjQueryUIのdatepickerプラグインを使用するためです。

これで「新規イベントを追加」画面に「イベント日時」というタイトルでカスタムフィールドが追加されているはずです。

追加されたカスタムフィールド

カスタムフィールドの値(メタキー)を保存する

カスタムフィールドはできましたが、これだけではだめで、入力されたメタキーの値を保存する処理をしなくてはいけません。

function.php

	add_action ('save_post', 'save_events');
	function save_events($post_id){
		global $post;
		if ( !wp_verify_nonce( $_POST['events-nonce'], 'events-nonce' )) {
			return $post_id;
		}
		if ( !current_user_can( 'edit_post', $post->ID )) {
			return $post_id;
		}
		$temp_date = $_POST['event_date'];
		$temp_stime = $_POST['start_time'];
		$temp_etime = $_POST['end_time'];
		$event_start = date('Y-m-d H:i:s', strtotime($temp_date .' '. $temp_stime));
		$event_end = date('Y-m-d H:i:s', strtotime($temp_date .' '. $temp_etime));
		
		update_post_meta($post->ID, 'event_start', $event_start);
		update_post_meta($post->ID, 'event_end', $event_end);
	}
	

受け取ったデータを今度は「event_start」「event_end」の2つの値に変換して保存します。

jQuery UI で日付を入力しやすくする

時間の入力なら間違いなさそうですが、日付の書式は「2011/05/01」だったり「2011-05-01」とかいろいろ考えられますね、テキストフィールドだけでは入力に誤りが出そうです。
入力補助としてjQueryUIの「datepicker」プラグインを使用します。

jQuery UI の使い方はこちらの過去記事も参考にしてください。
リッチなユーザーインターフェイスを簡単に実装できる「jQuery UI」

jQuery UI のダウンロード

まずは jQuery UI をダウンロードしましょう。
下記URLにアクセスしたら、左ナビゲーションの「Gallery」タブを選択して、好みのデザインで「Download」を押します。
次の画面に移動したら、とりあえずすべてチェックした状態で右カラムにある「Download」ボタンを押します。
ここでは現時点で最新である「1.8.12」を使用します。

jQuery UI – ThemeRoller

jsファイルの設置

ダウンロードしたzipファイルを解凍すると「development-bundle」フォルダが出てきます。
その中の「ui」→「minified」→「jquery.ui.datepicker.min.js」と同階層の「i18n」→「jquery.ui.datepicker-ja.js」を現在使用しているテンプレートの「js」フォルダにコピーします。

「query.ui.datepicker-ja.js」は日本語にする言語ファイルですが、データフォーマットの設定もできます。
初期設定では「yy/mm/dd」のようになってますので、17行目あたりを下記のように修正します。

jquery.ui.datepicker-ja.js

		dateFormat: 'yy-mm-dd',
		

その他にjQuery UI を実行するためのコードが必要です。
「myjs.js」という名称でファイルを新たに作成して、下記のコードを記述してください。

myjs.js

		(function($) {
			$(function() {
				$('#datepicker').datepicker();
			});
		})(jQuery);
		

cssファイルの設置

続いてcssファイルです。
「themes」→「選択したテーマ名」→「jquery-ui-1.8.12.custom.css」と同階層にある「images」フォルダを下層の画像ごとテンプレートの「css」フォルダにコピーします。

新たに追加したファイルの構造は下図のようになります。

jQueryUIを設置したフォルダ構造

管理画面でのjs&cssファイルの読み込み

設置したjQueryUIを管理画面で読み込んで使用できるようにしましょう。
管理画面のイベントカスタム投稿タイプだった場合(’events’ == $post_type)にファイルが読み込まれるようにします。

functions.php

		//cssの読み込み
		function events_styles() {
			global $post_type;
			if( 'events' == $post_type ) {
				wp_enqueue_style('ui-datepicker', get_bloginfo('template_url') . '/css/jquery-ui-1.8.12.custom.css');
			}
		}
		add_action( 'admin_print_styles-post.php', 'events_styles', 1000 );
		add_action( 'admin_print_styles-post-new.php', 'events_styles', 1000 );
		//jsの読み込み
		function events_scripts() {
			global $post_type;
			if( 'events' == $post_type ) {
				wp_enqueue_script('ui-datepicker', get_bloginfo('template_url') . '/js/jquery.ui.datepicker.min.js');
				wp_enqueue_script('datepicker-ja', get_bloginfo('template_url') . '/js/jquery.ui.datepicker-ja.js');
				wp_enqueue_script('myjs', get_bloginfo('template_url') . '/js/myjs.js');
			}
		}
		add_action( 'admin_print_scripts-post.php', 'events_scripts', 1000 );
		add_action( 'admin_print_scripts-post-new.php', 'events_scripts', 1000 );
		

「wp_enqueue_style」「wp_enqueue_script」を使用することで、css&jsが適切な位置で読み込まれます。

これで日付の入力欄を選択するだけでカレンダーが表示され入力しやすくなりましたね。

jQueryUIを設置した日付入力欄

イベント一覧画面に項目(カラム)を追加する

管理画面の最後の仕事として、イベント一覧画面に新たに作成したイベントの日付や時間のカラムを追加します。
カラムの追加方法はこれまた過去記事の下記を参考にしてください。

WordPress管理画面の投稿記事一覧をカスタマイズする

上記に加え、今までさんざんやってきたdateフォーマットの変換を組み合わせると次のようになります。

functions.php

	function manage_events_columns($columns) {
		global $post_type;
		if( 'events' == $post_type ) {
			$columns['event_date'] = 'イベント日付';
			$columns['event_time'] = 'イベント時間';
			$columns['ecategory'] = 'カテゴリー';
		}
		return $columns;
	}
	function add_column($column_name, $post_id) {
		//日付表示
		if( $column_name == 'event_date' ) {
			echo date('Y年m月d日', strtotime(get_post_meta($post_id, 'event_start', true)));
		}
		//時間表示
		if( $column_name == 'event_time' ) {
			//開始時間
			$start_time =  date('H:i', strtotime(get_post_meta($post_id, 'event_start', true)));
			//終了時間
			$end_time =  date('H:i', strtotime(get_post_meta($post_id, 'event_end', true)));
			echo $start_time . '~' . $end_time;
		}
		//カテゴリー表示
		if( $column_name == 'ecategory' ) {
			$terms = get_the_terms($post_id, 'eventcategory');
			foreach ($terms as $key => $value) {
				echo attribute_escape($value->name);
				//最後以外は「,」を
				if (end(array_keys($terms)) != $key) {
					echo ', ';
				}
			}
		}
	}
	add_filter( 'manage_posts_columns', 'manage_events_columns' );
	add_action( 'manage_posts_custom_column', 'add_column', 10, 2 );
	

これでイベント一覧は次のような項目が追加されたはずです。

イベント関連の項目が追加された一覧画面

カスタム投稿タイプのアーカイブ専用テンプレート

Wordpress3.0まではカスタム投稿タイプのアーカイブページはページ機能を利用してやりくいしていましたが、3.1からは待望のカスタム投稿タイプ専用のテンプレートを作成することができるのです。
「カスタム投稿タイプの設定で(register_post_type)」で下記のようにしていましたよね。

function.php

 
	'has_archive' => 'event'
	

「’has_archive’ => ‘event’」とすることで、「/event」にアクセスするとこのテンプレートファイルが読み込まれます。
で、このURLにアクセスしたとき表示されるテンプレートは次になります。

archive-カスタム投稿タイプ名.php

今回は「events」という投稿タイプなので「archive-events.php」というファイル名ですね。

パーマリンクの設定では「/%category%/%post_id%」のようなカテゴリーを含めた形にする必要があるようです。
またこの形になっていても一度「パーマリンク設定」画面で「変更を保存」ボタンを押してパーマリンクの更新を行ってください。

未来のイベントだけを表示するアーカイブテンプレート

カスタム投稿タイプのアーカイブテンプレートでは現在より未来のイベントのみを表示します。
普通にやってしまうと投稿した順に表示されてしまうので、メタキーを取得して、イベントの日付を基準にソートを行います。

archive-events.php

	<h1><?php echo post_type_archive_title()."一覧"; ?></h1>
	<?php
	$query = new WP_Query(array(
		'post_type' => 'events',		/* イベント投稿タイプ */
		'post_status' => 'publish',	/* 公開している */
		'meta_value' => date('Y-m-d H:i'), /* 現在の日付を基準 */
		'meta_key' => 'event_start',	/* 比較するmeta_key */
		'meta_compare' =>'>=',		/* meta_keyがvalueより大いものだけ */
		'orderby' => 'meta_value',	/* ソートはmeta_valueを基準に */
		'order' => 'ASC',	/* 昇順に並び替え	*/
		'paged' => get_query_var('paged')	 /* ページネーションできるように */
	));
	?>
	<ul>
	<?php if($query->have_posts()): while($query->have_posts()) : $query->the_post(); ?>
	<li>
	<a href="<?php the_permalink() ?>"><?php the_title(); ?></a>
	<?php echo get_the_term_list( $post->ID, 'eventcategory', '<ul><li>', '</li><li>', '</li></ul>' ); ?>
	<?php
		//日付だけ表示
		$event_date = date("Y-m-d", strtotime(post_custom('event_start')));
		//開始時間
		$start_time =  date("H:i", strtotime(post_custom('event_start')));
		//終了時間
		$end_time =  date("H:i", strtotime(post_custom('event_end')));
		echo '日時:'. $event_date .' '. $start_time . '~' . $end_time
	?>
	</li>
	<?php endwhile; endif; ?>
	</ul>
	

「WP_Query」を使えばいろいろ指定することができます。
コメントを見ていただければわかると思いますが、meta_value(現在の日付)とmeta_key(event_start)を比較して大きい(meta_compare)のだけ取得しています。
あとは普通にループさせるだけですね。

月別にイベントを表示する

たとえば4月のイベントだけとか、5月のイベントだけとか月別に表示させたいじゃないですか。
月別だから「date.php」に作成すればよさそうだけど問題があります。
「date.php」は投稿された日時を基準にしているので、例えば5月にはイベントはあるけど5月に投稿された記事が無い場合は「date.php」ではなくて「404.php」が表示されてしまいます。
あとは「date.php」を使ってしまう場合通常のブログ用の「date.php」はどうするかってとことろです。

あまり良い方法ではないですが、ここでは「404.php」「date.php」に同じ内容を入れます。
といっても同じ内容をコピーしたりするのはあれなので新たに「date-event.php」というファイルを作成してこれを2つのファイルにインクルードさせます。
さらにポストタイプが「events」だった場合のみこの「date-event.php」をインクルードさせれば、通常の「index.php」や「404.php」は一応機能しますよね。

404.php & date.php

		<?php
		$ptype = get_query_var(post_type);	//現在のポストタイプ取得
		//ポストタイプが「events」だったら「date-event.php」読み込む
		if ($ptype == 'events'):
		get_template_part('date-event');
		else:
		?>
		//ここに通常のindex.php & date.phpの処理が入る。
		<?php endif; ?>
		

「archive.php」がある場合はそちらにも入れる必要があるかもしれません。

カスタム投稿タイプの月別URL

「date-event.php」を作成する前にURLを確認してみましょう。
たとえば2011年5月のアーカイブを見たいときはこうですね。

/date/2011/05

これにポストタイプを追加すると次のようになります。

/date/2011/05?post_type=投稿タイプ名

ここでは「events」が投稿タイプ名なので「/date/2011/05?post_type=events」になります。
でもせっかくパーマリンク設定してるのに「?」とか「=」とか嫌なんですけど、という場合は方法があるみたいです。

WordPressのカスタム投稿タイプのアーカイブをパーマリンクで表示できるようにしてみた | Simple Colors

上記サイトのコードを「function.php」にコピーすると、次のURLで表示できます。

/events/date/2011/05

すばらしいですね。美しいURLです。

「date-event.php」の作成

指定月のアーカイブを表示するには、その月の1月から翌月の1月までのデータを取得する必要があります。
まずは年と月の数値で取得し、date型に変換。そこから翌月を計算します。

date-event.php

		//年・月を取得
		$get_year = intval( get_query_var('year'));
		$get_month = sprintf("%02d", intval( get_query_var('monthnum')));
		//タイムスタンプに変換
		$current_date = strtotime($get_year. '/'.$get_month. '/'. '01');
		//次の月取得
		$next_date = strtotime('+1 month', $current_date);	
		//date型に変換
		$current_date  = date('Y-m-d', $current_date);
		$next_date  = date('Y-m-d', $next_date);
		

「/events/date/2011/05」にアクセスすれば「$current_date」は「2011-05-01」となり「$next_date」は「2011-06-01」になります。

では、この日付を元に「WP_Query」を書きましょう。
アーカイブページでは●月以上とすればよかったのですが、今回は●月以上、●月以下と2つ条件を書く必要がありますよね。その場合は「meta_query」というのを使います。

date-event.php

$query = new WP_Query( array(
	'post_type' => 'events',
	'post_status' => 'publish',
	'meta_key' => 'event_start',	/* event_startメタキー */
	'order' => 'ASC',
	'orderby' => 'meta_value',	/* ソートの値を指定 */
	'meta_query' => array(
		array(
			'key' => 'event_start',
			'value' => $current_date,
			'compare' => '>=',
			'order' => 'ASC',
		),
		array(
			'key' => 'event_start',
			'value' => $next_date,
			'compare' => '<',
			'order' => 'ASC',
		),
	),
	'paged' => get_query_var('paged')
));
		

こんな感じで「meta_query」は配列で条件を追加していきます。
表示方法はアーカイブページと同じになるので省略します。

月別にイベントにリンクするナビゲーション

月別のイベントページを作ったけど、どうやってこのページにアクセスするんだよって感じですね。
「wp_archives」なんかをカスタマイズすれば良いのかなと思ったのですが、僕のスキルでは無理だったので「$wpdb」でSQL文を書きます。
functions.phpに下記コードを追加します。

functions.php

	function event_archives() {
		global $wpdb;
		$results = $wpdb->get_results("
			SELECT DISTINCT DATE_FORMAT(meta_value,'%Y/%m') AS edate 
			FROM $wpdb->postmeta
			WHERE meta_key = 'event_start' 
			ORDER BY meta_value ASC							   
		");
		foreach ($results as $value) {
			echo '<li><a href="'. get_bloginfo('url') .'/events/date/'. $value->edate .'">'.$value->edate.'</a></li>';
		}
	}
	

あとはリンクを表示したいところに下記を配置しましょう。

	event_archives();
	

これでイベントがある月のリンクだけリストで表示されると思います。
もしかしたらMySQLのバージョンによってはできないのかな??

いろいろ問題や、間違ってることろとか、すごく効率悪いことしてるところとかあると思いますが。
とりあえず動いているような気がするので、これで終わります。

参考サイト
How-to: Custom Post Types for Events