NGW開発BLOG
← 記事一覧へ
·#037

NGWのアプリはどう動くかSubModeという背骨

「ひとつのアプリが動く仕組み」を公開していく連載の第1回。すべての画面が従う共通の型=SubModeディスパッチと、描画→部分再描画→確定→帳票というライフサイクルを、実際のコードで解説する。

この連載について

これまでは個々の機能の「設計判断」を書いてきた。ここからは趣向を変えて、ひとつのアプリが実際にどう動いているのか、その仕組みを公開していく。自分たちのコードの構造を、隠さずに見せる連載だ。

第1回は、個別のアプリに入る前の共通の土台。NGWの画面は数十あるが、どれもほぼ同じ「型」で動いている。その背骨が SubMode ディスパッチである。これさえ掴めば、あとはどのアプリも同じ読み方で追える。

ひとつのアプリを構成するレイヤ

ひとつの画面(例:スケジュール)は、役割の違う複数のファイルでできている。

  • 画面PHPngwtSchedule.php)… ページの入り口。HTMLの骨格、共通のJS/CSS、そして唯一のフォーム #frmNgw を吐く。
  • ajaxのPHPajax/ajaxNgwtSchedule.php)… サーバ側の実ロジック。ここが後述の SubMode で分岐する中核。
  • ajaxのJSajax/ajaxNgwtSchedule.js)… クライアント。フォームを丸ごと送り、返ってきたHTMLを差し込む。
  • classMasterTables/class/clsXxx.php)… テーブルに対応するデータオブジェクト(プロパティの器)。
  • clsFunctionclsFunction/fncXxx.php)… その class の I/O を制御する(DBとの読み書き)。
  • 共通ヘルパーinclude/ngwFunc.phpwfFunc.php)… POST取得、日付、決裁経路など、全アプリが使う道具箱。

面白いのは、この5点セット(画面PHP・ajaxのPHP/JS・class・clsFunction)が、DBのテーブル定義から自動生成されることだ。CreateDBWeb という自前のジェネレータで、たとえば簿冊のテーブル DOCU_BOSATU を定義すると、docuBosatu.php から ajaxDocuBosatu.php.jsclsDocuBosatu.phpfncDocuBosatu.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_FORMMAKE_..._HTML はHTMLの断片を返し、INSERTUPDATEREMOVE はDBを書き換える。表示を作るのも、データを書くのも、同じ一枚のディスパッチの上に並んでいる。これがNGWのアプリの、いちばん根っこの形だ。

ライフサイクル:描画 → 部分再描画 → 確定 → 帳票

この背骨の上で、画面はいつも同じ4拍子で動く。

  1. 初期描画SubMode=INSERT_FORM(新規)や EVENT_INFO(既存)で、編集フォームのHTMLを組んで返す。
  2. 部分再描画:課を選ぶと係の選択肢が変わる、といった連動。MAKE_..._HTML のようなSubModeで変わった部分のHTML片だけを返し、クライアントがそこに差し込む。ページ全体はリロードしないし、DBも触らない
  3. 確定SubMode=INSERTUPDATE。ここで初めてDBを書く。まとめて検証してからトランザクションで確定する。
  4. 帳票:必要なら別の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にも、コードが同じ顔で読める。一貫性は、他人を受け入れる余地そのものだ。

次回からは、この背骨の上に実際のアプリをひとつずつ載せて、その中身を追っていく。