Especialização em Backend com PHP & Laravel • Laravel 11 & PHP 8.3+ • Eloquent ORM, Sanctum, Filas, Horizon e APIs Corporativas


🗺️ Mapa Conceitual do Tópico

flowchart TD
    A["Ação no Backend (ex: Pedido atualizado)"] --> B["Event implements ShouldBroadcast"]
    B --> C["Fila de Broadcast"]
    C --> D["Servidor Reverb (WebSocket)"]
    D --> E["Laravel Echo no Cliente"]

    subgraph AUTH["Autorização de Canal"]
        F["routes/channels.php"]
    end

    D -.-> AUTH
    E --> G["UI atualizada em tempo real"]

    style A fill:#e1f5fe,stroke:#03a9f4,stroke-width:2px
    style B fill:#e8f5e9,stroke:#4caf50,stroke-width:2px
    style C fill:#fff3e0,stroke:#ff9800,stroke-width:2px
    style D fill:#ede7f6,stroke:#7e57c2,stroke-width:2px
    style E fill:#fce4ec,stroke:#e91e63,stroke-width:2px
    style F fill:#f3e5f5,stroke:#9c27b0,stroke-width:2px

🏛️ 1. Do Polling ao WebSocket: Por que Broadcasting

Antes do broadcasting, a única forma de o frontend “saber” que algo mudou no backend era fazer polling — requisições HTTP repetidas em intervalo fixo, desperdiçando banda e adicionando latência perceptível. O Laravel Reverb é o servidor de WebSockets oficial do ecossistema, escrito em PHP assíncrono, que mantém conexões abertas e permite o backend empurrar (push) eventos para o cliente no instante em que ocorrem — sem depender de serviços pagos de terceiros como Pusher.

O fluxo é sempre o mesmo: um Evento de domínio implementa ShouldBroadcast, é despachado para a fila (broadcasting é assíncrono por padrão), e o worker publica a mensagem no canal correspondente do Reverb, que a repassa a todos os clientes conectados via Laravel Echo.

<?php
namespace App\Events;

use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Queue\SerializesModels;
use App\Models\Pedido;

class StatusPedidoAtualizadoEvent implements ShouldBroadcast
{
    use SerializesModels;

    public function __construct(public Pedido $pedido) {}

    public function broadcastOn(): array
    {
        return [new PrivateChannel('pedidos.' . $this->pedido->user_id)];
    }

    public function broadcastAs(): string
    {
        return 'status.atualizado';
    }
}

🔒 2. Tipos de Canal e Autorização

Existem três tipos de canal: público (Channel, sem autenticação, qualquer um escuta), privado (PrivateChannel, exige que o usuário autenticado seja autorizado) e de presença (PresenceChannel, privado + lista de quem está online). A autorização de canais privados e de presença é declarada em routes/channels.php e roda a cada tentativa de conexão do cliente àquele canal:

<?php
use Illuminate\Support\Facades\Broadcast;

// Canal privado: só o dono do pedido pode escutar suas atualizações.
Broadcast::channel('pedidos.{userId}', function ($user, $userId) {
    return (int) $user->id === (int) $userId;
});

// Canal de presença: retorna os dados do usuário que ficam visíveis a todos os presentes.
Broadcast::channel('chat.{salaId}', function ($user, $salaId) {
    return ['id' => $user->id, 'name' => $user->name];
});

Se o callback retornar false ou null, o Reverb recusa a conexão com HTTP 403 — isso é essencial, pois um canal privado mal configurado pode vazar dados de um usuário para outro.


⚙️ 3. Configurando e Subindo o Servidor Reverb

// config/reverb.php
return [
    'servers' => [
        'reverb' => [
            'host' => env('REVERB_SERVER_HOST', '0.0.0.0'),
            'port' => env('REVERB_SERVER_PORT', 8080),
            'hostname' => env('REVERB_HOST'),
        ],
    ],
];
php artisan reverb:start --debug

O Reverb é um processo de longa duração (não um request PHP-FPM tradicional), projetado para lidar com dezenas de milhares de conexões simultâneas usando um loop de eventos assíncrono. Em produção, ele roda atrás de um proxy reverso (Nginx/Caddy) com suporte a upgrade de protocolo HTTP → WebSocket, e normalmente é supervisionado por um processo como o Supervisor ou Horizon (para a fila de broadcast).

Gotcha comum: esquecer que o broadcast por padrão é assíncrono — se QUEUE_CONNECTION não estiver configurada corretamente ou nenhum worker estiver rodando (php artisan queue:work), os eventos ficam presos na fila e nunca chegam ao cliente, mesmo que o código pareça correto.