【PHP講座】カレンダーを作成する No.5

カレンダーを作成する

PHP でカレンダーを作成します。

前回の続きです。

【PHP講座】カレンダーを作成する No.4

2019.11.11

今回は、細かい修正をしていき、完成を目指します。



カレンダーを作成する

カレンダーを作成します。

今回の記事のポイントです。

  • 本日の日付部分を太字にする
  • オブジェクト指向に書き換えていく

それでは見ていきましょう。

本日の日付部分を太字にする

本日の日付部分を太字にしていきます。

カレンダーの日付生成部分の下記を修正していきます。

foreach ($daterange as $day) {
  if ($day->format('w') % 7 === 0) {
    $content .= '</tr><tr>';
  }
  $content .= sprintf('<td class="week_%d">%d</td>', $day->format('w'), $day->format('d'));
}

today クラスは、第1回目のときにスタイルシートでスタイルを指定しました。

それを今回使うようにしていきます。

$today = new DateTime('today');
foreach ($daterange as $day) {
  if ($day->format('w') % 7 === 0) {
    $content .= '</tr><tr>';
  }
	$todayClass = ($day->format('Y-m-d') === $today->format('Y-m-d')) ? 'today' : '';
  $content .= sprintf('<td class="week_%d %s">%d</td>', $day->format('w'), $todayClass, $day->format('d'));
}

todayClass は、もし本日の日付と一致すれば、today という文字列を格納し、そうでなければ、空文字を格納するようになっています。

CSS で複数スタイルを指定する場合、半角スペースを空けて記述します。

あとは、sprintf のパラメータを適切に増やしてあげれば、太字になるはずです。

オブジェクト指向に書き換えていく

このままでも問題なく動作するのですが、せっかくなんでオブジェクト指向っぽく書き換えていきましょう。

現状、ファイルの階層は下記のようになっています。

css
 ┗ style.css
index.php

これをまず変更して以下のようにします。

css
 ┗ style.css
index.php
Calendar.php

今のままだと index.php に表示部分も制御部分も全て記述されてしまっているので、他のファイルから使うのが難しい状態です。

Calendar.php を新たに作成し、名前空間を定義して、index.php から呼び出せるようにします。

コンストラクタで初期化し、メソッドやプロパティも書いていきます。

今回の記事のサンプルコード

今回の記事のサンプルコードです。

まずは、index.php から。

<?php

require 'Calendar.php';

function escapeHtml($s) {
  return htmlspecialchars($s, ENT_QUOTES, 'UTF-8');
}

$cal = new \App\Calendar();

?>
<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>My Calendar</title>
  <link rel="stylesheet" href="css/style.css">
</head>
<body>
  <table>
    <thead>
      <tr>
        <th><a href="?t=<?php echo escapeHtml($cal->prev); ?>">«</a></th>
        <th colspan="5"><?php echo escapeHtml($cal->yearMonth); ?></th>
        <th><a href="?t=<?php echo escapeHtml($cal->next); ?>">»</a></th>
      </tr>
    </thead>
    <tbody>
      <tr>
        <td>Sun</td>
        <td>Mon</td>
        <td>Tue</td>
        <td>Wed</td>
        <td>Thu</td>
        <td>Fri</td>
        <td>Sat</td>
      </tr>
      <?php $cal->show(); ?>
    </tbody>
    <tfoot>
      <tr>
        <th colspan="7"><a href="http:/test_php/my_calendar2/index.php">Today</a></th>
      </tr>
    </tfoot>
  </table>
</body>
</html>

続いて、新たに作成した Calendar.php です。

<?php

namespace App;

class Calendar {
  public $prev;
  public $next;
  public $yearMonth;
  private $_thisMonth;

  public function __construct() {
    try {
      if (!isset($_GET['t']) || !preg_match('/\A\d{4}-\d{2}\z/', $_GET['t'])) {
        throw new \Exception();
      }
      $this->_thisMonth = new \DateTime($_GET['t']);
    } catch (\Exception $e) {
      $this->_thisMonth = new \DateTime('first day of this month');
    }
    $this->prev = $this->_createPrevLink();
    $this->next = $this->_createNextLink();
    $this->yearMonth = $this->_thisMonth->format('F Y');
  }

  private function _createPrevLink() {
    $dt = clone $this->_thisMonth;
    return $dt->modify('-1 month')->format('Y-m');
  }

  private function _createNextLink() {
    $dt = clone $this->_thisMonth;
    return $dt->modify('+1 month')->format('Y-m');
  }

  public function show() {
    $tail = $this->_getTail();
    $content = $this->_getContent();
    $head = $this->_getHead();
    $html = '<tr>' . $tail . $content . $head . '</tr>';
    echo $html;
  }

  private function _getTail() {
    $tail = '';
    $lastDayOfPrevMonth = new \DateTime('last day of ' . $this->yearMonth . ' -1 month');
    while ($lastDayOfPrevMonth->format('w') < 6) {
      $tail = sprintf('<td class="gray">%d</td>', $lastDayOfPrevMonth->format('d')) . $tail;
      $lastDayOfPrevMonth->sub(new \DateInterval('P1D'));
    }
    return $tail;
  }

  private function _getContent() {
    $content = '';
    $daterange = new \DatePeriod(
      new \DateTime('first day of ' . $this->yearMonth),
      new \DateInterval('P1D'),
      new \DateTime('first day of ' . $this->yearMonth . ' +1 month')
    );
    $today = new \DateTime('today');
    foreach ($daterange as $day) {
      if ($day->format('w') === '0') { $content .= '</tr><tr>'; }
      $todayClass = ($day->focontentrmat('Y-m-d') === $today->format('Y-m-d')) ? 'today' : '';
      $content .= sprintf('<td class="week_%d %s">%d</td>', $day->format('w'), $todayClass, $day->format('d'));
    }
    return $content;
  }

  private function _getHead() {
    $head = '';
    $firstDayOfNextMonth = new \DateTime('first day of ' . $this->yearMonth . ' +1 month');
    while ($firstDayOfNextMonth->format('w') > 0) {
      $head .= sprintf('<td class="gray">%d</td>', $firstDayOfNextMonth->format('d'));
      $firstDayOfNextMonth->add(new \DateInterval('P1D'));
    }
    return $head;
  }

}

private なメソッドやプロパティに関しては、_(アンダースコア)をつけて区別しています。

一応、最後なので、スタイルシートも(第1回目から変更なし)。

body {
  font-family: Arial, sans-serif;
  font-size: 1em;
}

a {
  text-decoration: none;
}

table {
  margin: 15px auto;
  border: 1px solid #ddd;
  border-collapse: collapse;
}

th {
  background: #eee;
}

th, td {
  padding: 7px;
  text-align: center;
}

.week_0 {
  color: red;
}

.week_6 {
  color: blue;
}

.today {
  font-weight: bold;
}

.except {
  color: #dedede;
}

スクリーンショットは以下の通りです。

カレンダーを作成する

最後に

いかがでしょうか。

私は PHP の学習に XAMPP を使っています。

同じ環境の方で注意点ですが、リンクを扱うときに http://localhost/ にアクセスしようとすると http://localhost/dashboard/ にリダイレクトされます。

設定を変えるか、テスト用としてのリンクを指定する必要があるので注意しましょう。