فصل ۴: مفاهیم پایه Broadcasting و Events

در این فصل، به بررسی سیستم broadcasting لاراول و نحوه استفاده از آن با Laravel Reverb در نسخه ۱۲ می‌پردازیم. ابتدا مروری بر مفاهیم پایه events، listeners و queues خواهیم داشت، سپس نحوه ایجاد events قابل broadcast با استفاده از رابط ShouldBroadcast را توضیح می‌دهیم. در ادامه، کانال‌های عمومی و خصوصی را معرفی کرده و یک مثال عملی برای ارسال پیام‌های اولیه از backend به frontend ارائه می‌کنیم. تمام کدها و مثال‌ها بر اساس مستندات رسمی لاراول ۱۲.x تنظیم شده‌اند.

مروری بر سیستم Broadcasting لاراول

سیستم broadcasting لاراول امکان ارسال رویدادها (events) به کلاینت‌ها را به‌صورت real-time فراهم می‌کند. این سیستم با ابزارهایی مانند Reverb، Pusher یا Redis ادغام می‌شود تا داده‌ها را از سرور به کلاینت‌های متصل (مانند مرورگرها) ارسال کند.

اجزای اصلی سیستم Broadcasting

  1. Events: رویدادها اشیایی هستند که یک اتفاق خاص (مانند ارسال پیام یا تغییر وضعیت) را توصیف می‌کنند. در لاراول، events با استفاده از کلاس‌های PHP تعریف می‌شوند.
  2. Listeners: شنونده‌ها (listeners) منطقی را اجرا می‌کنند که به یک event خاص پاسخ می‌دهند. برای broadcasting، نیازی به listener جداگانه نیست، زیرا event مستقیماً به کلاینت‌ها ارسال می‌شود.
  3. Queues: برای بهبود عملکرد، events می‌توانند در یک queue (مانند Redis یا database) قرار گیرند تا به‌صورت غیرهمزمان پردازش شوند. این برای اپلیکیشن‌های با بار بالا مفید است.
  4. Broadcast Drivers: لاراول از درایورهای مختلفی مانند Reverb، Pusher یا Redis برای ارسال رویدادها پشتیبانی می‌کند. در اینجا، ما از Reverb استفاده می‌کنیم.

جریان کاری Broadcasting

  1. یک event تعریف می‌شود که رابط ShouldBroadcast را پیاده‌سازی می‌کند.
  2. این event به یک کانال (public، private یا presence) ارسال می‌شود.
  3. سرور Reverb پیام را از طریق WebSocket به کلاینت‌های متصل به کانال مربوطه منتقل می‌کند.
  4. کلاینت‌ها (معمولاً با Laravel Echo) پیام را دریافت کرده و نمایش می‌دهند.

ایجاد Events قابل Broadcast با ShouldBroadcast Interface

برای اینکه یک event بتواند به‌صورت real-time پخش (broadcast) شود، باید رابط ShouldBroadcast را پیاده‌سازی کند. این رابط سه متد اصلی را الزامی می‌کند:

  • broadcastOn(): کانال‌هایی که event روی آن‌ها پخش می‌شود را مشخص می‌کند.
  • broadcastAs() (اختیاری): نام رویداد در سمت کلاینت را تعریف می‌کند.
  • broadcastWith() (اختیاری): داده‌های اضافی برای ارسال را مشخص می‌کند.

مثال: ایجاد یک Event ساده

یک event برای ارسال پیام به یک کانال عمومی ایجاد می‌کنیم:

php artisan make:event BroadcastMessage

فایل app/Events/BroadcastMessage.php را به این صورت ویرایش کنید:

<?php

namespace App\Events;

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

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

    public $message;

    public function __construct($message)
    {
        $this->message = $message;
    }

    public function broadcastOn()
    {
        return new Channel('public-channel');
    }

    public function broadcastAs()
    {
        return 'message.broadcast';
    }

    public function broadcastWith()
    {
        return ['message' => $this->message, 'timestamp' => now()->toDateTimeString()];
    }
}

توضیحات:

  • Constructor: پیام ورودی را به‌عنوان یک ویژگی عمومی ذخیره می‌کند.
  • broadcastOn: کانال عمومی public-channel را مشخص می‌کند.
  • broadcastAs: نام رویداد در سمت کلاینت را به message.broadcast تنظیم می‌کند.
  • broadcastWith: داده‌های اضافی (مانند timestamp) را به پیام اضافه می‌کند.

تعریف کانال‌های عمومی و خصوصی (Public/Private Channels)

لاراول سه نوع کانال برای broadcasting پشتیبانی می‌کند:

  1. Public Channels: همه کلاینت‌ها می‌توانند به این کانال‌ها subscribe کنند. مناسب برای داده‌های عمومی مانند اعلانات یا به‌روزرسانی‌های عمومی.
  2. Private Channels: فقط کاربران احراز هویت‌شده می‌توانند به این کانال‌ها دسترسی داشته باشند. برای داده‌های حساس مانند پیام‌های خصوصی مناسب است.
  3. Presence Channels: علاوه بر احراز هویت، اطلاعات کاربران حاضر در کانال را ارائه می‌دهند (مانند لیست کاربران آنلاین در یک چت).

تعریف کانال‌های عمومی

کانال‌های عمومی نیازی به احراز هویت ندارند. در مثال بالا، public-channel یک کانال عمومی است که در broadcastOn تعریف شده است.

تعریف کانال‌های خصوصی

برای کانال‌های خصوصی، از کلاس PrivateChannel استفاده می‌کنیم و احراز هویت را در فایل routes/channels.php تنظیم می‌کنیم.

مثال: ایجاد یک Event برای کانال خصوصی

php artisan make:event PrivateMessage

فایل app/Events/PrivateMessage.php را به این صورت ویرایش کنید:

<?php

namespace App\Events;

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

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

    public $message;
    public $userId;

    public function __construct($message, $userId)
    {
        $this->message = $message;
        $this->userId = $userId;
    }

    public function broadcastOn()
    {
        return new PrivateChannel('user.' . $this->userId);
    }

    public function broadcastAs()
    {
        return 'private.message';
    }
}

احراز هویت برای کانال‌های خصوصی

فایل routes/channels.php را برای احراز هویت کاربران ویرایش کنید:

<?php

use Illuminate\Support\Facades\Broadcast;

Broadcast::channel('user.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});

این کد بررسی می‌کند که کاربر احراز هویت‌شده فقط به کانال مربوط به خودش (user.{id}) دسترسی داشته باشد.

ارسال پیام‌های اولیه از Backend به Frontend

در این بخش، یک مثال عملی برای ارسال پیام از backend به frontend با استفاده از Reverb و Laravel Echo پیاده‌سازی می‌کنیم. این پروژه یک سیستم نوتیفیکیشن ساده را نشان می‌دهد که پیام‌ها را در یک کانال عمومی و خصوصی ارسال می‌کند.

مرحله ۱: تنظیم Laravel Echo

  1. نصب Laravel Echo:
   npm install --save laravel-echo @reverbjs/laravel-echo
  1. فایل resources/js/bootstrap.js را به‌روزرسانی کنید:
import Echo from 'laravel-echo';
import * as Reverb from '@reverbjs/laravel-echo';

window.Echo = new Echo({
    broadcaster: Reverb.Reverb,
    host: 'localhost:8080',
    authEndpoint: '/broadcasting/auth',
    appId: 'my-reverb-app',
    key: 'your-app-key',
});

مرحله ۲: ایجاد Route برای ارسال پیام

فایل routes/web.php را به‌روزرسانی کنید:

<?php

use App\Events\BroadcastMessage;
use App\Events\PrivateMessage;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/send-public', function () {
    event(new BroadcastMessage('Hello from public channel!'));
    return 'Public message sent!';
});

Route::middleware('auth')->get('/send-private', function () {
    event(new PrivateMessage('Hello from private channel!', auth()->id()));
    return 'Private message sent!';
});

مرحله ۳: تنظیم Frontend

فایل resources/views/welcome.blade.php را برای دریافت پیام‌ها تنظیم کنید:

<!DOCTYPE html>
<html>
<head>
    <title>Reverb Broadcasting</title>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
    @vite(['resources/js/app.js'])
</head>
<body>
    <h1>Reverb Broadcasting Example</h1>
    <button onclick="sendPublicMessage()">Send Public Message</button>
    @auth
        <button onclick="sendPrivateMessage()">Send Private Message</button>
    @endauth
    <h2>Messages:</h2>
    <ul id="messages"></ul>

    <script>
        function sendPublicMessage() {
            $.get('/send-public');
        }

        function sendPrivateMessage() {
            $.get('/send-private');
        }

        window.Echo.channel('public-channel')
            .listen('.message.broadcast', (e) => {
                $('#messages').append(`<li>Public: ${e.message} (${e.timestamp})</li>`);
            });

        @auth
            window.Echo.private('user.{{ auth()->id() }}')
                .listen('.private.message', (e) => {
                    $('#messages').append(`<li>Private: ${e.message}</li>`);
                });
        @endauth
    </script>
</body>
</html>

مرحله ۴: تنظیم احراز هویت

برای استفاده از کانال‌های خصوصی، باید سیستم احراز هویت را فعال کنید:

  1. Laravel Sanctum را نصب کنید:
   composer require laravel/sanctum
   php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
   php artisan migrate
  1. فایل app/Http/Kernel.php را برای middleware به‌روزرسانی کنید:
   protected $middlewareGroups = [
       'web' => [
           // ...
           \Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class,
       ],
   ];
  1. یک کاربر تست ایجاد کنید و وارد سیستم شوید.

مرحله ۵: اجرای پروژه

  1. سرور Reverb را اجرا کنید:
   php artisan reverb:start
  1. سرور لاراول را اجرا کنید:
   php artisan serve
  1. فایل‌های جاوااسکریپت را کامپایل کنید:
   npm run dev
  1. به http://localhost:8000 بروید، دکمه‌های "Send Public Message" و "Send Private Message" را کلیک کنید و پیام‌ها را در لیست مشاهده کنید.

خروجی مورد انتظار

  • کلیک روی "Send Public Message" پیام عمومی را در <ul id="messages"> نمایش می‌دهد.
  • کلیک روی "Send Private Message" (پس از ورود) پیام خصوصی را فقط برای کاربر احراز هویت‌شده نمایش می‌دهد.

نکات و بهترین شیوه‌ها

  • استفاده از Queues: برای اپلیکیشن‌های با بار بالا، broadcasting را به queue منتقل کنید:
  BROADCAST_QUEUE_CONNECTION=redis

و مطمئن شوید که Redis نصب و تنظیم شده است.

  • نام‌گذاری رویدادها: از نام‌های معنی‌دار برای broadcastAs استفاده کنید تا در سمت کلاینت قابل شناسایی باشند.
  • امنیت: همیشه کانال‌های خصوصی را برای داده‌های حساس استفاده کنید و احراز هویت را به‌دقت تنظیم کنید.

تمرین پیشنهادی

  • یک فرم به صفحه اضافه کنید تا کاربران بتوانند پیام‌های سفارشی برای کانال عمومی یا خصوصی ارسال کنند.
  • یک event جدید برای یک کانال presence ایجاد کنید تا کاربران آنلاین را نمایش دهد.

منابع

  • مستندات رسمی لاراول: laravel.com/docs/12.x/broadcasting
  • مستندات Reverb: laravel.com/docs/12.x/reverb

در فصل بعدی، به مدیریت پیشرفته‌تر کانال‌ها و subscriptions خواهیم پرداخت.