Con este post continuamos con las guías sobre Laravel, específicamente las del tema de notificaciones. En esta ocasión utilizaremos el servicio Pusher para la interaccion con notificaciones en tiempo real.

¿Qué es Pusher?

De acuerdo a la documentación oficial, Pusher es una API simple para integrar de forma fácil, rápida y segura el envío de datos en tiempo real de forma bidireccional via Websockets para aplicaciones web y móviles o cualquier otro dispositivo conectado a Internet.
En otras palabras: mediante el uso de Websockets y la API de pusher puedes disparar eventos que envían y reciben información en tiempo real entre aplicaciones web y/o móviles.
Para realizar esta práctica debes ir al sitio https://pusher.com y registrarte, una vez dentro debes poder visualizar un panel como el siguiente:

Paso #1: Crear la aplicación:

Como pueden observar he definido un nombre a mi app, así como también un cluster. Además he seleccionado vanilla JS para el frontend y Laravel para el backend.
Paso #2: Una vez que nuestra app ha sido creada, debemos visualizar la siguiente pantalla:

El panel izquierdo (rojo) contiene el código que deberán desplegar en la vista para que su cliente frontend pueda conectarse a la app que han registrado previamente en Pusher.
2.1 Objeto Pusher, canales y eventos:
  • Objeto Pusher: Es la instancia mediante la cuál se conectarán a la app registrada en Pusher, se requiere de una clave (APP_KEY) la cuál pueden obtener en la pestaña “APP KEYS” en el dashboard de su app.
  • Canales: Es el medio por el cuál se establece la comunicación entre su app en Pusher y su página web.
  • Eventos: De manera similar a JS un evento es aquél que disparará una acción en nuestra página web. Los eventos deben ser “atados” a los canales, un canal puede tener 1 o más eventos y cada vez que atemos un evento a un canal también debemos definir un callback; en el ejemplo mostrado arriba la información recibida se muestra en un alert tal como se definio en el callback.
Paso #3: Cargar la dependencia para el envío de notificaciones desde el servidor:
Para nuestro ejercicio no usaremos la dependencia proveída en la imagen del paso #2 panel derecho, sino que utilizaremos otra librería (también llamadas wrapper o bridge) que pusher nos provee para Laravel:
composer require pusher/pusher-http-laravel
Una vez instalada procedemos a realizar la configuración requerida para poder enviar notificaciones, para ello definimos los siguientes valores en nuestro archivo .env
PUSHER_APP_ID=YOUR_APP_ID
PUSHER_APP_KEY=YOUR_APP_KEY
PUSHER_APP_SECRET=YOUR_APP_SECRET
PUSHER_APP_CLUSTER=YOUR_APP_CLUSTER
Paso #4: Cargar nuestra vista de notificaciones, para ello vamos a simular que estamos recibiendo ordenes de comida en un dashboard personalizado y que además tenemos la opción de cancelar como se muestra a continuación:

 

4.1: Iniciaremos definiendo la instancia a nuestra pusher app en nuestra vista con el siguiente código:
    // Enable pusher logging - don't include this in production
    Pusher.logToConsole = true;

    let pusher = new Pusher('{{env('PUSHER_APP_KEY')}}', {
        cluster: '{{env('PUSHER_APP_CLUSTER')}}',
        encrypted: true,
    });
4.2: Después definiremos el canal a utilizar y los 2 eventos que escuchan las notificaciones, uno para recibir el nuevo pedido, y otro para cancelar un pedido previamente registrado mediante el siguiente código:
    let channel = pusher.subscribe('orders-channel');
    channel.bind('new-order-event', nuevoPedido);
    channel.bind('cancel-order-event', cancelarPedido);
Puedes atar N eventos a 1 solo canal, el primer parámetro es el nombre del evento, el segundo parámetro es el callback a ejecutar una vez que  los eventos son recibidos y se describen a continuación:
    function nuevoPedido(data) {
        let table = document.getElementById("orders");
        let row = table.insertRow(1);
        row.insertCell(0).innerHTML = `# ${data.orden}`;
        row.insertCell(1).innerHTML = data.producto;
        row.insertCell(2).innerHTML = data.cantidad;
        row.insertCell(3).innerHTML = data.cliente;
        row.insertCell(4).innerHTML = `$ ${data.precio} MXN`;
        row.insertCell(5).innerHTML = `<button id="orden-${data.orden}" data-id="${data.orden}" class="btn-cancelar">Cancelar pedido</button>`;
    }

    function cancelarPedido(data) {
        document.querySelector(`#orden-${data.orden}`).parentNode.innerHTML = 'PEDIDO ELIMINADO';
    }
  • La función nuevoPedido() se encarga de recibir los datos de la orden y colocarlos en una nueva fila en la tabla.
  • La función cancelarPedido() recibe la orden cancelada y sustituye el botón CANCELAR PEDIDO de dicha orden por la leyenda PEDIDO ELIMINADO.
4.3 Disparando eventos: La ejecución de los eventos los realizaremos a través de los botones NUEVO PEDIDO y CANCELAR PEDIDO. Para fines prácticos los estamos ejecutando desde nuestra misma vista mediante peticiones AJAX, pero el método o forma de ejecución depende de la aplicación que estés creando, el código es el siguiente:
    document.querySelector('.btn-ordenar').addEventListener('click', ordenar);
    document.querySelector('#orders').addEventListener('click', cancelar);

    function ordenar() {
        axios
            .get('/nuevo-pedido')
            .then((res) => {
                console.log(res);
            });
    }

    function cancelar(e) {
        let id = e.target.dataset.id;
        axios
            .get(`/cancelar-pedido/${id}`)
            .then((res) => {
                console.log(res);
            });
    }
4.4 HTML:
<!doctype html>
<html lang="es">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
    <title>Pedidos</title>
</head>
<body>
<div class="container">
    <div class="page-header">
        <h1>Nuevos Pedidos</h1>
    </div>
</div>
<div class="container">
    <div class="row">
        <div class="col-sm-2 col-sm-offset-1 text-center">
            <button class="btn btn-success btn-ordenar">Nuevo pedido</button>
        </div>
        <div class="col-sm-8">
            <table id="orders" class="table table-striped table-hover table-responsive table-bordered">
                <thead>
                <tr>
                    <th>Orden</th>
                    <th>Producto</th>
                    <th>Cantidad</th>
                    <th>Cliente</th>
                    <th>Precio</th>
                    <th>Acciones</th>
                </tr>
                </thead>
            </table>
        </div>
    </div>
</div>

<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.17.1/axios.min.js"></script>
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
<script>
    // Enable pusher logging - don't include this in production
    Pusher.logToConsole = true;

    let pusher = new Pusher('{{env('PUSHER_APP_KEY')}}', {
        cluster: '{{env('PUSHER_APP_CLUSTER')}}',
        encrypted: true,
    });

    let channel = pusher.subscribe('orders-channel');
    channel.bind('new-order-event', nuevoPedido);
    channel.bind('cancel-order-event', cancelarPedido);

    document.querySelector('.btn-ordenar').addEventListener('click', ordenar);
    document.querySelector('#orders').addEventListener('click', cancelar);

    function nuevoPedido(data) {
        let table = document.getElementById("orders");
        let row = table.insertRow(1);
        row.insertCell(0).innerHTML = `# ${data.orden}`;
        row.insertCell(1).innerHTML = data.producto;
        row.insertCell(2).innerHTML = data.cantidad;
        row.insertCell(3).innerHTML = data.cliente;
        row.insertCell(4).innerHTML = `$ ${data.precio} MXN`;
        row.insertCell(5).innerHTML = `<button id="orden-${data.orden}" data-id="${data.orden}" class="btn btn-danger btn-cancelar">Cancelar pedido</button>`;
    }

    function cancelarPedido(data) {
        document.querySelector(`#orden-${data.orden}`).parentNode.innerHTML = 'PEDIDO ELIMINADO';
    }

    function ordenar() {
        axios
            .get('/nuevo-pedido')
            .then((res) => {
                console.log(res);
            });
    }

    function cancelar(e) {
        let id = e.target.dataset.id;
        axios
            .get(`/cancelar-pedido/${id}`)
            .then((res) => {
                console.log(res);
            });
    }
</script>
</body>
</html>
Paso #5: Definiendo Endpoints y ejecución de notificaciones
5.1 Nueva orden:
Route::get('/nuevo-pedido', function (Faker $faker) {
    $pedido = [
        'orden' => $faker->randomNumber(4),
        'producto' => $faker->randomElement(['Pizza', 'Hamburguesa', 'Tacos']),
        'cantidad' => $faker->randomNumber(1),
        'cliente' => $faker->name() . ' ' . $faker->lastName(),
        'precio' => $faker->randomNumber(2) . '.' . $faker->randomNumber(2)
    ];

    Pusher::trigger('orders-channel', 'new-order-event', $pedido);
});
Aprovechándonos de la librería Faker generamos ordenes aleatorias, y mediante el método trigger() del Facade Pusher es como enviaremos los datos al servicio para que este los reenvié a todos las instancias que están escuchando simultaneamente en el canal y evento especificados. Como pueden notar el primer parámetro es el nombre del canal, el segundo es el nombre del evento y el tercero los datos.
5.2 Cancelar orden:
Route::get('/cancelar-pedido/{orden}', function ($orden) {
    $pedido = [
        'orden' => $orden
    ];

    Pusher::trigger('orders-channel', 'cancel-order-event', $pedido);
});
Repetimos la misma operación solo que ahora enviando el número de orden que debe ser cambiado al estado CANCELADO.
Con este sencillo ejemplo pueden darse una idea del potencial que tiene Pusher y el uso que puedan darle dentro de sus aplicaciones, desde dashboards de métricas hasta generación de reportes en tiempo real. Si tienen alguna aclaración o duda escribanla en la caja de comentarios!

2 comments. Leave new

Hay alguna forma de hacer un tuto de notificaciones para chats y calendarios? Gracias.

En la mismas guías de Pusher viene como hacer un chat con Laravel y JS, sobre calendarios me imagino que es muy similar, usar alguna librería JS y guardar los eventos en BD para que al cargar el calendario te aparezcan los que ya se guardaron y los nuevos eventos escucharlos mediante JS y pusher para irlos marcando en tiempo real.

Leave a Reply