開発情報・ナレッジ

投稿者:SPIRERS ナレッジ向上チーム 2021年11月30日 (火)

PHP + Thymeleaf を使ったカレンダーのサンプルプログラム

ver.2.18 にて PHPが利用できるようになりました。
PHP が使えるようになったということで試しにカレンダーを作ってみました。
ver.2 では、PHP + Thymeleaf なので、使い方の参考になれば。

当月のみ表示のカレンダー

まず、当月のみ表示するカレンダーを作ります。
Thymeleaf の経験が少ないので、PHP 側でデータ成形を行います。

PHP
<?php

// 日付情報をセットし、Thymeleaf に渡す。
$year = date('Y');
$month = date('n');
$day = date('d');
$date = array(
    'year' => $year,
    'month' => $month,
    'day' => $day,
);
$SPIRAL->setTHValue("date", $date);

//当月1日をタイムスタンプとして取得
$unixmonth = mktime(0, 0, 0, $month, 1, $year);

// 曜日情報をセットし、Thymeleaf に渡す。
$week = array('日', '月', '火', '水', '木', '金', '土');
$SPIRAL->setTHValue("week", $week);

// カレンダー用データ生成データ生成
$calendarBoxNum = array();
$boxNum = 1;
$rowNum = 1;

// 当月1日の曜日の番号を取得し、1日以前の日付をマイナスとして扱う

// 当月1日の週初めから末日の土曜日まで枠を作る
for ($count = - (date('w', mktime(0, 0, 0, $month, 1, $year)) - 1); $count < 100; $count++) {
    // 1以上 当月数以内であれば配列にセット
    if (1 <= $count && $count <= date('t', $unixmonth)) {
        $calendarBoxNum[$rowNum][] = $count;
    } else {
        // マイナスなどの月の値でないものは枠を生成
        $calendarBoxNum[$rowNum][] = "";
    }

    // 末日を超えているかつ、末日の土曜日となった場合に終了
    if (date('t') <= $count && ($boxNum % 7) == 0) {
        break;
    }
    // 7 日分のデータがセットされた場合次のボックスへ
    if (($boxNum % 7) == 0) {
        $rowNum++;
    }
    $boxNum++;
}
// カレンダーのデータをThymeleaf に渡す。
$SPIRAL->setTHValue("calendarBoxNum", $calendarBoxNum);

?>

続いて、HTML(Thymeleaf) 側で受けとったカレンダーのデータを出力します。
動作確認のため、デザインはあてていません。(bootstrap の CSS のみ読み込んでいます。)

HTML
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">

<div class="sp-free-content-container-12" style="width: 80%;margin: 50px auto;">
    <!-- 正しくPHP のデータを受け渡せている場合に表示 -->
    <th:block th:if="${cp.result.value['error'] != true}">
        <!-- 当月の表示 -->
        <h3 class="mb-5" style="text-align: center;">
            &nbsp;<span th:text="${cp.result.value['date']['year']}"></span>年 <span th:text="${cp.result.value['date']['month']}"></span>月&nbsp;
        </h3>
        <!-- カレンダー -->
        <table class="table table-bordered">
            <!-- 曜日表示 -->
            <tr>
                <div th:each="week:${cp.result.value['week']}">
                    <th th:text="${week}">曜日</th>
                </div>
            </tr> 
            <!-- 1週間毎にまとめられている配列を回す。 -->
            <tr th:each="w:${cp.result.value['calendarBoxNum']}">
                <!-- 枠の中に日付を表示 日付が本日の場合、クラスを追加 -->
                <td th:each="d:${w.value}" th:text="${d}" th:classappend="${d} eq ${cp.result.value['date']['day']} ? bg-warning"></td>
            </tr>
        </table>
    </th:block>
</div>
完成イメージ

前月・翌月に移動できるカレンダー

次に前月・翌月に移動できるカレンダーを作ります。
URLパラメータに 年・月 をつける形を想定して、進めます。

PHP
<?php
// パラメータに年月の情報があるかチェック
if($SPIRAL->getParam("y") && $SPIRAL->getParam("m")){
    // パラメータに記載の年月のデータをセット
    $year = $SPIRAL->getParam("y");
    $month = $SPIRAL->getParam("m");
    $day = date('d');
    //パラメータの年月が不正の場合処理終了
    if(!checkdate($month, 1, $year)) {
        $SPIRAL->setTHValue("error",true);
        exit();
    }
}else{
    // パラメータがないもしくは、片方の場合は、当月をのデータをセット
    $year = date('Y');
    $month = date('n');
}

// 当月のカレンダーチェックの場合、本日の日付をセット
if(strtotime(date('Y-n')) == strtotime(date($year.'-'.$month))) {
    $day = date('d');
}else{
    $day = "0";
}

// 前月・翌月の情報を取得も日付情報をセット
$unixmonth = mktime(0, 0 , 0, $month, 1, $year); 
$date = array(
    'year' => $year,
    'month' => $month,
    'day' => $day,
    'prevY' => date('Y', strtotime('-1 month', $unixmonth)),
    'prevM' => date('m', strtotime('-1 month', $unixmonth)),
    'nextY' => date('Y', strtotime('+1 month', $unixmonth)),
    'nextM' => date('m', strtotime('+1 month', $unixmonth)),
);
// 日付情報をThymeleaf に渡す。
$SPIRAL->setTHValue("date",$date);

// 曜日情報をセットし、Thymeleaf に渡す。
$week = array('日','月','火','水','木','金','土');
$SPIRAL->setTHValue("week",$week);

// カレンダー用データ生成
$calendarBoxNum = array();
$boxNum = 1;
$rowNum = 1;

// 当月1日の曜日の番号を取得し、1日以前の日付をマイナスとして扱う
$earlyWeek = - (date('w', mktime(0, 0, 0, $month, 1, $year)) - 1);

// 当月1日の週初めから末日の土曜日まで枠を作る
for ($count = $earlyWeek; $count < 100; $count++) {
    // 1以上 当月数以内であれば配列にセット
    if (1 <= $count && $count <= date('t', $unixmonth)) {
        $calendarBoxNum[$rowNum][] = $count;
    } else {
        // マイナスなどの月の値でないものは枠を生成
        $calendarBoxNum[$rowNum][] = "";
    }

    // 末日を超えているかつ、末日の土曜日となった場合に終了
    if (date('t') <= $count && ($boxNum % 7) == 0) {
        break;
    }
    // 7 日分のデータがセットされた場合次のボックスへ
    if (($boxNum % 7) == 0) {
        $rowNum++;
    }
    $boxNum++;
}
// カレンダーのデータをThymeleaf に渡す。
$SPIRAL->setTHValue("calendarBoxNum", $calendarBoxNum);

?>

前月・翌月のリンクを設置
カレンダー部分は同様のデータ形式のため、修正はありません。

HTML
<!-- bootstrap -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet" crossorigin="anonymous">

<div class="sp-free-content-container-12" style="width: 80%;margin: 50px auto;">
    <!-- 日付が不正の場合、エラーを表示 -->
    <th:block th:if="${cp.result.value['error'] == true}">
        <p>不正な日付です。</p>
    </th:block>
    <!-- 日付が正しい場合 -->
    <th:block th:if="${cp.result.value['error'] != true}">
        <h3 class="mb-5" style="text-align: center;">
            <!-- 前月に戻るリンクを表示 -->
            <a th:href="${pages['p0997'].path} + '?y=' + ${cp.result.value['date']['prevY']} + '&m=' + ${cp.result.value['date']['prevM']}">&lt;</a>
            <!-- 当月の表示 -->
            &nbsp;<span th:text="${cp.result.value['date']['year']}"></span>年 <span th:text="${cp.result.value['date']['month']}"></span>月&nbsp;
            <!-- 翌月に戻るリンクを表示 -->
            <a th:href="${pages['p0997'].path} + '?y=' + ${cp.result.value['date']['nextY']} + '&m=' + ${cp.result.value['date']['nextM']}">&gt;</a>
        </h3>
        <!-- カレンダー -->
        <table class="table table-bordered">
            <!-- 曜日表示 -->
            <tr>
                <div th:each="week:${cp.result.value['week']}">
                    <th th:text="${week}">曜日</th>
                </div>
            </tr> 
            <!-- 1週間毎にまとめられている配列を回す。 -->
            <tr th:each="w:${cp.result.value['calendarBoxNum']}">
                <!-- 枠の中に日付を表示 日付が本日の場合、クラスを追加 -->
                <td th:each="d:${w.value}" th:text="${d}" th:classappend="${d} eq ${cp.result.value['date']['day']} ? bg-warning"></td>
            </tr>
        </table>
    </th:block>
</div>
完成イメージ

ソースコードも添付しておきます。
不具合やもっとこうしたほうがいい等あれば、リクエストボードよりご指摘ください。

解決しない場合はこちら コンテンツに関しての
要望はこちら