WordPressのget_calendar()を参考にボタン型のカレンダーを作る

PHPでカレンダーを作りたくて、リンクじゃなくてボタンにして、クリックした日付を取得したくて、WordPressのget_calendar()を改造して使ってみたメモ。何かの用途へのヒントにでもなれば幸いです。
枠組み
<?php
//タイムゾーンセット
date_default_timezone_set('Asia/Tokyo');
//本日を取得
$date = date('Y-m-d'); //YYYY-MM-DDの形
if ( isset($_POST['calendar']) ){
//クリックされた日付を取得
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Title</title>
</head>
<body>
<!-- クリックされた日付を出力 -->
<?php echo $date; ?>
<!-- カレンダー部分 -->
<div id="calendar_box">
<?php get_rsv_calendar(2014, 4, $date); ?>
</div>
</body>
</html>
2~5行目は下準備。タイムゾーンを合わせて、まずは現在の日にちを$dateという変数に入れておきます。7行目に入る記述はのちほど。
18行目で日付が出力されます。最初にページが開いた時は本日、カレンダーがクリックされたら、その日付が入るようにします。
21行目でカレンダーを出力します。get_rsv_calendar()という独自の関数を作って、指定した年と月のものを出力させます。
どんな構造のカレンダーが欲しいのか
本題に入る前に、htmlとしてどんな形のカレンダーを出力させたいのか考えてみます。
既存のものを見てみる
WordPressでカレンダーと言えば、get_calendar()というテンプレートタグがありますよね。当月の投稿リンク付きのカレンダーが出力されます。このテンプレートタグの実態はwp-includes/general-template.phpに書いてあって、出力されたhtmlは(私の環境では)こんな感じでした。
<table id="wp-calendar"> <caption>2014年4月</caption> <thead> <tr> <th scope="col" title="日曜日">日</th> <!-- 略(土曜まで続く) --> </tr> </thead> <tfoot> <tr> <td colspan="3" id="prev"><a href="先月のURL" title="2014年3月の投稿を表示">« 3月</a></td> <td class="pad"> </td> <td colspan="3" id="next" class="pad"> </td> </tr> </tfoot> <tbody> <tr> <td colspan="2" class="pad"> </td> <td><a href="URL" title="タイトル">1</a></td> <td>2</td> <td>3</td> <td><a href="URL" title="タイトル">4</a></td> <td>5</td> </tr> <tr> <!-- 略(最終日まで続く) --> </tr> </tbody> </table>
ここまでのテンプレートがあるんですから、これを使わない手はないですよねー! でも投稿へのリンクが出したいわけではなくて、日付部分はsubmitボタンにして、どのボタンが押されたか特定しないといけないとなると…
こんな形で出力したい
<table class="rsv_calendar"> <caption>2014年4月</caption> <thead> <tr> <th scope="col" title="日曜日">日</th> <!-- 略(土曜まで続く) --> </tr> </thead> <tbody> <tr> <td colspan="2" class="pad"> </td> <td> <form action="" method="post"> <input type="hidden" name="date" value="2014-04-01" /> <input type="submit" name="calendar" value="1"> </form> </td> <td> <form action="" method="post"> <input type="hidden" name="date" value="2014-04-02" /> <input type="submit" name="calendar" value="2"> </form> </td> <!-- 略 --> </tr> <tr> <!-- 略(最終日まで続く) --> </tr> </tbody> </table>
日付の情報をhidden属性で持たせて、ボタンのvalueを日にして、こんな形にして出せばいいのか、な? これなら、
<?php
//タイムゾーンセット
date_default_timezone_set('Asia/Tokyo');
//本日を取得
$date = date('Y-m-d'); //YYYY-MM-DDの形
if ( isset($_POST['calendar']) ){
//クリックされた日付を取得
}
?>
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Title</title>
</head>
<body>
<!-- クリックされた日付を出力 -->
<?php echo $date; ?>
<!-- カレンダー部分 -->
<div id="calendar_box">
<?php get_rsv_calendar(2014, 4, $date); ?>
</div>
</body>
</html>
さっきコードのこの部分を、
if ( isset($_POST['calendar']) ){
$date = (string)filter_input(INPUT_POST, 'date');
}
こう書くことで、クリックされたボタンと同じform内のhidden属性、dateという名前の「YYYY-MM-DD」というvalueを取得できます。これを、冒頭で現在の日付を格納した$dateへ上書きすることによって、クリックした日付を表示できる、という流れになります。
もうちょっと省コードで
上記のコードでも良いと思うのですが、formとhidden属性と、同じことを書いてる部分が多すぎるような…。。もうちょっと軽くならないかなぁと調べてみたところ、
こんな書き方が!ありがとうございます!!
<table class="rsv_calendar"> <caption>2014年4月</caption> <thead> <tr> <th scope="col" title="日曜日">日</th> <!-- 略(土曜まで続く) --> </tr> </thead> <tbody> <form action="" method="post"> <tr> <td colspan="2" class="pad"> </td> <td><input type="submit" name="calendar[2014-04-01]" value="1"></td> <td><input type="submit" name="calendar[2014-04-02]" value="2"></td> <!-- 略 --> </tr> <tr> <!-- 略(最終日まで続く) --> </tr> </tbody> </table>
このようにボタン名の配列に日付情報を持たせれば、
if ( isset($_POST['calendar']) ){
$date = !is_string(key($_POST['calendar'])) ? '' : key($_POST['calendar']);
}
取得部分はこんな風に書けば良いんじゃないでしょうか。formの記述も1月分で1組にまとめられて、スッキリしました!
カレンダーの関数を作る
では、上記の形のhtmlを出力すべく、関数をつくります。
get_calendar()のカスタマイズ
wp-includes/general-template.phpにあるのをコピーしてきて、get_rsv_calendar(年, 月, YYYY-MM-DD)という関数で呼び出せるよう修正してみます。
functions.php
function get_rsv_calendar($yyyy, $mm, $date, $initial = true, $echo = true) {
global $wpdb, $wp_locale;
// week_begins = 0 stands for Sunday
$week_begins = intval(get_option('start_of_week'));
// Let's figure out when we are
$thisyear = $yyyy;
$thismonth = $mm;
$unixmonth = mktime(0, 0 , 0, $thismonth, 1, $thisyear);
$last_day = date('t', $unixmonth);
/* translators: Calendar caption: 1: month name, 2: 4-digit year */
$calendar_caption = _x('%1$s %2$s', 'calendar caption');
$calendar_output = '<table class="rsv_calendar">
<caption>' . sprintf($calendar_caption, $wp_locale->get_month($thismonth), date('Y', $unixmonth)) . '</caption>
<thead>
<tr>';
$myweek = array();
for ( $wdcount=0; $wdcount<=6; $wdcount++ ) {
$myweek[] = $wp_locale->get_weekday(($wdcount+$week_begins)%7);
}
foreach ( $myweek as $wd ) {
$day_name = (true == $initial) ? $wp_locale->get_weekday_initial($wd) : $wp_locale->get_weekday_abbrev($wd);
$wd = esc_attr($wd);
$calendar_output .= "\n\t\t<th scope=\"col\" title=\"$wd\">$day_name</th>";
}
$calendar_output .= '
</tr>
</thead>
<tbody>
<form action="" method="post">
<tr>';
// See how much we should pad in the beginning
$pad = calendar_week_mod(date('w', $unixmonth)-$week_begins);
if ( 0 != $pad )
$calendar_output .= "\n\t\t".'<td colspan="'. esc_attr($pad) .'" class="pad"> </td>'."\n\t\t";
$daysinmonth = intval(date('t', $unixmonth));
for ( $day = 1; $day <= $daysinmonth; ++$day ) {
if ( isset($newrow) && $newrow )
$calendar_output .= "\n\t</tr>\n\t<tr>\n\t\t";
$newrow = false;
$sp_date = explode("-", $date);
if ( $day == $sp_date[2] && $thismonth == $sp_date[1] && $thisyear == $sp_date[0] )
$calendar_output .= '<td id="current">';
elseif ( $day == gmdate('j', current_time('timestamp')) && $thismonth == gmdate('m', current_time('timestamp')) && $thisyear == gmdate('Y', current_time('timestamp')) )
$calendar_output .= '<td id="today">';
else
$calendar_output .= '<td>';
$calendar_output .= '<input type="submit" name="calendar['.$thisyear.'-'.str_pad($thismonth,2,"0",STR_PAD_LEFT).'-'.str_pad($day,2,"0",STR_PAD_LEFT).']" value="'.$day.'">';
$calendar_output .= "</td>\n\t\t";
if ( 6 == calendar_week_mod(date('w', mktime(0, 0 , 0, $thismonth, $day, $thisyear))-$week_begins) )
$newrow = true;
}
$pad = 7 - calendar_week_mod(date('w', mktime(0, 0 , 0, $thismonth, $day, $thisyear))-$week_begins);
if ( $pad != 0 && $pad != 7 )
$calendar_output .= "\n\t\t".'<td class="pad" colspan="'. esc_attr($pad) .'"> </td>';
$calendar_output .= "\n\t</tr>\n\t</form>\n\t</tbody>\n</table>";
if ( $echo )
echo apply_filters( 'get_calendar', $calendar_output );
else
return apply_filters( 'get_calendar', $calendar_output );
}
カスタマイズしたものがこちら。長くてすみません…! 週の始まりをいつにするとかそういう一般設定からできるところも極力残しています。
呼び出すget_rsv_calendar(年, 月, YYYY-MM-DD)の3つめの引数の日付と一致するtdにはid=”current”を、本日のtdにはid=”today”を付加するので、CSSで色を調整できます。
WPじゃない場合
さらに、普通のPHPで動かしたいとき用に、上記のコードからWP特有の部分を外したものがこちら。
function get_rsv_calendar($yyyy, $mm, $date) {
$thisyear = $yyyy; //年
$thismonth = $mm; //月
$unixmonth = mktime(0, 0 , 0, $thismonth, 1, $thisyear); //該当月1日のタイムスタンプ
$last_day = date('t', $unixmonth); //該当月の日数
$calendar_output = '<table class="rsv_calendar">
<caption>' . $thisyear .'年' . $thismonth . '月' . '</caption>
<thead>
<tr>';
$myweek = array("日", "月", "火", "水", "木", "金", "土"); //曜日定義
foreach ( $myweek as $wd ) {
$calendar_output .= "\n\t\t<th scope=\"col\" title=\"$wd\">$wd</th>"; //曜日出力
}
$calendar_output .= '
</tr>
</thead>
<tbody>
<form action="" method="post">
<tr>';
$pad = date('w', $unixmonth); //該当月1日の曜日番号(日:0~土:6)
if ( 0 != $pad )
$calendar_output .= "\n\t\t".'<td colspan="'. $pad .'" class="pad"> </td>'."\n\t\t";
for ( $day = 1; $day <= $last_day; ++$day ) { //1日から最終日まで繰り返し
if ( isset($newrow) && $newrow )
$calendar_output .= "\n\t</tr>\n\t<tr>\n\t\t"; //行フラグ$newrowがtrueなら
$newrow = false; //行フラグリセット
$sp_date = explode("-", $date); //引数$dateを分割
if ( $day == $sp_date[2] && $thismonth == $sp_date[1] && $thisyear == $sp_date[0] )
$calendar_output .= '<td id="current">'; //引数と一致する日にid付加
elseif ( $day == date('j') && $thismonth == date('m') && $thisyear == date('Y') )
$calendar_output .= '<td id="today">'; //本日と一致する日にid付加
else
$calendar_output .= '<td>';
$calendar_output .= '<input type="submit" name="calendar['.$thisyear.'-'.str_pad($thismonth,2,"0",STR_PAD_LEFT).'-'.str_pad($day,2,"0",STR_PAD_LEFT).']" value="'.$day.'">';
$calendar_output .= "</td>\n\t\t";
if ( 6 == date('w', mktime(0, 0 , 0, $thismonth, $day, $thisyear)) )
$newrow = true; //最終列なら行フラグを立てる
}
$pad = 7 - date('w', mktime(0, 0 , 0, $thismonth, $day, $thisyear));
if ( $pad != 0 && $pad != 7 )
$calendar_output .= "\n\t\t".'<td class="pad" colspan="'. $pad .'"> </td>'; //余ったtdを埋める
$calendar_output .= "\n\t</tr>\n\t</form>\n\t</tbody>\n</table>";
echo $calendar_output; //出力
}
これで、WPじゃなくてもカレンダーが出力されます。
ブラウザで見てみる
ちゃんと実装できてれば、現時点ではこのような形になるはず。(CSSによって見え方は違います。)
ちゃんとカレンダーがボタンで出来てますね。クリックして動作を確かめてみましょう。日付が反映されればOKです!
CSSでテキストリンクっぽく
/* カレンダー */
table.rsv_calendar {
width:180px;
margin-bottom:30px;
}
table.rsv_calendar caption{
font-weight:bold;
margin-bottom:5px;
}
table.rsv_calendar td,
table.rsv_calendar th {
text-align:center;
}
table.rsv_calendar td#today {
background-color:#eee; /* 今日の背景色 */
}
table.rsv_calendar td#current {
background-color:#ffebae; /* クリックした日の背景色 */
}
table.rsv_calendar td input { /* カレンダー内inputボタン */
background: none;
border: none;
padding: 0;
box-shadow: none;
font-size: 12px;
color: #21759b;
text-decoration: underline;
}
table.rsv_calendar td input:hover {
color: #0f3647; /* リンクホバー色 */
background: none;
}
CSSをこんなふうに書いてみると、
カレンダーっぽくなりました。クリックした日と本日に色がつきます。もちろん色はお好みで。
なお、
table.rsv_calendar {
width:180px;
margin-bottom:30px;
}
この部分を
table.rsv_calendar {
width:180px;
margin-right:30px;
float:left;
}
と書けば、カレンダーが横並びになります。
今月から3ヶ月分出力する
さて、だいたいこれでOKなのですが、3ヶ月分出したいからと言って
<!-- カレンダー部分 --> <div id="calendar_box"> <h2>カレンダー</h2> <?php get_rsv_calendar(2014, 4, $date); get_rsv_calendar(2014, 5, $date); get_rsv_calendar(2014, 6, $date); ?> </div>
と、書くわけにはいかないですよね。ここも変数で書いてみます。
<!-- カレンダー部分 -->
<div id="calendar_box">
<h2>カレンダー</h2>
<?php
$get_y = date('Y'); //本日の年
$get_m = date('n'); //本日の月
$i = 0;
while ( $i < 3 ) { //今月から3つ出したかったら
get_rsv_calendar($get_y, $get_m, $date); //カレンダー出力
$get_m++; //月+1
if ( $get_m > 12 ) { //12月を超えたら
$get_m = 1; //1月へ
$get_y++; //年+1
}
$i++;
}
?>
</div>
このように書くことで、本日の日付から計算してカレンダーを取得してくれます。8行目の数値を変えれば、何ヶ月分でも取得できます。
キャプチャはこんな感じ。以上ですー!





コメントは承認制ですので、反映までしばらくお待ち下さい。(稀にスパムの誤判定にて届かないこともあるようですので、必要な際はお問い合わせからお願い致します。)
YouTubeでQ&Aコンテンツを企画しています
運営しているYouTubeチャンネルで、ご相談やご質問を募集しています。動画のコメントやお問い合わせページからお気軽にご相談をお寄せください。