NGWのアプリはどう動くかSubModeという背骨
「ひとつのアプリが動く仕組み」を公開していく連載の第1回。すべての画面が従う共通の型=SubModeディスパッチと、描画→部分再描画→確定→帳票というライフサイクルを、実際のコードで解説する。
この連載について
これまでは個々の機能の「設計判断」を書いてきた。ここからは趣向を変えて、ひとつのアプリが実際にどう動いているのか、その仕組みを公開していく。自分たちのコードの構造を、隠さずに見せる連載だ。
第1回は、個別のアプリに入る前の共通の土台。NGWの画面は数十あるが、どれもほぼ同じ「型」で動いている。その背骨が SubMode ディスパッチである。これさえ掴めば、あとはどのアプリも同じ読み方で追える。
ひとつのアプリを構成するレイヤ
ひとつの画面(例:スケジュール)は、役割の違う複数のファイルでできている。
- 画面PHP(
ngwtSchedule.php)… ページの入り口。HTMLの骨格、共通のJS/CSS、そして唯一のフォーム#frmNgwを吐く。 - ajaxのPHP(
ajax/ajaxNgwtSchedule.php)… サーバ側の実ロジック。ここが後述のSubModeで分岐する中核。 - ajaxのJS(
ajax/ajaxNgwtSchedule.js)… クライアント。フォームを丸ごと送り、返ってきたHTMLを差し込む。 - class(
MasterTables/class/clsXxx.php)… テーブルに対応するデータオブジェクト(プロパティの器)。 - clsFunction(
clsFunction/fncXxx.php)… その class の I/O を制御する(DBとの読み書き)。 - 共通ヘルパー(
include/ngwFunc.php・wfFunc.php)… POST取得、日付、決裁経路など、全アプリが使う道具箱。
面白いのは、この5点セット(画面PHP・ajaxのPHP/JS・class・clsFunction)が、DBのテーブル定義から自動生成されることだ。CreateDBWeb という自前のジェネレータで、たとえば簿冊のテーブル DOCU_BOSATU を定義すると、docuBosatu.php から ajaxDocuBosatu.php/.js、clsDocuBosatu.php、fncDocuBosatu.php までが一式で吐き出される。NGWの画面が数十あっても同じ顔をしているのは、みんなが規約を守っているからというより、そもそも同じ型で生成されているからだ(CreateDBWeb 自体の仕組みは、稿を改めて書く)。
生成された画面が動き出す流れはこうだ。画面PHP(docuBosatu.php)が読み込まれると、まずそのJS(ajaxDocuBosatu.js)が起動し、ajaxのPHP(ajaxDocuBosatu.php)へ初期化を依頼する。PHPは初期HTMLを組んで返し、JSがそれを画面に差し込む。最初の描画からして、この後で説明するSubModeの往復に乗っているのだ。画面PHPはあくまで器で、実際の処理はajaxのPHPが担う。JSは薄い。
リクエストの背骨:SubMode ディスパッチ
クライアントは、操作のたびにフォーム全体を serialize() し、SubMode を付けてPOSTする。
// ajax/ajaxNgwtSchedule.js — クライアントは #frmNgw を丸ごと送る
$.ajax({
type: 'POST',
data: $('#frmNgw').serialize() + '&SubMode=EVENT_INFO',
}).done(function (html) {
$('#modal-form').html(html); // 返ってきたHTML片をそのまま差し込む
});
サーバ側は、その SubMode を見て処理を振り分ける。中核は fncMainProc() の一枚の switch だ。
// ajax/ajaxNgwtSchedule.php — サーバ側の背骨
$SubMode = ngwFunc_GetPostData('SubMode');
switch ($SubMode) {
case 'INSERT_FORM': /* 新規登録フォームのHTMLを描いて返す */ return;
case 'EVENT_INFO': /* 既存予定のHTMLを描いて返す */ return;
case 'INSERT': /* DBへ新規登録して確定 */ return;
case 'UPDATE': /* DBを更新 */ return;
case 'REMOVE': /* 削除 */ return;
case 'MAKE_SC_EVDAY_HTML': /* 画面の一部だけを作り直して返す */ return;
// …部分描画やJSON返却のcaseが続く
}
INSERT_FORM や MAKE_..._HTML はHTMLの断片を返し、INSERT/UPDATE/REMOVE はDBを書き換える。表示を作るのも、データを書くのも、同じ一枚のディスパッチの上に並んでいる。これがNGWのアプリの、いちばん根っこの形だ。
ライフサイクル:描画 → 部分再描画 → 確定 → 帳票
この背骨の上で、画面はいつも同じ4拍子で動く。
- 初期描画:
SubMode=INSERT_FORM(新規)やEVENT_INFO(既存)で、編集フォームのHTMLを組んで返す。 - 部分再描画:課を選ぶと係の選択肢が変わる、といった連動。
MAKE_..._HTMLのようなSubModeで変わった部分のHTML片だけを返し、クライアントがそこに差し込む。ページ全体はリロードしないし、DBも触らない。 - 確定:
SubMode=INSERT/UPDATE。ここで初めてDBを書く。まとめて検証してからトランザクションで確定する。 - 帳票:必要なら別のPHPでPDFを出力する。
「見た目を作る」と「データを書く」を、SubModeで明確に分けているのがポイントだ。onChangeの連動が多い行政フォームでは、フルリロードせずに一部だけ差し替えられるこの形がよく効く。
読めば分かる、いくつかの規約
型が一貫していると、規約を一度覚えるだけで全アプリが読める。
- フォーム項目は
fdt*(fdtScheduleId,fdtStaffCd,fdtScheduleStartDate…)。取得は必ずngwFunc_GetPostData()を通す。 - フォームはひとつ(
#frmNgw)。画面の状態はここに集約され、操作はSubModeで表現される。 - 区分・団体差は
_XXX_のDB定数(NGWT_CONSTANT由来。定数をDBに置く話で書いた仕組み)。たとえばスケジュールの繰り返し種別は_SC_DOW_(毎週の曜日)や_SC_MDAY_(毎月の日)といった定数で表す。前回のFullCalendar記事で触れた「様々な繰り返し設定」の正体は、これだ。 - DB接続は
$mySqlConnObjを引数で持ち回る。
なぜこの形なのか
サーバ主導であることが肝だ。表示ロジックの真実はサーバのPHP側にある。JSは薄く、返ってきたHTMLを差し込むだけ。だから、誰に何を見せるかという権限や制度の判定を、すべてサーバ側で一貫して下せる。クライアントに判断を漏らさない。以前プライバシー保護で書いた「見せる/見せないはデータ取得の段で決める」も、この構造だからこそ素直に効く。
そして何より、この型が10年変わっていない。SubModeディスパッチ、fdt*のフィールド、ngwFunc_*のヘルパー、_XXX_のDB定数——これらを覚えれば、数十あるどのアプリも同じ顔で読める。新しいアプリも、この型に乗せれば自然に馴染む。
AIとの協働作業での学び
地味だが一貫した型は、強い。派手なフレームワークは無いが、「SubModeで振り分け、フォームを丸ごと送り、HTML片を差し込む」というひとつのパターンが全体を貫いているおかげで、10年後の自分にも、あとから来た人にも、そしてAIにも、コードが同じ顔で読める。一貫性は、他人を受け入れる余地そのものだ。
次回からは、この背骨の上に実際のアプリをひとつずつ載せて、その中身を追っていく。