#CRUD-L

Una aplicación que gestiona datos utiliza ampliamente las operaciones CRUD-L (Create, Read, Update, Delete y List).

El marco de desarrollo CRUD-L de Devkron facilita y automatiza la implementación de estas operaciones sobre las entidades de datos del sistema.

CRUD-L está implementado según el patrón MVC Model View Controller

Instalación y configuración

Este marco se encuentra en el repositorio https://github.com/Induxsoft/dkl-web

De manera predeterminada, los archivos controller.dkl, entity.dk, error.dk, form.dk, list.dk, mysql.dbr.dkh, view.dk, websencia.dk deben de estar en una carpeta llamada 'crudl' directamente en la raíz de binarios de Devkron (junto a la carpeta 'web' o 'fastcgi'). Estos archivos se usan por todos los sitios Web.

  • entry-point.dkl. Es el punto de entrada para el controlador y debe ser colocado dentro de la carpeta del sitio Web que lo va a emplear para que sus configuraciones sean exclusivas.

  • controller.dkl. Es el controlador principal y es responsable de la selección del modelo y vista apropiado para cada tipo de entidad..

  • entity.dk. Es el modelo genérico que implementa las acciones CRUD-L con entidades de datos que se almacenan en tablas con el patrón de diseño de Devkron (Induxsoft TableModel) previsto por la biblioteca de funciones dbr.dkh y que incluye los campos de control sys_pk, sys_guid, sys_recver, sys_dtcreated, sys_timestamp y sys_deleted. Más información aquí

  • view.dk. Es la vista genérica que proporciona una salida en JSON para las entidades provistas por el modelo.

  • error.dk. Salida de error, esta es una vista predeterminada en caso de error, probablemente deberá modificarla para adecuarla a sus requisitos de usabilidad y estilo.

  • mysql.dbr.dkh. Contiene funciones específicas para MySQL, incluyendo la suplantación de identidad de una base de datos en otra.

  • websencia.dk. Contiene funciones para renderizar vistas creadas con Websencia (páginas en formato JSON)

Conexión a la base de datos

Existen tres formas de conectar con la base de datos:

  1. Conexión a través del proveedor de indentidades. La autenticación de la identidad del usuario y conexión a la base de datos llegan establecidos por la capa de autorización.
  2. Conexión por suplantación de identidad. La identidad del usuario y sus permisos se establecen por la capa de autorización, pero se deberá obtener un identificador de sesión en la base de datos.
  3. Conexión constante. La base de datos y el usuario son establecidos como valores constantes en el punto de entrada (entry-point.dkl).

Dependiendo la forma de conexión elegida, el controlador requiere que se suministren parámetros a través de parámetros en la url (recuerde que los parámetros de url se encuentran disponibles en @http_context/request/get).

Parámetros de URL conocidos por el controlador

  • _entities_type Este parámetro en la url indica el tipo de entidades sobre los que aplicarán las operaciones.
  • _entity_id Este parámetro indica la clave primaria o identificador de la facilita cuando se requiere (opcional)
  • _key Indica el nombre del campo que usado específicamente como identificador, si no se indica se asume el campo sys_pk o sys_guid si _entity_id es alfanumérico
  • _app_group (Opcional) El nombre de un grupo de aplicaciones configurado en el archivo connections.xml.
  • _connection (Opcional) El nombre de una conexión en el grupo de aplicaciones indicado.
  • ws (Opcional) Identificador de espacio de trabajo si la conexión se establece por suplantación de identidad desde un workspace de Induxsoft.

Para cualquier forma de conexión deberá indicar _entities_type, los demás solo se requieren si así lo considera en sus configuraciones.

Mapeo hacia el punto de entrada

Aunque los parámetros de Url puede establecerse como tales, resulta conveniente indicarlos a través de mapas en routes.map

/misistema/{_entities_type}/{_entity_id?} > carpertademisistema/entry-point.dkl

Ejemplos:

  • Url sin mapa: https://midominio.com/misistema/entry-point.dkl?_entities_type=cliente&_entity_id=10
  • Url con mapa: https://midominio.com/misistema/cliente/10

Configuraciones en el punto de entrada

entry-point.dkl Es el punto de entrada y el lugar en donde se puede configurar el funcionamiento del marco.

Enmascaramiento de url

Para brindar Urls más descriptivas y organizar mejor su sistema, es posible enmascarar los puntos finales y redirigirlos a entidades específicas.

Por ejemplo suponga que tiene un CRUD-L para comprobantes de movimientos de almacen.

Las entidades podrían llamarse moventrada y movsalida, lo que significaría tener rutas como:

/misistema/moventrada
/misistema/movsalida

No obstante estas rutas son más descriptivas y ofrecen una mejor organización:

/misistema/inventarios/movimientos/entrada
/misistema/inventarios/movimientos/salida

Para lograr esto use las funciones crudl.routes.pattern y crudl.routes.entity junto con un enrutamiento en routes.map

Ejemplo

(entry-point.dkl)

do crudl.routes.pattern("/module/submodule/process")
do crudl.routes.entity("inventario/movimientos/entrada","moventrada")
do crudl.routes.entity("inventario/movimientos/salida","movesalida")

(routes.map)

/{module}/{submodule}/{process}/{_entity_id?} > entry-point.dkl

Variables globales configurables

@path_root="web/nombre_host"
@base_path="ruta relativa en donde se encuentran las entidades"
@db_engine="Contiene el tipo de gestor de base de datos, por defecto viene con MY_SQL"

// Establezca estos valores únicamente si no va a conectarse a través de la capa de autorización de bases de datos
@crudl.qname="Nombre cualificado hacia una conexión de la base de datos"

//Solo si la conexión y usuario serán constantes
@crudl.user="Nombre del usuario"
@crudl.pwd="Contraseña del usuario"

/*
Si no se define el valor de @crudl.qname puede pasarse la conexión por la url como se muestra a continuación
el archivo controller.dkl esta preparado para recibir variables a través de una petición GET para poder definir una conexión o bien las entidades.

- _app_group //define la aplicación hacia el archivo de conexión de devkron
- _connection //define la conexión hacia el archivo de conexión de devkron


ejemplo
http://myhost/entry-point.dkl?_app_group=myapp&_connection=myconnection&_entities_type=myentidad

También puede utilizar un enrutamiento como lo muestra en:
https://docs.induxsoft.net/es/devkron/Sitios-y-aplicaciones-Web-con-DKL/flujo-http.md

*/

Lógica del controlador

Selección de operación

Con base en el método HTTP y la URL

Método ...{_entities_type}/ ...{_entities_type}/{_entity_id}
GET list read
POST create error
PUT error update
PATCH error update
DELETE error delete

Selección de vista

La vista (salida devuelta) se basa en el tipo de contenido indicado en el encabezado Accept de la solicitud HTTP y la operación realizada

Si Accept incluye el tipo y subtipo MIME text/html, se intentará responder contenido HTML de acuerdo a la siguiente tabla:

Operación Éxito Fracaso
LIST list view error view
READ form view error view
CREATE redirect form view
UPDATE redirect form view
DELETE redirect error view

Si Accept NO incluye text/html, la respuesta será JSON (Content-type:application/json)

Cada tipo de entidad deberá tener una carpeta con su nombre en la ruta indicada por @base_path

Por ejemplo para una entidad cliente con @base_path="/misistema/entidades" puede tener los siguientes archivos:

/misistema/entidades/cliente/form.dk
/misistema/entidades/cliente/list.dk
/misistema/entidades/cliente/model.dk
/misistema/entidades/cliente/controller.dk

Donde

  • form.dk Es la vista específica de la entidad para la presentación como formulario
  • list.dk Es la vista específica de las entidades presentadas como lista
  • controller.dk Es un controlador específico para la entidad (opcional)
  • model.dk Es un modelo específico para la entidad (opcional)

En general, si no se ha existe un archivo controller.dk o model.dk en la carpeta de la entidad, se usan el controlador (controller.dk) y modelo (entity.dk) predefinidos.

Operación y formatos de datos de respuesta

La operación se determina con base en el método HTTP utilizado en la solicitud de una url.

  • Create. Método HTTP POST sin el parámetro _entity_id o con _entity_id=_new
  • Read. Método HTTP GET indicando el parámetro _entity_id
  • Update. Método HTTP PATCH con el parámetro _entity_id o POST con encabezado HTTP Accept:text/html y el parámetro _entity_id
  • Delete. Método HTTP DELETE indicando el parámetro _entity_id
  • List. Método HTTP GET sin indicar el parámetro _entity_id

El formato de los datos de respuesta será HTML producido por una vista específica si el encabezado Accept de la solicitud HTTP es text/html, en cualquier otro caso responderá la vista genérica (view.dk) con los datos proporcionados por el modelo como JSON.

Las operaciones CREATE y UPDATE aceptan datos codificados como application/json útil si se envían programáticamente con Javascript (u otros lenguajes/herramientas) o bien, como application/x-www-form-urlencoded que es simple de enviar mediante formularios HTML estándar.

Interfaz Web (Html)

La implementación del CRUD-L con interfaz de usuario Web con vistas específicas realiza las acciones de adición y edición de elementos siguiendo el patrón PRG (Post - Redirect - Get).

Para solicitar el contenido HTML del formulario vacío para agregar, se realiza una solicitud GET con _entity_id=_new, mientras que para obtener el formulario con los datos de una entidad para editarla se indica su identificador (sys_pk, sys_guid u otro); el valor del identificador para adición (_new) puede establecerse a través de la variable global @entity_id_blank="_new"

Siempre debe establecerse el encabezado HTTP Accept: text/html para que la respuesta del método POST usado para agregar (create) o actualizar (update) envíe la redirección (encabezado Location) en lugar de los datos en JSON de la entidad creada o actualizada.

Se ha implementado el comportamiento de actualización en el método POST (además de en PATCH) para facilitar la programación del lado del agente del usuario (navegador).

Web services REST

Como puede deducirse, las operaciones Create, Read, Update, Delete y List que son invocadas a través de los correspondientes métodos HTTP (POST, GET, PATCH y DELETE) sin el encabezado ACCEPT: text/html, implementan automáticamente la funcionalidad esperada para un servicio REST

Funcionando como Web services, las vistas específicas no se emplean, pero sí los modelos específicos si existen.

Se espera que la carga útil (payload) se envíe en JSON y la respuesta generada por la vista genérica (view.dk) será tamién JSON.

Puede deshabilitar este comportamiento automático estableciendo @auto_crud=@false en el entry-point.dkl

CREATE

Crea (agrega) una nueva entidad.

Solicitud

End points válidos:

  • {aplicación}/{conexión}/{tipo_entidad_plural}/
  • {aplicación}/{conexión}/{tipo_entidad_plural}/{identificador}/

Método HTTP: POST

Content-Type: application/JSON;charset=utf-8

Carga útil: JSON

Respuesta
  • Content-Type: application/JSON;charset=utf-8
  • Código de estado de respuesta HTTP: 200 (Ok)
  • Cuerpo de la respuesta: Entidad completa en JSON
Observaciones

Si no se incluye el id de la entidad en la URL de solicitud HTTP, deberá incluirse en el cuerpo de la carga útil; si se incluye en ambos lugares se le dará prioridad a la URL.

READ

Obtiene una entidad completa

Solicitud

End point válido:

  • {aplicación}/{conexión}/{tipo_entidad_plural}/{identificador}/

Método HTTP: GET

Respuesta
  • Content-Type: application/JSON;charset=utf-8
  • Código de estado de respuesta HTTP: 200 (Ok)
  • Cuerpo de la respuesta: Entidad completa en JSON
UPDATE

Actualiza una entidad

Solicitud

End point válido:

  • {aplicación}/{conexión}/{tipo_entidad_plural}/{identificador}/

Métodos HTTP: PUT y PATCH

Respuesta
  • Content-Type: application/JSON;charset=utf-8
  • Código de estado de respuesta HTTP: 200 (Ok)
  • Cuerpo de la respuesta: Entidad completa en JSON
Observaciones

Use PUT si va a incluir todos los campos de la entidad, en caso contrario use PATCH. Si usa PUT y no incluye todos los campos, los que no se hayan incluído se establecerán a NULL o su valor predeterminado.

DELETE

Elimina una entidad

Solicitud

End point válido:

  • {aplicación}/{conexión}/{tipo_entidad_plural}/{identificador}/

Método HTTP: DELETE

Respuesta
  • Código de estado de respuesta HTTP: 204 (Sin cuerpo en la respuesta)
  • Cuerpo de la respuesta: Vacío
LIST

Devuelve una lista de todas las entidades

Solicitud

End point válido:

  • {aplicación}/{conexión}/{tipo_entidad_plural}/

Método HTTP: GET

Respuesta
  • Content-Type: application/JSON;charset=utf-8
  • Código de estado de respuesta HTTP: 200 (Ok)
  • Cuerpo de la respuesta: Array de entidades en JSON
Observaciones

Pueden establecerse parámetros de filtro, orden, tamaño de la lista, etcétera en la URL

Identificadores y campos de sistema

Las entidades con fuerte control de concurrencia implementan el patrón de diseño de tablas de bases de datos relacionales de Devkron, que incluye al menos los siguientes campos controlados por el sistema:

  • sys_pk
  • sys_guid
  • sys_dtcreated
  • sys_timestamp
  • sys_recver
  • sys_deleted

Puede usar como identificador tanto sys_pk,sys_guid o el campo que se haya definido como tal.

En actualizaciones de entidades que incluyan sys_recver, siempre deberá enviarlo en la carga útil.

Ningún campo de sistema (sys_*) es actualizable por el usuario (programador).

Parámetros pre-definidos

Por conveniencia, los parámetros pre-definidos (previamente conocidos) inician con el caracter _

  • _method Método HTTP
  • _entities_type Nombre del tipo de entidades
  • _entity_id Identificador de la entidad
  • _limit Cantidad máxima de elementos a devolver en LIST
  • _fields lista delimitada por comas de los campos a devolver (READ y LIST)
  • _order especificación del orden de la lista (LIST)
  • _start Índice de inicio de la lista
  • _key Nombre del campo clave usado

Implementación

Implementación del modelo

Para definir un modelo personalizado que extienda la funcionalidad predeterminada debe crear un archivo denominado model.dk en la carpeta de la entidad de datos.

Variables globales del modelo

Las siguientes variables globales permiten configurar varias características de la funcionalidad predeterminada.

// Nombre de la tabla subyacente, si no se establece se asume el nombre del parámetro _entities_type
@table_name=""

// Nombre del campo clave predeterminado (no debería modificar este valor)
@keyfield="sys_pk"

// Consulta SQL para obtener los datos de 1 elemento de datos (usada en la operación READ), aquí puede definir uniones, renombrar o limitar los campos a devolver
@read_query="select * from #<@table_name> where #<@keyfield>=@_entity_id limit 1;"

// Consulta para obtener la clave primaria de una tabla 'principal o maestra'
@get_sys_pk="select sys_pk from #<@table_name> where #<@keyfield>=@_entity_id and ifnull(sys_deleted,0)=0 limit 1;"

// Consulta para obtener la lista de elementos de datos (usada en la operación LIST)
@list_query="select * from #<@table_name> where ifnull(sys_deleted,0)=0;"

// Lista de campos considerados de sistema (no es necesario modificar nada)
@sys_fields="sys_pk, sys_guid, sys_dtcreated, sys_timestamp, sys_recver,sys_deleted, sys_lock"

// Campos que se actualizaran (operación UPDATE), * indica que todos los que se envíen se actualizaran o bien puede indicar una lista delimitada por comas
@update_fields="*"

// Campos que se establecerán en una inserción (operación CREATE), * indica que todos los que se envíen se establecerán o bien puede indicar una lista delimitada por comas
@create_fields="*"

// Campos que se excluirán en una operación CREATE
@create_exclude_fields=""

// Campos que se excluirán en una operación UPDATE
@update_exclude_fields=""

// Alias de campos en la actualización, es una lista de pares alias:campo delimitados por comas
@update_alias_fields=""

// Alias de campos en la inserción (CREATE), es una lista de pares alias:campo delimitados por comas
@create_alias_fields=""

// Establece que cuando se realicen operaciones de entidades con elementos dependientes (hijos maestro/detalle) 
// se realizará en el contexto de una transacción de base de datos (no debería modificar este valor)
@use_transaction=@true

Referencias a funciones del modelo

La extensión (personalización) del modelo debe realizarse mediante la creación de funciones que realicen las tareas específicas necesarias y además, estableciendo punteros de referencia a ellas para que se integren al flujo previsto.

Las siguientes variables globales son referencias a funciones:

  • @create Referencia a una función que realiza la inserción de datos, requiere los parámetros: &db, &params, &data
  • @read Referencia a una función que devuelve un objeto de datos, requiere los parámetros: &db, &params
  • @update Referencia a una función que realiza la actualización de datos, requiere los parámetros: &db, &params, &data
  • @delete Referencia a una función que elimina un elemento de datos, requiere los parámetros: &db, &params
  • @list Referencia a una función que devuelve una lista de elementos de datos, requiere los parámetros: &db, &params
  • @blank Referencia a una función que devuelve un elemento de datos vacío (o con datos predeterminados), requiere los parámetros: &db, &params

Use la sentencia point to de Devkron como en el ejemplo:

mifuncion_update::&db,&params,&data
{
    // Escriba aquí la funcionalidad para actualizar los datos que recibe a través de data en la base de datos db con los parámetros (generalmente de url) de params si fuese necesario
}

// Reemplace la función predeterminada de actualización por su propia implementación
point @update to mifuncion_update

Implementación de las vistas

Las vitas se ejecutan en un contexto diferente al programa principal que responde a la solicitud HTTP, por lo que reciben la información necesaria a través de la variable global @crud_context que tiene los siguientes miembros:

  • output Es una referencia a los datos de salida (que generalmente serán pintados en el formulario, usualmente se trata de los campos del elemento de datos)
  • . Es un alias de output
  • input Es una referencia a los datos de entrada de la operación (usualmente lo que se envió desde el formulario)
  • parameters Una referencia a los parámetros de la solicitud (usualmente parámetros de la URL)
  • error Es nulo si todo ha ido bien o una referencia a un objeto con información del error ocurrido
  • database Es una referencia a la base de datos
  • http Es una referencia a todo el objeto @http_context de la solicitud HTTP en curso

Vista de lista

Para definir una vista de lista personalizada se require un archivo llamado list.dk en la carpeta de la entidad.

Vista de formulario

Para definir una vista de formulario personalizada se require un archivo llamado form.dk en la carpeta de la entidad.

Otras configuraciones

Cambio del motor de base de datos cuando se requiere la suplantación de identidad

Modifique controller.dkl únicamente si el gestor de base datos que va utilizar es diferente a MY_SQL

Ejemplo

@db_engine="sql_server" //variable global de entry-point

switch @db_engine  //bloque de código al principio de controller.dkl
{
    case "sql_server"
    {
        include "crudl/sql_server.dbr.dkh"
    }
}

sql_server.dbr.dkh debe implementar las mismas funciones que mysql.dbr.dkh