Librería Manager.js (1). Instanciación

En aplicaciones de servicios es enormemente corriente una estructura particular de base de datos en la que las tablas se relacionan de forma jerárquica. Por ejemplo, una organización que trabaja con proyectos, que se subdividen en tareas. O un deportista, que ejercita rutinas, que contienen series agrupadas por días. Podría seguir describiendo casos que, si no se amoldan exactamente a esta forma de organizar la información, sí lo hacen en su parte troncal. Cuando se quiere afrontar una aplicación de este tipo con arquitectura cliente-servidor, es preciso manejar esta estructura jerárquica al menos en tres ámbitos: la base de datos, el código en el lado del servidor y el código en el lado del cliente. Si lo que se pretende es desarrollar una aplicación web, existen muchos entornos de desarrollo que permiten agilizar la tarea, como Django, Ruby on Rails, Symfony… He usado y probado varios y, si bien son prácticos y ahorran mucho tiempo, tenía la espina de construir algo aún más ágil para estos casos sencillos en los que se trabaja con tablas enlazadas en forma de árbol. Algo con la inmediatez de Django, pero que permita trabajar directamente sobre el código final y sin riesgo de echar a perder la información almacenada por alterar una tabla. En cualquier caso, es un ejercicio modesto para aplicaciones sencillas, sin la ambición de acercarse a las capacidades de los entornos de desarrollo existentes. A aquellos lectores que desconozcan el tema, antes de seguir les recomiendo leer una breve introducción a la programación web.

Hecha la introducción, voy a replantear la situación que deseamos afrontar. Se quiere construir una aplicación web que gestione información estructurada en tablas que se enlazan en árbol. La tabla raíz contiene el usuario que se ha identificado, y que se va a gestionar en el lado del cliente con la librería Users.js, que ya comenté anteriormente. El resto de tablas tendrán un campo que indexará a la clave principal de la tabla de usuarios, o de otra. En el navegador se representan los datos de estas tablas, según se vayan seleccionando los valores de las claves principales. No tiene por qué poder accederse necesariamente a todas (en particular, queda excluida la tabla de usuarios por motivos de seguridad), ni a todos los datos. La gestión de la información, mediada por el código en el lado del servidor, va a ser sencilla: consulta, inserción, borrado, modificación… Por supuesto, se debe vigilar la seguridad (un usuario no puede ver o alterar los datos de otro, por ejemplo). Este tipo de aplicaciones básicas, como decía, son muy comunes, y es sencillo crear librerías genéricas para acometer su desarrollo. En esta entrada voy a describir la librería Manager.js, que va a simplificar la programación en el lado del cliente. Lo ideal es disponer de una librería similar en el servidor, e incluso mantener relacionadas las estructuras que manejan ambas con las de la propia base de datos.
A diferencia de la descripción pormenorizada que hice de Users.js, en este caso me voy a ceñir al API. En Manager.js, cada tabla de la base de datos (menos la de usuarios) viene representada por una instancia de la clase. Los parámetros del constructor van a contener tanto información de dicha tabla como de su relación con las otras y su modo de representación:

El parámetro tabla recoge el nombre de la tabla (real o traducido por el servidor, si se quiere), y nombre es el texto con el cual se va a identificar en la página web. Le sigue un array con los campos de información que la tabla va a contener; más adelante me detendré en su formato, puesto que almacena información adicional relativa a la representación. Por último, padre es el objeto que representa a la tabla a la que se indexa. Si fuese una de las tablas inmediatamente descendientes de la raíz (la tabla de usuarios), este último parámetro es nulo. Por ejemplo, vamos a imaginar que desde nuestra aplicación se gestionan proyectos. La construcción de la instancia asociada de Manager se haría tal como sigue:

Es decir, los proyectos se van a almacenar en la tabla de nombre project, cada una de sus filas es un «Proyecto», no contienen información adicional y no dependen sino del usuario que los crea. Obviamente, esta simplicidad oculta varios sobreentendidos. El primero es que la tabla project (todas nuestras tablas en realidad) debe contener los tres siguientes campos:

  • id: es la clave principal; debe ser un valor entero con autoincremento.
  • descriptor: es la descripción de cada elemento; en nuestra tabla de proyectos, será el nombre que el usuario da al proyecto; si fuese una tabla con recetas, por ejemplo, sería el título de cada receta.
  • [tablapadre]_id, donde [tablapadre] es el nombre de la tabla enlazada. Así, en la tabla project este campo se llama user_id, puesto que un usuario puede tener varios proyectos. Su contenido es, como es fácil adivinar, las claves principales del campo indexado.

Vamos a crear otra tabla que dependa de ésta. Imaginemos que nuestros proyectos son instalaciones que poseen varios controladores. Creamos por tanto una segunda tabla llamada controller. Sus campos van a ser los mínimos obligatorios: id, descriptor y project_id. Y el manejador de esta tabla se instancia en Javascript con el siguiente código:

Con lo dicho, es posible representar la jerarquía de tablas que comentaba al principio. Sin embargo, para darle riqueza a nuestra construcción, es preciso poder almacenar información adicional. Me dejé antes por describir el tercer parámetro del constructor, que va a tener información de los campos adicionales de las tablas, además de su representación. Dicho parámetro es un array de arrays. El primer nivel hace referencia a cada campo, y el segundo contiene lo siguiente:

  1. El nombre del campo en la tabla.
  2. El nombre del campo tal y como se representa en el navegador.
  3. Un objeto con información sobre su representación. Por defecto, la edición se hará mediante un campo de texto. Si el objeto contiene un elemento class, se usará una entrada de otro tipo:
    • Si class vale «checkbox», se muestra una casilla de selección.
    • Si class es «select», se usa un desplegable; los valores a elegir serán los contenidos en el elemento values, que debe guardar un objeto con parejas valor/representación.

Por ejemplo, si queremos guardar por cada controlador su IP (o dominio) y su MAC, haríamos la siguiente instanciación:

Como no se ha especificado un elemento class, tanto la dirección como la MAC se van a introducir y editar mediante un cuadro de texto.
Vamos a hacer algo más compleja nuestra base de datos, y a cada controlador le asociaremos una serie de señales, en la tabla channel. Estas señales podrán ser de entrada/salida o de memoria, y queremos que se elija un tipo u otro mediante un desplegable. El objeto que describe esta representación será entonces el siguiente:

Es decir, indicamos mediante «select» que queremos usar un desplegable, y sus valores posibles serán «E/S» o «Memoria», que se almacenarán en la base de datos como «F» y «M» respectivamente. También vamos a crear campos para almacenar información adicional de la señal, como si se historiza o no (campo checkbox):

Esta entrada continúa en Librería Manager.js (2). Uso.
Recursos asociados:
Librería Manager.js para gestión de información con estructura de árbol
Ejemplo de uso de la librería Manager.js

Facebooktwitterlinkedin