操作別に見るサーバースクリプトと拡張 SQL の実行順
サーバースクリプトと拡張サーバースクリプトは、同じ実行条件で動きます。同じ条件では拡張サーバースクリプト → サイトのサーバースクリプトの順です。拡張 SQL は、それとは別に SQL を組み立てたり DB を更新したりする位置で適用されます。
「更新前」という名前だけでは、計算式・入力チェック・DB 更新との前後関係までは分かりません。入力途中の自動ポストバック・項目連携・選択肢検索も、保存とは別の経路として確認します。このページでは操作ごとに、適用する条件・呼び出し順・通常の保存との違いを確認できます。拡張SQLと併用する場合は、同じ列の変更・再取得・関連情報更新の取り合いも確認してください。設定を選ぶときは「目的から条件を選ぶ」、動作を照合するときは「自分の設定で確認するときの手順」を参照してください。
読む前の前提
公式のサーバスクリプトの条件と拡張サーバスクリプト設定に対して、このページでは複数の機能をまたぐ呼び出し順を補います。
図は記録テーブル(Results)の通常の処理をソースから整理したものです。期限付きテーブル(Issues)も、作成・更新の主要なフックの前後関係は同じです(IssueModel.cs の作成、更新)。Wiki・フォルダは対象に含めません。一覧編集・一括操作・APIは、通常の画面保存との違いを後半で説明します。
図中の「SS」は、条件に合う拡張サーバースクリプトとサイトのサーバースクリプトの両方を表します。有効なスクリプトと拡張 SQL が登録されている前提です。通知・サマリ・添付ファイルなどの細部は省略しています。図は実機ログではなく、ソースで確認した主要なフックの相対順です。リンク項目などによる追加取得や再計算があるため、呼び出し回数を固定する図ではありません。
「いつ」と「どの操作」を分ける
| 判定するもの | 意味 | 例 |
|---|---|---|
context.Condition | 今どのフックで実行されているか | BeforeOpeningPage、BeforeUpdate |
context.Controller | どのコントローラへのリクエストか | items、forms |
context.Action | リクエストの操作名 | index、edit、update、create |
Controller・Action はルートの値を小文字にしたものです。画面の見た目ではなく、現在のリクエストを表します(Context.cs)。
| 操作 | Controller | Action | まず着目する SS の条件 | 保存用の拡張 SQL |
|---|---|---|---|---|
| 一覧画面を開く | items | index | WhenViewProcessing、BeforeOpeningPage、各行の WhenloadingRecord・BeforeOpeningRow | 作成・更新系は呼ばれない |
| 既存レコードの編集画面を開く | items | edit | WhenViewProcessing、WhenloadingRecord、BeforeOpeningPage | 作成・更新系は呼ばれない |
| 新規作成画面を開く | items | new | BeforeFormula・AfterFormula、BeforeOpeningPage | 作成系はまだ呼ばれない |
| 新規レコードを作成する | items | create | BeforeCreate、AfterCreate | OnCreating、OnCreated |
| 既存レコードを更新する | items | update | BeforeUpdate、AfterUpdate | OnUpdating、OnUpdated |
| フォームを開く | forms | new | BeforeFormula・AfterFormula、BeforeOpeningPage | 作成系はまだ呼ばれない |
| フォームを送信する | forms | create | BeforeCreate、AfterCreate | OnCreating、OnCreated |
| 送信完了画面に移動する | forms | thanks | WhenloadingSiteSettings | 再度の作成は行わない |
| 自動ポストバック対象の項目を変更する | items | edit または new | 計算式の前後、BeforeOpeningPage。クエリに control-auto-postback=1 | 標準経路では作成・更新系なし |
| 項目連携の親を変更する | items | relatingdropdown | WhenloadingSiteSettings、候補取得のビュー処理。JSONの式側では編集モデルのフックもある | 標準経路では作成・更新系なし |
| 選択肢の検索ダイアログを開く | items | searchdropdown | WhenloadingSiteSettings、候補取得のビュー処理。JSONの式側では編集モデルのフックもある | 標準経路では作成・更新系なし |
上の表は着目点の早見表です。各経路でサイト設定を読み込む WhenloadingSiteSettings があり、保存時には入力の反映・計算式・レコードの再取得もあります。全条件が1回ずつ動くという意味ではありません。
操作名は ItemsController.cs、新規・編集、作成・更新、FormsController.csで確認できます。
同じ条件での実行順
図を読み込み中…
本体は拡張サーバースクリプトを先に並べ、サイトのスクリプトを後ろに連結します。その一覧から、現在の実行条件に合うものを選びます(SiteSettings.cs、ServerScriptUtilities.cs)。
拡張側の Controllers・Actions は対象リクエストを絞る条件です。Actions に update を指定するだけでは実行タイミングは決まりません。BeforeUpdate なども指定します。評価ルールは拡張サーバースクリプトの適用条件を参照してください。
一覧画面を開いたとき
操作手順は「記録テーブルの一覧を開く」です。通常の一覧表示では items/index になります。
図を読み込み中…
一覧データの取得は、BeforeOpeningPage より先です。一覧の検索条件を変えるなら WhenViewProcessing を使います。 BeforeOpeningPage では取得済みの gridData を扱えますが、そこでビューを変更するだけで、先に行われた一覧取得をやり直すわけではありません。
WhenloadingRecord は一覧全体に対して1回ではなく、行のモデルを作る際にも呼ばれます。その後に BeforeOpeningRow があり、行の見た目を組み立てます。表示行が多いほど、行ごとの処理も増えます。
根拠は ResultUtilities.cs の一覧取得から画面フックまで、GridData.cs の SELECT、行モデルと行フック、レコード読み込みフックです。
選択系の拡張 SQL は何をするか
| 設定 | 適用する場所 |
|---|---|
OnSelectingWhere | SELECT の WHERE 条件 |
OnSelectingOrderBy | SELECT の ORDER BY |
OnSelectingColumn | SELECT の列式 |
これらは、作成・更新系のように独立した SQL を前後に流すフックとは性格が異なり、本体の SELECT に組み込む SQLです。設定名に「Selecting」があっても、別の SQL が3本順番に実行されるという意味ではありません(View.cs の WHERE、ORDER BY、Rds.cs の列式)。
WhenViewProcessing は一覧専用ではありません。編集対象の取得も View.SetColumnsWhere を通るため、編集時や保存中の再取得でも呼ばれます。一覧だけを対象にするなら、操作名も確認します(ResultModel.Get)。
フィルター・ソートで一覧を読み直す
初回の一覧表示の後に、フィルターやソートを変更して行を取得し直す items/gridrows の経路もあります。ここでは一覧データを取得した後に BeforeOpeningPage を呼び、返す行を描画します。index だけに絞った処理は、この経路に適用されません。一覧の検索条件を継続して適用するなら、gridrows も対象に含めます(ItemsController.GridRows、ResultUtilities.GridRows)。
編集画面を開いたとき
操作手順は「一覧で既存レコードを選び、通常の編集画面を開く」です。items/edit で、作成・更新はまだ行いません。
図を読み込み中…
BeforeOpeningPage には、取得済みの編集対象モデルが渡されます。一覧で各行に使う BeforeOpeningRow は、編集対象1件の通常のエディタ描画には使いません。ただし編集画面内のリンク先一覧などが行を描画する場合は、追加の行フックがあり得ます。
根拠は 編集モデルの生成、取得してから入力を反映するコンストラクタ、画面フックへのモデルの受け渡し、リンク先の行フックです。
編集画面を保存したとき
「保存」は、新規なら作成、既存なら更新に分かれます。まず、既存レコードの値を変更して更新する場合を見ます。
図を読み込み中…
押さえたい順序は BeforeUpdate → OnUpdating → 本体更新 → OnUpdated → AfterUpdate です。
標準の更新時検証は BeforeUpdate より先に行われます。「更新前なら、すべての検証より前」ではありません。入力値を反映する SetByForm は、その中で計算式の処理を呼びます。したがって、BeforeFormula・AfterFormula も BeforeUpdate より前に現れます。プロセス処理などによる追加の計算は図では省略しています(ResultUtilities.Update、入力反映、計算式フック)。
OnUpdating は本体更新の SQL リストの先頭側に追加されます。OnUpdated は、その最初の更新処理が終わった後、関連情報を更新する別の SQL リストに追加されます。前後の拡張 SQL と保存全体を、単一のトランザクションで囲んでいるわけではありません。 後続処理のエラーで、既に終わった DB 更新まで一括で戻ると想定しないでください(更新前と本体更新、関連更新と更新後フック、関連更新内の SQL)。
保存後の画面表示も同じリクエストに含まれる
通常の更新応答は、編集画面を再描画する経路を通ります。このとき BeforeOpeningPage の Action は update のままです。BeforeOpeningPage と Action = edit を組み合わせると、最初に編集画面を開いたときに絞れますが、更新後の再描画は対象から外れます。
General.json の UpdateResponseType の既定値は 0 です。通常の未ロックのレコードで 1 にすると項目差分を返す経路になり、同じ再描画フックを必ず通るとは限りません。ダイアログ編集も一覧行の置換という別経路です(General.json、ResponseByUpdate、EditorResponse)。
新規作成を保存する場合
新規作成画面を開く操作は items/new、作成ボタンを押す操作は items/create です。画面を開いただけでは BeforeCreate は呼ばれません。
図を読み込み中…
更新の名前を単に置き換えるだけでなく、ID が確定する位置が異なります。作成後のレコード ID を拡張 SQL で使うなら OnCreated を選びます。OnCreating の ID プレースホルダーは 0 です(Rds.cs)。
作成の入力反映・検証は ResultUtilities.Create、保存前フックと INSERT の準備は ResultModel.Create、保存後の SQL とフックは 同メソッドの後半で確認できます。
フォームを開いたとき・送信したとき
フォームは forms コントローラを使います。通常の新規作成画面と同じ new、通常の作成と同じ create という操作名なので、フォームだけを対象にする場合は Controller も確認します。
フォームを開く
操作手順は「フォーム機能で発行した URL を開く」です。利用可能な期間内では FormsController.New が ItemModel.New を呼び、記録テーブルの EditorNew に進みます。
図を読み込み中…
既存レコードを開く操作ではないので、標準の新規フォーム表示に「保存済みの送信レコードを読む段階」はありません。入力反映時に計算式処理を通ることと、作成フックをまだ通らないことを分けて考えます。参照コピーなどの追加条件はこの図から除いています。
根拠は FormsController.New、ItemModel.New、EditorNew、新規モデルの入力反映です。
フォームを送信する
操作手順は「フォームに入力して送信する」です。FormsController.Create は公開期間と CAPTCHA の検証を通った後に ItemModel.Create を呼びます。以降の作成フックは、先ほどの新規作成の図と同じ順です。ただし Controller は forms、Action は create です。
図を読み込み中…
送信完了画面は別のリクエストです。thanks はサイト設定を読み込んで完了メッセージを描画し、新たにレコードを作成しません。そのため AfterCreate が「送信完了画面を開いたとき」の条件になるわけではありません。また、完了画面の経路は通常のエディタ描画を通らないので、そのメッセージ描画に BeforeOpeningPage は使いません(FormsController.Create・Thanks、送信成功時の応答、ItemModel.FormThanks)。
Controllers を items に限定した拡張サーバースクリプト・拡張 SQL は、フォームの forms リクエストには一致しません。通常画面では動いてフォームで動かない場合、実行フックに加えて適用対象の絞り込みも確認します。
自動ポストバックで項目を変更したとき
操作手順は「編集画面で、自動ポストバックを有効にした項目の値を変更する」です。この節では通常の items の編集画面を対象にします。
送信先は、既存レコードなら items/edit、新規作成画面なら items/new です。URL に control-auto-postback=1 が付き、フォームの入力値と、変更した項目を示す ControlId が送られます。自動ポストバック専用の Action 名はありません。 edit・new だけでは初回表示と区別できないので、クエリのフラグと変更項目も確認します(_controllevents.js、ItemModel.NewJson)。
図を読み込み中…
ここでは、本体の Create・Update へ進まず、入力途中の値で項目を描き直します。本体の保存用フックである BeforeCreate・AfterCreate・BeforeUpdate・AfterUpdate と、保存用拡張 SQL の OnCreating・OnCreated・OnUpdating・OnUpdated は、この標準経路では呼ばれません。 サーバースクリプト内で明示的にレコードを更新する場合は、その追加処理を別に考えます。
画面項目の値や表示を入力直後に調整するなら、BeforeOpeningPage が着目点です。計算式へ渡す値を変えるなら BeforeFormula、計算後の値を扱うなら AfterFormula です。ただし、この2つは新規画面を開くときや保存時にも使われるため、実行元の操作とフラグを合わせて確認します。
根拠は EditorJson・EditorResponse・EditorFields、モデルの入力反映、項目の応答生成です。新規画面では取得対象の ID が 0 なので、保存済みレコードが見つかった場合の WhenloadingRecord と、入力反映で呼ばれる計算式のフックを区別してください。
絞り込み対象の項目が応答に含まれるかも確認する
ColumnFilterExpressions を持つリンク項目は、項目 HTML の差し替え対象に入ります。その描画で式が評価されます。ただし、変更した項目に「自動ポストバック時に返す項目」を設定していると、返す項目がそのリストで絞られます。式を持つリンク項目がリストから外れている場合、その項目は描き直されません(差し替え対象の選定、返す項目の絞り込み)。
項目連携で親の値を変更したとき
操作手順は「項目連携を設定した親項目を変更し、子の選択肢が変わるのを確認する」です。項目連携は、参照先テーブル同士のリンク構造から親子の条件を作る機能です。自動ポストバックの設定がなくても候補取得を送信します。
親項目の変更後は、500ミリ秒のデバウンスを経て items/relatingdropdown に、親の選択 ID・子の選択済み ID・親へのリンク項目名などを送ります。子が通常の SELECT の場合、画面の初期表示でも IsInitDisplay=true で候補を取得するため、一覧から編集画面を開く操作だけでも、edit に続く relatingdropdown が現れます。
図を読み込み中…
標準の項目連携は、子の候補を取得して option を差し替える経路です。編集画面全体の EditorFields を通らず、BeforeOpeningPage を直接呼びません。 候補取得に使う View の処理では WhenViewProcessing が呼ばれ得ます。JSONの式を評価する分岐では、その前に編集モデルを作るため、入力反映による BeforeFormula・AfterFormula も呼ばれます。取得できるレコードがあるときは WhenloadingRecord もあります。
つまり、relatingdropdown で呼ばれる条件を一律に「サイト設定の読み込みだけ」「編集画面表示と同じ」とは扱えません。JSONの式を使うかどうかで、モデルを作る段階が増えます。 編集元レコードの取得とリンク先候補の取得では対象のサイト設定も異なるため、WhenViewProcessing の適用対象サイトも確認します。
根拠は 親子条件の設定、初期表示・変更時の送信、サイト設定の読み込み、候補取得と応答、候補取得の分岐、リンク先候補の SELECTです。
項目連携と自動ポストバックを併用したとき
親項目に自動ポストバックも設定すると、同じ変更イベントから別々の要求が発生します。さらに、項目連携の応答は子の change を発火するので、次の段の項目連携や、子項目の自動ポストバックも起こり得ます。
自動ポストバックの応答にある initRelatingColumnEditorNoSend は、差し替えた項目へ項目連携の情報を付け直し、その再初期化からの候補取得送信を止めるものです。親の変更から発生する2つの要求を1つにまとめるものではありません。ログは、edit/new と relatingdropdown を分けて読みます(送信なしの再初期化、子の変更イベント)。
選択肢の JSON で絞り込むとき
選択肢のJSONには、リンク先の View に固定の検索条件を書く ColumnFilterHash と、画面の入力値を条件に展開する ColumnFilterExpressions があります。JSONの設定そのものは、項目変更時の送信を起こすイベントではありません。 候補が必要になった経路で条件を使います。
どの操作で式が評価されるか
| 操作 | 条件を評価する位置 | 着目する実行元 |
|---|---|---|
| 編集画面・新規作成画面の初期表示 | BeforeOpeningPage の後、対象項目の Field を描画するとき | items/edit・items/new |
| 自動ポストバックでリンク項目を描き直す | BeforeOpeningPage の後、差し替える項目の Field を作るとき | items/edit・items/new と control-auto-postback=1 |
| 項目連携で子の候補を取り直す | SearchDropDownColumn の式を評価する分岐に入ったとき | items/relatingdropdown |
| 選択肢の検索ダイアログを開く | SearchDropDownColumn の式を評価する分岐に入ったとき | items/searchdropdown |
描画時は、Field が SetChoiceHashByFilterExpressions を呼び、式の中の [項目名] を現在の編集モデルの値へ置き換えて、リンク先の候補を取得します。式の中の記述は、サーバースクリプトのJavaScriptや独立したSQLとして実行するものではありません(Field の候補取得、式の展開と候補取得)。
操作例として「分類Aの入力値で分類Bのリンク候補を絞る」場合、分類Aを変更した直後に分類Bを更新したいなら、分類Aの自動ポストバックを有効にし、分類Bが返す項目に含まれるようにします。式を書くだけで、分類Aの変更を監視して自動的に候補を取り直すわけではありません。
検索ダイアログを開いたとき
操作手順は「まだ保存していない入力を変更し、リンク項目の検索ダイアログを開く」です。ダイアログを開く処理はメインフォームのデータを検索用フォームへ写し、新規かどうかと編集対象のレコード ID も渡します。
図を読み込み中…
これは ColumnFilterExpressions があり、式を評価する分岐に入る場合の図です。固定の候補・固定の条件だけなら、式の評価用の編集モデルを作る段階は通りません。式を評価する場合も、BeforeOpeningPage を経るエディタの再描画ではありません。自動ポストバックが無効でも、検索ダイアログを開く操作を契機に、送信された保存前の入力値を条件へ展開できます(検索ダイアログの送信データ、検索アクションの入り口、入力値からの編集モデル生成)。
選択肢の SELECT と拡張 SQL の対象を分ける
リンク先の候補取得でも View.Where・View.OrderBy を使うため、適用条件に合う OnSelectingWhere・OnSelectingOrderBy が組み込まれます。このSQLの対象は候補の取得であり、編集中のレコードを保存するための OnUpdating などではありません(Link.Items)。
候補取得でも、同じ View インスタンスで既に WhenViewProcessing を実行済みなら再実行しません。図の候補取得は、要求ごとのフックの実行回数を保証するものではありません。
Controllers・Actions で拡張機能を絞る場合、自動ポストバックでの候補取得の実行元は items/edit または items/new、項目連携は items/relatingdropdown、検索ダイアログは items/searchdropdown です。同じリンク項目でも、候補取得を起こした操作によって対象条件が変わります。
項目連携の親条件と式の条件は、常に AND ではない
SearchDropDownColumn は、JSON形式のリンクに式があり、IsNew が真か、サイト ID と候補取得に渡したレコード ID が異なる場合、式を評価する側へ分岐します。この側には項目連携の parentColumn・parentIds を渡しません。親の ID での絞り込みを、式の条件へ自動的に追加するものではありません。
また、項目連携の通常の送信には検索ダイアログ用の DropDownSearchReferenceId がなく、サーバーでは 0 として読みます。式側で作るモデルが保存済みの編集対象を読むと決めつけず、要求に含まれるフォーム値と、新規判定を確認してください(送信項目、レコード ID の読み取り、分岐と親条件の引数、未指定の数値の読み取り)。
細かな設定と併用時の分岐は、項目連携・自動ポストバック・画面項目での絞り込みの関係を参照してください。
一覧編集・一括更新・CSVインポート
一覧のインライン編集で保存する
操作手順は「一覧でインライン編集を有効にし、既存行を変更して保存する」です。新規行を追加した場合も含め、Action は updatebygrid です。
既存行には BeforeUpdate、新規行には BeforeCreate が適用されます。ただし、通常の編集画面の保存とは異なり、行ごとの前処理を済ませてから、一覧編集用の SQL をまとめて実行します。
図を読み込み中…
ここでは通常の OnCreating・OnCreated・OnUpdating・OnUpdated ではなく、OnUpdatingByGrid・OnUpdatedByGrid を使います。既存行の検証も BeforeUpdate の後です。通常の編集画面で確認した検証との順序を、そのまま一覧編集へ当てはめないでください。
また、ここでの BeforeOpeningRow は DB 実行前です。「行を表示するフックだから保存後」とは判断できません。後処理を行うなら、既存行と新規行に対応する AfterUpdate・AfterCreate を確認します。
根拠は UpdateByGrid、保存成功後の処理です。
複数レコードを一括更新する
操作手順は「一覧で複数行を選択し、一括更新する」です。Action は bulkupdate です。
図を読み込み中…
各レコードでは通常の BeforeUpdate・AfterUpdate を使います。一方、モデルの更新に extendedSqls: false を渡すため、各レコードの通常の OnUpdating・OnUpdated は適用されません。SQL は一括操作全体の OnBulkUpdating・OnBulkUpdated で設定します。
前後の一括 SQL と各レコードの更新は、それぞれ別の DB 実行です。この図を、一括操作全体が1つのトランザクションに入るという意味では読まないでください(BulkUpdate)。
CSVをインポートする
操作手順は「インポート設定で新規作成・更新の扱いを確認し、CSVを送信する」です。画面からの送信時の Action は import です。
| 対象 | SS の条件 | 拡張 SQL |
|---|---|---|
| 新規レコードとして作成する行 | BeforeCreate・AfterCreate | 行ごとの OnCreating・OnCreated は無効 |
| 既存レコードとして更新する行 | BeforeUpdate・AfterUpdate | 行ごとの OnUpdating・OnUpdated は無効 |
| インポート全体 | 行ごとの作成・更新条件と区別する | OnImporting・OnImported |
インポートでも、行ごとのモデル保存には extendedSqls: false が渡されます。通常の保存用 SQL だけを登録しても、インポートの各行では動きません。また、既存データと比較して更新不要と判断された行は、更新メソッドを呼びません。CSVの行数と BeforeUpdate の回数が一致するとは限りません。
根拠は インポートの開始・行ごとの保存・終了、インポート前後の SQL 実行です。
バックグラウンド処理に振り分けられる設定では、画面への応答はジョブの受付です。保存処理は後からワーカーが ImportCore を実行します。送信リクエストの完了をインポート完了と見なさず、ジョブ側のログも確認してください。ワーカーはリクエストを持たない別の Context を作るので、画面の items/import という条件をそのまま前提にできません(受付時の分岐、ジョブの実行と Context の生成)。
単件削除と一括削除
操作手順は「編集画面で1件を削除する」と「一覧で複数行を選択して一括削除する」を別々に試すことです。一括削除は、単件削除のフックを対象件数だけ繰り返す経路ではありません。
| 操作 | Action | 削除前の SS | DB 実行内の順序 | 削除後の SS |
|---|---|---|---|---|
| 単件削除 | delete | BeforeDelete | OnDeleting → 削除 → OnDeleted | AfterDelete |
| 一括削除 | bulkdelete | BeforeBulkDelete | OnBulkDeleting → 一括削除 → OnBulkDeleted | AfterBulkDelete |
一括削除のフックに渡すモデルは、対象レコードを1件ずつ読み込んだモデルではありません。対象テーブルの標準経路では、行ごとの BeforeDelete・AfterDelete も呼びません。「削除される各行の値を model で読む」処理を単件削除から移す場合、この違いを確認します。リンク先レコードの連動削除は、ここで示す対象テーブルの一括削除と分けて追ってください。
コピー・プロセスボタン・カレンダー・API
これらは、画面の通常の作成・更新と同じ保存メソッドへ到達する場合があります。ただし、保存の条件名とリクエストの操作名は別です。拡張機能の Actions を create・update だけに限定すると、同じ保存フックでも対象から外れる操作があります。
| 試す操作 | Action | 保存フック・SQL の着目点 |
|---|---|---|
| 既存レコードのコピーボタンを押す | copy | 新しいレコードの BeforeCreate・AfterCreate、OnCreating・OnCreated |
| プロセスの追加ボタンを押す(アクション種別が保存) | 新規なら create、既存なら update | 通常の作成・更新条件。ボタンごとに別の Action が作られるわけではない |
| プロセスの追加ボタンを押す(アクション種別がポストバック) | 新規なら new、既存なら edit | 編集応答の再生成。通常の作成・更新フックへは進まない |
| カレンダー上でレコードを移動する | updatebycalendar | 更新対象なら BeforeUpdate・AfterUpdate、OnUpdating・OnUpdated |
| カンバン上でレコードを移動する | updatebykamban | 更新対象なら BeforeUpdate・AfterUpdate、OnUpdating・OnUpdated |
| APIでレコードを作成・更新する | create・update | 通常の作成・更新条件へ進むが、編集画面の再描画は行わない |
カンバンの操作名は、ソース上の綴りも updatebykamban です。カレンダー・カンバンでは、モデルに変更があると判定された場合に更新メソッドを呼びます。
コピーは元レコードを更新する処理ではなく、IDをリセットして新規作成する処理です。リンク項目からコピー元を指定して新規画面を開く経路は、コピーボタンによる即時作成と区別します。
プロセスボタンは Process_番号 というコントロール ID を持ちますが、送信先 Action はアクション種別と新規・既存の状態で決まります。ポストバックを、自動ポストバックの control-auto-postback=1 付き要求と同一視しないでください。
APIで作成・更新を行う場合、BeforeOpeningPage を保存後の処理の置き場所にはできません。必要な処理は保存の前後の条件で確認し、画面とAPIの区別は実際の要求の Controller・Action も照合します。
根拠は コピー、プロセスボタンの送信先、カレンダー、カンバン、API作成、API更新です。
拡張 SQL とサーバースクリプトを併用する
同じ処理に両方を設定するときは、SELECTを組み立てる段階、メモリ上のモデル、本体が保存する列、DBへ直接書くSQLを分けて確認します。条件名の「前」「後」だけでは、最終的な値や関連情報への反映まで判断できません。
取得条件はスクリプトの後にSQLへ組み込まれる
WhenViewProcessing が変更した view.ColumnFilterHash などは、SELECTの条件を組み立てるときに使われます。その後に OnSelectingWhere のSQL断片を追加します。これは、SQLの結果を受け取ってからスクリプトがフィルターをかけ直す処理ではありません。
view.OnSelectingWhere に名前を入れると、SpecifyByName: true の定義を名前で選べます。ただし、名前を指定しても SpecifyByName: false の定義は対象に残ります。 名前指定した1本だけに置き換わるとは限りません。また、OnSelectingWhereParams を設定した定義は、指定したキーがフィルターハッシュにすべて存在する場合に対象になります。
| 取り合いが起きる設定 | 確認すること |
|---|---|
| SSでフィルターを変え、SQLでも取得条件を足す | 同じSELECTに両方が組み込まれる。SQL断片を含めた条件全体を確認する |
| SSで名前を指定する | 名前指定対象に加え、自動適用の定義も残る |
| スクリプトでフィルターのキーを削除する | OnSelectingWhereParams のキー判定にも影響する |
| 一覧だけを絞るつもりでSQLを登録する | 編集対象の取得・保存後の再取得にも同じWHERE構築がある。index・gridrows などの適用対象を確認する |
| 取得後に表示を加工する | WhenloadingRecord・BeforeOpeningRow は取得後。既に実行されたSELECTの取得対象を変える位置ではない |
根拠は SSからViewへの反映、WHEREの構築順、キー判定とSQLの追加、名前の適用判定です。
保存前のSQLは、変更済みモデルを自動的に受け取らない
BeforeUpdate で変更するのは、保存に使うモデルの値です。OnUpdating は、モデルから本体の更新SQLを作る前にリストへ追加されますが、DBへの実行は本体の更新SQLと一緒で、OnUpdating が先です。そのSQLで保存対象の行をSELECTしても、本体の更新がまだ実行されていない段階のDB値を読みます。
通常の自動実行の OnUpdating には、サイトID・レコードID・タイムスタンプを渡します。サーバースクリプトの model 全体を渡しているわけではありません。SSで変更した項目が、そのまま拡張SQLの入力値になると考えないでください(更新SQLの組み立てと実行、OnUpdatingの適用、SQL文の生成)。
同じ列を変更すると、後から書く処理が結果を変える
本体の更新SQLは、モデルの更新判定などに従って列を選びます。したがって、OnUpdating が直接変更した列を本体も書く場合、そのSQLの値は後続の本体更新で上書きされます。本体がその列を書かない場合まで、必ず上書きされるという意味ではありません。 UpdatedTime を変更するSQLは、本体の更新条件にあるタイムスタンプ比較にも影響します。
次は、更新対象の ClassA を各処理が変更する場合を、ソース上の順序からたどった例です。対象列は通常の更新可能な項目とし、各DB実行が成功すること、取得時の加工や追加の保存がないことを前提にします。実機で採取した結果ではありません。
| 段階 | 処理 | メモリ上のモデル | DBの ClassA |
|---|---|---|---|
BeforeUpdate | 保存対象のモデルを A に変更 | A | 変更前の値 |
OnUpdating | 同じ行の ClassA をSQLで B に変更 | A | B |
| 本体更新 | 更新対象の ClassA をモデルから保存 | A | A |
| 本体更新後の再取得 | DBからモデルを読み直す | A | A |
OnUpdated | SQLで ClassA を C に変更 | 再取得前は A | C |
OnUpdated 後の再取得 | 通常の更新経路でモデルを読み直す | C | C |
AfterUpdate | モデルだけを D に変更。追加保存なし | D | C |
この例では、「最後に動いたスクリプトの値」と「最後にDBへ書いた値」が異なります。画面に表示された値だけで保存結果を判断せず、モデルのログとDBの保存値を別々に確認します。
根拠は 本体がモデルから作る更新SQL、保存列と値の選定、再取得とAfterUpdate、SSのモデル値の反映です。
保存後のSQLと、関連情報の更新にも順序がある
通常の更新では、本体更新後にモデルを取得し、関連情報のSQLを組み立てます。そのリストは Items のタイトル・全文検索用データの更新、リンク情報の作り直し、その後に OnUpdated という順です。OnUpdated がタイトルやリンク項目を直接変更しても、先に組み立てて実行した関連情報を、この再取得だけで作り直すわけではありません。 タイトル変更に伴う別の処理もあるため、更新対象と関連情報を個別に確認します。
図を読み込み中…
AfterUpdate が読むモデルは、通常の更新では OnUpdated の後の再取得を経ています。ただし取得は WhenViewProcessing・選択系の拡張SQL・WhenloadingRecord の経路も通るため、「DBの生の値を必ずそのまま読む」とは限りません。get: false を渡す経路、通常の OnUpdated を使わない一覧編集・一括更新・インポートも区別します。
作成時も、OnCreated のSQL実行後に、適用対象の定義があり get が有効なら再取得してから AfterCreate へ進みます。SQLの後処理とSSの後処理を併用する場合は、この再取得も含めて値を追います。
根拠は 関連情報更新と条件付き再取得、Getでの選択系SQLの適用、作成後のSQLと再取得です。
スクリプトから明示的に呼ぶSQLは、呼んだ位置で動く
extendedSql.ExecuteDataSet・ExecuteNonQuery などをスクリプトから呼ぶ経路は、OnUpdating などの自動実行とは別です。呼び出し時に Api: true と名前に合う定義を探し、明示した引数をSQLパラメータにして、その位置でDB実行します。モデルの入力値を使う場合は、必要な値を引数として明示的に渡す経路です。
この経路も Controllers・Actions などの適用判定を通ります。また、現在の実装では名前の一致を先に調べた後、名前を渡さず ExtensionWhere を呼びます。明示呼び出し用の定義に SpecifyByName: true を設定すると、この経路の適用判定では対象になりません。 view.OnSelectingWhere の名前指定と同じ設定として扱わないでください。
同じ定義に Api: true と OnUpdating: true を設定し、更新前のSSから明示呼び出しもすると、両方の適用条件が合う場合は、明示呼び出しと自動実行の2か所でSQLを実行します。処理が1回だけ必要なら、どちらの経路が担当するかを決めます。
根拠は extendedSqlオブジェクトのメソッド、定義の選定・引数・DB実行、適用条件です。
エラーと追加保存の境界を確認する
BeforeUpdate の実行後に context.ErrorData が設定されていれば、本体はSQLリストを実行する前に戻ります。一方、通常の更新で OnUpdated を含む後段のSQLが失敗しても、その前の本体更新と同じトランザクションではありません。スクリプトからの明示的なSQL実行も別のDB呼び出しです。「スクリプトが終了したら、そこまでのDB変更をすべて戻せる」という構造ではありません。
さらに model.UpdateOnExit を使うと、モデル値の反映後に通常の更新メソッドを呼びます。表示用のスクリプトや保存後のスクリプトでも、追加保存によって更新前後のSSと拡張SQLの経路へ再度入ります。DB値を読み直すだけのつもりで設定していないか、SQL実行が増えていないかを確認します。
根拠は 保存前SSのエラー確認と最初のDB実行、別のDB実行になる後段、UpdateOnExitの処理です。
同期実行の処理時間が画面・APIの応答待ちに加わる
通常の画面・API処理では、SSと拡張SQLの完了を待ってから後続へ進みます。追加した処理の時間は、利用者が応答を待つ時間に加わります。 OnUpdated・AfterUpdate の「後」は本体の更新後であり、利用者へ応答を返した後ではありません。保存後の条件へ重い処理を移しても、この待ち時間は残ります。
ブラウザからAjaxで送信していても、サーバー側では保存・拡張SQL・SS・応答生成を終えるまで、その要求への結果を返しません。extendedSql.ExecuteNonQuery などの明示呼び出しも、SQLの実行を終えてからスクリプトの次の処理へ進みます。
| 処理を増やす場所 | 応答時間への影響 |
|---|---|
WhenViewProcessing・選択系の拡張SQL | 一覧・編集対象・候補などの取得経路で実行する。取得し直す経路にも適用される |
BeforeOpeningRow の中でSQLを明示呼び出しする | 表示する行ごとに呼び出しが増える。1回のSQLだけでなく、対象行数と実行回数を確認する |
OnUpdating・OnUpdated と保存前後のSS | 本体の保存や再取得に加えて、その処理の完了も待つ |
OnCreated・OnUpdated 後の再取得 | SQL実行だけでなく、条件付きの追加SELECTと取得時のSSの時間も加わる |
| 自動ポストバック・項目連携・選択肢検索 | 保存前の操作にもサーバー処理の待ちが生じる。併用によって増える要求を個別に確認する |
| 一括更新・インポートの行ごとのSS | 対象レコードごとに処理が増える。1件で短い処理も、件数と繰り返しを含めて確認する |
明示呼び出しと自動実行の重複、UpdateOnExit による追加保存 | SQLや保存の経路が増え、その実行時間も応答までの処理に加わる |
実行順が変わらなくても、SQLの所要時間や呼び出し回数が増えると操作は遅くなります。複数の処理が並行して終わるという前提で、最も遅い1つだけを見積もらないでください。所要時間は設定・データ・DBの状態によるため、このページの順序図から固定の秒数は算出できません。
根拠は 更新処理を終えてから応答するコントローラ、モデル更新後の応答生成、スクリプト実行と値の反映、DBコマンドの同期実行、明示呼び出しのDB実行です。
バックグラウンド処理に振り分けられるインポートは、画面にはジョブの受付結果を返す経路です。この場合もワーカー内のSS・SQLはその処理の完了までジョブを進められないため、画面の応答時間とジョブ全体の所要時間を分けて確認します。このページの「CSVをインポートする」でも、受付と実行の経路を説明しています。
併用時の確認手順
- SSと拡張SQLそれぞれの適用対象を一覧にします。条件名だけでなく、サイト・レコード・
Controller・Action・名前指定も照合します。 - 通常の更新では、更新対象の1項目に絞り、入力値・保存前SSのモデル値・DB実行後の値・保存後SSのモデル値を記録します。上の
A・B・C・Dの表と比較します。 - SQLがその項目を変更していても、本体がその列を更新対象に含める場合と含めない場合を分けます。タイムスタンプを変更するSQLも別に確認します。
OnUpdatedでタイトル・リンク項目などを変更する場合は、対象行のDB値に加え、関連情報と画面表示も確認します。- 明示呼び出し・自動実行・
UpdateOnExitを区別してログを読みます。一覧編集・一括更新・インポートは、それぞれ専用のSQL条件で確認します。 - ブラウザのNetworkで要求の所要時間を確認し、SSの各処理とDB側のSQLの所要時間・実行回数を照合します。通常保存だけでなく、一覧の表示行数を増やした場合、入力途中の再評価、一括操作の件数を増やした場合も確認します。バックグラウンドのインポートはジョブの所要時間を別に記録します。
目的から条件を選ぶ
| やりたいこと | 条件・絞り込みの選び方 |
|---|---|
| 一覧の取得条件を変える | SS の WhenViewProcessing。初回の items/index と行再取得の items/gridrows を考慮する |
| 一覧の行ごとに表示を調整する | SS の BeforeOpeningRow |
| 編集画面を初めて開くときに表示を調整する | SS の BeforeOpeningPage と items/edit |
| 新規画面・フォームの初期表示を調整する | SS の BeforeOpeningPage と new。通常画面とフォームは Controller で分ける |
| 計算式へ渡す値を調整する | SS の BeforeFormula。保存専用と考えず、呼び出し元の操作も確認する |
| 保存するモデルの値を作成・更新前に変更する | SS の BeforeCreate・BeforeUpdate。標準検証との前後関係に注意する |
| 保存後の処理を行う | SS の AfterCreate・AfterUpdate。同じリクエストで再取得も起こることを考慮する |
| DB に作成された ID を使って SQL を流す | 拡張 SQL の OnCreated |
| フォーム送信だけに処理を適用する | forms/create と BeforeCreate または AfterCreate。SQL なら OnCreating または OnCreated |
| 入力直後に項目の値・表示を評価し直す | 自動ポストバックの edit/new と BeforeOpeningPage。クエリのフラグも確認する |
| 入力値でリンク候補を絞る | ColumnFilterExpressions。入力直後なら自動ポストバック、ダイアログで取り直すなら searchdropdown |
| 項目連携の候補取得だけに拡張機能を適用する | items/relatingdropdown。親条件側とJSONの式側で経路が異なることも確認する |
SS の条件別にモデル変更が保存へ反映される範囲は、model の内部構造と反映条件を参照してください。
自分の設定で確認するときの手順
ここまでの図はソース上の順序です。実機で確認するときは、テスト用の記録テーブルを使い、次の手順でリクエストと条件を照合します。
- テーブルの管理のサーバースクリプトで、調べる条件を選びます。まずは
BeforeOpeningPage、BeforeOpeningRow、BeforeCreate・AfterCreate、BeforeUpdate・AfterUpdateを操作ごとに確認します。 - デバッグを有効にして実行箇所を確認するか、
context.Logを使う既存のログ出力にCondition・Controller・Actionと、拡張側かサイト側かを区別できる名前を含めます。デバッグ設定はサーバースクリプトの内部構造を参照してください。 - ブラウザの開発者ツールの Network を開き、一覧を開く → 既存レコードを開く → 更新する → 新規画面を開く → 作成する、の順に操作します。
- フォーム URL を開き、送信します。
forms/new、forms/create、成功後のforms/thanksを別のリクエストとして確認します。 - 自動ポストバック対象の項目を変更し、次に項目連携の親を変更します。JSONの式で絞り込むリンク項目は、入力を保存する前に検索ダイアログも開きます。
edit/new、relatingdropdown、searchdropdownを別々の要求として確認します。併用時は子の変更から増える要求も確認します。 - 一覧編集・一括更新・CSVインポート・単件削除・一括削除も、テスト用データで別々に試します。CSVは新規・変更あり・変更なしの行を用意し、バックグラウンド実行ならジョブの完了も確認します。コピー、保存型・ポストバック型のプロセスボタン、カレンダー・カンバンの移動、APIの作成・更新も操作名と保存条件を照合します。
- スクリプトの実行を記録したログは、リクエストごとに区切って読みます。次の表と照合し、追加取得・再計算で同じ条件が増えていないかも見ます。
| 操作 | 重点的に照合する組み合わせ |
|---|---|
| 一覧を開く | index と BeforeOpeningPage、index と各行の BeforeOpeningRow |
| 既存レコードを開く | edit と WhenloadingRecord、edit と BeforeOpeningPage |
| 更新する | update と BeforeUpdate・AfterUpdate、再描画経路の update と BeforeOpeningPage |
| 新規画面を開く | new と BeforeOpeningPage。BeforeCreate はまだ呼ばれない |
| 作成する | create と BeforeCreate・AfterCreate |
| フォームを開く | forms・new と BeforeOpeningPage |
| フォームを送信する | forms・create と BeforeCreate・AfterCreate |
| 自動ポストバック対象の項目を変更する | edit/new と BeforeFormula・AfterFormula・BeforeOpeningPage。標準経路では保存用フックなし |
| 項目連携の親を変更する | relatingdropdown。JSONの式の有無、送信されたフォーム値とレコード ID を確認する |
| 一覧編集を保存する | updatebygrid と行ごとの作成・更新条件。SQLは OnUpdatingByGrid・OnUpdatedByGrid |
| 一括更新・CSVインポートを行う | 行ごとの保存条件と操作全体のSQL条件を区別する。変更なしのCSV行も確認する |
| 単件削除・一括削除を行う | BeforeDelete・AfterDelete と BeforeBulkDelete・AfterBulkDelete を区別する |
| コピー・カレンダー・カンバンで保存する | 作成・更新条件は共通でも、Action は copy・updatebycalendar・updatebykamban |
| APIで保存する | 作成・更新条件とAPI応答。画面再描画の条件を後処理に使っていないか確認する |
| 入力を変更して選択肢の検索を開く | searchdropdown。式の評価用モデルのフックを初回の edit と区別する |
context.Log はブラウザのコンソールではなく、サーバー側の LogBuilder に追加します(ServerScriptModelContext.cs)。拡張 SQL の実行は SS のログだけでは観測できないので、DB 側の実行ログとソース上の呼び出し箇所も照合します。
呼び出し回数を固定して考えない
WhenloadingRecordは、画面を最初に開くときだけでなく、保存後の再取得でも呼ばれます。OnCreated・OnUpdatedがある場合の追加再取得もあります。- 計算式処理は入力反映の中で呼ばれます。
SetByFormDataとSetByFormの両方に呼び出しがあるため、BeforeFormula・AfterFormulaを「保存1回につき各1回」と扱えません(SetByFormData の末尾、SetByForm の末尾側)。 WhenViewProcessingはViewインスタンスごとに実行済みを記録します。新しいビューで取得し直す場合は再び呼ばれます(View.cs)。- 権限・入力・CAPTCHA などの検証で途中終了した場合、後続の保存フックまで到達しません。