Technology

Programming Real-Time Web Applications With TALL Stack

#Real-Time Web Applications

Real-time web applications reflect immediately to each user the concurrent actions of other users.

Widely used real-time applications include:

  • Auctions
  • Chat systems
  • Shared whiteboards
  • Collaborative document editors
  • Online education

#The TALL Stack

The TALL stack is a prominent combined front-end and backend framework in the PHP world.

The stack:

  1. Laravel
  2. Alpine.js
  3. Livewire
  4. Tailwind CSS

#The Triggering Action

Action in the Blade template of a Livewire component is triggered on a button click,

<input name="bid_price" wire:model="bid_price"/>
<button wire:click="bid()/>

Which invokes a function in the component,

namespace App\Http\Livewire;

use Livewire\Component;

class Bidder extends Component
{
    public float $bid_price;
    
    public float $price;
    
    public function bid()
    {
    }
    
}

#The Connecting Pipes

By broadcasting events in Laravel, the actions of users are propagated to other users.

So say we have an event that is broadcast when a user sends a bid,

namespace App\Events;

use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

use App\Models\Auction;
use App\Models\User;

class LiveBid implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;\
    
    public function __construct(
       private Auction $auction,
       private $item,
       private $user,
       private float $bid_price,
    )
    {
        //
    }

    /**
     * Get the data to broadcast.
     *
     * @return array
     */
    public function broadcastWith()
    {
        return [
            'item' => $this->item,
            'type' => $this->type,
            'bid' => $this->bid_price,
            'user' => $this->user,
        ];
    }
}

Using:

use App\Events\LiveBid; 

broadcast(new LiveBid(
	$this->auction, 
	$this->item, 
	$this->user, 
	$bid_price
))->toOthers();	 

#How the Events Flow

Using Laravel Websockets, broadcast events are sent to Livewire components.

Components list the events they listen to,

namespace App\Http\Livewire;

use Livewire\Component;

class Bidder extends Component
{
    public float $price;
    
    protected function getListeners()
    {
        
			return [
				"echo:auction.{$this->auction->id},LiveBid" => 'live_price_notification',
			];
		}  

}

Where we have a channel per auction,

"echo:auction.{$this->auction->id},LiveBid"

with many auctions running in parallel.

On receiving the event, the listener calls the assigned function with the data that was broadcast as an array,

public function live_price_notification($data)
{
	    // update price
    $this->price = $data['price'];
}

#Dynamic Update to the Front-End

So for the Bidder component, its Blade template, bidder.blade.php is automatically dynamically updated by Livewire once the price property is updated,

Price: {{ $price }} EUR

Summing up, the flow is,

Livewire -> Livewire

Where no JavaScript was needed.

This is the more straightforward case.

#When We Need JavaScript

In a whiteboard application, we must use JavaScript to listen to user actions, such as moving the mouse, as they are purely browser events. Consequently, to reflect the actions of other users, we must render things at the browser level, such as pointer positions on the screen, for which we must use JavaScript.

The TALL stack uses the lightweight Alpine.js framework to do the browser lifting.

To integrate the actions from the browser into the Livewire component, we use Alpine.js Livewire.emit,

Livewire.emit('MouseMoved', x, y)

To dynamically update the front-end, say the position on the canvas, we use in the Livewire component,

$this->dispatchBrowserEvent('SetPointerPosition', [
	'x' => $x, 'y' => $y
]);                 

Summing up, the flow is,

Alpine.js -> Livewire -> Livewire -> Alpine.js

#A Recap

For PHP lovers, the TALL stack supplies a superb way to program real-time web applications with a pure PHP playbook.

Yoram Kornatzky

Yoram Kornatzky