PHP
Completed
-
Section 1: Getting Started
-
Section 2: Core Syntax
-
Section 3: Functions
-
Section 4: Object-Oriented PHP
-
Section 5: Working with Data
-
Section 6: Modern PHP (PHP 8)
-
Section 7: Working with Files and Networking
-
Section 8: Common PHP Frameworks Overview
-
Section 9: Tooling and Ecosystem
-
Section 10: Practical Projects
-
Section 11: Interview Practice
-
Section 12: More Standard Library
-
Section 13: Data Structures and Algorithms in PHP
-
Section 14: More Practice Exercises
-
Section 15: More Security and Best Practices
-
Section 16: More Testing and Tooling
-
Section 17: WordPress-Style CMS Concepts
-
Section 18: Advanced OOP Practice
-
Section 19: More Web Fundamentals
-
Section 20: Database Practice
-
Section 21: PHP Manual: Array Functions
-
Section 22: PHP Manual: Date and Calendar Functions
-
Section 23: PHP Manual: Filesystem and Directory Functions
-
Section 24: PHP Manual: Filter and Var Handling
-
Section 25: PHP Manual: Math Functions
-
Section 26: PHP Manual: JSON and XML
-
Section 27: PHP Manual: Network and Stream Functions
-
Section 28: PHP Manual: Error and Exception Handling
-
Section 29: PHP Manual: Output Control and Misc
-
Section 30: PHP Manual: FTP, Zip, and Mail
-
Section 31: Modern PHP Frameworks Deep Dive
-
Section 32: PHP Design Patterns
-
Section 33: More Practice Exercises
-
Section 34: PHP Performance and Deployment
-
Section 35: More Interview Practice
-
Section 36: More PHP Standard Library
-
Section 37: PHP Concurrency and Async
-
Section 38: More Web Development Practice
-
Section 39: PHP Testing Deep Dive
-
Section 40: Composer and Package Development
-
Section 41: PHP Security Deep Dive
-
Section 42: More Practical Projects
-
Section 43: Legacy PHP Maintenance
-
Section 44: More Algorithm Practice
-
Section 45: Final Practice and Review
-
Section 46: PHP for E-Commerce Patterns
-
Section 47: PHP API Design Deep Dive
-
Section 48: PHP Caching Strategies
-
Section 49: PHP Queue and Background Jobs
-
Section 50: PHP Multi-Tenancy Patterns
-
Section 51: PHP Real-Time Features
-
Section 52: PHP CMS and Content Modeling
-
Section 53: PHP Internationalization
-
Section 54: More Framework-Specific Practice
-
Section 55: PHP Legacy Code Refactoring
-
Section 56: More Practice Projects Round 2
-
Section 57: PHP Command-Line Applications
-
Section 58: PHP and Microservices
-
Section 59: More Interview and Review Round 2
216: Laravel Broadcasting and Echo
A few years back, I was working on a logistics dashboard for a client. We had this "Order Status" screen where dispatchers monitored shipments. The problem was that the dispatchers were obsessively hitting F5 every ten seconds because they were terrified of missing a "Delayed" or "Cancelled" update. It was a nightmare for the database—hundreds of redundant requests per minute—and a frustrating experience for the users. I remember telling the lead dev, "We're treating the web like it's 1998." That was the day we ripped out the polling logic and implemented broadcasting. The moment we saw the status badge flip from 'Processing' to 'Shipped' in real-time without a page refresh, the entire mood in the room changed. That's the power of moving from a "Pull" architecture to a "Push" architecture.
Turning the Server into a Broadcaster
In a standard Laravel app, your server is reactive; it waits for a request and then sends a response. Broadcasting flips this. It allows your server to push a message to a client-side listener via a WebSocket. To make this happen, you don't just fire a standard event; you create an event that implements the ShouldBroadcast interface. When Laravel sees that interface, it knows that after the event is handled internally, it also needs to be pushed out to a broadcasting driver—like Pusher, Soketi, or Laravel Reverb.
The most important thing to remember here is that broadcasting is almost always handled by your queue. You don't want your user's HTTP request to hang while your server is trying to talk to a WebSocket server. I've seen developers wonder why their real-time updates aren't appearing, only to realize their queue:work process wasn't running. If you aren't using a queue, you're blocking your application's execution flow, which defeats the purpose of a snappy, real-time UI.
namespace App\Events;
use App\Models\Order;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class OrderStatusUpdated implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
public function __construct(public Order $order) {}
public function broadcastOn(): array
{
// We use a private channel so only the customer/admin can see this order
return [
new PrivateChannel('orders.' . $this->order->id),
];
}
public function broadcastWith(): array
{
// Only send the data the frontend actually needs
return [
'status' => $this->order->status,
'updated_at' => $this->order->updated_at->toDateTimeString(),
];
}
}
Listening with Laravel Echo
Now that the server is shouting into the void, you need something on the frontend to actually listen. That's where Laravel Echo comes in. Echo isn't the WebSocket server itself; it's a JavaScript wrapper that makes subscribing to channels incredibly intuitive. Instead of dealing with raw WebSocket frames, you use a fluent API to listen for specific events.
You'll encounter three main types of channels. Public channels are for everyone (like a live sports score). Private channels require authentication—which Laravel handles via a channels.php route file—ensuring that User A can't listen to User B's private order updates. Then there are Presence channels, which are essentially private channels that also let you know who else is currently subscribed (perfect for that "User is typing..." indicator in a chat app).
import Echo from 'laravel-echo';
import Pusher from 'pusher-js';
window.Pusher = Pusher;
window.Echo = new Echo({
broadcaster: 'pusher',
key: import.meta.env.VITE_PUSHER_APP_KEY,
cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER,
forceTLS: true
});
window.Echo.private(`orders.${orderId}`)
.listen('OrderStatusUpdated', (e) => {
console.log('Order status changed to: ' + e.status);
// Here you'd update your DOM or trigger a toast notification
updateOrderStatusUI(e.status);
});
Securing the Stream
One common mistake I see is developers leaving their channels public because they find the authorization step tedious. If you use a PrivateChannel, you must define the authorization logic in routes/channels.php. This is essentially a middleware for your WebSockets. If the callback returns true, the connection is allowed; if it returns false, the user is blocked.
I always recommend being as strict as possible here. Check not only if the user is authenticated, but if they actually own the resource they are trying to listen to. It's an easy vector for data leaks if you simply check Auth::check() without verifying the resource ID.
Broadcast::channel('orders.{orderId}', function ($user, $orderId) {
return (int) $user->id === (int) \App\Models\Order::find($orderId)->user_id;
});
📋 Practical Task
Exercise: Building a Real-Time Order Status Tracker
Your goal is to implement a real-time update system for a fictional e-commerce store. You will need to build the backend event and the frontend listener to ensure the user sees their order status change without refreshing the page.
- The Event: Create a
ShipmentTrackedevent that implementsShouldBroadcast. This event should accept aShipmentmodel and broadcast on aPrivateChannelnamedshipments.{shipmentId}. - The Authorization: In
routes/channels.php, write the authorization logic to ensure that only the user who owns the shipment can subscribe to that specific channel. - The Payload: Override the
broadcastWithmethod in your event to send only thecurrent_locationand theestimated_arrivalproperties of the shipment. - The Frontend: Write a Laravel Echo script that subscribes to the private shipment channel and logs the
current_locationto the browser console whenever theShipmentTrackedevent is received.
There are no comments for now.