Una página CRUD web en Delphi en cinco minutos

· Componentes
Una página CRUD web en Delphi en cinco minutos

Toda aplicación de negocio contiene la misma página, escrita una y otra vez para cada tabla: una lista con un cuadro de búsqueda y un paginador, un formulario para crear y editar, una confirmación para borrar, y el código que escribe. Nuestra propia demo de administración tenía cuatro manejadores de peticiones y unas cuatrocientas líneas de constructores de página solo para clientes, y luego lo mismo otra vez para proveedores, y otra vez para facturas.

sgcWebSockets 2026.10 añade TsgcHTMLComponent_CRUD, que se hace cargo de esa página. La demo ahora construye el área de clientes con un adaptador de seis métodos, y mantiene al lado la versión escrita a mano para que puedas leer las dos.

La página

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;

Enlazado a un dataset, lee sus propias columnas, y eso es toda la página: lista, búsqueda, paginación, el formulario de creación y edición, la confirmación de borrado y una exportación a CSV de lo que la lista esté mostrando.

O tu propio SQL

Un dataset es una forma de guardar registros, no la única. Todo lo que modifica un registro pasa por un adaptador, así que el componente nunca ve SQL, un registro ni un driver de base de datos:

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));

Seis métodos sobre lo que ya tengas. En la demo, cada uno es una única llamada al pool de base de datos de la aplicación.

Las partes que habrías tenido que escribir después

Bloqueo optimista. Da nombre a una columna de versión y, al guardar, se compara lo que envía el navegador con lo que hay guardado, de modo que a la segunda persona que pulsa Guardar se le avisa de que el registro ha cambiado en lugar de sobrescribir en silencio a la primera:

FCRUD.VersionField := 'UPDATED_AT';

Maestro y detalle. Las listas hijas de un registro son otros componentes CRUD, mostrados bajo el formulario mientras el registro se está editando:

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

Validación que trata sobre tus datos, no sobre formularios. Las columnas obligatorias son cosa del componente; OnValidate es donde viven las reglas que tratan sobre los clientes. Añade una línea por cada problema y la escritura se rechaza y el formulario vuelve con ellas.

Dos formas de empezar

En el IDE hay ahora un asistente: Tools › New sgcHTML CRUD page. Indica la tabla, la clave y las columnas, y el componente se crea en el formulario que se está diseñando, con las dos líneas que lo conectan ya en el portapapeles.

Fuera del IDE hay una herramienta de línea de comandos, sgcHTMLGen, que escribe el código fuente de la página en Object Pascal o en C#:

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

Cómo responde la página

Cada control de la página envía los datos de la misma manera: el cuadro de búsqueda, el botón Nuevo, el Editar y el Delete de una fila, y el propio formulario, llevan tres campos ocultos, action, crud y, cuando corresponde, key. El host los lee y llama a ProcessAction, que devuelve el marcado con el que hay que sustituir.

Un mensaje enviado por el socket no lleva ninguna ruta, así que nunca llega al router y nunca pasa por su propia comprobación. Autorizarlo es trabajo de la aplicación, y el motor ahora te da lo necesario para hacerlo:

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;

La clave que llega desde el navegador se localiza antes de que se ejecute cualquier escritura, así que una clave que no pertenece a nadie no escribe nada.

Ver ambas versiones

En Demos\60.HTML\01.RunTime\02.AdminCRUD, /customers es el componente y /customers-classic es la página que sustituye. Misma base de datos, mismos datos, mismo aspecto. Uno de los dos tiene cuatrocientas líneas menos.

Actualización

El componente forma parte de sgcHTML y es aditivo: nada cambia en una página existente hasta que colocas uno en un formulario.

Sigue leyendo

Míralo en vídeo

Hay un vídeo breve de esto en el canal de eSeGeCe.

¿Preguntas, comentarios o ayuda con la migración? Ponte en contacto — recibirás respuesta de las mismas personas que escribieron el código.