logo

midulive


Transcribed podcasts: 605
Time transcribed: 13d 15h 9m 33s

This graph shows how many times the word ______ has been mentioned throughout the history of the program.

¿Os acordáis de los premios ESLAND? Bueno, los premios ESLAND, ahí que The Grefg le encantó nuestra página web que hicimos juntos, excepto que estuviese en catalán, eso no me gustó ni una puta mierda, pero por lo demás, pues seguimos con la página de los ESLAND, que además, web de los premios ESLAND para fines didácticos, que no se diga, ¿eh? Y lo hemos hecho todos juntos, ha quedado muy bonita, muy bonita, ¿eh? Venga, vamos a volver a hacer, a hablar de lo bonito que nos ha quedado, no, pero en serio, nos quedó muy bonito y hay gente que ya sabéis que esto no hemos hecho el código abierto, que ha estado contribuyendo,
de hecho un montón de gente ha contribuido, ¿eh? 60 y pico, 55, y hay gente que ha mejorado algunas cositas, fijaos en el sistema de votaciones, ¿eh? Qué bonito, que ahora le haces clic, se queda en amarillo, además está como rankeado, o sea, puedes poner 1, 2, 3, y esto ahora mismo no tiene base de datos, o sea, esto no funciona del todo, visualmente sí que funciona, pero le vamos a poner hoy la base de datos, hoy vamos a guardar esto, pero, oye, no me digáis que no quedó bonito la animación entre cada uno,
esto lo hizo alguien, ¿eh? Esto que las cosas, que lo sepáis, esto lo hizo alguien, el hecho de esta animación, y nada, la animación esta de que esto salga así, esto sí que lo hice yo.
Y puse aquí un cerrar sesión para que pudierais cerrar la sesión también y todo esto, ¿eh? Y ahora el tema, ves, que pone, debes votar 4 candidatos por categoría, bueno, me has puesto un alert porque no me hago tiempo de hacer muchas cosas, ¿eh?
Pero ahora lo que nos faltaría es que cuando ya tenemos todos los votos, o sea, cuando volvemos atrás, ves, aquí tengo votos, aquí otros votos, aquí otros votos, bueno, cuando los votamos a todos, ¿vale?
Deberíamos ir a una página final que es donde por fin podemos grabar los votos y eso lo tenemos que hacer en una base de datos.
Pues vamos a grabar, vamos a crear la base de datos desde cero, la vamos a desplegar, vamos a ver dónde la vamos a desplegar, cómo desplegarla, todo, todo, lo vamos a hacer todo.
Y vamos a crear el backend para que justamente, pues, grabe esto, ¿vale? Se supone que ya tengo aquí todos los votos.
Ah, además los votos se guardan ahora mismo en local storage, algo es algo, no es lo ideal, ¿vale?
¿Por qué es necesario votar en todas categorías? Bueno, yo esto me lo he copiado de cómo era el funcionamiento original.
Es lo que hay, el funcionamiento original es que tienes que votar en todas las categorías.
Me entiendo que lo hacen porque así te obligan a que se esparza más el voto, ¿no?
Porque si no la gente, pues, solo pondría un voto en la categoría, solo en las que les interesa, y así te obliga a que tengas que ir y hacerlas todas.
No sé, me imagino, ¿eh? Me imagino que van por ahí los tiros.
Entonces, ahora sí que tenemos todas y ya al darle aquí al siguiente debería, debería ir a la página final, ¿vale?
Pues lo vamos a ver.
Venga, vamos a darle cañita. Vamos a darle cañita que le tomo muchas ganas a esto y hay mucho que hacer.
Yo creo que vamos a empezar por la base de datos porque me parece lo más interesante.
Como he dicho, vamos a crear la base de datos.
Yo tengo esta aplicación que se llama Ducklet, que es una aplicación visual para crear una base de datos con SQLite.
Pero si tú tienes otra aplicación o lo quieres hacer de otra forma o lo que sea, pues hazlo.
Yo lo voy a hacer aquí porque, bueno, me ha gustado visualmente la página, o sea, la aplicación esta, pero tampoco es que la haya utilizado mucho, ¿eh?
Tampoco os voy a decir que, no, es que la has usado un montón.
No.
De hecho, ya veo que le falta, por ejemplo, el hecho de hacer grande la fuente, lo cual me parece un error bastante grande, ¿sabes?
No puedes hacer esto más grande.
Empezamos mal.
Empezamos mal, la verdad.
¿Qué vamos a hacer?
Vamos a, primero, os voy a decir la creación de tablas, ¿vale?
Create tables.
Esto es lo primero.
Vamos a crear tablas.
Vamos a intentar hacer, obviamente, un sistema lo suficientemente sencillo para que podamos hacerlo una primera versión en menos de dos horas.
Si intentamos aquí hacer la versión más completa va a ser bastante complicado.
Así que voy a dejar lo más sencillo posible, o sea, lo mínimo, lo mínimo que necesitamos para que esto funcione.
Primero vamos a tener categories, ¿vale?
La tabla de categorías.
La tabla de categorías, básicamente, pues, donde vamos a tener las categorías donde van a poder votar los usuarios.
Tenemos el category ID, que va a ser de tipo texto, porque ya tiene una ID.
Y esto es el primary key.
La primary key.
Estaba pensando, porque me ha salido un autocomplete, de hecho.
¿Esto qué?
Y luego vamos a tener el nombre, ¿vale?
Esto también es del tipo texto, pero no puede ser null, ¿ok?
Muy bien.
Esta sería la primera tabla.
Luego la otra tabla, no sé por qué lo ponen en rojo, pero está en rojo.
Luego tendríamos las opciones, ¿vale?
Las opciones, y me falta el punto y coma aquí.
Luego tendríamos las opciones.
Las opciones de cada uno vamos a tener la ID.
Vamos a utilizar SQLite.
Y vais a ver cómo lo desplegamos y tal.
Pero al final es muy parecido a MySQL, a PostgreSQL.
Todo lo que vais a ver es independiente.
O sea, lo vais a poder utilizar en cualquiera.
El category ID, que esto sería text, y esto no va a ser un key.
Esto lo dejamos así.
Y luego tendríamos el option name, que esto va a ser un text,
y que no puede ser null, ¿vale?
O sea, vamos a tener la base de datos, las opciones de votación también.
Las categorías, y de cada categoría tenemos las opciones de votación.
Y lo importante es que tengamos aquí el enlace entre que los options,
o sea, cada opción, pertenece a una categoría.
Entonces, vamos a poner aquí que esto es un foreign key, ¿vale?
El category ID, y lo vamos a enlazar, ¿vale?
Referencia a justamente a categories, a category ID, ¿vale?
Ahí ya estamos creando el enlace, donde este category ID,
este category ID aquí, ¿vale?
Lo que hace es referenciar a este que tenemos aquí.
Porque una opción pertenece a una de las categorías.
Así que ya teníamos ahí ese enlace, la llave foránea.
Y luego vamos a crear finalmente la última que vamos a necesitar ahora mismo,
que es la de los votos, que esta es la más complicada, ¿vale?
Es complicada porque, fíjate en una cosa, los votos están rankeados.
¿Qué quiere decir esto?
Que los votos estén rankeados, lo que quiere decir es que se supone
que van a tener un peso específico en cada uno, ¿no?
¿Por qué has decidido hacerla en SQLite?
Porque la podría haber decidido hacerla en MySQL, en Postgre, en lo que sea, ¿no?
Pero, ¿qué pasa?
Que SQLite se puede desplegar muy fácil y muy barato con Turso.
Y la verdad es que Turso me gusta mucho.
Pero, si os gusta más MySQL, pues tenéis también PlanetScale.
¿Qué os gusta más Postgre?
Pues tenéis Neon, ¿vale?
Entonces, es un tema más de gustos.
Pero, lo que vamos a hacer es que os funciona con cualquiera de las tres.
O sea, que si os gusta más PlanetScale, os gusta más Neon, o sea, os gusta más MySQL, Postgre,
o sea, es indiferente.
Las tres son igual, igual.
O sea, lo que vais a ver hoy es exactamente igual.
Pero Turso me gusta bastante, porque es bastante sencillo.
El tema, lo que os comentaba, están rankeados.
Esto quiere decir, ¿veis que pone un 1?
Esto lo que quiere decir es que se supone que el chico este, el Mariana,
es como mi voto principal, el más importante.
Y el segundo sería Spring, y el tercero Rivers, y el cuarto Rubius.
Y entonces, cada uno tendríamos como que le doy más importancia a mi primer voto que al cuarto, ¿vale?
Así que es importante que además tengamos aquí esta posibilidad, ¿vale?
Tengamos, en vez de Text, ¿por qué no usa Sint y tal?
A ver, esto es simplemente porque las IDs ya son de tipo texto, ¿vale?
Esto es porque si vamos aquí en el Vote System,
Ya tendríamos por aquí, a ver si lo encuentro.
¿Ves? Las IDs son de tipo string.
Que aquí podríamos haber puesto cualquier otra cosa.
Podría haber sido una llave UUID, podría haber sido cualquier cosa.
Pero como no tenía decidido cómo iba a ser las IDs,
lo he puesto de tipo texto, y el día de mañana ya los cambiaremos.
¿Ves? Por ejemplo, aquí los candidatos también son de tipo texto y ya está.
Por eso lo hemos puesto de tipo texto.
Y si el día de mañana, por lo que sea, pues cambiamos y ponemos aquí un UID de estos rechulones,
pues bueno, al menos ya lo tendremos funcionando y no habrá ningún problema.
Así que por eso lo hemos puesto de tipo texto, para que lo tenga así y sea exactamente el mismo que el JSON,
que luego generaremos cómo hacer los inserts de todos los datos, ¿vale?
Así que esa es la razón.
Luego, los votos.
Venga, vamos con los votos, que los votos es un poquito, es el más complicado, pero nada grave.
Como aquí los votos sí que se generan en la base de datos, los votos estos sí que vamos a hacer que sean un ínteger
y vamos a hacer que sea la key primaria y vamos a poner que se autoincremente, ¿vale?
Autoincrement.
Y así, pues cada vez que se hace un voto, pues tendrá una ID, se autoincrementará y ya está.
Vamos a guardar que tenga el nombre de usuario.
Normalmente deberíamos crear una tabla para el usuario,
pero no lo voy a hacer, primero, porque nos complica un poco la historia,
y lo segundo es porque realmente tampoco es importante para nosotros.
Mientras haya iniciado sesión y solo ese usuario pueda cambiar sus votos, ya no sirve.
Lo más fuerte, que luego lo enseñaré, es que en la biblioteca que estoy utilizando
no te devuelve la ID del usuario, lo cual es bastante rata.
Entonces, bueno, lo he decidido simplificar básicamente para ir un poquito más rápido.
Y directamente en los votos ponemos el nombre de usuario
y solo los usuarios que tengan ese nombre de usuario podrán cambiar sus propios votos.
Pero bueno, lo he decidido hacer así justamente para simplificarlo.
Pero si tú tienes más tiempo, le puedes llegar y lo puedes llevar a siguiente nivel.
Categoría ID, el Option ID, o sea, cada voto va a ser de un usuario
donde le decimos el Categoría ID y el Option ID.
Y luego del Option ID también tenemos que tener el ranking,
que el ranking va a ser un ínteger.
Una cosa interesante es que SQLite, si no me equivoco,
igual una nueva versión lo tiene, solo tiene ínteger, ¿vale?
Aunque tú le pongas teenyint o lo que sea, ¿vale?
Lo cierto es que siempre utiliza el tipo ínteger.
No sé si es una cosa que ha cambiado últimamente,
porque SQLite también va evolucionando.
Que yo recuerde, cada vez que tú utilizas cualquier cosa aquí,
si tú intentas utilizar el teenyint o lo que sea,
en realidad por debajo siempre va a utilizar ínteger, ¿vale?
También, porque lo hacen para simplificar.
Estos serían los campos de nuestra base de datos.
Vamos a poner unos pequeños checks.
El primero va a ser el del ranking.
Vamos a chequear que el ranking siempre esté entre 1
y también el ranking sea menor o igual a 4, ¿vale?
Porque solo podemos votar a 4 y no tiene sentido que en nuestra base de datos
alguien intente votar y le ponga ranking 28.
Así que vamos a chequear que esto siempre se cumpla.
Y luego ahora, pues nada, tenemos que hacer otra vez lo mismo de los foreign keys, ¿no?
Foreign key, donde el category ID hace referencia al categories
y esto va a category ID.
Y por otro lado tenemos ahora también el foreign key del option ID,
que esto hace referencia al options, que es el option ID, ¿vale?
Así que con esto también tendríamos ya las referencias de nuestras test tablas.
Ahora lo que podemos hacer es básicamente probar si esto funciona.
Lo podemos ejecutar aquí, ¿vale?
Estos cambios, sí, dale, ¿vale?
Se han ejecutado correctamente y ahora podríamos ver aquí nuestras tablas,
que ahora mismo están vacías, pero ya tendríamos las tablas aquí.
Para que simplifiquemos cuando vayamos a ir creando las tablas,
una cosa que voy a poner aquí arriba es básicamente drop table if exists.
O sea, cada vez que ejecutemos todo este código, voy a eliminar la tabla de categorías.
Dime.
Vale, lo que dice Fabián es, a veces me confunde esa parte de desplegar.
¿La base de datos tiene que estar en otro servidor que la app
o puede estar en el mismo servidor?
Lo cierto es que puede estar en el mismo servidor, pero no suele ser recomendable.
Puede estar en el mismo servidor y que esté publicando un puerto diferente
y te conectes a la base de datos a través de otro puerto en el mismo servidor.
Pero no suele ser buena idea, no suele, a veces sí que tiene sentido,
pero no suele ser buena idea tener en el mismo sitio que tengas el servidor web
la base de datos.
Porque si por lo que sea necesita muchos recursos la base de datos,
se van a pelear por los recursos, tanto la web como lo que va a tener la base de datos.
Y además, a la hora de escalar los servicios, es mucho más complicado.
Si tú tuvieras la base de datos separada, lo bueno es que a la hora de escalarlo
va a ser mucho más fácil.
Y lo mismo al revés, ¿no?
Con justamente el servidor web.
Si necesitas el servidor web, escalarlo a 85, ¿vale?
85 máquinas.
Lo puedes hacer y a lo mejor la base de datos se queda solo en uno.
Entonces, es complejo porque hay muchas veces en las que puede tener sentido,
pero para proyectos bastante pequeños, pero normalmente no tiene sentido.
Entonces, categorías, options y votes, ¿vale?
Le hacemos un drop básicamente para que podamos ejecutar esto cada dos por tres.
Y así, veis, siempre se irá reiniciando por si me he equivocado algo o lo que sea, ¿vale?
Ahora que ya tenemos las tablas que vamos a utilizar,
lo siguiente que vamos a hacer va a ser crear los inserts.
Porque vamos a insertar justamente, bueno, vamos a poner aquí, crear inserts.
Vamos a insertar la información tanto de las categorías como de las opciones.
Porque la base de datos la tengo vacía y esta información la deberíamos tener en la base de datos
para saber que el usuario puede votar.
Entonces, voy a hacer un poco de scripting, primero porque me gusta.
Lo segundo porque me parece un ejercicio muy interesante y es un ejercicio que puede ser muy de prueba técnica.
Me recuerda bastante a esto de tener que, no sé, iterar un array, manejar objetos y todo esto.
Entonces, tenemos este JSON que está en la fuente de la verdad ahora mismo,
donde tenemos toda la información de IDs y todo esto.
Pero esta información, al menos las IDs, necesitamos volcarla a la base de datos
para que justamente podamos encontrar ahí para guardar bien el voto y todo esto, ¿no?
Entonces, lo que vamos a hacer, voy a ir al Ranges, ¿vale?
Ranges y voy a poner aquí const categories y voy a pegar todas las categorías, ¿vale?
Entonces, con esto ya tendría como las categorías, pero voy a hacer un script con JavaScript
que justamente es para lo que es bueno JavaScript, ¿eh?
Que muchas veces, ¿para qué es bueno JavaScript? Para esto es buenísimo.
¿Qué vamos a hacer? Vamos a convertir este JSON en sentencias insert
para poder insertar los datos fácilmente.
Lo que necesitamos, pues nada, ya os podéis imaginar.
Vamos a crear aquí un insert statements, ¿vale?
Que va a ser un array.
Bien, lo voy a hacer un poco a lo rápido y seguramente a lo mal,
no a lo quiero hacer perfecto, no sé qué, no sé cuánto.
Así que no me voy a obsesionar con si utilizo un for each, un map o lo que sea.
Simplemente quiero que funcione.
Así que categories for each y aquí ya tendríamos toda la información.
A ver, que he puesto un poco de más.
No, está bien.
Vale, vamos a tener la información.
Por un lado, si vamos aquí para ver un poco el objeto, ¿vale?
Teníamos la categoría y los candidatos.
¿No? Categoría y candidatos.
Bueno, pues vamos a necesitar tanto la categoría como los candidatos.
Así que, a ver, un momento.
Voy a ir arriba del todo.
Vale, es de cada objeto tenemos categoría, candidatos.
Y esto, ¿por qué no lo puedo...?
Porque justo después tendríamos la ID también, ¿no?
Vale, categoría, candidatos e ID.
Vale, pues ya está.
Vamos para abajo.
Y tendríamos categoría, candidatos e ID.
¿Vale?
Estas serían las tres cosas que tenemos.
Dice...
Vale.
Entonces, vamos a iterar cada una de las cosas.
Y lo que vamos a hacer en el insert statements, vamos a hacer un push.
Y vamos a crear un template string.
Me voy a poner por aquí el template string.
¿Vale?
Donde vamos a hacer insert into categories, ¿vale?
Para cada una de las categorías vamos a meter la información.
Donde teníamos el category ID, el category name.
¿Y qué teníamos más?
Ya está, ¿no?
Solo teníamos eso.
Vale.
¿Y los valores qué serían?
Pues los valores los tendríamos justamente por aquí.
En cada categoría, el category ID lo tendríamos aquí.
O sea, el category punto...
Ah, no, category...
O sea, sería la ID directamente.
Que ya la tenemos ahí, ID.
Lo vamos a poner entre llaves porque justamente es como un texto.
Y luego el category name que debería ser categoría.
Es un poco raro el nombre aquí.
Pero bueno, eso podemos cambiar.
Category name.
¿Vale?
Y este también eso podemos cambiar a category...
Category ID.
Y este también eso podemos cambiar a category candidates.
¿Vale?
Category candidates.
Yo creo que tiene más sentido.
Y así lo dejaremos mucho mejor.
Y esto igual.
Vamos aquí.
Category name.
¿Vale?
Entonces ya tenemos aquí...
Tendríamos o deberíamos tener...
Si hacemos un console log de insert statements, deberíamos tener, ¿vale?
Todas las categorías.
Pero esto no es todo lo que necesitamos.
No solo necesitamos la de los valores de...
Ah, espérate.
¿Por qué?
Ah, vale.
Porque digo...
¿Y este insert?
Vale.
Insert categories.
Clip del año.
Vale.
Esto está bien.
Pero de cada categoría también necesitamos los candidatos.
O sea, que vamos a ir aquí y vamos a poner también...
Por cierto, voy a poner esto a la derecha que creo que se verá mejor.
Disposición vertical.
Sí.
Creo que se va a ver un poquito mejor.
Y así esto lo hacemos un poquito más pequeño que tampoco es muy importante.
¿Vale?
Entonces ahora de category candidates vamos a hacer otra vez un for each.
Aquí tenemos la información del candidate.
Y aquí pues sería un poco la misma idea.
Solo que de cada candidato tenemos que sacar...
Vamos a ver.
Cada candidato que tiene el id.
Y creo que ya está.
No necesitamos otra cosa.
O sea, que id, candidate id, lo sacamos de candidate, ¿vale?
Y ya tendríamos que hacer este mismo insert, pero en lugar de categories, esto sería para options.
Y en options tendríamos el option id, tendríamos el category id y creo que ya está, ¿no?
Option id y el option name.
Claro, el option name.
Vamos a poner aquí.
Option name.
¿Vale?
Y el option name lo tenemos que sacar de aquí, del campo nombre.
Un poco raro.
Vamos a poner candidate name, ¿vale?
Para que tenga más sentido.
Y ya está.
Con esto, con esto, a ver, candidate name, el option id sería este.
El candidate name sería este.
Y finalmente el category id sería el category id.
Así que ahora ya tendríamos aquí, pues, todos los inserts, ¿vale?
Todos los inserts creados.
Y luego, finalmente, lo que podemos hacer aquí es un join con barra n.
Y ya tendríamos aquí todos los inserts separados, además, por salto de línea.
En un momento hemos hecho un script que justamente esto le da el nombre.
JavaScript.
JavaScript.
La verdad es que Descripting está súper genial, JavaScript, ¿eh?
Para que lo sepáis.
Vale.
Lo único que nos faltaría es que cada insert debe tener un punto y coma, ¿vale?
Que no se nos olvide que si no esto nos petará.
Y creo que con esto ya estaría.
¿Qué os parece?
¿Todo bien?
El punto y coma.
¿Veis?
El punto y coma.
¡Qué fumada!
Hombre, tampoco tanto, ¿no?
¿Los templates de string no son peligrosos para los SQL Injections?
Claro que sí.
Pero en este caso, a Brixen, el SQL Injection hay que tener en cuenta quién es la persona que te va a meter esto.
En este caso, nosotros estamos creando un script que nosotros somos los creadores de JSON y estamos creando este script.
Lo que es importante es, obviamente, si este JSON fuese que me lo pasa un usuario y yo no tengo control, obviamente tengo que tener muchísimo cuidado porque si no aquí lo que va a ocurrir es que me va a inyectar SQL.
Pero en este caso, nosotros estamos en control.
Digamos que estamos en modo administrador, ¿vale?
Ustedes siempre están sucios, totalmente.
Pero bueno, esta es un poco la idea.
Que lo que estamos haciendo es controlando nosotros.
Es porque si no, tú imagínate si tengo que hacer esto manualmente de meter cada candidato a mano, pues no tiene sentido, ¿vale?
Entonces, lo que hemos hecho es crear un script para extraer información de nuestro JSON a la base de datos.
Eso es lo que hemos hecho, ¿vale?
Bueno, pues yo creo que me puedo...
No sé si aquí está el comando copy.
Yo creo que no.
¿Copy?
No.
Hubiera sido buenísimo.
Hubiera sido tremendo.
Vale, pues venga.
Me traigo esto.
Aquí tendríamos ya todo el JSON.
Ah, el JSON, el SQL.
Y así ya vamos a ver si esto...
Vamos para arriba del todo.
Pam.
Y esto para abajo.
Le quitamos el último template string aquí.
Y ya podríamos ir aquí a Ducklet y ya tenemos todos los inserts.
Vamos a ver si le gustan los inserts, ¿eh?
Igual no le gustan los inserts.
Vamos a ver.
Vamos a ejecutarlo todo.
Le decimos que sí.
Y...
¡Ay!
Create table boats.
Vale, puede ser que se me ha olvidado algo.
Sí.
¡El último punto y coma!
El último punto y coma.
Vale.
Pues nada.
Ahora sí.
Ahora sí.
Vamos a ver ahora.
Ahora sí.
¿Vale?
Ahora veis, ya tenemos toda la información y si vamos a las tablas, pues veis, ya tenemos
aquí las categorías con el category name y también tendríamos, pues, las opciones
que las tenemos por aquí y tenemos todas las opciones, opción, la ID de la opción
1, 1, 1, de categoría ID y el nombre de esa opción, Auronplay, Cristian Molli, Gemita,
Guañar, no sé qué.
Esto en el caso de que cuando después tengamos los votos, el option name nos ayudará para
anunciar el ganador, saber a cuál se refiere exactamente la ID de la opción.
¿Vale?
Lo importante es que ya tenemos aquí toda la información en la base de datos.
Básicamente, ¿qué hemos hecho?
Hemos volcado un JSON a la base de datos.
Es lo que hemos hecho.
¿Vale?
Así que nada.
Perfecto, ¿eh?
Pues ya tendríamos los inserts.
Ahora viene una cosa muy chula porque, claro, ahora ya podríamos empezar a insertar
votos.
O sea, ya se supone que podríamos insert into votes y ahora podríamos insertar, por ejemplo,
cómo eran los votos, cómo eran los votos.
El category ID, o sea, username, username, category ID.
Es que no sé por qué no tiene...
Me lo voy a copiar.
Me lo voy a poner aquí abajo y ya será más fácil.
Username, category ID, option ID y el rank.
¿Vale?
Y ya tendríamos aquí los valores.
Entonces, ah, el vote ID, esto es autoincremental.
No tenemos que ponerlo.
Aquí podemos decir que midudev está votando en la categoría 1.
1, ¿vale?
Que este sea el ID, a la opción 1, 1, ¿vale?
Y en el ranking le ponemos el 1.
Luego aquí también, pues, vamos a separarlo en más de 1 para que si nos peta 1 lo veamos.
Pero lo interesante de esto, vamos a poner más de un voto, es que vamos a ver que hay
problemas, hay errores, hay problemas.
Porque, a ver, ¿esto funcionar va a funcionar?
Sí que va a funcionar, me parece.
Vale, no va a funcionar porque...
Ah, porque el drop table lo he hecho mal.
Fijaos, esto es interesante porque he puesto el drop table primero el de categories, pero
primero la primera tabla que hay que quitar es la de votos.
¿Por qué?
Porque si intento eliminar la de categorías y ya he creado una tabla, una relación entre
dos tablas y hay un elemento que lo utiliza, como el voto, pues, el voto o las opciones.
Las opciones tienen la relación con la categoría ID, claro.
Entonces, no podemos quitar esto.
Entonces, lo mismo pasaría con las opciones.
El último que tenemos que eliminar es el de categories, porque categories no tiene ninguna
referencia.
¿Veis?
Entonces, primero eliminamos la de votos, que tiene dos referencias.
Luego la de options, que tiene una referencia.
Y luego la de categories.
Este tiene que ser el orden correcto a la hora de eliminar las tablas cuando queramos
reiniciar.
Vale, vamos a darle.
Y ahora sí que vemos que ha funcionado bien, pero ha funcionado mal.
¡Tatán!
Ha funcionado bien, pero ha funcionado mal.
Porque, fijaos en una cosa.
Aquí, lo que estamos...
Le estamos dejando que el usuario Midudep ha votado en la categoría 1 cuatro veces al
mismo, a la misma opción, la ha votado cuatro veces.
Y si vamos a los votos, pues fíjate, el Midudep este se ha vuelto loco.
Y esto, esto no puede ser.
Y hay más problemas, ¿no?
Hay más problemas porque no solo es esto, sino que también el Midudep este, pues al
mismo le puede estar poniendo diferentes rangos, puede votar al mismo más de una
vez, puede votar más de cuatro veces, ¿no?
O sea, hay un montón de problemas aquí.
Estamos viendo aquí que hay muchos problemas porque esto debería petar de alguna forma.
Y fíjate, esto se lo está comiendo con unas patatas fritas que no veas.
Esto se puede solucionar de diferentes maneras.
Yo voy a decir la que yo recomendaría o la que a mí me parece que tiene un poquito
más de sentido.
Aunque hay gente que también le ve ventajas a hacerlo de otra forma.
Y es el hecho de...
Yo utilizaría triggers en este caso, ¿no?
Yo intentaría que la base de datos evitase y tuviese un control para evitar que este tipo
de cosas ocurriese.
Hay millones de formas de hacer esto, podríamos crear constraints y tal.
Yo voy a enseñar los triggers porque me parece que es bastante interesante el hecho de tener
como alguna lógica para hacer ciertas validaciones y me parece bastante fácil hacerlo con triggers
y además se entiende muy bien.
Pero lo malo de los triggers es que puede costar, puede costar no dinero, pero peticiones en la
base de datos, ¿entendéis?
Entonces, digamos que tenemos tres problemas.
Vamos a ponerlos aquí.
Problema 1.
Problema 1.
Un usuario solo puede...
Un usuario solo...
Joder.
Solo puede votar cuatro veces por categoría.
Este sería el primer problema.
Entonces, para arreglar este problema, lo que vamos a crear es un trigger.
¿Vale?
Un trigger es básicamente una función que vamos a ejecutar antes de que ocurra el insert.
Segundo problema.
Hay más problemas, amigos.
El segundo problema es que un usuario no puede votar dos veces a la misma opción en
la opción en la misma categoría.
¿Vale?
Esto tampoco tiene sentido.
Y el problema 3 es que un usuario no puede votar con el mismo ranking a dos opciones
en la misma categoría.
Estos serían los tres problemas que tenemos.
Igual hay más.
Igual hay más, pero estos son los tres que se me ocurren así rápido, ¿no?
Y estos son los tres que tendríamos que arreglar.
Entonces, vamos a arreglarlo.
Hay gente que esto dice, no, esto mejor en el backend porque así tenemos la lógica
y no sé qué, no sé cuánto.
Pero lo cierto es que con un trigger, justo antes de hacer el insert, también lo podrías
hacer y además puedes decir, vale, pues esto tiene esta información, este problema y
voy a lanzar una alerta, ¿no?
Por ejemplo, vamos a poner este, create trigger.
Y le ponemos un número, limit user vote once, same option, no, same option, no, ¿cuál ponemos
primero?
El de limit, vamos a poner limit user votes per category, ¿vale?
Vamos a poner este primero.
Entonces, ¿cuándo se tiene que ejecutar?
Pues antes de insertar una fila o antes de insertar en votes, ¿vale?
No sé si ponerlo en minúscula o en mayúscula.
Bueno, lo he puesto así, no sé si, va, vamos a dejarlo así, ¿vale?
Antes de insertar en votos.
¿Y qué es lo que tiene que ocurrir?
Vale, pues esto le vamos a poner una condición.
¿Qué condición vamos a hacer aquí?
Y por eso te decía que está bastante chula porque así puedes aprender un montón de
SQL, no solo que hemos creado las tablas y tal, sino que aquí también teníamos que
pensar, bueno, ¿cómo limitamos al usuario?
Bueno, pues vamos a decirle que cuando vamos a contar el número de veces que un usuario,
por ejemplo, en votes, from votes, ¿vale?
¿Cuántas veces un usuario ya ha votado?
Entonces seleccionamos y cuando la categoría id sea igual al new, ¿era mayúscula o minúscula?
Ah, no sé.
¿New?
Ah, no me acuerdo.
Creo que es en mayúscula.
O creo que da igual si es en mayúscula o en minúscula, ¿vale?
Vale.
¿Qué estamos haciendo aquí?
El new va a ser el nuevo campo.
O sea, en este caso, este new que vemos aquí sería este valor que ves aquí, ¿vale?
Este valor que ves aquí, este 1, es el que va a llegar aquí, ¿ok?
No sé por qué sale así.
No sé si es por la...
Lo vamos a...
Ahora lo probaremos.
Ahora lo probaremos.
A ver.
Porque ¿veis que se me pone en rojo?
Yo no sé por qué se me pone en rojo.
Yo no sé.
No estoy haciendo nada, ¿vale?
Y lo otro que tenemos que hacer, obviamente, es mirar que el username sea igual al new username.
Y una vez que tenemos esto, vamos a mirar además que cuando esto sea mayor o igual a 4,
o sea, que ya ha votado 4 veces, entonces, ¿vale?
Cuando pase esto, entonces, vamos a iniciar y vamos a elevar un error, ¿vale?
Vamos a elevar un error que vamos a decirle que aborte la petición del insert y podemos decirle too many.
Esto está bien porque además tenemos este mensaje y ya está.
Demasiados votos para esta categoría.
Y con esto, ¿vale?
Nos faltaría el end y ya lo tendríamos, ¿vale?
Ya tendríamos esto y nos falta aquí esto y ya está.
Ya tendríamos el primer trigger, ¿vale?
El trigger antes de insertar en votes, ¿vale?
Cuando hacemos una selección para contar cuántos votos tiene el usuario.
Y si tiene más de 4, entonces lo que hacemos es decir, oye, pues entonces tienes demasiados votos para esta categoría.
Voy a hacer un drop de esto, por si acaso.
Drop trigger if exist.
Y le ponemos el nombre este.
Solo por si hay que cambiarlo después, que seguramente lo tenemos que cambiar.
Y ya tendríamos uno de los problemas arreglados.
Pero el problema este, ¿cuál sería?
Que no puede votar más de 4 veces.
Vamos a ver si ocurre.
Si le damos aquí.
Vale.
¡Ojo!
¿Vale?
¿Veis?
Too many votes for this category.
¿Qué es lo que ha pasado?
Que este usuario ha intentado votar 7 veces y solo puede votar 4.
Así que ya vemos que este ya funciona.
Ahora, ¿qué más deberíamos?
Un usuario no puede votar 2 veces a la misma opción, a la misma categoría.
Claro, en este caso, ¿veis?
Esto está votando siempre a la misma opción.
Está, claro, esto es trampa.
Esto no está bien.
Entonces, vamos a crear otro trigger.
Create trigger.
Y ponemos aquí limit.
¿Cómo he hecho de menos?
A Gijacopilot.
La madre que os parió, ¿eh?
Vamos a limitar que el usuario pueda votar una vez.
Same option.
Per category.
Venga.
Pues bastante parecido.
Vamos a...
Me lo voy a copiar directamente.
Porque total es bastante parecido.
Solo que ahora el check que tenemos que hacer cuando hacemos el count,
la categoría tiene que ser la misma, el username tiene que ser el mismo.
Pero además, el option, el option ID,
si es el mismo que el nuevo que estamos añadiendo y este es mayor o igual a 1,
pues entonces vamos a decir...
You cannot vote same option twice, ¿no?
Esto sería.
¿Vale?
Y aquí ya tendríamos el segundo problema arreglado.
Vamos a ver si le doy al execute, a ver si ahora me deja hacer esto o si me peta.
¿Vale?
Run.
Tu, tu, tu, tu, tu, tu, tu.
Vale.
No puedes votar a la misma opción dos veces.
¿Vale?
Porque fijaos, aquí estoy intentando votar a la misma opción cuatro veces.
Pero ahora que tenemos este trigger, pues ya no me está dando este problema.
Voy a hacer el drop trigger este también.
¿Vale?
Para que no tengamos problemas.
Drop trigger.
Tu, tu, tu.
Me copio esto.
Esto lo pongo por aquí.
¿Vale?
Y ya está.
Vale.
Perfecto.
Vamos con el problema 3.
Un usuario no puede votar con el mismo ranking a dos opciones en la misma categoría.
O sea, sería muy parecido a este también.
Muy parecido a este.
Lo vamos a poner aquí.
Le vamos a cambiar el nombre.
Que sería esto por aquí.
Vamos a poner limit rank user vote per category.
¿Ok?
Y esto me lo voy a copiar.
Y esto por aquí.
Y ya tendríamos...
Y esto es ya lo último que tenemos que hacer de SQL.
O sea, ya lo tendríamos casi listo.
¿Vale?
Entonces aquí tenemos la categoría ID.
El username.
Y en lugar de mirar el option ID, tendríamos que mirar el ranking.
Si el nuevo ranking que queremos agregar es...
Ya lo tenemos en la categoría.
Claro.
Entonces, si es mayor a uno, pues ya está.
No podemos utilizar.
Sí.
En convertir las tablas en unique.
Bueno, en este caso, es lo que os comentaba antes.
Podríamos hacer un constraint de que sean...
De que los votos sean únicos y tal.
Pero es que eso lo explicamos en el otro curso el otro día.
Y os quería explicar el tema de los triggers porque me parecía interesante.
Más que nada, para que sepáis.
Que se pueden ejecutar triggers justo antes de hacer un insert.
Y además, que podemos hacerlo así.
Que podáis mirar hacer un select.
Entonces, así podríamos ver también el select.
Podríamos ver cómo crear triggers.
Y cómo podríamos hacer un abort de la petición antes de hacer el insert.
Básicamente, era un poco más para aprender que no para que lo hagáis como las buenas prácticas.
Que es lo que os decía.
Que mi idea a la hora de hacer esto tampoco era de que lo hagáis con las mejores prácticas.
O sea, enseñaroslo con las mejores prácticas.
Sino que emprendamos un poquito más de las posibilidades de SQL.
Y por eso lo había decidido hacer con los triggers.
Que es lo que comentábamos antes.
Si no, lo que podríamos haber hecho también.
Pues haber hecho que el category ID, el option ID y el midudev.
Algunas sí que las podríamos haber hecho para evitar duplicidades.
Y nos podríamos haber evitado alguna.
Y ya lo tendríamos.
¿Vale?
Un usuario no puede votar con el mismo rank que dos veces.
You cannot vote with the same rank more than once.
¿Vale?
El tema es que ya al menos con esto, ahora sí, lo deberíamos tener solucionado.
Y tenemos que tener todos arreglados.
¿Vale?
Vamos a ver.
Vale.
Y aquí nos dice, no puedes votar la misma opción.
Si cambiamos las opciones.
Vamos a cambiar las opciones.
Y vamos a intentar ahora votar con el mismo ranking.
Por ejemplo.
¿Vale?
¿Vale?
Y le damos aquí.
Y vamos a ver.
Y ya.
Vale.
No puedes votar con el mismo ranking.
Así que ya podríamos poner dos, tres, cuatro.
Y ahora ya.
Esto sí que nos lo debería dejar.
Vale.
Perfecto.
Con esto, todo esto ya lo tendríamos arreglado.
Pero las restricciones solo valdrían para algunos de los casos.
Las restricciones valdrían para algunos de los casos, pero sobre todo para los repetidos.
No, ¿se podría validar eso en el front?
No.
En el front seguro que no.
En el front podrías intentar validarlo para mejorar la experiencia de usuario.
Pero como mínimo tendrías que tenerlo en el backend y en la base de datos.
¿Por qué?
Porque lo que quieres en el frontend al final alguien se lo podría saltar y hacerte una petición de una API directamente a tu API.
¿Sabes?
Sin el frontend intentar hacer esto.
Entonces, las validaciones en el frontend sirven para mejorar la experiencia de usuario, pero no sirven para evitar que realmente llegue esa información.
¿Vale?
Así que tened cuidado, porque si no, os pueden colar información que no queréis tener en la base de datos.
Al menos ahora no nos pueden colar votos que no tengan sentido.
¿Vale?
Y ya tendríamos esto.
Ahora que ya tenemos todo el tema de la base de datos.
Uf, nos ha costado, pero ya lo tenemos.
Ya podemos ir a nuestra página web y nos vamos a poner con el backend.
¿Vale?
Para la última página, y porque no os quería aburrir con crear otra vez toda la página entera y porque sé que el frontend ya lo hemos visto mucho,
yo me he creado ya el archivo final, el componente visual.
¿Vale?
Pero no tiene todo el código y el código lo vamos a hacer juntos.
Entonces, yo he creado aquí un componente que le he llamado VoteFinal, me parece.
A ver.
VoteSystem era...
Vale, pues VoteFinal.tsx.
¿Vale?
Y he creado este pedazo de componente que os lo voy a explicar paso a paso para que no os asustéis.
Pero lo que he creado básicamente es la página final visual donde el usuario puede ver sus votos, ¿no?
Y puede darle a un botón para enviar al backend, que el backend es lo que sí que vamos a hacer, que es lo más interesante,
para enviar al backend y a la base de datos la información.
Entonces, básicamente, ¿qué es lo que tenemos aquí?
Lo que tenemos es un componente que se llama VoteFinal que le llegan los candidatos, los votos y una forma de resetear la categoría.
Y lo que tenemos es básicamente un efecto que nada más renderizarse este componente sube el scroll para arriba
y luego lo que tendríamos por aquí es un UseMemo para recuperar la información de los candidatos seleccionados,
ir a buscar la imagen, el nombre y utilizamos un UseMemo para que por más que se vuelva a renderizar, ¿vale?
Pues lo tengamos ya.
Alguien me decía que si mejor esto, un Enum.
La verdad es que muchas veces no me gustan los Enums y me gustan más el asconst que no los Enums.
Porque los Enums como que están más lejos de JavaScript, pero el asconst me gusta más porque además lo hacen que sean totalmente inmutable.
O sea, que no lo pueden modificar.
En lugar, ya no puedes hacer un result, punto status, punto A, hola, ¿vale?
Porque ves, te dice, no, esto no existe y no se puede cambiar, no sé qué.
Entonces, muchas veces me gusta más el asconst que otra cosa.
El UseMemo, ¿qué es lo que va a hacer?
Lo que va a hacer es que hasta que no cambien los votos, esto solo lo va a calcular una vez.
O sea, lo calcula una vez y cuando cambien los votos, lo vuelve a calcular, ¿vale?
Así es como funciona el UseMemo.
Es una forma de crear una pequeña optimización.
Y en este caso, que sí que hay que hacer un poco de forEach, es muy parecido.
Este forEach que veis aquí es bastante similar a lo que hemos hecho aquí con el Run.js, ¿eh?
Tampoco es muy complicado, así que...
Entonces, lo que sí que nos falta por aquí son unas cuantas cosas.
Por ejemplo, hostia, aquí, pues mira, esa comparación...
Porque los tipos 0.1 no tienen superposición.
O sea, pues el asconst este no le ha gustado.
No le ha gustado.
Iba a decir que me gustaba más, pero en este caso no sé por qué.
Hay alguna comparación que no se pueda hacer del todo bien.
Pues mira, igual se lo tenga que haber hecho como en un...
Bueno, no pasa nada, ¿eh?
Lo podemos dejar así y luego lo podemos cambiar a en un...
Entonces, lo importante va a ser esto, el Handle Summit para hacer la petición al backend.
Y todo lo demás es visual de cuando hemos tenido éxito, pues ponerle aquí, gracias por votar.
De hecho, lo vamos a poner aquí más grande para que lo veamos bien.
Vale, mira, voy a mover esto.
Estoy pensando visualmente.
Lo voy a poner aquí, esto, lo de tus votos finales, lo vamos a poner aquí, aquí.
Y esto vamos a poner aquí, vamos a poner gracias por votar.
Lo vamos a poner aquí y así quedará más grande, ¿vale?
Gracias por votar.
Entonces, tenemos un estado que en el caso de que haya ido bien, pues le pondremos gracias por votar.
En el caso que haya un error, pues esto tendríamos que arreglarlo,
pero no quería tampoco hacer ahí una gestión de errores muy complicada.
Ya la arreglaremos.
Y luego tenemos aquí el resultado que, si estamos esperando que el usuario envíe algo
o si está cargando cuando el usuario ya lo ha dado, pues mostramos todo esto.
Aquí, ¿qué es lo que estamos haciendo?
Estamos iterando los votos y mostramos la imagen de los ganadores, de los candidatos, ¿vale?
De los candidatos y todo esto.
Y finalmente aquí, pues tenemos un botón que, si está cargando, pues muestra como el loading
y si no, lo que muestra es darle a enviar votos.
Y tenemos una forma como para volver atrás, ¿vale?
Entonces, este componente, VoteFinal, este componente, tenemos que mostrarlo en nuestro sistema de votos,
aquí, que tendríamos aquí, cuando la categoría, ¿vale?
Si la categoría es igual, cuando ya es la última página, si es MaxCategories,
si ya es la última página, ¿vale?
Pues, tu, tu, tu, tu, no, la categoría no, la category es MaxCategories, entonces lo que vamos a hacer es devolver el VoteFinal, ¿vale?
Y le tenemos que pasar la información que necesitamos.
Por un lado, necesitamos los candidates, que creo que esto nos lo pasa, candidates, los votos, que también nos lo pasa,
esto se lo vamos a pasar aquí, candidates, candidates, los votos, que son los votos del usuario,
y finalmente, también le tengo que pasar el ResetCategory, que esto, no sé si el VoteSystem tenía un ResetCategory por aquí,
SetPripNextCategory, SetVoteCategory, SetCategory, ¿qué podemos hacer para hacer el Category?
A ver, podríamos hacer aquí ResetCategory y resetear este estado para que vaya al cero, sin hacer esto, o sea, que vaya a la primera y ya está.
ResetCategory, que vaya para atrás y ya está.
Así que hacemos, o que vaya a la anterior, sí, que vaya a la anterior.
Mira, vamos a utilizar este, que así lo simplificamos, que vaya a la anterior y ya está, así no tengo que cambiar esto.
Lo ideal es que fuese al principio, pero bueno, por ahora lo podemos dejar así.
Entonces, cuando en el voto final llamemos al ResetCategory, lo que hará es ir para atrás, ¿vale?
Y este ResetCategory lo llamamos aquí, ¿vale? ¿Lo veis ahí? Pues ahí lo tenemos.
Vamos a ver este componente, ¿vale? Si vamos aquí, vamos a darle por aquí, votos realizados, aquí tenemos nuestros votos.
Se supone que cuando llegamos al último, vale, veo que no carga, no va, no funciona.
Vamos a ver qué problema tenemos aquí, porque no, ah, que no he guardado los cambios, detalle importante.
Ah, pues tampoco. Vamos a ver aquí el Category.
Category. Algo le pasa ahí. Algo pasa ahí que cuando estamos aquí, vale, si vamos aquí, aquí tiene que estar actualizando.
¡Hostia, no me sale! No me sale esto. No me sale este Category. A ver, pues vamos a ponerlo aquí.
No me sale la consola. Normalmente me sale ahí. Si vamos aquí, siguiente.
Oye, pero no se está. ¡Ay, coño! Que estamos en la página de producción. Estamos en la página de producción.
Madre mía, podría haber estado ahí todo el día, todo el día intentando ver esto.
Madre mía. Madre mía. Hubiera estado ahí todo el día intentando.
Oye, ¿y por qué no se ven mis cambios? ¿Por qué no se ven mis cambios?
Bueno, claro. ¿Cómo se van a ver tus cambios, hijo? O sea, la madre que me parió. Parió.
Bueno, encima esto. Vale. No pasa nada porque me he preparado para esta eventualidad, para no tener que estar constantemente votando.
Y como estamos utilizando Local Storage, voy a hacer... El truco del almendruco.
Voy a hacer Local Storage, Set Item, donde pongo todos mis votos, donde refresco y ya tengo los votos. ¿Veis? Ya tengo los votos.
Ya está. Entonces, vamos a ver ahora. Vale. Vale. El diseño no está bien del todo.
Vamos a arreglarlo, que el diseño no está bien del todo. Y para que me vaya directamente a esta página, lo que voy a hacer por ahora, para no tener que darle todo el rato a siguiente, siguiente, siguiente.
Voy a poner aquí un If True y luego lo cambiamos. ¿Vale?
Y aquí, que esto está mal. Esto está mal por culpa de esto, ¿no?
Que he puesto este H1 aquí y yo creo que debería estar aquí.
Y ahora, esto sería... Esta sería la página final. Falta el nombre de la categoría. Eso lo voy a dejar para que hagáis una PR, ¿vale?
Porque es bastante fácil y, bueno, así lo tenéis.
Y esto sería lo que hemos votado, ¿vale? Lo que hemos votado son todas estas cositas.
Hemos votado en la primera categoría, hemos votado estos cuatro, aquí estos cuatro, no sé qué.
Y ahora le deberíamos dar a enviar mis votos para justamente guardarlo en la base de datos. Estos sean los votos finales.
Si le doy a quiero editar mis votos, pues no funciona. No funciona, pero no sé por qué no funciona.
Ah, no funciona porque como tengo un True, claro, como tengo aquí el True, anda que yo también, como he puesto este True, pues no funciona.
Se supone que esto ahora debería funcionar bien. Vamos a ver. Se supone que deberíamos poder ir todo recto, ¿vale?
Ahora sí, y quiero editar mis votos, debería ir para atrás, ¿vale? Y así todo el rato.
Y vamos al 12, True Voto Finales, quiero editar mis votos, ¿vale? O sea que ya tenemos como la navegación de los votos
y estamos a punto ya de enviar nuestra base de datos, ¿ok? O sea, ya estamos súper cerca, ¿eh?
Podrían clicarse la categoría que quieran editar. Pues sí. Además no es muy difícil porque, ¿queréis que lo hagamos en un momento?
Venga, lo voy a hacer en un momento. Es bastante fácil. O sea, podríamos poner aquí Set Category y aquí podríamos poner en lugar de Reset Category,
podríamos hacer Set Category, Set Category, ¿vale? Y este Set Category lo podríamos pillar de aquí porque al final esto es un estado.
Set Category, ¿vale? Esto lo podemos poner aquí, esto aquí. En el vote final vamos a poner aquí,
Set Category y este Set Category, Set Category. Lo que podemos hacer es, por un lado, cuando queríamos hacer el Reset Category,
podríamos hacer Set Category y directamente Prev Category. Bueno, que esto en realidad ya es lo que habíamos hecho antes,
pero bueno. Este podría ser así. Tiene un tipo Any. No sé por qué me... Ah, porque le he puesto Function. Vale.
Bueno, lo arreglaré. Vale. Esto por aquí. Luego tendríamos Set Category. Luego tendríamos que cada uno, ¿no?
Aquí teníamos los Category Votes, no sé qué, no sé cuánto. Esto que es un UL, que hemos puesto que sea un Article,
debería ser un botón y entonces al botón cuando hacemos onClick deberíamos ser capaces de llamar al Set Category y aquí pasarle básicamente el Index.
Set Category le pasamos el Index y ya lo tendríamos. Y aquí, de hecho, le podríamos pasar, como me habéis dicho que empiece,
pues la pasamos de cero y así ya no teníamos el problema. Y aquí, ahora si le damos...
No, no ha funcionado. Set Category... No, no, no ha funcionado. ¿Por qué no ha funcionado cuando hago Set Category?
¿Se me ha pasado algo? ¿Se me ha olvidado algo? Sí. No guardar los cambios de las cosas.
Cuando uno no guarda los cambios de las cosas... ¿Veis? Ahora sí. Vale. Entonces podéis ir y ahora imagínate que decir,
vale, quiero cambiar la categoría a la 10. Pues veis, ahora va directamente a la categoría Streamer Revelación.
Y ya vamos a la categoría a la 10. Y ya lo tendríamos. Pues nada, en un momento lo hemos hecho.
En un momentito hemos hecho lo del Category. Y así, pues es verdad que queda... Yo creo que queda mejor.
Podemos hacer esto. Scale, que suba un poquito la Scale. Hover, BG, para poner este color un poquito más...
Vamos a poner este color un poquito más clarito, por ejemplo. A ver si lo podemos hacer un poquito más clarito.
Y le podríamos cambiar, que cuando se hace hover se cambie algo, ¿no? Aparte de... Pero bueno, ¿ves?
Para dar a pum y ya está. Necesitamos el backend. Para hacer el backend, lo que voy a hacer...
Primero voy a cerrar todas las pestañas, que ahora mismo no necesitamos. Quitamos todo esto.
Vamos a hacer un endpoint en nuestro proyecto. Vamos a ir aquí a Source, Pages... No.
Source, Data, Pages. Aquí sí. API. Y vamos a crear aquí un Vought.ts. ¿Vale?
Vamos a tener un endpoint que es Vought.ts.
Lo que vamos a querer hacer aquí es, primero, vamos a importar el tipo Type API Route. ¿Vale?
Que esto es... Estamos utilizando el framework de Astro. Podemos hacer el backend en Astro.
Te crea los endpoints y los podemos desplegar a la vez. ¿Vale?
Pero también necesitamos recuperar la sesión del usuario.
Para eso utilizamos el AuthAstroServer. Esto es una cosa que ya hemos utilizado.
Que ya explicamos el otro día cómo hacerlo. ¿Vale?
De hecho, qué raro que no... Ajá. Aquí. Aquí lo estamos utilizando. ¿Veis?
Lo estamos utilizando en la página de los Voughts para saber si el usuario había iniciado sesión o no había iniciado sesión.
Y además tener su información. ¿No?
Entonces, tenemos que exportar aquí el método que queremos utilizar para llamar a esta API.
En nuestro caso, va a ser tipo Post. Esto es del tipo API Route.
Y aquí tendríamos la llamada. ¿Vale?
Cuando hagamos un Post, pues se ejecutará esto que tenemos aquí.
Ahora, aquí lo que tenemos que devolver es una respuesta.
Esto es una cosa que es nativa de JavaScript. ¿Vale?
La Request, la Response, que es lo que utilizas con la API Fetch.
Esto funciona así. Y así ya no nos da ningún tipo de problemas.
En el Post, ¿qué es lo que tenemos?
Pues tenemos la Request, que es lo que necesitamos pasarle al GetSession para recuperar la sesión del usuario.
Esto es asíncrono. Así que utilizamos el Await.
Y aquí ya tendríamos la sesión.
Y aquí empezaríamos ya a hacer, pues, lo típico.
Si no tienes sesión, pues lo que vamos a hacer es decirle, oye, pues te vas a tu casa porque esto no está autorizado.
Y aquí tienes un 401.
Como mi amigo felino te va a explicar, un 401 es que no tienes acceso.
No tienes acceso. ¿Vale?
Entonces, así. Así te quedas.
No estás autorizado.
Entonces le decimos eso. Necesita el usuario que haya iniciado sesión.
Luego también podríamos revisar más cosas.
Por ejemplo, podríamos recuperar el nombre del usuario de session user.name.
¿Vale? Este sería el nombre de usuario.
Pero si por lo que sea no tiene nombre de usuario, también no está autorizado.
No puede buscar nada, ¿no?
Luego, lo que necesitamos es enviar los votos que tenemos que guardar en la base de datos.
Y se lo vamos a enviar como un JSON.
Así que vamos a, vamos, ¿qué podemos hacer?
Podemos recuperarlo de la request.
¿Vale? Así que vamos a tener aquí para la request, vamos a tener votes, ¿vale?
AwaitRequest.json.
Esto es lo mismo que hacéis muchas veces con el rest.json, pero al revés.
Igual que tenéis un método para la respuesta convertirla a JSON,
también existe un método JSON para la request.
Esto es una cosa que es nativa de JavaScript, ¿vale?
Que mucha gente dice, ay, no sé qué, ¿de dónde sale esto?
Es que esto es nativo.
Es la API de fetch.
Lo que pasa es que no solo es el método fetch la API de fetch.
Entonces, con esto ahí ya tendríamos los votos.
Podríamos mirar a ver los votos, ¿vale?
Voy a poner un console.log simplemente para asegurarme.
Y aquí esto lo podríamos poner en un try-catch, ¿vale?
Vamos a poner un try-catch por si el JSON que nos pasa es incorrecto o lo que sea.
Vamos a poner aquí una respuesta.
Va request, ¿vale?
Nos han intentado colar unos votos que están mal formados.
Pues le ponemos esto.
Aquí ponemos votes to save.
Vamos a poner esto.
Y así aquí podremos guardar el votes to save, el votes, ¿vale?
¿Y qué más?
Bueno, ya aquí lo que faltaría es llamar a la base de datos, ¿vale?
Que lo haremos ahora.
Pero esto es lo que nos faltaría.
Podríamos hacer más cosas, ¿eh?
O sea, aquí realmente tendríamos que validar la estructura de los votos, ¿vale?
Que se podría hacer con Zot, con un JSON schema también, pero con Zot, por ejemplo.
Y estaría bastante bien.
Lo hemos visto un montón de veces de cómo hacerlo con Zot.
Os invito a...
Podéis hacer una PR que nos iría bastante bien.
Y ya está.
Aquí en lugar de poner esto que no tiene mucho sentido, pues podemos decir, pues ok, con
el status 200, ¿vale?
Como que ha ido bien.
Y ya está.
Así que esto lo dejamos en todo, que lo vamos a hacer ahora.
Y ya está.
¿Ok?
Muy bien.
Ya tenemos la API hecha.
¿Qué necesitamos ahora?
La base de datos.
Antes me preguntabais dónde vamos a desplegar la base de datos y todo esto.
Voy a utilizar Tursor, pero podéis utilizar Tursor, es para SQLite, PlanetScale para MySQL, ¿vale?
Y luego tendréis Neon para PostgreSQL.
Las tres están geniales.
Cada uno tiene sus cosas buenas, sus cosas malas.
Y lo más importante, los tres tienen una capa gratuita que te da, vamos, para hacer de todo.
Fíjate, en el caso de Tursor, tienes 9 GB de storage, hasta 500 base de datos, 1.000 millones de lecturas.
PlanetScale, también, capa gratuita, 5 GB de storage, 1.000 millones de lecturas.
Neon, también, de PostgreSQL, 3 GB de storage, un poquito menos.
Y no sé, no lo pone.
O qué bonita es la página, por favor.
La página es bien bonita.
Pero también no os preocupéis que vais a tener ahí, vais a ir sobrados.
A mí Tursor me gusta y cada vez me gusta más, porque la verdad es que es bastante maravilloso.
Cloudflare de 1 también está muy bien, Cloudflare de 1.
Cloudflare de 1 me gusta mucho, pero lo que menos me gusta es la experiencia de desarrollo.
Creo que, no sé, Cloudflare no le ha dado todavía el toque.
Entonces, yo voy a utilizar Tursor, pero utilizad lo que os dé la gana el día de mañana, ¿vale?
Para utilizar Tursor, lo más interesante es que, primero, tenéis que instalarlo.
Y mucha gente, muchas veces, tiene problemas con Tursor.
Para instalar Tursor, necesitáis la línea de comandos.
Entonces, os vais aquí a Tursor.cl y si tenéis macos o tenéis Linux, como siempre, no vais a tener ningún problema.
Ejecutáis un comando y os va a funcionar maravillosamente.
De hecho, yo creo que lo tengo instalado ya.
Quiero pensar.
Tursor, sí, lo tengo instalado.
A ver, Tursor.
Tursor update, a ver si puedo actualizar.
¿Ves?
Han actualizado a la última versión esto.
O sea que, perfecto.
Vale.
Macos, un comando.
Linux, un comando.
Windows, para Windows, lo que os van a recomendar es que tengáis instalado WSL.
Porque una forma en condiciones sin WSL no se puede.
De hecho, lo pone aquí, requires, ¿vale?
Que lo necesita.
Solo para que lo sepáis.
También es verdad que si utilizáis Windows, es raro que no tengáis WSL.
Pero bueno, ahí cada uno con lo suyo.
Vale, esto está actualizándose.
Perfecto.
Vamos a ver la versión de Tursor.
Vale, ya la tenemos actualizada.
Una vez que tienes la línea de comandos, y puedes ejecutar este comando para saber si la tienes, ¿vale?
Tursor version.
Pues lo que necesitamos primero es iniciar sesión en Tursor.
Para eso tienes que hacer Tursor out login.
Igual me dice que, ¿ves?
Que yo ya he iniciado sesión.
Entonces no tengo ningún problema.
Pero si no, lo que haría esto es que te abriría la ventana del navegador y te diría,
inicia sesión con Tursor.
Es totalmente gratuito.
No tienes que poner tarjetas de crédito, cosa que se valora mucho, para iniciar sesión en Tursor
y para registrarte y para tener 500.000 bases de datos, ¿vale?
Entonces, una vez que ya tienes la base de datos, lo que puedes hacer es Tursor,
puedes hacer DB list para listar.
¿Ves?
Yo, por ejemplo, ya tengo una de Slam Boats, pero yo voy a crear una desde cero.
No os preocupéis.
Entonces, tenemos la de Pixelworks porque quería migrar el proyecto a Tursor
porque creo que iría mejor porque en Dino se acaba muy rápido la capa gratuita.
Entonces, para crear tu primera base de datos, pues pones Tursor DB Create y ya el nombre, ¿no?
Pues Slam Boat System, por ejemplo.
Haces esto y te la crean un momentito.
Aquí fíjate que pone en el grupo por defecto.
Y esto es porque una cosa que está muy chula de Tursor, que ahora verás,
es que está replicada en todo el mundo.
O sea, tu base de datos lo que hace es que se replique en todo el mundo.
¿Ves?
Aquí puedes ver un poco el ejemplo.
Entonces, ¿qué es lo que hace esto?
Que tú replicas la base de datos a donde están tus usuarios.
Entonces, tú puedes tener la base de datos en Ámsterdam y replicarla en Australia.
Y entonces, lo que va a ser la velocidad de acceso a la base de datos de los usuarios
desde Australia va a ser mucho más rápida.
Y así, lo que vas a tener es una menor latencia.
Bueno, esto depende porque si tu backend, que es el que llama la base de datos, al final está en San Francisco,
pues tienes que tener en cuenta que no es tanto donde esté tu usuario, sino donde está el cliente de la base de datos, ¿no?
Pero bueno, igualmente es muy interesante porque al final, si tu servidor está en San Francisco
y le puedes poner que Tursor tenga la base de datos en San Francisco, pues hará que la latencia sea mucho menor, ¿vale?
Y además también es totalmente gratis que puedas tener unas cuantas ahí desplegadas.
Así que, una maravilla.
Entonces, ahora que ya tenemos esto, yo ya he creado mi base de datos.
Ya me da aquí algunas opciones.
Por ejemplo, mostrar la base de datos.
Le puedo hacer show y aquí me debería decir la URL, que la vamos a necesitar, así que me la voy a copiar.
Y me da información como, por ejemplo, las localizaciones donde está la base de datos.
Fíjate que está en Bogotá y en Madrid, porque son mis dos sitios favoritos del mundo.
Nada, mentira.
Es mentira, pero básicamente es por...
No me acuerdo, igual lo configuré en algún momento.
Pero sí, Bogotá me gusta mucho, sinceramente.
Más que Madrid, las cosas como son.
Bueno, pues ya tenemos aquí, ¿veis?
Y el primario está en Madrid.
Normalmente el primario lo vais a querer tener directamente cerca de los servidores.
Pero bueno, en este caso tampoco es importante.
Y esta URL sí que es importante y la tenemos que guardar, y la voy a guardar, en la variable de entorno.
Así que voy a quitar un momento, porque veis que tenemos aquí las variables de entorno, ¿vale?
Voy a crear un database URL, donde tenemos la variable de entorno esta, y database token, donde vamos a tener un token que vamos a crear.
¿Cómo se crea?
Pues tienes que poner tursoDB, tokens, create y el nombre de la base de datos.
En este caso, slam vote system.
Le daríamos aquí un enter y aparece un token maravilloso para que podáis utilizar.
Entonces, podéis fijaros en mi reflejo de los ojos.
Para que lo veáis, ¿vale?
Lo estáis viendo, lo estáis viendo.
Pues ese es, ese mismo es, ese mismo es.
Bueno, vale, sí, por aquí.
Voy a ver si me acuerdo de limpiar esto.
Vale, ya está.
Y ahora ya os puedo enseñar la pantalla.
Ya está, ¿vale?
Ya está.
¿La habéis lukeado?
¿La habéis lukeado?
Es bastante larga.
Espero que la habéis podido ver entera.
¿La habéis visto entera?
Vale, pues entonces ya tendríamos tanto el database URL como el database token.
Que con un comando lo podéis tickar, ¿vale?
Solo tenéis que hacer database, el TURSO, DB tokens.
Y esto os devuelve un token que es el que tenéis que tener la variable en torno.
Ok.
Entonces, más cositas de TURSO.
Pues eso, la base de datos.
TURSO, DB list.
TURSO, DB shell.
Y le decimos que queremos entrar a la de SLAM Boat System, ¿vale?
Y aquí se conecta la base de datos y ya aquí podéis hacer lo que queráis.
O sea, podéis crear una tabla, create table, lo que sea.
Por suerte, hemos adelantado un montón de trabajo.
Todo esto que hemos hecho antes, pues por fortuna ya lo hemos hecho.
Así que si todo va bien y no tenemos ningún problema, lo que podemos hacer es copiar todo esto.
Irnos aquí, copiarlo, pegarlo y esperar que todo vaya bien y que me pegue.
Uy, he visto algún error.
No puede ser parseado.
Cerca de RP.
Cerca de RP.
He visto algún error, pero es raro, ¿no?
A ver.
A ver.
Es que me da la sensación.
Drop RP.
¿Dónde puede ser el RP?
¿Dónde puede ser el RP?
Es que me da la sensación, ¿veis?
Que se ha comido.
Se ha comido una parte de...
Que se ha comido...
No sé por qué.
Pero me da la sensación que se ha comido...
¿Cuál ha sido?
¿Cuál ha sido el que ha petado?
El 2.
Me da la sensación que se ha comido el count.
O sea, que se ha comido...
¿Veis?
Qué raro.
O sea, creo que se ha copiado mal.
Fijaos que en este está bien el count no sé qué, no sé cuánto, pero fijaos aquí que esto se ha pegado mal.
No había visto esto en mi vida.
Y por eso ha fallado.
Porque claro, la sintaxis está mal.
Pero a ver.
Cuando lo he copiado, estaba bien.
Acabo de flipar.
Es la primera vez que me pasa una cosa así.
Es súper...
Es súper raro.
No ha sido el punto y coma.
No ha sido el punto y coma.
Es como que se ha pegado mal.
Estoy flipando.
Estoy flipando.
No me haya pasado esto nunca.
Pero es que estoy bastante seguro que ha sido eso, ¿no?
No creo que sea de Turso como tal.
Creo que ha sido o del Shell o de la terminal.
Creo.
O sea, qué raro, ¿no?
Esto solo pasaba en Visual Basic.
Es muy raro.
Bueno, al menos me he dado cuenta, pero es raro.
Es muy, muy raro.
Bueno, bueno.
El tema es que ya tenemos por aquí esto.
Si nos vamos y hacemos un login, que por cierto, podéis iniciar sesión con GitHub, lo cual está bastante chulo, y os vais aquí a Databases.
Ah, gracias por la hidratación, que estoy aquí un poquito...
¿Sabes si el Shell te permite importar el archivo SQL?
Pues es que el tema es que no lo sé, la verdad.
Y por eso lo he hecho así, porque normalmente no falla y porque no sé si se puede hacer de otra forma, sinceramente, la verdad.
Es raro.
O sea, seguramente sí que se podrá importar el SQL directamente, pero como no tengo ni idea y siempre lo he hecho así, pues no he tenido ningún problema.
Vale, entonces, en categorías, categorías, pues tenemos aquí las categorías, ¿vale?
O sea, que esto sí que ha funcionado bien.
Deberíamos tener también en Databases, en Vote System, a ver, Vote System, Edit Tables, en las categorías.
Categorías ya la he visto.
Las options, ¿vale?
Aquí tenemos todas las opciones.
Ok.
Hostia, option.
Ah, vale, sí, tiene sentido.
Pero me parece, ¿ves?
Hay algunas que también se las ha saltado.
La madre que lo parió, parió.
Hay algunas que se las ha saltado también.
O sea, no las ha puesto todas.
No las ha puesto todas, tío.
La madre que lo parió.
Fíjate, es que 1, 2, 3, 4, 5, 6, 7, 8, 9, 10.
1, 2, 6, 7, 8, 9.
O sea, faltan.
Faltan algunas.
La falta...
O sea, la madre que lo parió, parió.
La madre que lo parió, parido.
A ver, le voy a dar otra vez para ver si así se copian las que faltan.
Pero flipo.
O sea, no sé si ha sido la terminal.
Me imagino que es la terminal.
Me imagino que es la terminal porque, a ver, al final,
Tursor, hasta que no le llega, no hace nada, ¿sabes?
Y mi sensación es, básicamente, que es la terminal que, al pegarlo,
no lo ha pegado todo, ¿sabes?
Es mi sensación.
A ver ahora.
Si lo reseteamos aquí.
1, 2.
No, otra vez.
El 3.
O sea, por ejemplo, el 2, 3.
El 2, 3.
O a lo mejor es que no lo he creado yo.
A ver si es que soy yo que no lo he creado.
No, no, está aquí.
A ver, puede ser que sea por el showcase, ¿no?
Que a lo mejor dice...
¿Ves?
Options ID.
Option ID, el showcase...
Ah, no, ahora sí.
Debe ser que se haya metido, pero aquí no lo veo.
2, 3.
Es que no lo veo aquí.
O sea, aquí no está.
Es cosa mía.
O es que tarda en aparecer.
No, no, aquí no está.
Aquí no está.
No está.
No está.
O sea, ¿qué está pasando aquí?
¿Qué está pasando?
O sea, ahora no sé si es porque no...
Porque no aparece.
Porque no están...
Ah, espérate.
Que igual es que los ha puesto después.
Vale.
Que igual...
Ah, vale.
Están aquí.
Ahora sí.
Ahora sí.
Pensaba que estarían ahí por orden, ¿eh?
Vale, vale.
Ahora ya sabemos lo que ha pasado.
Vale, vale.
Bueno, pues nada.
Ya está.
Arreglado.
Arreglado.
Vale.
Estaba alucinando, ¿eh?
Estaba alucinando.
Bueno, arreglado, arreglado.
Bueno, pues ya está.
Ya tenemos en la base de datos.
Uy, momento raro, ¿eh?
Momento extraño.
Momento extraño, ¿eh?
Ya tenemos todo en la base de datos.
Lo que nos falta es crear el cliente.
Así que, venga.
Vámonos aquí en source.
Voy a crear aquí, no sé si crearle un database.
Database.
Client.ts.
Y lo que necesitamos primero es instalar el cliente de SQLite.
SQLite, ¿vale?
Que básicamente es este de aquí.
Y es uno de Turso que tiene un muy buen rendimiento.
Así que lo instalamos.
Y este es con el que vamos a poder conectarnos a nuestra base de datos.
Vale, ¿por qué tarda esto tanto?
¿Por qué?
¿Por qué este PNP install está tardando tanto?
Como si no hubiera hecho un PNP install en mi vida.
11 segundos.
Mucho me parece.
Mucho me parece.
¿Vale?
Esta dependencia, que es el cliente oficial de Turso,
es el primero que vamos a importar.
Así que nada, importamos el createClient del cliente
y ya podemos crear el cliente.
Vamos, constClient, createClient.
Y aquí hay que pasarle dos cosas.
La URL, que hacemos import.meta.
No tenemos autocomplete.
Podría poner el .env para crearlo,
pero bueno, no debería haber ningún problema
en poner database URL.
Y le ponemos que si no es esto.
Y si el authToken de database,
y si no, authToken.
Esto no importa.
Esto sí, en el caso que esto sea undefined,
le pondrá un string vacío y petará el cliente
porque no podrá iniciarlo.
Así que no hay ningún problema.
Y aquí ya podríamos utilizar el cliente.
Con el cliente lo que vamos a hacer
es crear básicamente dos métodos.
Uno, que va a ser el de cleanUserVotes
para limpiar todos los votos del usuario.
Esto por un lado.
Y vamos a también tener el constAtUserVotes,
que va a ser para añadir los votos del usuario.
Y bueno, por ahora voy a poner un any,
pero ahora lo arreglo, ¿vale?
Ahora lo arreglo.
El any.
A ver, para hacer esto,
porque veis que cabrón, un any y tal,
en algún sitio,
voteSystem,
en algún sitio deberíamos tener
el tipado este,
que deberíamos extraerlo,
pero es que no me ha dado tiempo.
Mira aquí, ¿ves?
ExportTypeVotes y tal.
Vale, pues este Votes lo vamos a importar.
ImportTypeVotes.
Bueno, ya le importa.
No está bien importarlo de un hook.
Me gustaría tener tiempo de sobra
para sacarlo y ponerlo en un sitio correcto,
pero puedes hacer una PR
y extraer las declaraciones a un archivo
y te lo agradeceré.
¿Vale?
Estos van a ser los dos archivos,
las dos cositas que vamos a hacer.
El cleanUserVotes,
pues es lo primero que llamaríamos...
Lo llamaríamos aquí, ¿vale?
Con el username.
Y aquí haríamos una wait.
Y luego, ¿vale?
Lo importamos.
Y addUserVotes,
que le pasaríamos el username
y los votos a guardar.
Y con estas dos cositas,
si esto ha ido bien,
que esto podríamos poner aquí un tryCatch
también aquí para si hay algún problema
con estas dos cosas.
InternalServerError, ¿vale?
Tendríamos que poner esto,
enviarla atrás a algún sitio
que no sea el consoleError,
sino pues algún sitio de observabilidad
para ver los errores.
¿Vale?
InternalServerError,
tú, tú, tú.
Y ahora solo nos faltaría
la implementación de estos dos métodos
para hacer la petición con SQL
para guardar esto.
La más fácil es la del clean.
Bueno, vamos a hacer una cosa
antes de continuar.
Antes de continuar,
nos quedan dos cosas.
Una, esto, ¿vale?
Y por otro lado,
nos faltaría aquí en nuestra página
que cuando le demos a enviar votos,
o sea, cuando vamos aquí a la última página
y le damos a enviar votos,
que envíe los votos.
Como ya tenemos la API creada,
que es esta que hemos creado aquí,
esta de Vote,
vamos a probar la API más o menos
para ver si realmente funciona.
Total, no haría nada,
pero al menos me debería dar un 200
y deberíamos ver cómo funciona todo.
Así que vamos a ir al Vote Final,
que es el último componente que tenemos.
¿Veis que hemos dejado aquí
el Handle Submit vacío?
Pues esto lo vamos a arreglar.
El Handle Submit,
básicamente lo que tenemos que hacer aquí
es hacer una petición,
pero lo que vamos a hacer es
nada más le demos al botón,
¿vale?
Le damos al botón de enviar votos.
Lo primero que queremos es
cambiar el estado
en el que se encuentra la interfaz.
Así que le decimos que
el resultado por ahora
lo pasamos a Loading, ¿vale?
porque estamos cargando los datos
y ahora sí vamos a intentar
hacer el Fetch
de barra API barra Vote,
que esto va a ser pues del tipo JSON,
un post, vale, headers
y le pasamos los votos.
Ok, hacemos esto
y aquí tendríamos la respuesta.
Así que guardamos la respuesta,
aquí una Wait
y ya tendríamos esta parte
al menos de aquí.
Una vez que tenemos la respuesta,
¿qué puede ocurrir?
A ver, lo primero que puede ocurrir
es que la respuesta sea negativa,
o sea que la respuesta no sea ok,
por lo tanto aquí
tenemos que cambiar el resultado
a que ha habido algún error.
Podríamos mejorar
la gestión de errores,
pero bueno, por ahora
pues hacemos esto
y ya saldríamos,
dejaríamos de ejecutar nada.
Si ha ido correctamente,
pues nada,
ahí le decimos,
oye, los resultados
han ido todo bien,
perfecto,
y así cambiamos la interfaz
con este resultado.
Y si por lo que sea
no se puede,
no solo hay algún error,
no tanto de que la respuesta,
no, o sea,
de que no,
de que me devuelvan 404,
un 500 o lo que sea,
sino de que ha habido
un error de conectividad
o de lo que sea,
ponemos este catch final también
y ponemos aquí un result,
error,
y ya está.
Si llega,
aquí podríamos poner ya el return
también,
porque en todo caso,
ah, bueno,
ya está,
o sea,
no hace falta un return,
ya lo tendríamos con esto.
Con esto ya tendríamos
todos los casos,
o sea,
primero que estaría cargando,
luego que si hay un error,
le decimos,
si no hay ningún error,
pues tendríamos aquí el success
y si hacemos el catch
por si hay más un problema
de offline conectividad y tal,
pues tendríamos este final
y ya lo tendríamos.
Esto llama la API que hemos creado,
tipo post,
importante,
tipo JSON,
para enviarle los votos
y vamos a ver
si esto al menos funciona.
Si le doy,
vale,
gracias por votar.
Obviamente no ha pasado nada
muy tal,
pero sí que me he visto
ahí un momento el loading,
¿no?
No parecía el loading muy,
a ver,
loading,
loading.
Este es el SVG
que se supone
que hace la animación del spin,
se supone.
Vamos a poner esto
que sea más,
más así,
esto así,
fill blue,
es que claro,
fill blue
no vale la pena
que sea el fill blue.
Y el animated spin,
esto lo podemos quitar,
esto no es necesario,
¿vale?
Y para ver todo el rato
que estamos enviando los votos,
voy a hacer otra vez esto,
¿vale?
Para no tener que estar
todo el rato navegando
y así siempre iremos
a la última página.
Esto es un consejo
que os doy de vida
como programador.
Un consejo de vida
de programador
que tenéis que tener
muchas veces en cuenta
es el hecho
de que siempre que podáis
tenéis que facilitaros
la depuración.
Muchas veces
me he encontrado programadores
que por ejemplo aquí
pues están todo el rato
dándole al siguiente
para ir a la última página
no sé qué.
Una cosa que es clave
es el hecho de
iterar rápido
y para iterar rápido
es el hecho
de poder tener listo
la información,
ya sea que puedas
moquearlo con un local storage,
que puedas cambiar un estado
para forzar
que te aparezca
la información
que tú necesitas.
Es que he visto
cosas muy chungas
de gente
en formularios
de múltiples pasos
de en lugar
de perder
10 o 15 minutos
de tiempo
de estructurarlo
de forma
que puedas
inyectar
el estado
para tenerlo ya listo
para ir probando,
pues hacerlo manualmente
todo el rato
y, ostras,
eso marca la diferencia
mucho
a la hora
de que un programador
saque faena rápido,
mucho más
que utilizar
inteligencia artificial,
te lo digo.
Vale,
enviar votos.
Vale,
es que va muy rápido,
va demasiado rápido
las cosas como son.
¿No hay que enviar
algún token
en el post del voto?
No,
porque se está enviando
en las cabeceras,
se está enviando
la cookie
donde el usuario
ya tiene la sesión iniciada
y estamos recuperando
la sesión del usuario
y ahí es donde está
la autentificación
del usuario,
¿vale?
No es necesario
que hagamos
absolutamente nada
en ese caso.
¿Vale?
Entonces,
venga,
vamos por aquí.
Si queréis,
como para,
yo qué sé,
podríamos ponerle aquí
para que veamos
un poco el loading.
Ah, bueno,
ahora le pondremos
la base de datos,
así que a lo mejor
no necesitamos un loading,
o sea,
no necesitamos ponerle
un timeout
porque a lo mejor
va lo suficientemente lento
la base de datos
para que pase esto.
Vamos ya con lo que nos queda,
que es básicamente
que se guarden
los votos del usuario.
Primero,
tendríamos que quitar
los votos del usuario
para evitar que vote dos veces,
¿vale?
Para asegurarnos que,
oye,
este usuario solo puede votar una vez,
así que,
ojo,
cuidado.
Así que,
nos vamos aquí
y hacemos una
await client.execute
y vamos a ejecutar aquí
básicamente
una petición SQL,
para eso le ponemos SQL
y aquí sí que vamos a ver
el SQL injection.
Mirad,
si aquí pondremos esto
con un template string,
delete from votes,
votes,
where,
username,
sea igual al username,
¿vale?
Si hacemos esto,
esto es un SQL injection
de locura,
¿vale?
De locura.
¿Por qué?
¿Por qué?
Un argumento de tipo string
porque dice,
a ver,
la prueba de arcs
falta en el tipo,
ah,
Clara,
claro,
ya te obliga,
hostia,
qué bueno que te obligue,
qué bueno que te obligue,
te obliga básicamente
a que pongas aquí
los argumentos
para que al menos
sepas que se lo puedes pasar
por ahí.
Esto es un SQL injection
de manual,
un template string
que nos están pasando algo
desde fuera,
que no estamos validando,
que se lo estamos metiendo ahí,
nos lo estamos comiendo
con patatas
y que por lo tanto
este username
nos podría poner
cosas muy chungas
como por ejemplo
midudep,
coma,
select,
no sé qué,
no sé cuánto
o delete,
¿sabes?
Nos puede poner ahí una coma
y todo y ya está,
delete y drop table
y lo que nos dé la gana,
¿no?
Entonces,
para arreglar este tipo de cosas
no se puede hacer así,
obviamente,
esto no se puede hacer.
Lo que se utiliza
son los,
este tipo de cosas
que es como un placeholder
y el placeholder
lo rellenas
con los argumentos
que le pasas aquí.
Entonces,
automáticamente
lo que va a hacer
este sistema
es sanitizar la entrada,
¿vale?
Va a detectar,
va a intentar evitar
que te meta alguna inyección
y todo esto,
¿vale?
Así que es como lo haríamos
correctamente.
Normalmente esto
se hace en un montón
de bases de datos,
hay otros tipos de sistemas
que son totalmente diferentes,
pero esto es lo importante,
no utilizar el template string.
Por suerte,
en este caso,
te obliga a poner el arcs
para que al menos
te hagan la pregunta de
¿lo debería poner en el arcs?
¿vale?
Así que eso sería.
Y luego tendríamos
lo de añadir los votos
del usuario.
A ver,
esto lo único
es que vamos a tener
que hacer un pequeño mapeo,
¿vale?
O sea,
vamos a tener el SQL,
digamos,
base,
que sería
insert into votes,
¿vale?
Claro,
y no va a ser tan fácil
como esto,
porque
si miramos los votos,
si miramos
cómo son los votos,
lo que le estamos pasando,
veis,
los votos
son así.
Los votos son así.
Es un array de arrays.
Es un array de arrays.
Y cada array
es una,
cada posición
es una categoría.
Que si hubiéramos tenido
mucho tiempo,
la categoría hubiera tenido
una idea
en condiciones,
aquí estamos utilizando
por lo que veo también
el index,
o sea,
que claro,
con tiempo hubiéramos hecho
algo mejor.
Pero,
claro,
fijaos que es un array
de arrays
y que cada uno,
pues aquí tiene
todos los votos
de la categoría 1.
Está la categoría 1.
Podríamos haberlo hecho mejor,
que fuese un objeto,
que en cada categoría
y todo esto,
no tenemos tiempo,
pero bueno,
nos va a servir,
no pasa nada.
Cosas que podemos hacer.
Lo que podemos hacer
es,
estoy pensando,
estoy pensando,
estoy pensando.
Vale,
lo que voy a hacer
es utilizar el batch.
O sea,
como vamos a querer
insertar todos los votos
y además uno a uno
y si falla uno,
vamos a querer hacer
un rollback
para evitar
que se quede a medias
porque si no
la podemos liar muy parda.
Existe una cosa
en Turso
y en un montón
de bases de datos
que se llama,
¿cómo se llama?
Batch?
No,
joder,
¿cómo se llama?
Sí,
Client Batch.
Client Batch.
Vale,
aquí no sale.
A ver aquí.
Básicamente,
¿qué es el batch?
El batch es que tú
puedes decirle,
oye,
haz todas estas transacciones,
¿vale?
Quiero que hagas este insert,
quiero que hagas este insert,
vas a intentar hacer
todos estos inserts
y si falla uno,
me haces un rollback
de todo.
Podríamos hacer esto
o podríamos hacer
un insert
de un solo insert
con todos los values
y tal,
también lo podríamos intentar.
Pero con este,
yo creo que
lo vamos a probar
y así,
pues vamos a iterar
todos los votos
y vamos a utilizar esto.
En lugar de utilizar
este insert votes
que nos está poniendo aquí,
lo que vamos a creer es,
vamos a cambiar esto,
tenemos que pasarle
el username,
el category ID,
¿vale?
El option ID
y el ranking.
Claro,
en la posición del índice
también es importante
el voto.
Y estos valores
van a ir aquí,
¿vale?
Esto van a ser
los playholders.
Ahora,
vamos a crear
los inserts
y para eso
vamos a mapear
los votos.
Los votos
que le estamos pasando
por aquí,
que es de esta forma,
¿vale?
Esto es lo que serían
los votos.
De hecho,
lo voy a hacer aquí
en un momento,
¿vale?
Porque creo que
así va a ser más fácil.
Insert,
votes,
no sé si llamarle
todos los votos
para que así
tengamos mejor
la información.
Todos los votos.
Está bien
porque así también
estamos como
practicando temas
de manipulación,
de manipulación
de arrays
y todo esto.
Vale,
vamos a mapear
todos los votos.
Voy a copiarme
el insert,
este,
¿vale?
Que es el que vamos
a querer hacer también.
Así que lo insertamos aquí
y para crear los inserts,
lo primero que necesitamos
es la Category ID.
Lo podríamos sacar
de diferentes formas.
Yo me voy a quedar
con el índice
porque, bueno,
es lo que tenemos.
Lo ideal es que fuese
un objeto
y sacar el índice,
la ID mejor,
pero bueno,
en este caso,
ya,
¿qué le vamos a hacer?
Ahí ya tenemos
el Category ID,
¿vale?
Y ahora tenemos
categoría la 1
y ya sabríamos
que todo esto
es de la categoría 1,
¿vale?
Y ahora,
para crear cada insert
de cada uno
de los votos,
lo que podemos hacer
es básicamente
otro mapper.
Hacemos Category Votes,
Map,
y aquí vamos a tener
el índice
que va a ser el rank
y aquí tendríamos
el vote,
¿vale?
Cada uno de los votos.
Vale.
Cada uno de los votos.
Entonces,
para cada uno de los votos,
el ranking
vamos a utilizar,
voy a utilizar
que esto sea el index
para que quede más claro,
y hacemos index más 1.
¿Por qué?
Porque los votos
son rankeados,
como lo tenemos aquí,
a ver,
para lo que veáis,
bueno,
localhost 4,
3, 2, 1,
los votos son rankeados,
o sea,
que dependiendo
de su posición,
este voto
es más importante
que este de aquí.
Por lo tanto,
vamos a poner
el ranking,
se lo vamos a tener
que insertar
en la base de datos
y es importante
que lo hagamos
para saber
cuál fue el primero,
el segundo,
el tercero
y el cuarto,
¿vale?
Así que el voto rankeado
lo tenemos que guardar.
Entonces,
tenemos el voto rankeado
y ya de cada uno
tenemos que devolver
la petición SQL,
que siempre va a ser esta,
que va a ser la misma,
y luego los argumentos,
vamos a tener por un lado
el username,
que no lo tenemos aquí,
pero nos va a pasar
por parámetro,
¿vale?
Así que teníamos
el username,
el category ID,
el option ID,
que el option ID
sería el propio vote,
¿no?
El propio voto
y el ranking.
Estas serían
todos los pasos.
Tendríamos estos maps
y vamos a ver
lo que nos queda
aquí en el console log
de los inserts,
¿vale?
Vale.
Vale.
Tenemos aquí todo,
pero hay un problema.
Y es que es un array
de arrays.
Vale.
No pasa nada.
Es que qué maravilla,
JavaScript.
Vas pensando y dices,
hostia,
pues esto se puede hacer.
Vale.
¿Por qué es un array
de arrays?
Porque tenemos un map
para todos los votos,
pero tenemos un array
de arrays por esto,
porque esto es un array
de arrays.
Y lo que queremos
es que sea un array
de un solo nivel.
Esto es un array
de arrays.
O sea,
un array de dos niveles,
¿no?
Un array dentro de un array.
Y lo que quiero
es aplanarlo
y tener un array.
Entonces,
lo que podemos hacer aquí
en este all boats,
primero hacemos el map,
este,
y luego todo esto
le podríamos poner
un flat
para que lo aplane.
Y esto,
si no me equivoco mal,
si no hemos hecho
nada mal,
ahora veré.
A ver.
A ver.
Yo creo que esto
ahora sí
es un array
de un solo nivel.
Porque lo hemos podido
aplanar con el flat.
Y ya está.
O sea que,
perfecto.
Vale.
Genial.
Pues ya tenemos
los inserts.
Con esto ya deberíamos
tener los inserts.
Me voy a copiar
todo esto
que hemos hecho por aquí.
Vale.
Me lo voy a llevar
a mi código.
¿Ves?
Mucho mejor para eso.
Está muy bien el...
Vale.
No se encuentra
el all boats.
¿Vale?
Porque es este,
el all boats.
Vamos a ver.
TypeScript no se queja.
Buena cosa.
Y ya simplemente
tenemos que hacer
await client.batch
y le pasamos aquí
todos los inserts
y hay que pasarle
el write.
¿Vale?
Que es para escribir.
Y ya está.
Esto nos debería devolver...
Bueno,
si hacemos una wait directamente
y total ya tenemos
el try catch fuera.
Pues ya lo deberíamos tener.
Esto debería hacer
el batch
de todas las informaciones.
Podríamos tener aquí
el result si queremos.
Hacer el return del result.
No lo vamos a utilizar
pero por si acaso.
Aquí también.
Tenemos el resultado
que el resultado
básicamente nos diría
cuántas filas
se han tocado,
cuánto tiempo ha tardado,
qué errores se ha encontrado
y todo esto.
¿Vale?
Y con esto
ya lo tendríamos.
Lo habéis pillado aquí.
Lo habéis pillado.
Y si falla uno,
si falla uno,
esto es lo interesante.
Si falla uno,
va a hacer un rollback
a...
O sea,
no lo va a dejar a medias.
Si falla uno,
lo que va a hacer
es lo bueno
que tienen los batches.
¿Vale?
Batch.
A ver,
rollback.
¿Ves?
Dice,
el backend
hace el handle
de la transacción.
Si va bien
todos los cambios,
pues perfecto.
Pero si hay alguno
que tiene un problema,
hace un rollback completo
sin modificaciones.
Entonces,
esto está muy chulo
porque si falla uno,
en lugar de dejarlo a medias,
lo que hace es,
vale,
pues los tiro todos
para atrás,
¿sabes?
Para evitar básicamente esto,
¿no?
Ahora pensando
lo del flat este
que habíamos hecho aquí,
igual aquí podríamos utilizar
el flat map,
¿eh?
Claro,
es que como el flat
me he dado cuenta antes,
pues por eso lo he hecho.
Pero creo que aquí
si ponemos un flat map,
seguramente,
nos va a hacer exactamente
lo mismo
y así no necesitamos los dos.
El flat map
lo que hace es exactamente
lo mismo,
es mapear
mientras lo estás poniendo
en el mismo nivel.
Y con este lado tendríamos.
Como me he dado cuenta después,
pues claro,
por eso lo he hecho así.
Pero flat map
y ya lo tendríamos.
Vale,
pues con esto
se supone
que ya tendríamos
que tener esto,
amigos,
así que nos iríamos
a nuestro localhost
4.3.2.1,
iríamos a nuestra página
de los Airland
y aquí tendríamos
nuestros votos
y diríamos,
vamos a enviar los votos.
Y hubo un error.
Vamos a ver
cuál fue el error.
Ha petado.
Ha petado
porque
ha dado un 401.
401.
Row code
server error
401.
Vale,
un 401
es no autorizado.
Entonces,
¿qué es lo que puede pasar?
¿Qué es lo que ha podido pasar?
Lo que ha podido pasar
es más un tema
de tokens.
¿Sabes?
O sea,
es que ni siquiera
ha funcionado.
Entonces,
voy a mirar un momento
temas de tokens,
¿vale?
Para ver si es un tema
de las variables de entorno.
Se venció la sesión.
¿Podría pasar?
No,
no creo que sea la sesión.
Yo creo que es un tema
más de tokens.
Voy a mirar.
Un momento.
Enf local.
A ver.
Claro,
es que tengo aquí
enf,
pero no tengo local.
No sé si esto
enf.enf.local.
Pero tiene pinta
de ser más
un tema de conectividad.
Y si no,
ahora lo veremos.
O sea,
como que no sé.
No,
no os voy a enseñar.
No os voy a enseñar.
Vote system,
midudev,
torso,
database,
URL,
un momento,
database,
URLs,
vale.
Vale,
ya he visto el problema.
Ya he visto el problema.
El problema
es este.
El problema es que
había puesto
auth token
y era token,
¿vale?
Ah,
ajá.
Entonces no estaba haciendo
la conexión a la base de datos.
Vale,
vamos a probar ahora.
A ver ahora.
Gracias por votar.
Y nos vamos
a nuestra página de Turso.
Nos vamos a los votos.
Y aquí tenemos
todos los votos
de midudev.
Y podemos ver,
pues nada,
un 2,
3,
4,
un 2,
3,
4,
un 2,
3,
4,
que aquí tenemos
todos los votos
que ha hecho
midudev en todas
y cada una
de las categorías
que tendría que tener.
Y ahora
aquí tendríamos ya
la magia de la ciencia
de datos.
Aquí se trataría
de que viniese
el de ciencia
de datos,
¿no?
Y ya está.
Pero es verdad,
es verdad
que yo creo
que le falta
el detalle final.
Hay un detalle
que tú y yo
sabemos
que nunca jamás
puede faltar
en este tipo
de momentos,
estos hitos
históricos,
históricos,
yo también ya
no sé ni hablar.
Así que
vamos a hacer juntos
esto porque
me parece
que es importante.
Vamos a ver
esto por aquí.
La verdad
que no está
viendo la pantalla,
¿no?
¡Confetti!
¡Hombre!
El canvas confetti
hay que ponerlo
por aquí.
No se encontró
ninguna declaración.
¡Ay!
Estabar todos paquetes
de tipos que faltan.
¿Cuál es el tipo
que falta?
Canvas confetti...
¡Qué rabia
que no tenga!
Types
canvas
confetti
menos D.
A ver si ahora
con esto
deja de rayar.
A ver si con esto
deja de rayar.
Canvas confetti...
Confetti...
Es que no sé
si tiene aquí
confetti.
No.
Es que no es un confetti.
Yo creo que es así.
Confetti
y aquí
justamente
cuando esto va bien
pues hacemos esto
y ahora pues nada
volvemos a nuestra página
y ahora
ahora sí que es una página
en condiciones.
Joder.
Con un confetti.
Confetti.
Vale.
Vamos a asegurarnos
que no está grabando
los votos más de una vez.
Vale.
Perfecto.
Aquí podemos ver
1, 2,
1, 7,
1, 3,
1, 8.
Vale.
Y ya lo tendríamos.
Gracias por votar.
Así que nada amigos.
Ahora sí
hemos terminado.
Ya lo tenemos.
Una vez enviados
los votos
no se pueden cambiar
aunque hagas otro envío.
En este caso
nosotros efectivamente
hemos dejado
que sí que se puedan
cambiar los votos.
Vale.
pero la idea
pero la idea es que
no se pudieran
cambiar los votos.
O sea
que una vez que votas
ya no los puedas cambiar.
Pero bueno
sí que hemos dejado
que bueno
si vuelves a intentar votar
por ejemplo
ahora pues quiero votar
a AuronPlay
y quiero votar a
yo qué sé
no conozco a nadie
Rubius
y a IjoJuan
y a Gemita.
Vale.
Y ahora nos vamos al final
porque total
los votos están guardados.
Pues ahora deberíamos ver
que si le doy
a enviar votos
vale.
Y esto guarda los votos.
Si vamos aquí a la aplicación
ahora aquí
estos votos
que eran los de antes
deberían ser diferentes
a los votos
que tenemos ahora.
¿Ves?
Ahora he votado
AuronPlay
y antes no tenía
AuronPlay.
Entonces
no pasa nada
que cambien los votos
los usuarios
porque siempre están
machacando los suyos propios
ellos mismos.
O sea
pueden votar tantas veces
como quieran
pero estamos machacando
nuestros votos.
No se van a
incrementar
¿sabes?
Y es lo importante
que no ocurra eso.
Así que
perfecto.
Luego los admin
hacen un select con
con cada voto
y ya tienen el trabajo hecho.

o sea
a ver
lo que pasa
es que como está rankeado
habrá que hacer
ciertas ponderaciones
¿vale?
Es un poquito más complicado
en ese asunto
pero
ahí están los datos
ya.
Esto sería cuestión
de hacerlo
y ya está.
Bueno amigos
que ha sido un placer
verdadero placer
este es el último
directo que hacemos
de la página
de los SESLAN
espero que os haya gustado
este proyectazo
que hemos hecho junto
que hayáis aprendido
algo nuevo
que podéis seguir colaborando
obviamente
la página no se va
a ningún sitio
es de código abierto
ahí se queda
todo el código
y podéis seguir colaborando
mejorando las cosas
que veáis
podéis echarle un vistazo
muchas gracias
a los más de 55
contributors
que tenemos
en el proyectazo
espero que hayáis aprendido
algo nuevo
que hayáis aprendido
a los más de 55