Laravel & Vue: Notificaciones en Tiempo Real usando Reverb, Redis y TDD - 02

Capítulo 2: Creación de Canales de Notificaciones con Reverb en Laravel

Objetivo

Implementar canales de notificación seguros y personalizados en Laravel usando Reverb, comenzando con pruebas que fallan y siguiendo el ciclo TDD de rojo → verde → refactor. Esto incluye:

1. Crear canales para transmitir notificaciones a usuarios individuales o administradores.
2. Asegurar los canales con permisos y restricciones.
3. Validar el funcionamiento mediante pruebas guiadas por errores y soluciones.

Introducción

Este capítulo utiliza TDD para implementar canales de notificaciones personalizados en Laravel con Reverb. Primero escribiremos pruebas que fallarán para identificar lo que falta en la aplicación y, a partir de esos errores, desarrollaremos el código necesario.

Nos enfocaremos en dos tipos de canales:

  • Canales privados para usuarios.
  • Un canal global para administradores.

Por ahora, usaremos el modelo User con el atributo is_admin para simplificar la diferenciación de roles.

Ciclo TDD, para Canales de Usuarios

Escribir Pruebas para Canales Privados de Usuario

Crea un archivo de prueba:

sail artisan make:test UserNotificationChannelTest

Modifica tests/Feature/UserNotificationChannelTest.php:

namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class UserNotificationChannelTest extends TestCase
{
    use RefreshDatabase;

    public function test_user_can_access_own_notification_channel()
    {
        // Given
        $user = User::factory()->create();

        // When
        $response = $this->actingAs($user)->post('/broadcasting/auth', [
            'channel_name' => 'private-user-notifications.' . $user->id,
            'socket_id' => '12345.67890', // valid socket_id format
        ], ['X-CSRF-TOKEN' => csrf_token()]);

        // Then
        $response->assertStatus(200);
    }

    public function test_user_cannot_access_other_users_notification_channel()
    {
        // Given
        $user = User::factory()->create();
        $otherUser = User::factory()->create();

        // When
        $response = $this->actingAs($user)->post('/broadcasting/auth', [
            'channel_name' => 'private-user-notifications.' . $otherUser->id,
            'socket_id' => '12345.67890', // valid socket_id format
        ], ['X-CSRF-TOKEN' => csrf_token()]);

        // Then
        $response->assertStatus(403);
    }
}

Nota: El socket_id es un identificador único generado por el cliente (normalmente al establecer una conexión websocket) y debe seguir un formato específico esperado por Pusher. Un socket_id válido debe estar en el formato de digits.dot.digits como 1234.5678. Al usar Pusher durante los tests, es esencial proporcionar un socket_id válido para evitar errores y permitir la correcta autorización de los canales.

Ejecutar las Pruebas Iniciales (Rojo)

Ejecuta los tests:

sail artisan test --filter=UserNotificationChannelTest

Errores esperados:

  • Error 404: La ruta /broadcasting/auth no está definida.

Análisis del error: El error 404 indica que falta la ruta para la autenticación de canales. Este error nos guía a implementar la configuración básica de broadcasting.

Configurar la Autenticación de Canales

1. Crear el Proveedor de servicios Broadcast
sail artisan make:provider BroadcastServiceProvider
2. Habilitar el BroadcastServiceProvider:

Asegúrate de que el servicio esté registrado en bootstrap/providers.php:

<?php

return [
    App\Providers\AppServiceProvider::class,
    //...
    App\Providers\BroadcastServiceProvider::class,
];
3. Definir la Ruta de Broadcasting:

Modifica el método boot del BroadcastServiceProvider:

namespace App\Providers;

use Illuminate\Support\Facades\Broadcast;
use Illuminate\Foundation\Support\Providers\BroadcastServiceProvider as ServiceProvider;

class BroadcastServiceProvider extends ServiceProvider
{
   public function boot()
   {
       Broadcast::routes(); // Registra la ruta '/broadcasting/auth'

       require base_path('routes/channels.php'); // Define los canales
   }
}
4. Limpiar la caché:

Después de modificar los archivos de configuración, es importante limpiar la caché de Laravel para aplicar los cambios:

sail artisan config:clear
5. Ejecutar el Servidor de Reverb:

Inicia el servidor:

sail artisan reverb:start --host="0.0.0.0" --port=8080 --hostname="localhost"

Ejecutar pruebas para Canales Privados de Usuario (Rojo)

Corre los tests nuevamente. Ahora fallarán con un error 403, porque los canales no están definidos aún:

sail artisan test --filter=UserNotificationChannelTest

Implementar los Canales Privados (Verde)

Define los canales en routes/channels.php:

Broadcast::channel('user-notifications.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

Corre los tests nuevamente. Ambos deberían pasar:

sail artisan test --filter=UserNotificationChannelTest

Refactorización

Revisa la organización de las rutas y el código del canal. Si más canales se agregan en el futuro, podrías crear un archivo dedicado para ellos.

Repetir el Ciclo TDD, para Canales de Administradores

Escribir Pruebas para el Canal de Administradores

Crea un archivo de prueba:

sail artisan make:test AdminNotificationChannelTest

Modifica tests/Feature/AdminNotificationChannelTest.php:

namespace Tests\Feature;

use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class AdminNotificationChannelTest extends TestCase
{
    use RefreshDatabase;

    public function test_admin_can_access_admin_notification_channel()
    {
        // Given
        $admin = User::factory()->state(['is_admin' => true])->create();

        // When
        $response = $this->actingAs($admin)->post('/broadcasting/auth', [
            'channel_name' => 'admin-notifications',
            'socket_id' => '12345.67890', // valid socket_id format
        ], ['X-CSRF-TOKEN' => csrf_token()]);

        // Then
        $response->assertStatus(200);
    }

    public function test_regular_user_cannot_access_admin_notification_channel()
    {
        // Given
        $user = User::factory()->state(['is_admin' => false])->create();

        // When
        $response = $this->actingAs($user)->post('/broadcasting/auth', [
            'channel_name' => 'admin-notifications',
            'socket_id' => '12345.67890', // valid socket_id format
        ], ['X-CSRF-TOKEN' => csrf_token()]);

        // Then
        $response->assertStatus(403);
    }
}

Ejecutar las Pruebas para Administradores (Rojo)

Ejecuta los tests. Ambos fallarán, ya que el canal de administradores no está definido.

sail artisan test --filter=AdminNotificationChannelTest

Implementar el Canal de Administradores

Agrega el canal en routes/channels.php:

Broadcast::channel('admin-notifications', function ($user) {
    return $user->is_admin;
});

Ejecutar las Pruebas para Administradores (Rojo)

Ejecuta los tests. Ambos fallarán, ya que el atributo is_admin no existe en el modelo User.

sail artisan test --filter=AdminNotificationChannelTest

Añade el atributo faltante al modelo User

1. Crear la Migración

Ejecuta el siguiente comando para crear una nueva migración:

sail artisan make:migration add_is_admin_to_users_table --table=users
2. Modificar la Migración

Abre la migración creada en database/migrations/ y añade el campo is_admin:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration {
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->boolean('is_admin')->default(false); // Añadir campo is_admin
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::table('users', function (Blueprint $table) {
            $table->dropColumn('is_admin'); // Eliminar campo is_admin
        });
    }
};
3. Actualizar el Modelo User

Ahora, actualiza el modelo User (app/Models/User.php) para que is_admin sea tratado como un booleano:

protected $casts = [
    'email_verified_at' => 'datetime',  
	'password' => 'hashed',
    'is_admin' => 'boolean',
];
4. Ejecutar la Migración

Ejecuta la migración para aplicar los cambios en la base de datos:

sail artisan migrate

Ejecutar las Pruebas para Administradores (Verde)

sail artisan test --filter=AdminNotificationChannelTest

Ahora corre este comando y verás como todos los tests pasan:

sail artisan test

Refactorizar (Si es Necesario)

Revisa el archivo routes/channels.php. Si los canales crecen, considera agruparlos en un archivo dedicado para mejorar la organización.

Conclusión

En este capítulo, implementamos canales seguros para usuarios y administradores siguiendo el ciclo TDD. Los errores 404 y 403 nos guiaron a desarrollar las rutas y los permisos necesarios para cada canal.

El próximo capítulo, configuraremos Redis para almacenar y administrar notificaciones, optimizando el rendimiento y gestionando mensajes no leídos. 🚀

José Rafael Gutierrez

Soy un desarrollador web con más de 14 años de experiencia, especializado en la creación de sistemas a medida. Apasionado por la tecnología, la ciencia, y la lectura, disfruto resolviendo problemas de...

Suscríbete para Actualizaciones

Proporcione su correo electrónico para recibir notificaciones sobre nuevas publicaciones o actualizaciones.