Introducci贸n
En la entrada de hoy, vamos a programar un CRUD b谩sico con javascript.
Ser谩 independiente del sistema de persistencia que utilicemos, aunque en este caso vamos a utilizar las variables de sesi贸n de javascript (sessionStorage).
Existen m茅todos m谩s 贸ptimos seg煤n el sistema de persistencia que tengamos y el n煤mero de datos a trabajar. Pero como gu铆a para que pod谩is construir vuestro propio CRUD os puede valer.
Si os est谩is preguntando que es CRUD, sus siglas corresponden a las 4 operaciones b谩sicas que se hacen al trabajar con sistema de persistencia Create, Read, Update y Delete, o sea, crear, leer, actualizar y borrar.
Sin m谩s demora vamos primero a crear nuestro CRUD. Para hacerlo m谩s f谩cil y no tener que instalar node.js, vamos a correr nuestro CRUD en el navegador usando HTML.
Primero creamos nuestro index.html
<!DOCTYPE html> <html> <head> <title>CRUD JD</title> </head> <body> <script src="./js/index.js" type="module"></script> </body> </html>
Recuerda que es una buena pr谩ctica a帽adir el script de js antes de finalizar el body, para que as铆 ya est茅 cargado todo el DOM. Como vamos a utilizar m贸dulos no olvides a帽adir el type=禄module禄 al script, sino, nos sale un error como este en el inspector de nuestro navegador:

Ahora procedemos a crear nuestro index.js, que generalmente tendremos dentro de la carpeta /js
function app(){ debugger } app();
Creamos la funcion app() que ser谩 la funci贸n que dar谩 paso a toda la ejecuci贸n de javascript, y usamos debugger, para comprobar que todo est茅 correctamente enlazado. Si todo esta correcto, al correr todo en el navegador se activar谩 el modo debugger

Perfecto!
Ahora vamos a crear nuestro archivo CRUD.js que contendr谩 nuestra clase CRUD, la cual manejar谩 el sistema de persistencia.
export class CRUD{}
Usamos export delante de la declaraci贸n de la clase para poder importarla como m贸dulo en index.js
import { CRUD } from "./CRUD.js"; function app(){ debugger } app();
Ahora lo ideal es que cada vez que instanciemos la clase CRUD, le digamos el nombre de la tabla con la que queremos trabajar, por tanto ese argumento se lo pasamos por constructor a la clase CRUD, el cual ser谩 asignado a un atributo privado para poder acceder a 茅l desde cualquier m茅todo interno de la clase CRUD, pero no desde fuera de la clase.
export class CRUD{ #tableName = null; constructor(tableName){ this.tableName = tableName; } }
Por cuestiones de integridad, deber铆amos obligar a que cada vez que se instancie la clase CRUD, se tenga que pasar el nombre de la tabla obligatoriamente, para as铆 no tener instanciaciones inconsistentes dentro de nuestra aplicaci贸n. Vamos a a帽adir una validaci贸n que lance un error en caso de no pasar ning煤n argumento. Cabe destacar que podr铆amos a帽adir m谩s condiciones de validaci贸n, as铆 como una manera de capturar ese error, pero no es objetivo del curso, simplemente queremos detener el flujo de nuestra aplicaci贸n en caso de que alguien instancie la clase CRUD sin el nombre de la tabla.
export class CRUD{ #tableName = null; constructor(tableName){ this.#setTableName(tableName); } #setTableName(tableName){ this.#tableNameValidate(tableName); this.#tableName = tableName; } #tableNameValidate(tableName){ if(tableName == undefined) throw new Error("Table name required"); } }
Como podemos ver hemos creado el m茅todo privado setTableName(), cuya funci贸n como dice su nombre es la de asignar un valor al atributo privado tableName de la clase CRUD. Para separar responsabilidades (Principio de Responsabilidad 脷nica), separamos la asignaci贸n de la validaci贸n, as铆 en el momento que queramos modificar los criterios de validaci贸n solo tenemos que tocar el m茅todo cuya responsabilidad es la de validar tableNameValidate().
Ahora vamos a crear nuestros cuatro m茅todos CRUD.
export class CRUD{ #tableName = null; constructor(tableName){ this.#setTableName(tableName); } #setTableName(tableName){ this.#tableNameValidate(tableName); this.#tableName = tableName; } #tableNameValidate(tableName){ if(tableName == undefined) throw new Error("Table name required"); } create(data){} read(id){} update(id, data){} delete(id){} }
Como podemos ver, al m茅todo create() le pasamos como argumento la informaci贸n que queremos guardar. Recuerda que cada informaci贸n que guardamos debemos asignarle un id, en ese caso hemos decidido que ese id lo genere nuestra propia clase CRUD, en otros casos, y es muy buena pr谩ctica generar el id desde fuera de la clase CRUD, a trav茅s de alg煤n sistema generador de id como uuid (pero eso da para otra entrada).
El m茅todo read() nos revolver谩 la informaci贸n con el id que le pasemos por argumento. Update() nos actualiza la informaci贸n que tenga el id que recibe por argumento con la data que tambi茅n recibe por argumento y delete(), simplemente borrar谩 la informaci贸n que tenga el id que recibe por argumento.
Para obtener toda la informaci贸n de la tabla podr铆amos utilizar el m茅todo read(), evaluando que no se le pase argumento, y as铆 devolver todos los registro de la tabla, pero preferimos respectar el Principio de responsabilidad 煤nica y el Principio Abierto Cerrado, as铆 que crearemos un 5 m茅todo que nos devuelva todos los registros readAll().
Para ir almacenando todos los registros de la tabla, vamos a crear otro atributo privado llamado data con el cual interactuar谩n nuestros 5 m茅todos p煤blicos.
export class CRUD{ #tableName = null; #data = null; constructor(tableName){ this.#setTableName(tableName); this.#setData(); } #setTableName(tableName){ this.#tableNameValidate(tableName); this.#tableName = tableName; } #setData(){ this.#data = []; } #tableNameValidate(tableName){ if(tableName == undefined) throw new Error("Table name required"); } create(data){} read(id){} update(id, data){} delete(id){} }
Como podemos ver hemos declarado el atributo privado data en la l铆nea 3, y luego le asignamos un valor durante la instanciaci贸n de la clase en la l铆nea 7 usando el m茅todo privado setData(). Por ahora ese m茅todo solo asigna un array en blanco, aunque m谩s adelante har谩 m谩s cosas.
BIEN!!! Ahora que ya tenemos la variable con la que va a interactuar nuestro CRUD, vamos a crear la l贸gica de cada uno de nuestros m茅todos.
Con el m茅todo create() simplemente tenemos que a帽adir la variable data que recibe por argumento al atributo privado data, por tanto podemos hacerlo con el m茅todo push() el cual a帽ade la informaci贸n al final del array y le asigna un id incremental. Como nuestro m茅todo es el que asigna un id al registro insertado, es buena pr谩ctica que devuelva dicho id, por si quien ha llamado a nuestro m茅todo create() lo necesita para seguir con su ejecuci贸n.

El m茅todo read() nos tiene que devolver el elemento del atributo privado data que tenga el id que ha recibido por par谩metro.

El m茅todo readAll() nos tiene que devolver directamente el atributo privado data.

El m茅todo update() simplemente tiene que asignarle el valor data que recibe por par谩metro al elemento del atributo privado data. Es buena pr谩ctica que si el proceso de actualizaci贸n es correcto este m茅todo devuelva un valor booleano true.

Por 煤ltimo el m茅todo delete() elimina el registro del atributo privado data que tenga el id que recibe por par谩metro. Al igual que update(), es buena pr谩ctica que si el proceso de borrado ha sido exitoso, este m茅todo nos devuelva un valor booleano true.

VAMOS AVANZANDO!!!!
Ahora seguramente te estar谩s preguntado. Hasta aqu铆 todo muy bien, pero NO HEMOS GUARDADO LOS DATOS TODAV脥A EN NING脥N SISTEMA DE PERSISTENCIA.
Efectivamente, y en eso radica todo esto, en que toda la l贸gica del CRUD que estamos haciendo es independiente del sistema de persistencia que vayamos a usar, ya sean variables de sesion, una SQL, una no SQL, una api externa, un archivo .json o un .txt. NO GUARD脡IS INFORMACI脫N EN UN .json NI EN UN .txt.
Trabajar as铆 generalmente es bueno, aunque depende de cada caso, hay que tener en cuenta la escalabilidad y el rendimiento.
驴c贸mo a帽adimos el sistema de persistencia?
Una r谩pida soluci贸n es crear dos m茅todo, uno que guarde en el sistema de persistencia usando una clave y un valor, donde la clave sea el nombre de la tabla(tableName) y el valor ser铆a todo el contenido de el m茅todo privado data, lo llamaremos save(), y otro m茅todo que obtenga el valor(data) dada una clave (tableName) lo llamaremos get().

El proceso es el siguiente, una vez instanciada la clase CRUD, obtenemos la data del sistema de persistencia y la guardamos en el atributo privado data (en el caso que no tenga informaci贸n asignamos un array vac铆o), y despu茅s de hacer cualquier cambio en el atributo privado data, guardamos el atributo privado data en el sistema de persistencia.

Como podemos ver hemos modificado setData(), donde obtenemos la data del sistema de persistencia tambi茅n llamado repositorio, si nos retorna un null, es que no existe la tabla y por tanto inicializamos un array vacio.
Tambi茅n podemos observar que despues de los m茅todos create(), update() y delete() que son los que modifican el atributo privado data, siempre llamamos a save() para que actualice la informaci贸n en el sistema de persistencia.
Hasta aqu铆 tendriamos lo que es un CRUD b谩sico, aunque nos dejamos muuuuchas cosas en el tintero para optimizar mejor este CRUD, si que podemos a帽adirle algunas restricciones, por ejemplo, 驴que pasa si intentamos leer, actualizar o eliminar un elemento con un id que no existe?
Vamos a a帽adir un m茅todo que devuelva true si existe un elemento con un id determinado y false en caso contrario.

Y a帽adimos esta validaci贸n siempre que usemos un elemento con id.

Como vemos hemos encapsulado la validaci贸n en una funcion checkThatElementExistWithId() que verifica que existe un elemento con el id pasado por el argumento, lanzando una excepci贸n en caso incorrecto.
Nuestra clase CRUD quedar铆a as铆:

Nuestro index.js quedar铆a as铆 a帽adiendo una implementaci贸n de CRUD:

Y tendr铆amos este resultado en la consola del navegador:

Bueno, nos dejamos muchas cosas pero como ejercicio creo que cumple si objetivo.
Hemos desarrollado un CRUD, donde nos acoplamos lo m铆nimo posible al sistema de persistencia (repositorio), desarrollando toda la l贸gica principal del CRUD nosotros mismo y de esta manera tener el control. Esto nos permite cambiar el sistema de persistencia del CRUD cambiando como mucho 2 l铆neas de c贸digo.
Como siempre, no estoy diciendo que sea el m茅todo m谩s efectivo, ya que generalmente los sistemas de persistencia ya traen CRUD quiz谩 m谩s optimizados, sacrificando que si en alg煤n momento tenemos que cambiar el sistema de persistencia tendr铆amos que modificar muchas m谩s l铆neas de c贸digo O PUEDE QUE NO NECESARIAMENTE JEJE (pero eso lo veremos en otra entrada).
Quiz谩s te interese esta otra entrada sobre como hacer un Crud en PHP
Entradas Relacionadas
- Hablemos de Javascript
- Evoluci贸n de los Modelos de Datos: Jer谩rquico, en Red, Relacional y Orientado a Objetos
- C贸mo Gestionar la Informaci贸n en Archivos Planos y Bases de Datos
- Obtener valores de un input con Javascript
- Las Mentes que Transformaron la Computaci贸n y la Programaci贸n
- Introducci贸n al Manejo de Datos en los Ordenadores