1. 本書の対象と前提
このドキュメントは、リマインダー配信アプリに対して、 後からコード改修・機能追加・障害対応を行う技術者(開発・インフラ担当)向けのマニュアルです。
- PHP / MySQL にある程度馴染みがあること
- Linux サーバーでの CLI 実行・cron 設定が可能であること
- ACL・認証・セッションは別アプリ(/auth)で提供されている前提で理解できること
エンドユーザー向けの操作説明は「一般ユーザー向けマニュアル」を参照してください。 本書は、構造とコードの意図を理解することを目的としています。
2. 技術スタックと依存関係
使用技術
- 言語:PHP 8 系を想定
- DB:MySQL / MariaDB(PDO による接続)
- フロントエンド:素の PHP+HTML/Bootstrap 5/素の JavaScript(fetch API)
- 文字コード:UTF-8(mbstring 拡張を利用)
主な PHP 拡張
- pdo_mysql
- mbstring
- json
- openssl(SMTP ライブラリの利用内容により)
3. ディレクトリ構成
アプリ本体のディレクトリ直下は、概ね次の構成になっています。
config.php # DB 接続設定(DSN・ユーザー名・パスワード)
build_mail.php # mail_no をもとにメール本文を生成する
send_mail.php # メール送信エントリポイント(ブラウザ/CLI 両対応)
send_today_reminders.php # 当日分 mail_no の一括送信スクリプト
generate_reminder_data.php # 指定年の reminder をまとめて生成
generate_reminders.php # horizon 期間で reminder を自動生成
reminder_list.php # リマインダー一覧画面(年+月タブ)
reminder_toggle_disabled.php# reminder.disabled を更新する API
mail_preview.php # mail_no を元にプレビュー表示
mail_settings.php # メール設定GUI(mail_config.json / template / attachments)
provider_list.php / provider_form.php # 宛先(機関)管理
plan_list.php / plan_form.php # 計画管理
partials/
nav.php # 共通ナビゲーション
scripts/
config_app.php # APP_KEY 定義
require_login.php # /auth と連携したログイン必須チェック
private/
mail_template.txt # メール本文テンプレート(テキスト)
attachments/ # 共通添付ファイル格納ディレクトリ
mail_config.json # メール送信設定(JSON)
ポイント: バッチ系(generate_*, send_*)と Web 画面系(*_list.php, *_form.php)、 そして共通ロジック(build_mail.php, mail_settings.php)で分かれている構造です。
4. 設定ファイルと環境
機密情報の管理に注意config.php(DB 接続)
- PDO を用いて MySQL へ接続するための DSN・ユーザー名・パスワードを定義しています。
- 全ての画面/スクリプトで require_once __DIR__ . '/config.php' を通じて $pdo を共有します。
- 本番環境では、Git 管理外の別ファイルに移す or 環境変数から読み込む等のセキュリティ強化を推奨します。
mail_config.json(メール送信設定)
- mail_settings.php から編集する JSON ファイルです。
- 差出人メールアドレス、表示名、SMTP ホスト・ポート・ユーザー名などを保持します。
- send_mail.php で読み込まれ、実際の送信に使用されます。
テンプレート/添付ファイル
- private/mail_template.txt … メール本文テンプレート
- attachments/ … 共通添付ファイル用ディレクトリ(存在しない場合は mail_settings.php 側で作成)
5. 認証・セッション
アプリ単体ではログイン画面を持たず、/auth 配下の共通認証基盤に依存して動作します。
scripts/require_login.php の役割
require __DIR__.'/../../auth/config.php';
require __DIR__ . '/config_app.php'; // const APP_KEY = 'reminder';
if (empty($_SESSION['login_user'])) {
// /auth/login.php へリダイレクト
}
$user = $_SESSION['login_user'];
$appInfo = $user['apps'][APP_KEY] ?? null;
$role = $appInfo['role'] ?? 'guest';
if (!in_array($role, ['admin', 'staff'], true)) {
http_response_code(403);
// エラーメッセージ表示
}
- config_app.php 内で APP_KEY='reminder' を定義し、認可情報を切り出します。
- 全ての管理画面(provider_*, plan_*, reminder_list, mail_settings 等)で require_login.php を読み込むことで、未ログイン/権限不足を排除しています。
- ログイン後に戻る URL は $_SESSION['after_login_redirect'] に一時保存されています。
6. データベース設計(概要)
SQL ファイルは含まれていませんが、コードから推測できる 最低限必要なカラムは以下の通りです。 実際のスキーマは運用中 DB を確認してください。
provider(宛先)テーブル
- provider_id(PK)
- provider_name … 機関名
- staff_name … 担当者名
- email … 宛先メールアドレス
- その他:作成日時など(任意)
plan(計画)テーブル
- plan_id(PK)
- provider_id(FK → provider.provider_id)
- plan_number … 計画番号
- plan_name … 計画名
- accepted_date … 受理日(DATE)
- status … 'active' 等の状態
reminder(リマインダー)テーブル
- reminder_id(PK)
- plan_id(FK → plan.plan_id)
- provider_id(FK → provider.provider_id)
- reminder_year … 対象年(INT)
- scheduled_date … 送信予定日(DATE)
- status … 'pending'|'sent'|'disabled' 等
- disabled … 0/1 フラグ(UI のトグルと連動)
- reminder_mail_no … メール単位のグルーピング番号(INT)
- sent_at … 実際の送信日時(DATETIME, NULL 可)
7. リマインダー生成ロジック
generate_reminder_data.php(特定年向け一括生成)
- CLI もしくはブラウザから特定の年を指定して実行します。
- 実行フロー(簡略):
- 対象年(year)を CLI 引数または GET パラメータから取得。
- plan.status='active' のレコードを全件取得。
- 各 plan について、指定年の「記念日」(受理日の月日)に対応する reminder が存在するか確認。
- 存在しなければ新規 reminder を挿入し、reminder_mail_no を付与。
- 主に「年度初めにその年の reminder をまとめて作る」用途を想定したスクリプトです。
generate_reminders.php(ローリング生成)
php generate_reminders.php # デフォルト: 1年先まで
php generate_reminders.php # ブラウザでは ?years=1&months=0&days=0
- 「今日から n 年 m ヶ月 d 日先まで」の範囲を horizon とし、その間に発生する記念日に対して reminder を生成します。
- すでに存在する reminder との二重登録を避けるため、同じ plan_id / year が存在しないかチェックしてから挿入します。
- 実運用では cron で毎日実行し、常に先の一定期間分の reminder を保つ、といった構成が可能です。
8. メール生成・送信フロー
build_mail.php(メール組み立て)
build_mail(int $mailNo): array が中核です。
- 入力: mail_no(reminder_mail_no)
- 処理概要:
- 指定 mail_no で reminder を取得(pending && disabled=0 のみ)。
- provider_id/reminder_year/scheduled_date が全行で一致することを検証。
- 紐づく plan を取得し、「計画リスト文字列」を組み立て。
- mail_config.json から subject を取得(なければ空)。
- private/mail_template.txt を読み込み、プレースホルダ {{staff_name}} / {{plan_list}} を埋め込み。
- 出力: 連想配列
- status … 'ok'|'error'
- subject
- body
- to_email
send_mail.php(単一 mail_no の送信)
ブラウザ/CLI 両方から呼び出せる送信エントリポイントです。
php send_mail.php 123 # mail_no=123 を送信
# ブラウザからの例
/send_mail.php?mail_no=123
- 内部で build_mail() を呼び出し、status='ok' のときのみ送信処理へ。
- mail_config.json を読み込み、差出人・返信先・CC/BCC・添付ファイル一覧を組み立て。
- send_mail_with_attachments() が実際の SMTP 送信を実行。
- 送信成功時には、対象 reminder 行の status や sent_at を更新する設計です(実装を確認)。
send_today_reminders.php(当日分一括送信)
- fetch_today_reminders() で scheduled_date=CURDATE() かつ disabled=0 かつ status='pending' の mail_no 一覧を取得。
- 取得した mail_no をループして send_mail.php をサブプロセス的に実行する作りになっています。
- cron による毎日送信の際は、このスクリプトを直接呼び出します。
9. Web UI の挙動(JS 含む)
共通ナビ(partials/nav.php)
- render_nav($active) を呼ぶと、タイトル+ボタン群が表示されます。
- $active に 'provider_list' 等を渡すと、そのボタンが active クラス付きになります。
- ナビの中で「新規機関登録」「新規計画登録」「リマインダー一覧」「メール設定」「ログアウト」へのショートカットボタンを提供しています。
リマインダー一覧の JS(reminder_list.php)
主に以下の 2 つの役割があります。
- 行クリックで mail_preview.php へ遷移
- 無効トグルの変更を reminder_toggle_disabled.php に POST
// 行クリック(無効行はスキップ)
document.querySelectorAll('tbody tr.reminder-row').forEach(function (row) {
row.addEventListener('click', function () {
if (this.classList.contains('reminder-disabled')) return;
const mailNo = this.dataset.mailNo;
if (!mailNo) return;
window.location = 'mail_preview.php?mail_no=' + encodeURIComponent(mailNo);
});
});
// disabled トグル
document.querySelectorAll('.js-disabled-toggle').forEach(function (cb) {
cb.addEventListener('click', function (e) {
e.stopPropagation(); // 行クリックへのバブリングを防止
});
cb.addEventListener('change', function () {
const id = this.dataset.id;
const disabled = this.checked ? 1 : 0;
const row = this.closest('tr');
// fetch で reminder_toggle_disabled.php に POST
});
});
});
reminder_toggle_disabled.php(JSON API)
- POST メソッドのみ許可。
- パラメータ:
- id … reminder_id(int)
- disabled … 0 or 1
- バリデーション NG の場合: { "success": false, "message": "Invalid ..." } を返します。
- 成功時: { "success": true } を返します。フロント側では行に reminder-disabled クラスを付与/削除して UI を更新します。
10. バッチ運用と cron 例
推奨イメージ
- 初期導入時:過去・当年度分を generate_reminder_data.php で一括生成。
- 運用中:毎日 generate_reminders.php を実行し、常に先 1 年分などの reminder を確保。
- 毎日決まった時間に send_today_reminders.php を実行して当日分を送信。
cron 設定例
0 3 * * * /usr/bin/php /path/to/reminder-app-clone/generate_reminders.php >> /var/log/reminder_gen.log 2>&1
# (2) 毎朝 10:00 に当日分の reminder を送信
0 10 * * * /usr/bin/php /path/to/reminder-app-clone/send_today_reminders.php >> /var/log/reminder_send.log 2>&1
11. ロギングとエラー処理
ログ出力の方針
- DB 接続失敗など致命的なエラーは die() で画面にメッセージ表示(運用環境では Web サーバーログに残ります)。
- メールテンプレートファイルの読み込み失敗などは error_log() で記録し、status='error' を返す設計です。
- バッチからは標準出力に「成功件数/エラー情報」を出しており、cron のリダイレクト先ログで確認できます。
典型的なエラーパターン
- リマインダーが存在しない: build_mail() が status='error' を返し、 「no pending, enabled reminder for mail_no=...」というログが出力されます。
- プロバイダ情報の不整合: ある mail_no に紐づく reminder の provider_id / year / scheduled_date が揃っていない場合、エラーとしてログ出力されます。
- メールテンプレートがない/読めない: template ファイルの存在チェックや読み込み失敗時に error_log が呼ばれます。
12. 機能追加・改修時のガイド
共通方針
- DB へのアクセスは、既存コード同様 $pdo(PDO)+プリペアドステートメントを利用する。
- 画面追加時は、require_login.php を必ず読み込み、認証なし利用を防ぐ。
- ナビゲーションボタンを増やしたい場合は partials/nav.php を編集する。
よくある改修ポイント例
- メール件名/本文のプレースホルダ追加:
- build_mail.php にて、{{...}} プレースホルダを追加します。
- 同時に private/mail_template.txt にも対応する記述を追加します。
- リマインダーの追加条件:
- generate_* 系スクリプトで、plan.status 以外の条件(例:プラン種別)で絞り込みたい場合、該当 SQL を修正します。
- reminder_list.php でも同様の条件を考慮する必要がある場合があります。
- 権限ロールを増やす:
- require_login.php の in_array($role, [...]) 部分を拡張します。
改修時のチェックリスト
- 開発環境で DB・mail_config.json・テンプレートをテスト用に用意して動作確認する。
- 本番用の config.php など機密情報を Git などに含めない構成になっているか確認する。
- 新規/変更コードに対して、典型的なユースケース(正常系・エラー系)を手動テストする。
- バッチ系スクリプト(generate_*, send_*)に変更がある場合は、cron のログを必ず確認する。