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; }
完成イメージ
雑感
ちょうどよいプラグインがあればどんどん使えばいいのですが、そうでない時は自作できるようになっておくといろいろ便利に使いまわせそうです。
今回のようにありがたいコメント(冒頭に記述)をいただけるのはいいですね。
ロジックを見直すことやWordPressの関数に精通することにより、スピードアップやセキュリティーにも配慮を払うというのはとても大事ですね。
公開してから、ちょっと内容が変わりましたが、とても勉強になりました。
コメントに基づいて参考にさせていただいたURLは:
WordPress を扱う上で憶えておいて欲しい3つのesc
WordPressのTransients APIを用いて表示の高速化を図る
wp_remote_get は file_get_contents よりも応用できてよいですね
さらにおかしな点に気付かれましたらお気軽にご指摘ください。
コメント失礼致します。
こちらの記事を参考に、イベントカレンダーを作成しようと考えております。
記事から大変時間が立っておりますが、うまくいかないので、どうかお力を貸して頂けると幸いです。
この記事の一番始めにでてくる
functions.phpの記述をコピペして、記述を試みると、phpエラーになってしまいます。
phpエラーは「Parse error: syntax error, unexpected ‘ ‘」となっております。
当方、まだphpを勉強の身で、至らない部分あるかと思いますが、
アドバイスをくださると幸いです。
どうぞよろしくお願いいたします。
尚、この記事での記述がない場合は正常にWEBサイトは稼働しております。
よろしくお願い致します。
再現しません。
>phpエラーは「Parse error: syntax error, unexpected ‘ ‘」となっております。
ということですが、『構文エラー:不要なコードが入っている』という意味ですが、このエラーメッセージ全文はどうなっていますか?
ご返信ありがとうございますm(__)m
大変感謝ししております。
Parse error: syntax error, unexpected ‘ ‘ (T_STRING) in /home/example/www/example/wp-content/themes/example_theme/functions.php on line 389
上記がエラーメッセージの全文となっております。
389列目は「 // ショートコードのパラメータを取得」の部分になります。
テキスト前の空白を部分的や、全て消してみたりもしましたが、そうなると他の部分の構文エラー表示になります。
まだ、コードの内容がイマイチ理解できておらず、
確認も含めお伺いさせて頂きたいのですが、47列目の「?>」と117列目の「<?php」はどの部分の囲いになるんでしょうか??
かなり奮闘しておられるようですね。
コピペの時点で、わたしが想定できない何かをしておられるのだと思います。
>47列目の「?>」と117列目の「<php」はどの部分の囲いになるんでしょうか??
と述べておられますが、これらは、『PHPコードの終わり』『PHPコードの始まり』を意味するものです。
このサイトで紹介しているのは、コピペで実装できるものはほとんどありません。
開発者がWP開発時のアイデアを紹介しているページです。
このページにあるコードについてお尋ねになる段階ではないものと思われます。
たいへん申し訳ありません。
すいません、私の書き方が悪かったですね..
PHPコードの終わりや始まりの意味はモチロン理解しております。
自身でショートコードの作成やfunction.phpへの記載は
他の部分は全く問題なく稼働しております。
また、色々自身で作業しましたら、無事カレンダーの表示まで至りました。
(やはり空白スペースと半角スペースの問題が原因でした。)
ご迷惑をおかけして申し訳なかったです。
手助け頂いて、本当にありがとうございます。
現在はカスタム投稿内のカスタムフィールドで選んだイベント日時と
カレンダーを関連付けできるよう奮闘しております。
ありがとうございましたm(__)m
解決できたということ、ほっとしました。なかなか核心をつくことができず申し訳ありませんでした。
質問よろしいでしょうか?
自作カレンダーで、
過去の同じ月日にあったモノも
表示させようとしているのですが
うまくいきません。
お知恵を拝借できないでしょうか?
お助けください。
はじめまして、トムソンと申します。
WordPressイベントカレンダー作成の参考にと検索を辿ってきました。
とても整理されていて分かりやすいです。
質問というか教えてください。
前後月へのリンクをこのカレンダーにつける場合はどうすればよいでしょうか?
URLにクエリを渡すようにしたらどうでしょうか。見出しで表示月を表示していますので、その前後に、$calendar_ymの前月と次月とのリンクを設ける感じです。そのリンク先は、src=”現在のURL/?ym=2015-02″ などとできればPHPでそのクエリを受け取り、それを、$calendar_ym として渡せれば実現できそうです。
早速の返信ありがとうございます。
教えて頂いた内容でトライしてみます。
ありがとうございました。
すいません、前後月の件ですが、よく理解できておりません。
前後月の具体的なコードを教えていただけますでしょうか?