Skip to main content
Markdown

Getting started with Laravel

Live updates, chat, who is online, calls and group calls in a Laravel app, through WordPlus Cloud, with the broadcasting built into Laravel. wordplus/realtime-php brings:

  • A wordplus broadcasting driver. routes/channels.php declares your rooms and who may join them, with what each keeps and allows as a third argument, and broadcast( new OrderUpdated( $order ) ) sends to the event rooms, sealed.
  • The browser routes: POST /wordplus/realtime/auth, /calls and /keys, on the web middleware, for the session user, and /webhook.
  • @wordplusRealtime: the page settings, with the signed-in user keys and the CSRF token.
  • The Realtime facade: calls, sending to users, asking about rooms, and keys.

The page joins rooms with @wordplus/realtime (emit, emitWithAck, volatile), not Laravel Echo, and you need neither Reverb nor Pusher. What goes through WordPlus Cloud is sealed with keys only your app and the browsers of your users hold. Ready-made parts come with it, all optional: the Better Messages chat, its list of who is online and its call screen. The example app is a whole shop built this way.

Laravel can run websockets of its own. Why WordPlus Cloud says what the cloud runs for you, and what it adds.

Try it live at laravel.docs.wordplus.cloud: the example app of this guide as a demo, a shop of your own for a day, with Ben and Cara opening in windows of their own.

Two browsers side by side: Ann marks order 1042 as shipped on the staff page of the shop, and the order page of Ben shows Shipped at once, without a reload

1. Your app in WordPlus Cloud​

In your WordPlus Cloud account, go to Apps → Add app. Name it, and list the addresses its pages are served from: https://shop.example.com, and http://localhost:8000 while you develop. Only pages on those addresses may connect.

You get the app key, public because every page carries it, and its secret, for your server only. Make a local key of your own too: everything sealed is sealed with keys derived from it, and it never leaves your servers.

BROADCAST_CONNECTION=wordplus
WORDPLUS_SITE=s1500000007
WORDPLUS_SECRET=…
WORDPLUS_LOCAL_KEY=… # openssl rand -hex 32; the same on every server of the app, kept as long as the app

2. Install​

composer require wordplus/realtime-php      # PHP 8.1 or later, with sodium
npm install @wordplus/realtime

Laravel finds the package by itself. Copy the browser files to public/ in your build, so every release serves the ones its package came with:

"scripts": { "build": "wordplus-realtime copy public && vite build" }

The worker lets the tabs of a user share one connection, the room client runs group calls, and realtime-ui.min.js is the ready-made parts.

Laravel 11 and later leave out config/broadcasting.php and routes/channels.php. Add the connection, and the channels file to bootstrap/app.php, without Reverb or Echo:

// config/broadcasting.php
return [
'default' => env( 'BROADCAST_CONNECTION', 'null' ),
'connections' => [
'wordplus' => [ 'driver' => 'wordplus' ],
'log' => [ 'driver' => 'log' ],
'null' => [ 'driver' => 'null' ],
],
];
// bootstrap/app.php
->withRouting(
web: __DIR__ . '/../routes/web.php',
channels: __DIR__ . '/../routes/channels.php',
// …
)

3. Your rooms​

// routes/channels.php: the room of an order, orders.1042, for its customer, the last 20 updates kept.
Broadcast::channel( 'orders.{order}', fn ( User $user, Order $order ) => $user->id === $order->customer_id, [ 'history' => 20 ] );

Each channel is a room of the same name. A PrivateChannel( 'orders.1042' ) is the room orders.1042, and the third argument says what the room keeps and allows (rooms).

4. The page​

<head>
@wordplusRealtime
@vite( 'resources/js/app.js' )
</head>
// resources/js/app.js
import { connect } from '@wordplus/realtime';

const rt = await connect( window.wordplusRealtimeConfig );

const order = rt.join( 'orders.1042' );
order.on( 'updated', ( data ) => showStatus( data.status ) );
order.on( 'error', ( e ) => console.warn( e.code ) ); // forbidden, no_rule …

@wordplusRealtime writes window.wordplusRealtimeConfig: where to connect, the routes with the CSRF token, and the signed-in user keys, so the page connects with no request first.

5. Send​

class OrderUpdated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets;

public function __construct( public Order $order ) {}

public function broadcastOn(): array
{
return [ new PrivateChannel( 'orders.' . $this->order->id ) ];
}

public function broadcastAs(): string
{
return 'updated';
}

public function broadcastWith(): array
{
return [ 'status' => $this->order->status ];
}
}
$order->update( [ 'status' => 'shipped' ] );
broadcast( new OrderUpdated( $order ) );

Everyone in the room gets { status }, sealed with the room key on the way, and a page that reconnects later gets it from the room history.

The config​

php artisan vendor:publish --tag=wordplus-realtime copies it to config/wordplus-realtime.php.

KeyDefault
site, secret, local_keyWORDPLUS_SITE, WORDPLUS_SECRET, WORDPLUS_LOCAL_KEY
routes.prefixwordplus/realtimewhere the browser routes are
routes.middleware['web']how the routes know the user
routes.enabledtruefalse to register RealtimeController yourself
worker, rooms/realtime-worker.js, /realtime-rooms.min.jswhere your app serves the files
generationsWORDPLUS_REALTIME_GENERATIONSthe cache store for moving keys on
sealtruefalse sends everything plain
broadcastingwordplusthe connection whose routes/channels.php declares the rooms
server, apiWordPlus CloudWORDPLUS_REALTIME_SERVER, WORDPLUS_REALTIME_API

Next​