CRUD

TsgcHTMLComponent_CRUD: 1 つのテーブルの一覧、検索、ページング、作成・編集フォーム、削除確認、書き込み処理を、TDataSet または独自の SQL に対して、Delphi、C++ Builder、.NET で 1 つのコンポーネントとして提供します。

TsgcHTMLComponent_CRUD

業務アプリケーションがテーブルごとに毎回書き直しているページを、1 つのコンポーネントにしました。キーフィールドとルートプレフィックスを指定し、TDataSet をバインドするか、独自の SQL 上に 6 つのメソッドで構成したアダプターを渡します。ShowList がマークアップを返し、ページからポストされた内容は ProcessAction が処理します。

コンポーネントクラス

TsgcHTMLComponent_CRUD。同じユニット内の TsgcHTMLCRUDAdapterTsgcHTMLCRUDAdapter_DataSet が付属します

レンダリング内容

検索とページャー付きの一覧、作成・編集フォーム、削除確認

ファミリー

データ & テーブル

言語

Delphi, C++ Builder, .NET

データセットをバインドするか、アダプターを書く

KeyFieldRoutePrefix を設定し、アクティブな DataSet をバインドして ShowList(search, page) を呼び出します。独自の SQL を使う場合は、代わりに SetAdapter でコンポーネントにアダプターを渡します。ページからポストされた内容は ProcessAction に振り分けます。

uses
  Classes, sgcHTML_Component_CRUD;

var
  oCRUD: TsgcHTMLComponent_CRUD;
  vHTML: string;
begin
  oCRUD := TsgcHTMLComponent_CRUD.Create(nil);
  try
    oCRUD.CRUDID := 'customers';
    oCRUD.Title := 'Customers';
    oCRUD.RoutePrefix := '/customers';
    oCRUD.KeyField := 'ID';
    oCRUD.PageSize := 15;
    oCRUD.Actions := [caCreate, caEdit, caDelete, caView, caExport];
    oCRUD.VersionField := 'UPDATED_AT';   // optimistic locking
    oCRUD.OnValidate := CustomersValidate;

    // an active dataset: the columns are read off it
    oCRUD.DataSet := qryCustomers;

    vHTML := oCRUD.ShowList('', 1);   // search text, page number
  finally
    oCRUD.Free;
  end;
end;

// The rules that are about your data. Add a line per problem: nothing is
// written and the form comes back with the messages.
procedure TServer.CustomersValidate(Sender: TObject;
  aOperation: TsgcHTMLCRUDOperation; const aKey: string;
  aValues, aErrors: TStrings);
begin
  if Length(Trim(aValues.Values['NAME'])) < 3 then
    aErrors.Add('The name needs at least three characters.');
end;

// A message from the browser carries action, crud and key with the form fields
if oCRUD.ProcessAction(vAction, vKey, oFields, vHTML) then
  aResponse := vHTML;   // the markup to swap into the page

// Or over your own SQL: an adapter of six methods
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;

oCRUD.SetAdapter(TCustomersAdapter.Create(oCRUD));   // the component owns it
// includes: sgcHTML_Component_CRUD.hpp

TsgcHTMLComponent_CRUD *oCRUD = new TsgcHTMLComponent_CRUD(NULL);
try
{
  oCRUD->CRUDID = "customers";
  oCRUD->Title = "Customers";
  oCRUD->RoutePrefix = "/customers";
  oCRUD->KeyField = "ID";
  oCRUD->PageSize = 15;
  oCRUD->Actions = TsgcHTMLCRUDActions() << caCreate << caEdit << caDelete
    << caView << caExport;
  oCRUD->VersionField = "UPDATED_AT";   // optimistic locking
  oCRUD->OnValidate = CustomersValidate;

  // an active dataset: the columns are read off it
  oCRUD->DataSet = qryCustomers;

  String html = oCRUD->ShowList("", 1);   // search text, page number
}
__finally
{
  delete oCRUD;
}

// The rules that are about your data. Add a line per problem: nothing is
// written and the form comes back with the messages.
void __fastcall TServer::CustomersValidate(TObject *Sender,
  TsgcHTMLCRUDOperation aOperation, const String aKey,
  TStrings *aValues, TStrings *aErrors)
{
  if (aValues->Values["NAME"].Trim().Length() < 3)
    aErrors->Add("The name needs at least three characters.");
}

// A message from the browser carries action, crud and key with the form fields
String html;
if (oCRUD->ProcessAction(action, key, fields, html))
  response = html;   // the markup to swap into the page

// For your own SQL, derive from TsgcHTMLCRUDAdapter, override its six methods
// (Locate, Read, List, Insert, Update, Delete) and call oCRUD->SetAdapter().
using System.Collections.Generic;
using esegece.sgcWebSockets;

var crud = new TsgcHTMLComponent_CRUD();
crud.CRUDID = "customers";
crud.Title = "Customers";
crud.RoutePrefix = "/customers";
crud.KeyField = "ID";
crud.PageSize = 15;
crud.Actions = TsgcHTMLCRUDActions.caCreate | TsgcHTMLCRUDActions.caEdit
    | TsgcHTMLCRUDActions.caDelete | TsgcHTMLCRUDActions.caView
    | TsgcHTMLCRUDActions.caExport;
crud.VersionField = "UPDATED_AT";   // optimistic locking

// The key travels with the page: it is in neither the list nor the form
var id = crud.Columns.Add();
id.FieldName = "ID";
id.InList = false;
id.InForm = false;

var name = crud.Columns.Add();
name.FieldName = "NAME";
name.Caption = "Name";
name.Required = true;
name.MaxLength = 120;

// There is no TDataSet: read a DataTable, an IList<T> or a table behind a DbConnection
crud.SetAdapter(new TsgcHTMLCRUDAdapter_DataTable(crud, customersTable));

// The rules that are about your data
crud.OnValidate += (object sender, TsgcHTMLCRUDOperation op, string recordKey,
    List<string> values, List<string> errors) =>
{
    if (TsgcHTMLComponent_CRUD.ValueOf(values, "NAME").Trim().Length < 3)
        errors.Add("The name needs at least three characters.");
};

string html = crud.ShowList("", 1);   // search text, page number

// A message from the browser carries action, crud and key with the form fields
if (crud.ProcessAction(action, key, fields, out string reply))
    response = reply;   // the markup to swap into the page

主なプロパティとメソッド

最もよく使うメンバーです。

アダプター

TsgcHTMLCRUDAdapter は書き込み処理を担い、6 つの仮想メソッドを持ちます: LocateReadListInsertUpdateDeleteTsgcHTMLCRUDAdapter_DataSet はバインドされたデータセットを操作します。SetAdapter は独自のアダプターをコンポーネントに渡し、その所有権を引き継ぎます。List は 1 ページ分をタブ区切りの行として返し、キーは最後のセルに入れ、セット全体の行数を戻り値として返します。

.NET の場合

.NET には TDataSet がないため、.NET 版には 3 つのアダプターが付属します: TsgcHTMLCRUDAdapter_DataTableDataTable を対象とし、TsgcHTMLCRUDAdapter_List<T> は任意の IList<T> を対象とし、TsgcHTMLCRUDAdapter_DbConnectionDbConnection の背後にあるテーブルを対象として、すべての値をパラメーターとして送信します。Columns は宣言してください。テーブルから読み取られることはありません。

ページ

ShowList(search, page)ShowNewShowEdit(key)Save(key, values, html)DeleteRecord(key, html) は、それぞれホストが差し替えるマークアップを返します。ProcessAction は、ポストされた crudListcrudNewcrudEditcrudSavecrudDelete をこれらに振り分け、それ以外のアクションには False を返すので、1 つのページに複数のコンポーネントを置けます。

ページのオプション

ActionscaCreatecaEditcaDeletecaViewcaExport のセットで、caView だけのページはレポートになります。EditModecemModal または cemPage で、ConfirmDelete は削除の前に確認を求めます。PageSize の既定値は 25 で、500 を超えることはありません。SearchFields は検索ボックスが対象とするフィールドをセミコロン区切りで列挙します。

Columns

TsgcHTMLCRUDColumnFieldNameCaptionVisibleReadOnlyRequiredInListInFormWidthMaxLength を持ちます。1 つも宣言しない場合、コンポーネントはアクティブなデータセットから読み取ります。キャプションは DisplayLabel から、必須フラグと長さも同様で、キーは表示されますが編集はされません。LoadColumnsFromDataSet を呼ぶと、必要なときにこの処理を実行できます。

検証

必須の列が空の場合と、MaxLength より長い値は、最初に拒否されます。次に OnValidate が呼ばれます。問題ごとに aErrors に 1 行追加すると何も書き込まれず、メッセージ付きでフォームが戻ります。OnApplyUpdates はアダプターの前に実行され、aHandled を設定すると書き込みを独自に行えます。OnApplied は書き込みが成功した後に発生するので、ログの記録や、一覧を見ている他の画面の更新に使えます。

楽観的ロック

VersionField にバージョンフィールドを指定します。保存時にブラウザーから送られた値と保存済みの値を比較し、その間に他のユーザーがレコードを変更していた場合は書き込みを拒否するので、2 人目に Save を押したユーザーが 1 人目の変更を上書きすることはありません。

マスターと詳細

Details はレコードの子リストを保持します。各項目は、別の TsgcHTMLComponent_CRUDCRUD に、子側でマスターキーを保持するフィールドを MasterField に、キャプションを Caption に指定します。これらはレコードの編集中にフォームの下に表示されます。SetMaster(field, value) は一覧を 1 つのマスターレコードに絞り込みます。

CSV エクスポート

caExport を指定すると、ツールバーに RoutePath('export.csv') への Export リンクが表示されます。このルートには GetExportCSV(search) で応答します。エクスポートされるのは一覧が表示している検索結果で、最大 500 行までであり、テーブル全体ではありません。すべての値が引用符で囲まれるため、カンマや改行でファイルが壊れることはありません。

最初にキーを特定します

キーはすべてブラウザーから届きます。ShowEditSaveDeleteRecord は、編集や削除を行う前にアダプターに Locate を依頼するので、URL の数値を書き換えても別のレコードには到達しません。Actions も強制されます。caDelete のないページは、偽造されたフォームが削除をポストしても拒否します。セルとフィールドはエスケープされるため、マークアップを含むレコードもテキストとして表示されます。

ソケットと HTTP

検索ボックス、New、Edit、Delete、およびフォームは、data-sgc-ws-send が付いたフォームで、それぞれに隠しフィールドの actioncrudkey があり、ホストがそれらを ProcessAction に渡します。ソケット経由のメッセージにはパスがなく、ルーターには届かないため、認可は自分で行ってください。Delphi では、TsgcHTMX_Engine_Server.MessageSession がハンドシェイクの Cookie に対応するセッションを返し、なければ nil を返します。HTTP では、RoutePath がホストがエンジンのルーターに宣言するパスを組み立てます: /customers/customers/new/customers/{id}/edit/customers/save/customers/{id}/delete

始め方

上記のようにコードで作成するか、Delphi IDE のウィザードを使います。Tools › New sgcHTML CRUD page は、開いているフォーム上のデータセットを一覧表示し、選択したデータセットのフィールドを読み取って、設定済みのコンポーネントをフォームに追加します。接続に必要な 2 行のコードはクリップボードに入ります。Delphi 10.4 以降が必要です。IDE の外では、sgcHTMLGen コマンドラインツールが、テーブル、ルートプレフィックス、フィールドから、Object Pascal または C# でページのソースを書き出します。フィールドは name:flag の形式で指定し、たとえば ID:keyNAME:req:60 のように書きます。

提供状況

sgcHTML の一部で、sgcWebSockets とは別に販売されています。ユニットは SGC_HTML が定義されている環境でコンパイルされ、sgcVer.inc は Android と iOS を除くすべてのプラットフォームで HTML パック用にこれを定義します。デモは Demos\60.HTML\01.RunTime\02.AdminCRUD で、顧客エリアがこのコンポーネントで構築されており、置き換えられた手書きのページと並べて確認できます。

さらに詳しく

オンラインヘルプCRUD ページ、アダプター、書き込み処理の使用ガイドです。
すべての sgcHTML コンポーネント80 以上のコンポーネントの全機能マトリックスを閲覧できます。
無料体験版のダウンロード30 日間の体験版には 60.HTML デモプロジェクトが付属します。
価格完全なソースコード付きの Single、Team、Site ライセンス。
最もお得な選択: All-AccesseSeGeCe の全製品にプレミアムサポートが付いて、年間 €1,059 からご利用いただけます。
All-Access の価格を見る

始める準備はできましたか?

無料体験版をダウンロードして、Delphi、C++ Builder、.NET で Web UI の構築を始めましょう。