つながるnet

カレンダーを自作してイベントと関連付けよう!

WordPressでカレンダーを表示するプラグインはたくさんありますが、なかなか希望する仕様のものがないことってありませんか?そのような場合は、カレンダーを自作してしまいましょう。

公開した後にありがたい次のようなコメントをいただきました。

  • WordPressで date関数を使うと時差分ずれるので、date_i18n関数を使った方がよいです。
  • エスケープ処理はしっかりと
  • 毎回APIのリクエストするのは遅延に繋がります。Transients APIの利用を検討ください。
  • file_get_contents で祝日を取得していますが、WordPress であれば、wp_remote_getを使った方が、タイムアウトなどの設定もできてお得です。
  • 投稿の関連付けの部分で、一カ月の日数分get_posts によるクエリーが発生してしまっています。カレンダーを表示するだけなら、月間のイベントを一気に取得して、PHPで振り分けた方がエコなはずです。

今回のレシピは以下のとおりです。

WordPressを題材にしていますが、ほとんどはPHPのコードですね。

カレンダーを自作してショートコード化しよう

ショートコードの書式:

[my-calendar ym=$yyyy-mm]

ymの値がなければ現在の年月を表示

functions.phpの記述:

/***********************************************
 * カレンダーを表示しよう
 ***********************************************/
function shortcode_my_calendar($params = array()) {

		// ショートコードのパラメータを取得
    extract(shortcode_atts(array(
        'ym' => null
    ), $params));

		// 現在の年月日を取得
		$current_y = date_i18n('Y');
		$current_m = date_i18n('m');
		$current_d = date_i18n('d');

		// ショートコードで年月が指定していなければ、現在の年月を設定
		$calendar_ym = ( $params['ym'] ) ? $params['ym'] : $current_y . '-' . $current_m;

		// 該当月のイベントを取得し配列に格納
		$args = array(
				'post_status' => 'publish',
				'posts_per_page' => -1, // 全件取得
				'meta_query' => array(
						array(
								'key' => 'event_date',
								'value' => $calendar_ym,
								'compare' => 'LIKE'
						)
				)
		);
		$event_posts = get_posts( $args );
		$events = array();
		if ( $event_posts ) {
				foreach ( $event_posts as $post ) {
						$event_date = esc_html( get_post_meta( $post -> ID, 'event_date', true ) );
						$event_link = get_permalink( $post -> ID );
						$events[$event_date][] = "<a href='{$event_link}'>●</a>";
				}
		}

		// 表示年月の日数を取得
		$calendar_t = date_i18n('t', strtotime($calendar_ym.'-01'));

		// 祝日の取得
		$holidays = get_holidays ($calendar_ym, $calendar_t);

		// カレンダー表示 ?>
<h3><?php echo $calendar_ym ?></h3>
<table class="calendar">
		<tr>
				<th class="w0">日</th>
				<th class="w1">月</th>
				<th class="w2">火</th>
				<th class="w3">水</th>
				<th class="w4">木</th>
				<th class="w5">金</th>
				<th class="w6">土</th>
		</tr>
		<tr>
				<?php
		$dayArr = array('日', '月', '火', '水', '木', '金', '土');
		$index = 0;
		for ( $i = 1; $i <= $calendar_t; $i++ ):
				$calendar_day = date_i18n('w', strtotime($calendar_ym . '-' . $i));
				$calendar_date = ( $i < 10 ) ? '0' . $i : $i;

				// 1日が日曜日ではない場合の空白セル
				if ( $i == 1 && $calendar_day != 0 ):
						for ( $index = 0; $index < $calendar_day; $index++ ):
						?>
				<td class="<?php echo 'w' . $index ?>"> </td>
				<?php
						endfor;
				endif;

				// 祝日かどうか
				$hol = ( in_array( $calendar_ym . '-' .  $calendar_date, $holidays ) ) ? ' hol' : '';

				// 日付表示 ?>
				<td class="<?php echo 'w' . $calendar_day .$hol ?>"><span class="date"><?php echo $i ?></span><?
						// 該当日付のイベントがあればリンクを表示
						if ( isset($events[$calendar_ym . '-' .  $calendar_date]) && count( $events[$calendar_ym . '-' .  $calendar_date] ) > 0 ) {
								foreach ( $events[$calendar_ym . '-' .  $calendar_date] as $event ) {
										echo $event;
								}
						} ?></td>
				<?php

				// 土曜日なら行末
				if ( $calendar_day == 6 ): ?>
		</tr>
		<tr>
				<?php
				endif;

				$index++;

				// 最終日の後の空白
				if ( $i == $calendar_t && $index < 42 ):
						for ( $index; $index < 42; $index++ ):
								if ( $calendar_day == 6 ) {
										$calendar_day = 0; ?>
		</tr>
		<tr>
				<?php
								} elseif ( $calendar_day < 6 ) {
										$calendar_day++;
								} ?>
				<td class="<?php echo 'w' . $calendar_day ?>"> </td>
				<?php
						endfor;
				endif;
		endfor;
		?>
		</tr>
</table>
<?php
}
add_shortcode('my-calendar', 'shortcode_my_calendar');

特に説明を加えるポイントはないのですが、
80行目:日付を表示。曜日クラスを付けていますので、CSSで装飾。

もう少し全体をすっきりできそうな気がします。

加えて
45行目 get_holidays():祝日取得については下のファンクションを参照
58行目 my_function():例えばイベント投稿を取得(下のファンクションを参照)
イベントの関連付けは「ありがたいコメント」にしたがい、上記コード内のカレンダー表示の前に処理しています。
その概説は:

カレンダーに投稿を関連付けよう

20-39行目:カスタムフィールドのキーで絞り込み抽出し、配列に一旦格納
81-86行目:該当日付のイベントが配列に存在していれば表示
各日付ごとにクエリが発生していたのに対し、一月まとめてクエリすることによりわずか一月分の表示でも、確かに体感スピードもアップしました。

祝日を反映させよう

/***********************************************
 * 祝日取得
 ***********************************************/
function get_holidays ($ym, $t) {

		$holidays = get_transient( 'holidays'.$ym );
		if ( !$holidays ) {
				$holidays = array();
				$holidays_url = sprintf(
						'http://google.com/calendar/feeds/%s/public/full-noattendees?start-min=%s&start-max=%s&max-results=%d&alt=json' ,
						'outid3el0qkcrsuf89fltf7a4qbacgt9@import.calendar.google.com' ,
						"{$ym}-01",   // 取得開始日
						"{$ym}-{$t}", // 取得終了日
						50            // 最大取得数
				);
				if($results=wp_remote_get($holidays_url, array('timeout'=> 5 ))) {
						$results = json_decode($results['body'], true);
						foreach($results['feed']['entry'] as $val) {
								$date = $val['gd$when'][0]['startTime']; // 日付を取得
								$holidays[] = $date;
						}
				}
				set_transient( 'holidays'.$ym, $holidays, 3600 * 24 );
		}

	return $holidays;

}

Google APIを利用しています。以下を参照させていただきました。
http://gomokuankake.com/2013/04/08/3403/

wp_remote_getを利用してタイムアウトを設定

file_get_contents で祝日を取得していましたが、WordPressの関数 wp_remote_get に変更して、タイムアウトも設定しました。

Transients APIの利用

これが少し難しかったのですが、詳しくはCodexを参照してみてください。

誰かがページにアクセスするたびにAPIを読み込んで処理をするのではなくて、データベースに期限付きで格納しておくことによりここもスピードアップすることができます。

しかし、今回のケースで問題なのが、月が変われば当然祝日も変わってしまうので、単純に何日たったらリフレッシュとは定義できないことです。それで、年月も合わせて格納するキーの名前にしてみました。

おまけ:CSSで装飾

.calendar {
	border-collapse:collapse;
	width:100%;
}
.calendar tr th,
.calendar tr td {
	border:solid 1px #CCC;
	text-align:center;
	height:3em;
	width:14%;
}
.calendar tr th {
	font-weight:bold;
}
.calendar tr th.w0,
.calendar tr td.w0 {
	background-color:#FFDFDF;
	color:#F33;
	width:15%;
}
.calendar tr th.w6,
.calendar tr td.w6 {
	background-color:#DDF4FF;
	color:#09F;
	width:15%;
}
.calendar tr td.hol {
	background-color:#FFDFDF;
	color:#F33;
}
.calendar tr td span.date {
	display:block;
	line-height:1;
}

完成イメージ
calendar01

雑感

ちょうどよいプラグインがあればどんどん使えばいいのですが、そうでない時は自作できるようになっておくといろいろ便利に使いまわせそうです。

今回のようにありがたいコメント(冒頭に記述)をいただけるのはいいですね。
ロジックを見直すことやWordPressの関数に精通することにより、スピードアップセキュリティーにも配慮を払うというのはとても大事ですね。
公開してから、ちょっと内容が変わりましたが、とても勉強になりました。

コメントに基づいて参考にさせていただいたURLは:
WordPress を扱う上で憶えておいて欲しい3つのesc
WordPressのTransients APIを用いて表示の高速化を図る
wp_remote_get は file_get_contents よりも応用できてよいですね

さらにおかしな点に気付かれましたらお気軽にご指摘ください。

直接メールしてみる

お名前 (必須)
メールアドレス (必須)
件名
ご相談内容
画像文字をご入力ください
captcha

コメントをどうぞ

コメント:5 件

  1. トムソン

    質問よろしいでしょうか?
    自作カレンダーで、
    過去の同じ月日にあったモノも
    表示させようとしているのですが
    うまくいきません。
    お知恵を拝借できないでしょうか?
    お助けください。

    返信
  2. トムソン

    はじめまして、トムソンと申します。
    WordPressイベントカレンダー作成の参考にと検索を辿ってきました。
    とても整理されていて分かりやすいです。
    質問というか教えてください。
    前後月へのリンクをこのカレンダーにつける場合はどうすればよいでしょうか?

    返信
    1. hayasaki yusuke

      URLにクエリを渡すようにしたらどうでしょうか。見出しで表示月を表示していますので、その前後に、$calendar_ymの前月と次月とのリンクを設ける感じです。そのリンク先は、src=”現在のURL/?ym=2015-02″ などとできればPHPでそのクエリを受け取り、それを、$calendar_ym として渡せれば実現できそうです。

      返信
      1. トムソン

        早速の返信ありがとうございます。
        教えて頂いた内容でトライしてみます。
        ありがとうございました。

        返信
      2. Tsubasa Oji

        すいません、前後月の件ですが、よく理解できておりません。
        前後月の具体的なコードを教えていただけますでしょうか?

        返信

コメントを残す