skip to content

Signals & Outbound Messages

Laravel's outbound signals: events handled by listeners, broadcasts pushed to browsers through Reverb and Echo, and mail and notifications. Interviewers probe what runs inline versus queued.

on this pageshow

explore

questions

25

In Laravel 13, what steps does it take to broadcast a server-side event such as BidPlaced so that browsers receive it through Echo?

level: juniorimportance: must knowfreq 55%

answer

  1. broadcasting is opt-in
  2. install:broadcasting writes channels.php
  3. ShouldBroadcast needs broadcastOn()
  4. a queue worker sends it
  5. Echo listen or useEcho

basics

~20 s

Run php artisan install:broadcasting, implement ShouldBroadcast on the event with a broadcastOn() returning its channels, dispatch it as usual with a queue worker running, and subscribe in the browser with Echo's channel()->listen() or a useEcho hook.

solid answer

~30 s

Broadcasting is **off** in a new Laravel 13 app. `php artisan install:broadcasting` (optionally `--reverb`) publishes `config/broadcasting.php`, creates `routes/channels.php`, adds `channels:` to `withRouting()` in `bootstrap/app.php`, sets `BROADCAST_CONNECTION` in `.env` and installs `laravel-echo` with `pusher-js`. The event then implements `ShouldBroadcast` and defines `broadcastOn()`, returning a `Channel`, `PrivateChannel` or `PresenceChannel` such as `new Channel('auctions.'.$this->bid->auction_id)`. You dispatch it like any event, `BidPlaced::dispatch($bid)`; the dispatcher pushes a broadcast job, so a **queue worker** must run. In the browser, `Echo.channel('auctions.42').listen('BidPlaced', e => ...)` or, in the React, Vue and Svelte stacks, `useEcho` (private by default) or `useEchoPublic` receives the payload.

code

php · 22 lines
php
<?php

namespace App\Events;

use App\Models\Bid;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class BidPlaced implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(public Bid $bid) {}

    public function broadcastOn(): Channel
    {
        return new Channel('auctions.'.$this->bid->auction_id);
    }
}

go deeper

for a junior

Recall the four steps: install:broadcasting, ShouldBroadcast with broadcastOn(), dispatch with a worker running, and Echo or useEcho in the browser.

for a middle

Explain what install:broadcasting changes in the skeleton, how the three channel classes map to wire names, and why the queued broadcast job needs a worker.

for a senior

Diagnose a silent broadcast pipeline end to end: driver setting, queue worker, WebSocket server, channel and event names on both sides.

for a principal

Decide when realtime pushes justify running a WebSocket tier at all versus polling, and which driver fits the team's hosting and operations budget.

## What broadcasting does **Broadcasting** sends a server-side Laravel event to JavaScript running in the browser over a WebSocket, so the page updates without polling. On an auction site, when someone places a bid, every visitor watching that auction should see the new highest bid immediately. Laravel does this with three pieces: - a **broadcast driver** on the server side (Reverb, Pusher Channels, Ably, Mercure, or `log`/`null` for development and tests) - **channels**, named streams the browser subscribes to - **Laravel Echo**, the JavaScript client that subscribes to channels and receives events ## Step 1: turn it on Broadcasting is **opt-in** in the Laravel 13 skeleton: there is no `config/broadcasting.php` and no `routes/channels.php` until you run: ```bash php artisan install:broadcasting --reverb ``` The command: 1. publishes `config/broadcasting.php` 2. creates `routes/channels.php` for channel authorization callbacks 3. adds `channels: __DIR__.'/../routes/channels.php'` to `withRouting()` in `bootstrap/app.php`, which also registers the `/broadcasting/auth` route 4. writes `BROADCAST_CONNECTION` to `.env` for the chosen driver 5. installs the driver's packages and the Echo client (`laravel-echo` and `pusher-js`, plus `@laravel/echo-react` or `@laravel/echo-vue` in those stacks) Without a flag it prompts for the driver; `--pusher`, `--ably` and `--mercure` pick the others. ## Step 2: mark the event An event becomes broadcastable by implementing `Illuminate\Contracts\Broadcasting\ShouldBroadcast`. The interface requires `broadcastOn()`, which returns one channel or an array of channels: | Class | Who may subscribe | Wire name | |---|---|---| | `Channel` | anyone, no authorization | `auctions.42` | | `PrivateChannel` | authenticated and authorized users | `private-auctions.42` | | `PresenceChannel` | authorized users, who also see each other | `presence-auctions.42` | The `make:event` stub already imports these classes and contains a `broadcastOn()` method, so only `implements ShouldBroadcast` and the channel name need to be written. ## Step 3: dispatch and run a worker The event is fired like any other: `BidPlaced::dispatch($bid)` or `event(new BidPlaced($bid))`. The event dispatcher notices `ShouldBroadcast` and pushes a broadcast job onto the queue, in addition to running any ordinary listeners of the event. A **queue worker** has to pick that job up; with the skeleton's `QUEUE_CONNECTION=database` and no `queue:work` running, nothing ever reaches the browser. A WebSocket server has to be running too, for example Reverb started with `reverb:start`. ## Step 4: listen in the browser With plain Echo: - `Echo.channel('auctions.42').listen('BidPlaced', callback)` for a public channel - `Echo.private(...)` for a private channel, `Echo.join(...)` for a presence channel With the React, Vue or Svelte stacks, the `useEcho` hook subscribes when the component mounts and leaves the channel when it unmounts. Note its default: `useEcho` subscribes to a **private** channel, while `useEchoPublic` and `useEchoPresence` cover the other two kinds. The event name is the PHP class name; Echo prepends the `App.Events` namespace for you, so `'BidPlaced'` matches `App\Events\BidPlaced`. ## Checking the whole pipeline When a broadcast does not show up, walk the chain in the order the data travels: 1. `BROADCAST_CONNECTION` names a real driver such as `reverb`, not the skeleton's `log` 2. the event implements `ShouldBroadcast` and `broadcastOn()` returns the channel the page subscribes to 3. a queue worker is running and the broadcast job is not failing 4. the WebSocket server is running and reachable from the browser on the host and port Echo was built with 5. the browser's developer tools show a subscription to the exact channel name, and the listener uses the exact event name Each link can fail silently, which is why the order matters: checking the browser first tells you little if the job never left the queue. ## What arrives By default the payload is every **public property** of the event, with Eloquent models converted to arrays. Methods such as `broadcastWith()` and `broadcastAs()` reshape the payload and the name when the default is too much or too tied to the PHP class. ## Common mistakes - dispatching the event and expecting delivery with no queue worker running - subscribing with `useEcho` to a channel the server declares public, which makes Echo request authorization for a private channel that nothing is broadcast on - forgetting that `routes/channels.php` is only loaded once `install:broadcasting` has wired it in - leaving `BROADCAST_CONNECTION=log` from the skeleton and wondering why nothing reaches the socket

  • Bids are saved but no browser ever receives BidPlaced; what do you check first?
    Check that a queue worker is running, because `ShouldBroadcast` events are sent by a queued job, and that `BROADCAST_CONNECTION` is not `log` or `null`. Then confirm the WebSocket server is up and that the channel name and event name in Echo match `broadcastOn()` and the class name exactly.
  • Why does useEcho fail on a channel that the server broadcasts as a public Channel?
    `useEcho` defaults to a private subscription, so Echo subscribes to `private-auctions.42` and asks `/broadcasting/auth` to authorize it. Nothing is broadcast on that private name, and without a matching `Broadcast::channel` callback the authorization is refused. Use `useEchoPublic` for a public `Channel`.

saying these in an interview costs you the question

  • Broadcasting works in a new Laravel 13 app without running install:broadcasting.
  • ShouldBroadcast events reach the browser even when no queue worker is running.
  • Echo needs the full App\Events\BidPlaced class name in every listen() call.
  • useEcho subscribes to a public channel unless told otherwise.
  • A broadcast event cannot also have ordinary listeners.
open as a page

In Laravel 13, how do you create an event and a listener with Artisan, and how does the framework connect the listener to its event?

level: juniorimportance: must knowfreq 68%

basics

~10 s

Run make:event OrderPlaced and make:listener ReserveInventory --event=OrderPlaced. Laravel 13 discovers listeners in app/Listeners and registers each handle* or __invoke method for the event class type-hinted on its first parameter; Event::listen registers one by hand.

open as a page

In Laravel, what do a mailable's envelope(), content() and attachments() methods define, and how do you send that mailable to a customer?

level: juniorimportance: must knowfreq 62%

basics

~10 s

A mailable, generated by make:mail, splits one email into envelope() for subject and sender metadata, content() for the Blade or Markdown view, and attachments() for files; Mail::to($customer)->send(new OrderConfirmed($order)) delivers it.

open as a page

In Laravel, what does a notification class contain, how does its via() method pick delivery channels, and how do you send it?

level: juniorimportance: must knowfreq 58%

basics

~20 s

A notification, made with make:notification, is one message with a format per channel: via() returns the channel names for each recipient, and toMail(), toDatabase() or toVonage() build each version. Send it with $user->notify(...) or Notification::send($users, ...).

open as a page

What is Laravel Reverb, how does it sit between a Laravel app and Echo, and why does it speak the Pusher protocol?

level: juniorimportance: must knowfreq 38%

basics

~20 s

Reverb is Laravel's first-party WebSocket server, run as a long-lived php artisan reverb:start process. It implements the Pusher protocol, so Laravel broadcasts to it through the Pusher driver and browsers connect with Echo and pusher-js, identified by REVERB_APP_KEY.

open as a page

In Laravel broadcasting, how are private and presence channels authorized with Broadcast::channel, and what must a presence channel callback return?

level: middleimportance: must knowfreq 50%

basics

~20 s

Echo asks /broadcasting/auth before subscribing; Laravel runs the matching Broadcast::channel callback from routes/channels.php with the authenticated user and wildcard values. Private callbacks return true or false; presence callbacks return an array describing the user, or false/null to refuse.

open as a page

In Laravel, how do you queue an event listener and control its connection, queue name, delay and whether it queues at all?

level: middleimportance: must knowfreq 57%

basics

~20 s

Implement ShouldQueue on the listener (make:listener --queued). The dispatcher then pushes a job carrying the event; viaConnection, viaQueue and withDelay methods or the #[Connection], #[Queue] and #[Delay] attributes route it, and shouldQueue($event) returning false skips the listener.

open as a page

In Laravel, how do Mail::to()->send(), queue() and later() differ, and what changes when a mailable implements ShouldQueue?

level: middleimportance: must knowfreq 58%

basics

~10 s

send() delivers during the request unless the mailable implements ShouldQueue; queue() pushes a SendQueuedMailable job for a worker; later() queues it with a delay. With ShouldQueue even send() queues, and sendNow() forces synchronous delivery.

open as a page

In Laravel, when would you send an email as a notification built with MailMessage rather than as a Mailable, and how can the two be combined?

level: middleimportance: must knowfreq 50%

basics

~20 s

Use a notification when one event may reach a person over several channels and its email is a simple templated message built with MailMessage. Use a Mailable for a designed, email-only message. toMail() can return a Mailable, but then you set its recipient.

open as a page

In Laravel, what is the difference between ShouldBroadcast and ShouldBroadcastNow, and when would you pick each for an auction's bid updates?

level: middleimportance: should knowfreq 40%

basics

~20 s

ShouldBroadcast sends the event through a queued job, so a worker delivers it later and the request stays fast. ShouldBroadcastNow extends it and sends immediately inside the current process, adding the broadcaster call to the request but needing no worker.

open as a page

In a Laravel app using Echo, a bidder sees their own bid appear twice; how does broadcast()->toOthers() fix it, and what must be in place?

level: middleimportance: should knowfreq 34%

basics

~20 s

The page adds the bid from its own HTTP response and again from the broadcast. broadcast(new BidPlaced($bid))->toOthers() excludes the sender's socket, read from the X-Socket-ID header; the event needs the InteractsWithSockets trait and the request must carry that header.

open as a page

In Laravel, what is an event subscriber, how do you register one, and when would you choose it over separate listener classes?

level: middleimportance: should knowfreq 32%

basics

~20 s

An event subscriber is one class whose subscribe(Dispatcher $events) method maps several events to its own handler methods. Register it with Event::subscribe() in AppServiceProvider, or let discovery find handle*-named methods; use it when several events share one concern.

open as a page

In Laravel, what is a Markdown mailable, how do you generate one, and how do you customise its components and theme?

level: middleimportance: should knowfreq 36%

basics

~20 s

A Markdown mailable renders a Markdown template built from Laravel's mail components (message, button, panel, table) into a responsive HTML email plus a plain-text version. Generate it with make:mail --markdown; restyle it by publishing the laravel-mail components and editing the theme CSS.

open as a page

In a Laravel mailable, how would you attach an order's PDF receipt, and where should the PDF bytes come from when the mail is queued?

level: middleimportance: should knowfreq 38%

basics

~20 s

Return Attachment objects from attachments(): fromStorage() or fromPath() for a saved file, fromData(fn () => $bytes, 'receipt.pdf') for generated bytes. For queued mail, pass the order, not the PDF bytes, and build the attachment in attachments().

open as a page

In Laravel, how do you build an in-app notification bell with the database channel, and how do toDatabase(), toArray() and markAsRead() fit in?

level: middleimportance: should knowfreq 42%

basics

~20 s

Create the table with make:notifications-table, add 'database' to via() and return an array from toDatabase() or toArray(); it is stored as JSON in data. Read $user->unreadNotifications for the bell and call markAsRead() to set read_at.

open as a page

After a deploy, a Laravel Reverb server keeps running old code; why, and how do reverb:restart and a process manager fix it?

level: middleimportance: should knowfreq 28%

basics

~20 s

Reverb is a long-running process that keeps the code it booted with. reverb:restart stores a timestamp in the cache; each server notices it, disconnects clients gracefully and exits, and a process manager such as Supervisor starts it with the new code.

open as a page

When deploying Laravel Reverb behind Nginx on port 443, what is the difference between REVERB_SERVER_HOST/PORT and REVERB_HOST/PORT, and what must the proxy forward?

level: middleimportance: should knowfreq 30%

basics

~20 s

REVERB_SERVER_HOST/PORT are where the Reverb process binds, such as 0.0.0.0:8080. REVERB_HOST/PORT/SCHEME are the public address the app sends broadcasts to and Echo connects to, such as ws.example.com:443 over https. The proxy must upgrade /app WebSockets and forward /apps API calls.

open as a page

In Laravel, a BidPlaced broadcast on a public auction channel exposes each bidder's email to every viewer; why does that happen, and how do broadcastWith and broadcastAs help?

level: seniorimportance: should knowfreq 36%

basics

~20 s

By default every public property of the event is broadcast, and an Eloquent model is sent as its toArray() output, loaded relations included. On a public channel anyone can read it. broadcastWith() sends only chosen fields; broadcastAs() renames the event.

open as a page

In Laravel, an OrderPlaced event dispatched inside DB::transaction reaches a queued listener that cannot find the order; why, and how do ShouldDispatchAfterCommit and ShouldQueueAfterCommit fix it?

level: seniorimportance: should knowfreq 42%

basics

~20 s

The listener's job is pushed while the transaction is still open, so a worker can run before commit and not see the row. ShouldDispatchAfterCommit on the event holds all listeners until commit; ShouldQueueAfterCommit on one listener delays only its job.

open as a page

In a Laravel checkout, when should placing an order dispatch an event to listeners rather than call a service directly or dispatch a job?

level: seniorimportance: should knowfreq 45%

basics

~20 s

Call a service directly when checkout needs its result or must fail with it; dispatch a job for one background task checkout owns; fire an event when several independent reactions, owned elsewhere, follow the order and can each be queued.

open as a page

Your Laravel app's only mail provider had an outage and order confirmations were lost; how would you configure its mailers, including failover and roundrobin, to reduce that risk?

level: seniorimportance: should knowfreq 32%

basics

~20 s

Define each provider as a named mailer in config/mail.php, then make the default a failover mailer listing them in priority order, so a transport error moves that message to the next one. Queue the mail too, so a total outage leaves retryable jobs.

open as a page

A queued Laravel appointment-reminder notification sometimes texts patients about cancelled appointments and fails when sent inside a transaction; what is going on, and how do you fix it?

level: seniorimportance: should knowfreq 35%

basics

~20 s

A ShouldQueue notification becomes one job per recipient and channel, with via() decided at dispatch. Push it after commit with afterCommit() or ShouldQueueAfterCommit, re-check the appointment in shouldSend() inside the worker, and in Laravel 13 drop jobs whose models vanished with #[DeleteWhenMissingModels].

open as a page

A Laravel Reverb server for a collaborative whiteboard stops accepting connections at about a thousand users; what limits cause this, and how do you raise them?

level: seniorimportance: should knowfreq 22%

basics

~20 s

Each WebSocket is an open file. Reverb's default ReactPHP loop uses stream_select, capped around 1,024 files, so install ext-uv, which Reverb uses automatically. Then raise the open-file limits everywhere: ulimit/limits.conf, Supervisor's minfds and Nginx's worker_rlimit_nofile and worker_connections.

open as a page

In Laravel, how do you notify someone who has no user account, such as a new patient's phone number, and how do notifiable models route each channel?

level: middleimportance: nice to knowfreq 24%

basics

~10 s

Notification::route('mail', $email)->route('vonage', $phone)->notify(...) builds an AnonymousNotifiable holding ad-hoc addresses. Models instead answer routeNotificationForMail() or routeNotificationForVonage(). The database channel cannot be used on demand.

open as a page

How do you run Laravel Reverb on several servers behind a load balancer, and what does REVERB_SCALING_ENABLED change?

level: seniorimportance: nice to knowfreq 18%

basics

~20 s

Set REVERB_SCALING_ENABLED=true on every Reverb server, point them at one shared Redis server, and put them behind a load balancer. Each server then republishes messages it receives through Redis pub/sub, so every server delivers them to its own local connections.

open as a page