PHPで作る週間スケジュール管理システム:データ構造の改善と機能拡張

はじめに

前回の記事では、PHPを使った週間スケジュール管理システムの基本を構築しました。今回は、そのシステムをより使いやすく、より管理しやすいものへと改良していきます。

具体的には、データ構造の見直し、未入力状態の追加、古いデータの自動削除など、実用的な機能を追加しました。開発を進める中で浮かび上がった問題点と、その解決策を見ていきましょう。

最初に浮上した課題

システムを実際に使い始めると、いくつかの改善点が見えてきました。

  • まず、公開ページと管理ページの行き来が不便でした。公開ページから管理画面へ、管理画面から公開ページへ、それぞれ直接移動できるリンクがなかったのです。管理者は毎回URLを直接入力する必要がありました。
  • 次に、表示時刻の問題です。公開ページには「現在時刻」が表示されていましたが、これではスケジュールがいつ更新されたのかわかりません。訪問者が見たいのは、このスケジュールがいつ登録されたものかという情報です。
  • そして、スタイルシートの問題もありました。CSSがHTMLに直接書かれていたため、デザインを変更したいときに不便でした。

ページ間リンクの追加

最初の改善は簡単でした。公開ページの下部に、目立たないリンクを追加します。小さな点「・」を薄いグレー色で配置しました。これなら一般の訪問者には気づかれにくく、管理者だけが知っているリンクとして機能します。

管理画面のログインページには、「スケジュールを表示」というリンクを追加しました。ログインする前でも公開ページを確認できるようになります。

CSSでは、リンクの色とサイズを調整しました。

.admin-link {
    text-align: center;
    margin-top: 30px;
    font-size: 10px;
}

.admin-link a {
    color: #ccc;
    text-decoration: none;
}
Code language: CSS (css)

更新時刻の記録

次に取り組んだのは、更新時刻の表示です。公開ページに表示する時刻を、現在時刻からデータの更新時刻に変更しました。

config.phpのsaveData()関数を修正して、データを保存するときに更新時刻も記録します。

function saveData($data) {
    $data['_updated_at'] = date('Y年n月j日 G時i分');
    file_put_contents(DATA_FILE, json_encode($data, JSON_PRETTY_PRINT));
}
Code language: PHP (php)

アンダースコアで始まる_updated_atというキーを使いました。これは週のデータとは別の、システム全体の情報であることを示すための慣習です。

index.phpでは、この更新時刻を読み込んで表示します。データが取得できないときは、何も表示しません。

$updatedAt = $data['_updated_at'] ?? '';
Code language: PHP (php)
<?php if ($updatedAt): ?>
    <?= htmlspecialchars($updatedAt) ?> 現在
<?php endif; ?>
Code language: HTML, XML (xml)

CSSの外部化

スタイルシートを外部ファイルにすることで、HTMLとCSSの役割を分離しました。index.phpからstyle要素をすべて取り除き、styles.cssという新しいファイルを作成します。

HTMLのhead要素には、外部CSSへのリンクを追加します。

<link rel="stylesheet" href="styles.css">
Code language: HTML, XML (xml)

これで、デザインの変更が必要になったとき、styles.cssだけを編集すればよくなりました。

データ構造の根本的な見直し

ここで、より大きな問題に直面しました。データ構造の問題です。

元のデータ構造は、このようになっていました。

{
  "2025-09-29": {
    "0": {
      "0": "○",
      "1": "×",
      ...
    }
  }
}
Code language: JavaScript (javascript)

最初の数字が時間帯、次の数字が曜日を表します。つまり、「時間帯ごとに各曜日のデータがまとまっている」構造でした。

しかし、実際にデータを見るとき、「月曜日のスケジュール」「火曜日のスケジュール」というように曜日ごとに考える方が自然です。現在の構造では、1つの曜日のデータを見るために、すべての時間帯を順番に確認する必要がありました。

そこで、構造を逆転させます。「週 → 曜日 → 時間帯」という順序に変更しました。

{
  "2025-09-29": {
    "is_draft": true,
    "schedule": {
      "mon": [1, 0, 1, 0],
      "tue": [0, 1, 1, 0],
      "wed": [1, 1, 0, 1],
      "thu": [0, 1, 1, 1],
      "fri": [1, 0, 0, 1]
    }
  }
}
Code language: JSON / JSON with Comments (json)

曜日には、mon、tue、wed、thu、friという英語の略称を使いました。数字のインデックスよりも、何を表しているかが一目でわかります。

また、○と×の代わりに、0と1の数値を使うことにしました。データとしてシンプルで、処理も高速になります。

チェックボックスの意味の逆転

ここで重要な仕様変更を行いました。チェックボックスの意味を逆にしたのです。

元の設計では、チェックを入れると「空いている(○)」を意味していました。しかし、実際に使ってみると、これは直感に反します。普通、カレンダーやスケジュール帳では、予定がある時間帯に印をつけるからです。

そこで、チェックONが「×(予定あり)」を意味するように変更しました。管理者は「埋まっている時間帯にチェックを入れる」という、より自然な操作ができるようになります。

データでは、チェックON = 1 = ×、チェックOFF = 0 = ○という対応関係になります。

未入力状態の導入

新しい機能として、「未入力」という状態を追加しました。まだスケジュールを登録していない週を、すべて「-」で表示する機能です。

週ごとにis_draftというフラグを持たせます。このフラグがtrueの場合、その週はすべて「-」として表示されます。実際にデータを入力していても、未入力として見せることができます。

function getDefaultWeekData() {
    global $dayKeys;
    
    $schedule = [];
    foreach ($dayKeys as $day) {
        $schedule[$day] = [0, 0, 0, 0];
    }
    
    return [
        'is_draft' => true,
        'schedule' => $schedule
    ];
}
Code language: PHP (php)

初期状態では、すべてのセルが0(空き)で、未入力フラグがONになっています。

管理画面には、「この週は未入力として表示する」というチェックボックスを追加しました。管理者は、スケジュールを入力した後でも、このチェックを入れることで、公開ページでは「-」として表示させることができます。

<div class="draft-control">
    <input type="checkbox" 
           id="is_draft" 
           name="is_draft" 
           <?= $currentData['is_draft'] ? 'checked' : '' ?>>
    <label for="is_draft">この週は未入力として表示する</label>
</div>
Code language: JavaScript (javascript)

公開ページでは、未入力フラグを確認してから表示を切り替えます。

if ($weekData['is_draft']) {
    $status = '-';
    $class = 'draft';
} else {
    $value = $weekData['schedule'][$dayKey][$slotIndex] ?? 0;
    $status = $value === 0 ? '○' : '×';
    $class = $value === 0 ? 'available' : 'unavailable';
}
Code language: PHP (php)

古いデータの自動削除

データが蓄積し続ける問題にも対処しました。4週間分しか表示しないのに、過去のデータがすべて残り続けていたのです。

解決策は、データを保存するときに古いデータを削除することです。今週より前のデータは、もう使うことがないので削除します。

function saveData($data) {
    $thisWeekKey = getThisWeekMonday();
    foreach ($data as $weekKey => $weekData) {
        if ($weekKey !== '_updated_at' && $weekKey < $thisWeekKey) {
            unset($data[$weekKey]);
        }
    }
    
    $data['_updated_at'] = date('Y年n月j日 G時i分');
    
    file_put_contents(DATA_FILE, json_encode($data, 
        JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));
}
Code language: PHP (php)

週のキーは日付形式(YYYY-MM-DD)なので、文字列として比較できます。「今週の月曜日」より小さい(古い)キーを持つデータを削除します。

JSON_UNESCAPED_UNICODEオプションを追加したことにも注目してください。これにより、日本語が文字化けせずに保存されます。

データ構造変更への対応

データ構造を変更したことで、すべてのPHPファイルを修正する必要がありました。

config.phpには、曜日キーの配列を追加しました。

$dayKeys = ['mon', 'tue', 'wed', 'thu', 'fri'];
Code language: PHP (php)

admin.phpの保存処理は、新しい構造に合わせて書き直しました。

foreach ($dayKeys as $dayIndex => $dayKey) {
    $data[$weekKey]['schedule'][$dayKey] = [];
    foreach ($timeSlots as $slotIndex => $slot) {
        $checkboxName = "status_{$dayIndex}_{$slotIndex}";
        $data[$weekKey]['schedule'][$dayKey][] = 
            isset($_POST[$checkboxName]) ? 1 : 0;
    }
}
Code language: PHP (php)

index.phpも、新しいデータ構造から値を読み取るように修正しました。

$value = $weekData['schedule'][$dayKey][$slotIndex] ?? 0;
Code language: PHP (php)

スタイルの追加

未入力状態を表示するために、CSSに新しいクラスを追加しました。

.draft {
    color: #999;
}
Code language: CSS (css)

グレー色で表示することで、未入力であることが視覚的にわかります。

完成したシステム

改良を重ねた結果、このようなシステムができあがりました。

データ構造は「週 → 曜日 → 時間帯」という自然な順序になり、JSONファイルを直接見ても内容が理解しやすくなりました。チェックボックスの意味も直感的になり、管理者は予定がある時間帯にチェックを入れるだけです。

未入力状態の機能により、まだスケジュールを決めていない週を「-」として表示できます。古いデータは自動的に削除されるため、ファイルサイズが無制限に大きくなることもありません。

そして、ページ間のリンクとCSS外部化により、保守性も向上しました。

まとめ

週間スケジュール管理システムの改良により、データ構造を「週→曜日→時間帯」の自然な順序に再設計し、値を0/1の数値表現に統一しました。未入力状態を表すis_draftフラグを週単位で管理し、公開ページでは「-」として表示する機能を実装しました。データ保存時に今週より前の週を自動削除する機能により、JSONファイルの肥大化を防止しています。チェックボックスの意味を逆転させ、チェックON=1=×(予定あり)とすることで、より直感的な操作を実現しました。