Programming Real-Time Web Applications With TALL Stack

kornatzky

Yoram Kornatzky

Posted on August 17, 2022

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()/>
Enter fullscreen mode Exit fullscreen mode

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()
    {
    }

}
Enter fullscreen mode Exit fullscreen mode

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,
      ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Using:

use App\Events\LiveBid; 

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

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',
      ];
    }  

}

Enter fullscreen mode Exit fullscreen mode

Where we have a channel per auction,

"echo:auction.{$this->auction->id},LiveBid"
Enter fullscreen mode Exit fullscreen mode

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'];
}
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

Summing up, the flow is,

Livewire -> Livewire
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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
]);  
Enter fullscreen mode Exit fullscreen mode

Summing up, the flow is,

Alpine.js -> Livewire -> Livewire -> Alpine.js
Enter fullscreen mode Exit fullscreen mode

A Recap

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

💖 💪 🙅 🚩
kornatzky
Yoram Kornatzky

Posted on August 17, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related