CakePHPでカレンダーヘルパーを作ってみた

http://cakephp.jp/modules/newbb/viewtopic.php?topic_id=1786&forum=9&post_id=4263からお越しの方、祝日判定付きではありませんので、ご注意ください。

CakePHPのhelperをテスト的に作ることにしました。とりあえず現実的に利用機会がありそうなCalandarHelperにしました。

本当は祝日判定とかやるべきなんでしょうけど、春分の日秋分の日の確定が面倒な上に、祝日情報はメンテナンスを考えるとデータベースで保持する方がよさそうなので、特にやっていません。(気が向いたらやるかもしれません)

ってことで単純にカレンダーを表示するだけになっちゃいました。(CSSも別途記述する必要があります)

CalendarHelperは、app/views/helpers/calendar.phpに作成します。

<?php
/**
 * カレンダーヘルパー.
 *
 */
class CalendarHelper extends AppHelper {
    var $_defaultLang = 'en';
    var $_week = array(
        'en' => array('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'),
        'ja' => array('', '', '', '', '', '', '')
    );

    /**
     * カレンダーを生成する.
     *
     * @param string $lang 言語
     * @param integer $date 日付
     * @return カレンダー
     */
    function makeCalendar($lang = null, $date = null) {
        if (is_null($date)) {
            $date = date('Ymd');
        }
        if (is_null($lang)) {
            $lang = $this->_defaultLang;
        }

        $year = substr($date, 0, 4);
        $month = substr($date, 4, 2);
        $day = substr($date, 6, 2);
        if (!checkdate($month, $day, $year)) {
            $this->log('Invalid date format!');
        }
        return $this->output(
            "<div id=\"calendar\"><div id=\"calendar-header\">".$year."".$month."月</div><table id=\"calendar-content\">".
            $this->_makeWeekHeader($lang).$this->_makeCalendarContent($year, $month, $date)."</table></div>"
        );
    }

    /**
     * 週ヘッダーを生成する.
     *
     * @param string $lang 言語
     * @return 週ヘッダー
     */
    function _makeWeekHeader($lang = null) {
        if (is_null($lang)) {
            $lang = $this->_defaultLang;
        }
        $cell = array();
        foreach ($this->_week[$lang] as $weekId => $week) {
            array_push($cell, "<td class=\"week_".strtolower($this->_week[$this->_defaultLang][$weekId])."\">".$week."</td>");
        }
        return "<tr id=\"week_header\">".implode("", $cell)."</tr>";
    }

    /**
     * カレンダーコンテンツを生成する.
     *
     * @param integer $year 年
     * @param integer $month 月
     * @param integer $selecteday 選択日
     * @return カレンダーコンテンツ
     */
    function _makeCalendarContent($year, $month, $selectedDay) {
        if (!is_numeric($year) || !is_numeric($month) || !is_numeric($selectedDay)) {
            throw new Exception("Invalid parameters!");
        }

        $calendar = array();
        $weekNo = 1; // 月内での週番号(第○週)
        $last = substr($this->_getLastDay($year, $month), 6, 2);
        for ($day = 1; $day <= $last; $day++) {
            $date = $year.$month.sprintf("%02d", $day); // Ymd形式
            $weekId = $this->_getWeekIdOfDay($year, $month, $day, $this->_defaultLang);
            $attr = array();
            if ($this->_getToday() == $date) {
                array_push($attr, 'today');
            }
            if ($selectedDay == $date) {
                array_push($attr, 'selected');
            }
            // TODO 祝日判定
            if (false) {
                array_push($attr, 'holiday');
            }
            $calendar[$weekNo][$weekId] = array('day' => $day, 'attribute' => $attr);
            // 翌週へ
            if ($weekId == count($this->_week[$this->_defaultLang]) - 1) {
                $weekNo++;
            }
        }
//        debug($calendar);

        $default = array('day' => '', 'attribute' => array());
        $c = array();
        for ($rowIdx = 1; $rowIdx <= count($calendar); $rowIdx++) {
            $elements = array();
            for ($colIdx = 0; $colIdx < count($this->_week[$this->_defaultLang]); $colIdx++) {
                $day = $default;
                if (array_key_exists($colIdx, $calendar[$rowIdx])) {
                    $day = $calendar[$rowIdx][$colIdx];
                }
//                debug($day);
                array_push(
                    $elements,
                    "<td class=\"week_".strtolower($this->_week[$this->_defaultLang][$colIdx])." ".implode(" ", $day['attribute'])."\">".$day['day']."</td>"
                );
            }
            array_push($c, "<tr class=\"week\">".implode("", $elements)."</tr>");
        }
        return implode("", $c);
    }

    /**
     * 月初の日付を返す.
     *
     * @param integer $year 年
     * @param integer $month 月
     * @param unknown_type $format フォーマット
     * @return 月初の日付
     */
    function _getFirstDay($year, $month, $format = 'Ymd') {
        return date($format, mktime(0, 0, 0, $month, 1, $year));
    }
    /**
     * 月末の日付を返す.
     *
     * @param integer $year 年
     * @param integer $month 月
     * @param string $format フォーマット
     * @return 月末の日付
     */
    function _getLastDay($year, $month, $format = 'Ymd') {
        return date($format, mktime(0, 0, 0, $month + 1, 0, $year));
    }
    /**
     * 日付を返す.
     *
     * @param integer $year 年
     * @param integer $month 月
     * @param integer $day 日
     * @param string $format フォーマット
     * @return 月末の日付
     */
    function _getDay($year, $month, $day, $format = 'Ymd') {
        return date($format, mktime(0, 0, 0, $month, $day, $year));
    }
    /**
     * 今日の日付を返す.
     *
     * @param string $format フォーマット
     * @return 今日の日付
     */
    function _getToday($format = 'Ymd') {
        return date($format);
    }
    /**
     * 曜日IDを返す.
     *
     * @param integer $year 年
     * @param integer $month 月
     * @param integer $day 日
     * @param string $lang 言語
     * @return 曜日ID
     */
    function _getWeekIdOfDay($year, $month, $day, $lang = null) {
        if (is_null($lang)) {
            $lang = $this->_defaultLang;
        }
        return $this->_getDay($year, $month, $day, 'w');
    }
    /**
     * 曜日を返す.
     *
     * @param integer $year 年
     * @param integer $month 月
     * @param integer $day 日
     * @param string $lang 言語
     * @return 曜日
     */
    function _getWeekOfDay($year, $month, $day, $lang = null) {
        if (is_null($lang)) {
            $lang = $this->_defaultLang;
        }
        return $this->_week[$lang][$this->_getDay($year, $month, $day, 'w')];
    }
}
?>

view側では、以下のように記述します。

  • 日付を指定する場合
<?php echo $calendar->makeCalendar('ja', '20090101'); ?>
  • 今日の日付の場合
<?php echo $calendar->makeCalendar('ja'); ?>

日付の指定をしなかった場合の出力は以下のようになります。(1行が長くなってしまうので適宜改行を入れています)

<div id="calendar">
  <div id="calendar-header">2009年2月</div>
  <table id="calendar-content">
    <tr id="week_header">
      <td class="week_sun"></td>
      <td class="week_mon"></td>
      <td class="week_tue"></td>
      <td class="week_wed"></td>
      <td class="week_thu"></td>
      <td class="week_fri"></td>
      <td class="week_sat"></td>
    </tr>
    <tr class="week">
      <td class="week_sun ">1</td>
      <td class="week_mon ">2</td>
      <td class="week_tue ">3</td>
      <td class="week_wed ">4</td>
      <td class="week_thu ">5</td>
      <td class="week_fri ">6</td>
      <td class="week_sat ">7</td>
    </tr>
    <tr class="week">
      <td class="week_sun ">1</td>
      <td class="week_mon ">2</td>
      <td class="week_tue ">3</td>
      <td class="week_wed ">4</td>
      <td class="week_thu ">5</td>
      <td class="week_fri ">6</td>
      <td class="week_sat ">7</td>
    </tr>
    <tr class="week">
      <td class="week_sun ">8</td>
      <td class="week_mon ">9</td>
      <td class="week_tue ">10</td>
      <td class="week_wed ">11</td>
      <td class="week_thu ">12</td>
      <td class="week_fri ">13</td>
      <td class="week_sat ">14</td>
    </tr>
    <tr class="week">
      <td class="week_sun ">15</td>
      <td class="week_mon ">16</td>
      <td class="week_tue ">17</td>
      <td class="week_wed ">18</td>
      <td class="week_thu ">19</td>
      <td class="week_fri ">20</td>
      <td class="week_sat ">21</td>
    </tr>
    <tr class="week">
      <td class="week_sun ">22</td>
      <td class="week_mon ">23</td>
      <td class="week_tue ">24</td>
      <td class="week_wed ">25</td>
      <td class="week_thu ">26</td>
      <td class="week_fri today selected">27</td>
      <td class="week_sat ">28</td>
    </tr>
  </table>
</div>