5分で作るDelphi Web CRUDページ

· コンポーネント
5分で作るDelphi Web CRUDページ

すべての業務アプリケーションには、テーブルごとに何度も書き直される同じページが存在します。検索ボックスとページャーを備えたリスト、作成と編集のためのフォーム、削除の確認、そして書き込みを行うコードです。私たち自身の管理者用デモでも、顧客のためだけに四つのリクエストハンドラーと約四百行のページビルダーがあり、それが仕入先についても、請求書についても繰り返されていました。

sgcWebSockets 2026.10は、そのページをまるごと引き受けるTsgcHTMLComponent_CRUDを追加します。デモでは今、顧客エリアを六つのメソッドからなるアダプターで構築しており、両方を読み比べられるように手書き版もそのまま隣に残してあります。

ページ

uses
  sgcHTML_Component_CRUD;

FCRUD := TsgcHTMLComponent_CRUD.Create(Self);
FCRUD.CRUDID := 'customers';
FCRUD.RoutePrefix := '/customers';
FCRUD.Title := 'Customers';
FCRUD.KeyField := 'ID';
FCRUD.PageSize := 15;
FCRUD.Actions := [caCreate, caEdit, caDelete, caView, caExport];
FCRUD.DataSet := FDQueryCustomers;

データセットに結び付けると、コンポーネントは自分でその列を読み取ります。それだけでページ全体が完成します。リスト、検索、ページング、作成と編集のフォーム、削除確認、そしてリストに表示されているものすべてのCSVエクスポートです。

あるいは独自のSQLで

データセットはレコードを保存する一つの方法であり、唯一の方法ではありません。レコードを変更する処理はすべてアダプターを経由するため、コンポーネント自体はSQLもレコードもデータベースドライバーも一切目にしません。

type
  TCustomersAdapter = class(TsgcHTMLCRUDAdapter)
  public
    function Locate(const aKey: string): Boolean; override;
    function Read(const aKey: string; aValues: TStrings): Boolean; override;
    function List(const aSearch: string; aPage, aPageSize: Integer;
      aRows: TStrings): Integer; override;
    function Insert(aValues: TStrings; var aKey: string;
      aErrors: TStrings): Boolean; override;
    function Update(const aKey: string; aValues, aErrors: TStrings)
      : Boolean; override;
    function Delete(const aKey: string; aErrors: TStrings): Boolean; override;
  end;

FCRUD.SetAdapter(TCustomersAdapter.Create(FCRUD));

すでにあるものの上に六つのメソッドを重ねるだけです。デモでは、それぞれがアプリケーションのデータベースプールに対する一回の呼び出しになっています。

次に自分で書くはずだった部分

楽観的ロック。バージョン列の名前を指定するだけで、保存時にブラウザから送られてきた値と保存されている値が比較されます。そのため、二番目に保存ボタンを押した人には、最初の変更を黙って上書きする代わりに、レコードが変更された旨が伝えられます。

FCRUD.VersionField := 'UPDATED_AT';

マスターと詳細。あるレコードの子リストは別のCRUDコンポーネントであり、そのレコードが編集されている間、フォームの下にレンダリングされます。

with FCRUD.Details.Add do
begin
  CRUD := FCRUDOrderLines;
  MasterField := 'ORDER_ID';
  Caption := 'Lines';
end;

フォームについてではなく、データについての検証。必須列はコンポーネントの仕事です。OnValidateは、顧客に関するルールが置かれる場所です。問題ごとに一行追加すれば、書き込みは拒否され、それらの内容とともにフォームが戻ってきます。

始めるための二つの方法

IDEには今、ウィザードがあります。Tools › New sgcHTML CRUD pageです。テーブル名、キー、列を指定すると、設計中のフォーム上にコンポーネントが作成され、それを配線する二行がクリップボードにコピーされます。

IDEの外には、コマンドラインツールsgcHTMLGenがあり、ページのソースをObject PascalまたはC#で書き出します。

sgcHTMLGen crud --table Customers --prefix /customers ^
  --fields "ID:key CODE:req:32 NAME:req:120 CITY:80 NOTES:noform"

ページはどう応答するか

ページ上のあらゆる操作は同じ方法で送信されます。検索ボックス、新規作成ボタン、行の編集操作とDelete操作、そしてフォーム自体が、三つの隠しフィールドactioncrud、そして意味を持つ場合にはkeyを運びます。ホストはそれらを読み取り、差し替えるマークアップを返すProcessActionを呼び出します。

ソケット経由で送られるメッセージにはパスが含まれないため、ルーターに届くことはなく、ルーター自身のチェックを受けることもありません。それを認可するのはアプリケーションの仕事であり、エンジンは今、そのために必要なものを渡してくれます。

oSession := FHTMXEngine.MessageSession(aConnection, FAuth);
try
  if not Assigned(oSession) then
    Exit;   // a write with nobody behind it is not a write
  oPage.ProcessAction(vAction, oParams.Values['key'], oParams, aResponse);
finally
  oSession.Free;
end;

ブラウザから届いた鍵は、書き込みが実行される前に検索されるため、誰のものでもない鍵では何も書き込まれません。

両方のバージョンを見る

Demos\60.HTML\01.RunTime\02.AdminCRUDでは、/customersがコンポーネント版で、/customers-classicがそれによって置き換えられたページです。同じデータベース、同じデータ、同じ見た目です。一方はもう一方より四百行短くなっています。

アップグレード

このコンポーネントはsgcHTMLの一部であり、追加的なものです。フォームにドロップするまでは、既存のページで何も変わりません。

次に読む

動画で見る

この内容についての短い動画eSeGeCeチャンネルにあります。

質問、フィードバック、移行のお手伝いが必要ですか?お問い合わせください — コードを書いた本人たちから返信が届きます。