Bitácora de crédito (vol. 2)

por Halí - publicado: Julio 13, 2023
LaravelBackend

En este «sprint» (o lo que sea), vamos a continuar con el desarrollo de dos características principales.

Primero comprobaremos que se puede iniciar sesión con un usuario, y que tiene acceso a ciertos permisos, luego continuaremos con la creación y edición de tarjetas de crédito asociadas a un usuario.

Índice

Previos

Después de los pasos anteriores, tenemos que agregar algunas cosas al app service provider, migrar nuestra base de datos, es decir agregar todos los cambios que agregamos a las migraciones, de manera estructurada y también editar algunas de nuestras variables de entorno.

App service provider

Primero cambiamos algunas cosas en el AppServiceProvider, comenzamos agregando la prevención de lazy loading en los modelos, para evitar algunos problemas de performance.

También cambiaremos una parte de la gramática del la conexión a la base de datos, particulamente el formato de la fecha, ya que por defecto no tenemos precisión de milisegundos.

Ver código  (GitHub)

Migraciones

Las migraciones son muy útiles, pues nos permiten realizar cambios en la base de datos de manera controlada.

Esta es una forma segura de «interactuar» con la base de datos, ya que existe una tabla particular que lleva el registro de las migraciones que ya se han ejecutado de acuerdo a su fecha y las migraciones ejecutadas no se repiten.

Además en caso de que una de nuestras migraciones salga mal, y necesitemos hacer rollback de esta, podemos hacerlo sencillamente y con un solo comando.

Puedes leer más acerca de las migraciones en la documentación oficial de Laravel.

Comenzamos creando la tabla de migraciones.

sail artisan migrate:install

Como ya definimos el esquema que queremos en la base de datos e incluso ya definimos algunas de las migraciones, no queda más que ejecutarlas en nuestro entorno local.

Utilizaremos la opción --step para que podamos hacer rollback a cada migración individualmente.

sail artisan migrate --step

Si todo va bien, la consola nos mostrará un error… 😥 y es porque olvidé algo importante, por defecto las columnas uuid en Laravel no son llaves primarias, por lo que no se pueden usar como llaves foráneas en otra tabla, así que vamos allá a corregir el desastre…

Ver código  (GitHub)

Ahora sí podemos ejecutar las migraciones, pero como algunas tablas ya fueron creadas, hay que destruirlas para crearlas de nuevo.

Esto es seguro solamente en entornos de desarrollo, por favor nunca ejecutes el siguiente comando en producción (igual Laravel te advertirá si lo intentas).

sail artisan migrate:fresh --step

Excelente, nuestras tablas ya han sido creadas.

Env

También cambiaremos algunas cosas de nuestro archivo .env.

Por ejemplo el valor de APP_URL así como la sesión, la queue y la caché, ya que queremos usar redis y ya de paso queremos aumentar el tiempo de la sesión, a… 3 días o sea 259200 segundos.

Los valores que cambiaremos quedan algo así:

APP_URL=http://laravel.test
CACHE_DRIVER=redis
QUEUE_CONNECTION=redis
SESSION_DRIVER=redis
SESSION_LIFETIME=259200

API

Para probar nuestra API utilizaremos Postman, y comenzaremos agregando los endpoints necesarios para registrar a un usuario.

Agregamos un environment ahí creamos la variable API_URL y luego una petición HTTP.

Si nuestro servidor está ejecutandose con sail, tenemos la url: laravel.test, como queremos poder acceder a ella desde nuestro entorno local debemos instruirle a nuestra computadora como hacerlo.

Editamos el archivo: /etc/hosts y agregamos la siguiente línea:

127.0.0.1	laravel.test localhost

Así nuestra computadora sabrá redirigir las peticiones a las urls: laravel.test y localhost a sí misma. (A partir de ahora obviaremos el dominio, pero debe estar en todas nuestras solicitudes).

Primero queremos obtener la cookie csrf, para evitar algunos ataques, y lo haremos con la url: /sanctum/csrf-cookie

Esta nos devolverá una cookie llamada XSRF-TOKEN, que se ve más o menos así: eyJpdiI6IjVDY1NrKzN6YzNBNFJVe...MWUxNmM5IiwidGFnIjoiIn0%3D, si nos fijamos bien, la parte final está algo rara %3D, y es porque antes de enviarla en un header deberíamos decodificarla para urls con una función similar a decodeURIComponent. En nuestro caso, sabemos que %3D es =, así que lo sustituimos y agregaremos a un header llamado X-XSRF-TOKEN en cada una de nuestras llamadas siguientes. Como ahora estamos probando con Postman, entonces hacemos algo así:

Imagen de X-XSRF-TOKEN en Postman

Usuario

Seguramente utilizaremos una SPA para este proyecto en futuras iteraciones, así que dejaremos que Sanctum utilice sesiones para autenticar a los usuarios por lo que estaremos haciendo uso de cookies.

Registro

Para registrarnos utilizaremos Fortify, y aunque ya hicimos algunos pasos, nos faltó agregar el proveedor de Fortify a nuestra lista de proveedores para la app, así que hacemos eso.

También corregimos una regla de nuestras validaciones 😅

Ver código  (GitHub)

Después podemos hacer la solicitud a la url: /register. Agregamos los datos necesarios al body de la solicitud y lo enviamos.

Datos de Tim y respuesta

Si todo salió bien obtendremos una respuesta 201, y sabremos que nuestro usuario ha sido registrado en el sistema.

Logout

Probaremos el logout antes del login ya que cuando un usuario se registra, automáticamente inicia sesión. Actualizamos la ruta home en nuestra configuración.

Ver código  (GitHub)

Vamos a guardar el X-XSRF-TOKEN como una variable en Postman y luego hacemos la petición a la url: /logout. Si todo sale bien, obtendremos una respuesta 204 como esta.

Logout

Login

Ahora probamos con la ruta /login. Y podremos ver algo así.

Login

Como Sanctum utiliza las cookies para la autenticación, no debemos preocuparnos por tokens además del de csrf cuando hacemos una petición post, así que mientras usemos las cookies de sesión, estaremos loggeados.

Tarjetas de crédito

Rutas

Vamos a agregar las rutas para las tarjetas de credito en nuestra API, así que en el archivo routes/web.php colocamos lo siguiente:

Lo pondremos en web y no en api, porque consideramos que usaremos una SPA así que queremos enviar cookies, pero luego veremos como replicar esto para usar un token también.

Route::middleware(['auth:sanctum'])->prefix('api')->group(function () {
    Route::prefix('v1')->group(function () {
        Route::apiResources([
            'credit-cards' => CreditCardController::class
        ], [
            'parameters' => [
                'credit-cards' => 'creditCard'
            ],
            'shallow' => true
        ]);
    });
});

Ahí estamos agrupando dentro de un middleware las versiones, y dentro de la primera versión, agregamos un apiResource que sirve para registrar solamente las rutas que no tienen vistas asociadas.

También ponemos las opciones parameters y shallow, la primera es para cambiar el casing de los parámetros y la segunda para hacer que las rutas se colapsen cuando sea posible.

Así debería quedar nuestra tabla de rutas para credit-cards:

Método HTTP URI Nombre de ruta
GET api/v1/credit-cards credit-cards.index
POST api/v1/credit-cards credit-cards.store
GET api/v1/credit-cards/{creditCard} credit-cards.show
PUT/PATCH api/v1/credit-cards/{creditCard} credit-cards.update
DELETE api/v1/credit-cards/{creditCard} credit-cards.destroy

Políticas

Ahora registramos las políticas, porque aunque Laravel tiene registro automático, nuestras políticas no están en el folder por defecto, por lo que tenemos que decirle donde están.

Las rutas y políticas quedan así:

Ver código  (GitHub)

Traducciones

El idioma por defecto de nuestra aplicación será ✨ español ✨ pero de todas formas vamos a agregar algunos archivos de traducción ya que nos servirán para estandarizar los mensajes y nombres de atributos.

Cambiamos el idioma por defecto en config/app.php y luego publicamos los archivos de traducción.

sail artisan lang:publish

Tenemos que copiar los archivos a una nueva carpeta es_MX y entonces traducir todo… 😩

Pero bueno, aquí estamos (lo hizo chat gpt 🤷🏽).

Ver código  (GitHub)

Luego agregamos las nuevas etiquetas.

Ver código  (GitHub)

Form requests

Vamos a utilizar form requests para validar los datos que llegan y saber si debemos guardarlos en la base de datos o solicitar correcciones al usuario…

Pero esto lo dejaremos para el siguiente sprint.