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. ディレクトリ構成

アプリ本体のディレクトリ直下は、概ね次の構成になっています。

reminder-app-clone/
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/session_bootstrap.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 もしくはブラウザから特定の年を指定して実行します。
  • 実行フロー(簡略):
    1. 対象年(year)を CLI 引数または GET パラメータから取得。
    2. plan.status='active' のレコードを全件取得。
    3. 各 plan について、指定年の「記念日」(受理日の月日)に対応する reminder が存在するか確認。
    4. 存在しなければ新規 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
  • 処理概要:
    1. 指定 mail_no で reminder を取得(pending && disabled=0 のみ)。
    2. provider_idreminder_yearscheduled_date が全行で一致することを検証。
    3. 紐づく plan を取得し、「計画リスト文字列」を組み立て。
    4. mail_config.json から subject を取得(なければ空)。
    5. private/mail_template.txt を読み込み、プレースホルダ {{staff_name}} / {{plan_list}} を埋め込み。
  • 出力: 連想配列
    • status'ok'|'error'
    • subject
    • body
    • to_email

send_mail.php(単一 mail_no の送信)

ブラウザ/CLI 両方から呼び出せる送信エントリポイントです。

# 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 行の statussent_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 つの役割があります。

  1. 行クリックで mail_preview.php へ遷移
  2. 無効トグルの変更を reminder_toggle_disabled.php に POST
document.addEventListener('DOMContentLoaded', function () {
// 行クリック(無効行はスキップ)
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 設定例

# (1) 毎朝 3:00 に 1年先までの reminder を生成
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.phpin_array($role, [...]) 部分を拡張します。

改修時のチェックリスト

  1. 開発環境で DB・mail_config.json・テンプレートをテスト用に用意して動作確認する。
  2. 本番用の config.php など機密情報を Git などに含めない構成になっているか確認する。
  3. 新規/変更コードに対して、典型的なユースケース(正常系・エラー系)を手動テストする。
  4. バッチ系スクリプト(generate_*, send_*)に変更がある場合は、cron のログを必ず確認する。