برنامه نویسی ۲۶۳۹ بازدید

در این مقاله به بررسی مفهوم انتشار لاراول می‌پردازیم. این قابلیت باعث می‌شود بتوانید در زمان وقوع اتفاقی در سمت سرور اعلان‌هایی را با استفاده از پروتکل وب سوکت به سمت کلاینت ارسال کنید. در این مطلب قصد داریم از کتابخانه شخص ثالث Pusher برای ارسال اعلان‌ها به سمت کلاینت بهره بگیریم. اگر تاکنون با موقعیتی مواجه شده‌اید که در زمان وقوع اتفاقی در سمت سرور می‌بایست اعلان‌هایی از سمت سرور به کلاینت ارسال می‌کردید، در واقع نیازمند استفاده از سازوکار انتشار لاراول (Laravel Broadcasting) بوده‌اید.

برای نمونه تصور کنید که یک اپلیکیشن پیام‌رسان را پیاده‌سازی کرده‌اید که به کاربران امکان می‌دهد پیام‌هایی به همدیگر ارسال کنند. اینک زمانی که کاربر الف پیامی را به کاربر ب می‌فرستد، باید بی‌درنگ این موضوع را به کاربر ب اطلاع دهید. برای مثال می‌توانید یک پنجره باز کنید یا یک کادر هشدار روی سیستم کاربر ب نمایش دهید تا پیام تازه خود را مشاهده کند.

پیاده‌سازی انتشار لاراول

این یک کاربرد مناسب برای بررسی مفهوم انتشار لاراول است و از این رو در این مقاله اقدام به پیاده‌سازی آن خواهیم کرد. اگر کنجکاو هستید که سرور چگونه می‌تواند اعلان‌هایی را به کلاینت ارسال کند، باید بگوییم که لاراول به این منظور در پس‌زمینه از سوکت‌ها استفاده می‌کند. بنابراین پیش از ادامه مقاله و وارد شدن به مراحل عمیق‌تر پیاده‌سازی، ابتدا گردش کار سوکت‌ها را مورد بررسی قرار می‌دهیم.

  • در ابتدا به یک سرور نیاز دارید که از پروتکل web-sockets پشتیبانی کند و به کلاینت امکان ایجاد یک وب سوکت را بدهد.
  • شما می‌توانید سرور خود را راه‌اندازی کنید و یا این که از یک سرویس شخص ثالث ماند Pusher استفاده کنید. ما در این مقاله از روش دوم استفاده می‌کنیم.
  • کلاینت یک اتصال وب سوکت به سرور وب سوکت برقرار کرده و به محض برقراری موفق اتصال، یک شناسه یکتا دریافت می‌کند.
  • زمانی که اتصال با موفقیت برقرار شد، کلاینت در کانال‌های به خصوصی ثبت نام می‌کند و بدین ترتیب امکان دریافت رویدادها را به دست می‌آورد.
  • در نهایت کلاینت در کانالی که ثبت نام کرده است روی رویدادهایی که دوست دارید در مورد آن‌ها پیام‌هایی دریافت کنید ثبت نام می‌کند.
  • اینک در سمت سرور زمانی که یک رویداد خاص اتفاق بیفتد، ما با ارسال نام کانال و نام رویداد، این موضوع را به سرور وب سوکت اطلاع می‌دهیم.
  • در پایان سرور وب سوکت این رویداد را به کلاینت‌های ثبت نام کننده در آن کانال خاص اطلاع‌رسانی می‌کند.

اگر از مشاهده همه این مراحل دچار نگرانی شده‌اید، باید اعلام کنیم که جای نگرانی نیست. ما در ادامه این مقاله همه این مراحل را به صورت گام به گام با کمک همدیگر طی خواهیم کرد.

در ادامه نگاهی به فایل پیکربندی پیش‌فرض انتشار در مسیر config/broadcasting.php خواهیم داشت:

<?php
 
return [
 
    /*
    |--------------------------------------------------------------------------
    | Default Broadcaster
    |--------------------------------------------------------------------------
    |
    | This option controls the default broadcaster that will be used by the
    | framework when an event needs to be broadcast. You may set this to
    | any of the connections defined in the "connections" array below.
    |
    | Supported: "pusher", "redis", "log", "null"
    |
    */
 
    'default' => env('BROADCAST_DRIVER', 'log'),
 
    /*
    |--------------------------------------------------------------------------
    | Broadcast Connections
    |--------------------------------------------------------------------------
    |
    | Here you may define all of the broadcast connections that will be used
    | to broadcast events to other systems or over websockets. Samples of
    | each available type of connection are provided inside this array.
    |
    */
 
    'connections' => [
 
        'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
        ],
 
        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
        ],
 
        'log' => [
            'driver' => 'log',
        ],
 
        'null' => [
            'driver' => 'null',
        ],
 
    ],
 
];

لاراول به صورت پیش‌فرض در هسته خود از آداپترهای چندگانه انتشار پشتیبانی می‌کند.

در این مقاله، قصد داریم از آداپتر انتشار Pusher استفاده کنیم. برای مقاصد دیباگ کردن می‌توانید از آداپتر log نیز استفاده کنید. اما توجه داشته باشید که در صورت استفاده از آداپتر log، کلاینت دیگر نمی‌تواند اعلان‌های رویداد را دریافت ‌کند، چون همه آن‌ها در فایل laravel.log ثبت می‌شوند.

در بخش بعدی مستقیماً وارد مراحل پیاده‌سازی عملی کاربردهایی خواهیم شد که در بخش قبل مورد اشاره قرار دادیم.

تنظیم پیش‌نیازها

در فرایند انتشار یا broadcasting انواع مختلفی از کانال‌ها وجود دارند که شامل کانال‌های عمومی، خصوصی و presence می‌شوند. زمانی که یک رویداد به صوت عمومی انتشار می‌یابد، می‌بایست از کانال عمومی استفاده کرد. به طور عکس کانال خصوصی زمانی مورد استفاده قرار می‌گیرد که بخواهیم اعلان‌های رویداد به برخی کانال‌های خصوصی معین محدود شوند.

در زمینه مثالی که ما مورد بررسی قرار می‌دهیم، قصدمان این است که هر زمان کاربران پیام جدیدی دریافت کردند، یک اعلان به آن‌ها ارسال شود. برای این که یک کاربر شرایط دریافت اعلان‌های انتشار را داشته باشد، باید وارد حساب خود شده باشد. از این رو باید از کانال خصوصی در این مثال استفاده کنیم.

قابلیت احراز هویت در هسته مرکزی لاراول

قبل از هر چیز، باید سیستم احراز هویت پیش‌فرض لاراول را فعال کنیم تا قابلیت‌هایی مانند ثبت نام، ورود به حساب و موارد مشابه به صورت آماده در اختیار ما قرار بگیرند. اگر در این خصوص دچار ابهاماتی هستید بهتر است به مستندات رسمی (+) این بخش مراجعه کنید.

نصب و پیکربندی Pusher SDK

از آنجا که ما قصد داریم از سرویس شخص ثالث Pusher به عنوان سرور وب سوکت خود استفاده کنیم، باید یک حساب کاربری در این سرویس (+) ایجاد کنید و مطمئن شوید که اطلاعات احراز هویت API را پس از ثبت نام به دست آورده‌اید.

سپس باید SDK مربوط به Pusher را برای PHP نصب کنیم تا اپلیکیشن لاراول ما بتواند اعلان‌هایی به سرور وب سوکت Pusher ارسال کند. در ریشه اپلیکیشن لاراول دستور زیر را اجرا کنید تا به صورت یک پکیج کامپوزر نصب شود:

$composer require pusher/pusher-php-server "~3.0"

اینک فایل پیکربندی انتشار را طوری تغییر می‌دهیم که آداپتر Pusher به عنوان درایور انتشار پیش‌فرض ما فعال شود.

<?php
 
return [
 
    /*
    |--------------------------------------------------------------------------
    | Default Broadcaster
    |--------------------------------------------------------------------------
    |
    | This option controls the default broadcaster that will be used by the
    | framework when an event needs to be broadcast. You may set this to
    | any of the connections defined in the "connections" array below.
    |
    | Supported: "pusher", "redis", "log", "null"
    |
    */
 
    'default' => env('BROADCAST_DRIVER', 'pusher'),
 
    /*
    |--------------------------------------------------------------------------
    | Broadcast Connections
    |--------------------------------------------------------------------------
    |
    | Here you may define all of the broadcast connections that will be used
    | to broadcast events to other systems or over websockets. Samples of
    | each available type of connection are provided inside this array.
    |
    */
 
    'connections' => [
 
        'pusher' => [
            'driver' => 'pusher',
            'key' => env('PUSHER_APP_KEY'),
            'secret' => env('PUSHER_APP_SECRET'),
            'app_id' => env('PUSHER_APP_ID'),
            'options' => [
                        'cluster' => 'ap2',
                        'encrypted' => true
            ],
        ],
 
        'redis' => [
            'driver' => 'redis',
            'connection' => 'default',
        ],
 
        'log' => [
            'driver' => 'log',
        ],
 
        'null' => [
            'driver' => 'null',
        ],
 
    ],
 
];

همان طور که می‌بینید، ما درایور انتشار پیش‌فرض را به Pusher تغییر داده‌ایم. همچنین گزینه‌های پیکربندی کلاستر و رمزنگاری را نیز که در ابتدا از حساب Pusher دریافت کرده بودیم، اضافه کرده‌ایم. ضمناً برخی مقادیر را نیز از «متغیرهای محیطی» (environment variables) به دست می‌آوریم. بنابراین باید مطمئن شویم که متغیرهای زیر را در فایل env. به طرز صحیحی تنظیم کرده‌ایم.

BROADCAST_DRIVER=pusher
PUSHER_APP_ID={YOUR_APP_ID}
PUSHER_APP_KEY={YOUR_APP_KEY}
PUSHER_APP_SECRET={YOUR_APP_SECRET}

سپس باید چند تغییر در بخش‌های مختلف فایل‌های اصلی لاراول ایجاد کنیم تا با جدیدترین SDK مربوط به Pusher سازگار شود. البته ایجاد تغییر در فریمورک اصلی توصیه نمی‌شود؛ اما ما صرفاً مواردی را که باید انجام یابند مشخص می‌سازیم. فایل زیر را باز کنید:

vendor/laravel/framework/src/Illuminate/Broadcasting/Broadcasters/PusherBroadcaster.php

عبارت ;use Pusher را با ;use Pusher\Pusher عوض کنید. سپس فایل زیر را باز کنید:

vendor/laravel/framework/src/Illuminate/Broadcasting/BroadcastManager.php

و تغییر مشابهی را در قطعه کد زیر اعمال کنید:

return new PusherBroadcaster(
  new \Pusher\Pusher($config['key'], $config['secret'],
  $config['app_id'], Arr::get($config, 'options', []))
);

در نهایت با حذف کامنت از خط زیر در فایل config/app.php، سرویس انتشار را فعال کنید:

App\Providers\BroadcastServiceProvider::class,

تا به اینجا ما موفق شده‌ایم کتابخانه‌های مربوط به سرور را نصب کنیم. در بخش بعدی قصد داریم کتابخانه‌های کلاینت مورد نیاز را نیز نصب کنیم.

نصب و پیکربندی کتابخانه‌های Pusher و Laravel Echo

در فرایند انتشار، مسئولیت سمت کلاینت این است که در کانال‌ها ثبت نام کند و منتظر شنیدن رویدادهای مورد نظر باشد. این کار در پس‌زمینه با باز کردن یک اتصال جدید به سرور وب سوکت صورت می‌پذیرد.

خوشبختانه نیاز نیست هیچ گونه کتابخانه پیچیده جاوا اسکریپت را نصب کنیم، چون لاراول از قبل یک کتابخانه کلاینت مفید در این زمینه دارد. Laravel Echo کتابخانه سمت کلاینت لاراول است که امکان کار کردن با سوکت‌ها را در اختیار ما قرار می‌دهد. ضمناً این کتابخانه از سرویس Pusher که در این مقاله مورد استفاده قرار می‌دهیم نیز پشتیبانی می‌کند.

شما می‌توانید Laravel Echo را با استفاده از ابزار مدیریت بسته NPM نصب کنید. البته اگر node و npm را روی سیستم خود نصب ندارید، ابتدا باید آن‌ها را نصب کنید. بقیه کار کاملاً آسان است و در دستور زیر مشخص شده است:

$npm install laravel-echo

ما به دنبال فایل node_modules/laravel-echo/dist/echo.js هستیم که باید آن را به مسیر public/echo.js کپی کنیم.

البته شاید به نظر شما انجام این همه کار برای دریافت یک فایل جاوا اسکریپت، اضافه‌کاری باشد. در این صورت اگر نمی‌خواهید این مسیر را طی کنید، می‌توانید این فایل echo.js را از این آدرس گیت‌هاب (+) دانلود کنید. بدین ترتیب کار ما در بخش تنظیم کتابخانه‌های سمت کلاینت به پایان می‌رسد.

تنظیم فایل بک‌اند

اگر به خاطر داشته باشید در ابتدا گفتیم که قصد داریم اپلیکیشنی بسازیم که به کاربران امکان ارسال پیام به همدیگر را می‌دهد. از سوی دیگر اعلان‌هایی به کاربرانی که وارد حساب خود شده‌اند، ارسال می‌کنیم تا آن‌ها را از دریافت پیام جدید از سوی کاربران دیگر مطلع سازیم. در این بخش فایل‌هایی را که برای پیاده‌سازی این موارد لازم هستند ایجاد می‌کنیم.

در آغاز کار یک مدل Message ایجاد می‌کنیم که پیام‌های ارسالی از سوی کاربران به همدیگر را نگهداری می‌کند.

$php artisan make:model Message –migration

همچنین چند فیلد دیگر مانند to ،from و message به جدول messages اضافه می‌کنیم. بنابراین فایل migration خود را پیش از اجرای دستور migrate به صورت زیر تغییر می‌دهیم:

<?php
 
use Illuminate\Support\Facades\Schema;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
 
class CreateMessagesTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('messages', function (Blueprint $table) {
            $table->increments('id');
            $table->integer('from', FALSE, TRUE);
            $table->integer('to', FALSE, TRUE);
            $table->text('message');
            $table->timestamps();
        });
    }
 
    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('messages');
    }
}

اکنون می‌توانیم دستور migrate را اجرا کنیم تا جدول پیام‌ها در پایگاه داده ایجاد شود:

$php artisan migrate

هر زمان که می‌خواهیم یک رویداد سفارشی را در لاراول ایجاد کنیم، باید یک کلاس برای آن بسازیم. بر اساس نوع رویداد، لاراول واکنش متناسب را نشان می‌دهد و اقدامات لازم را اجرا می‌کند.

اگر رویداد یک رویداد نرمال باشد، لاراول «کلاس‌های شنونده» (Listener classes) مرتبط را فراخوانی می‌کند. در سوی دیگر، اگر رویداد از نوع انتشار باشد، لاراول آن رویداد را به سرور وب سوکت ارسال می‌کند که در فایل config/broadcasting.php پیکربندی شده است. از آنجا که ما در این مقاله از سرویس Pusher استفاده می‌کنیم، لاراول رویدادها را به سرور Pusher ارسال می‌کند. از دستور آرتیزان زیر برای ایجاد یک کلاس رویداد سفارشی به نام NewMessageNotification استفاده می‌کنیم:

$php artisan make:event NewMessageNotification

دستور فوق کلاس app/Events/NewMessageNotification.php را ایجاد می‌کند. محتوای این فایل را با کد زیر عوض می‌کنیم:

<?php
 
namespace App\Events;
 
use Illuminate\Broadcasting\Channel;
use Illuminate\Queue\SerializesModels;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow;
use App\Message;
 
class NewMessageNotification implements ShouldBroadcastNow
{
    use SerializesModels;
 
    public $message;
 
    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(Message $message)
    {
        $this->message = $message;
    }
 
    /**
     * Get the channels the event should broadcast on.
     *
     * @return Channel|array
     */
    public function broadcastOn()
    {
        return new PrivateChannel('user.'.$this->message->to);
    }
}

نکته مهمی که باید توجه داشته باشید این است که کلاس NewMessageNotification اینترفیس ShouldBroadcastNow را پیاده‌سازی می‌کند. از این رو زمانی که یک رویداد رخ می‌دهد، لاراول می‌داند که این رویداد باید انتشار یابد.

در واقع، می‌توان یک اینترفیس ShouldBroadcast را نیز پیاده‌سازی کرد و لاراول رویداد را به صف رویدادها اضافه می‌کند. این رویداد زمانی که نوبتش شود، از سوی ورکر صف رویداد پردازش می‌شود. در مورد مثال خودمان، قصد داریم آن را مستقیماً منتشر کنیم و به همین دلیل از اینترفیس ShouldBroadcastNow استفاده می‌کنیم.

در این مثال، ما می‌خواهیم اعلانی به کاربر نمایش داده شود که به اطلاع وی می‌رساند پیامی دریافت کرده است و از این رو مدل Message در آرگومان سازنده ارسال می‌شود. بدین ترتیب داده‌ها به همراه رویداد ارسال می‌شوند.

سپس یک متد broadcastOn وجود دارد که نام کانالی را تعریف می‌کند که رویداد در آن منتشر خواهد شد. در مورد مثال خودمان از یک کانال خصوصی استفاده شده است، زیرا می‌خواهیم انتشار رویداد به کاربرانی که وارد حساب کاربری خودشان شده‌اند محدود باشد.

متغیر this->message->to$ به ID کاربری اشاره می‌کند که قرار است رویداد برای وی انتشار یابد. بدین ترتیب بهتر است که نام کانال چیزی مانند {user.{USER_ID باشد.

در مورد کانال‌های خصوصی، کلاینت باید خود را پیش از برقراری اتصال با سرور وب سوکت، احراز هویت کند. بدین ترتیب اطمینان پیدا می‌کنیم که رویدادهایی که روی کانال خصوصی منتشر می‌شوند صرفاً به کلاینت‌های احراز هویت شده ارسال می‌شوند. در مثال مورد بررسی، این بدان معنی است که تنها کاربرانی که وارد حساب کاربری خود شده‌اند، خواهند توانست در کانال {user.{USER_ID ثبت نام کنند.

اگر از کتابخانه کلاینت Echo لاراول برای ثبت نام استفاده می‌کنید، شانس آورده‌اید، زیرا این کتابخانه به طور خودکار بخش احراز هویت را انجام می‌دهد و کافی است مسیرهای کانال را تعریف کنید. در ادامه یک مسیر برای کانال خصوصی خود در فایل routes/channels.php اضافه می‌کنیم:

<?php
 
/*
|--------------------------------------------------------------------------
| Broadcast Channels
|--------------------------------------------------------------------------
|
| Here you may register all of the event broadcasting channels that your
| application supports. The given channel authorization callbacks are
| used to check if an authenticated user can listen to the channel.
|
*/
 
Broadcast::channel('App.User.{id}', function ($user, $id) {
    return (int) $user->id === (int) $id;
});
 
Broadcast::channel('user.{toUserId}', function ($user, $toUserId) {
    return $user->id == $toUserId;
});

همان طور که شاهد هستید، ما مسیر {user.{toUserId را برای کانال خصوصی خود تعریف کرده‌ایم. آرگومان دوم متد کانال باید یک تابع «بستار» (Closure) باشد. لاراول به طور خودکار کاربری که اینک وارد حساب خود شده را به عنوان آرگومان نخست تابع بستار ارسال می‌کند و آرگومان دوم نیز معمولاً از نام کانال واکشی می‌شود.

زمانی که کلاینت تلاش کند در کانال خصوصی {user.{USER_ID ثبت نام کند، کتابخانه Echo لاراول احراز هویت ضروری را در پس‌زمینه با استفاده از شیء XMLHttpRequest که به صوت XHR شناخته‌شده‌تر است، انجام می‌دهد. بدین ترتیب ما کار تنظیمات را به پایان بردیم و اینک نوبت تست پیاده‌سازی است.

تنظیم فرانت‌اند

در این بخش فایل‌هایی را که برای تست پیاده‌سازی ما ضروری هستند ایجاد می‌کنیم. ابتدا یک فایل کنترلر را در مسیر app/Http/Controllers/MessageController.php با محتوای زیر ایجاد می‌کنیم:

<?php
namespace App\Http\Controllers;
 
use App\Http\Controllers\Controller;
use App\Message;
use App\Events\NewMessageNotification;
use Illuminate\Support\Facades\Auth;
 
class MessageController extends Controller
{
    public function __construct() {
        $this->middleware('auth');
    }
 
    public function index()
    {
        $user_id = Auth::user()->id;
        $data = array('user_id' => $user_id);
 
        return view('broadcast', $data);
    }
 
    public function send()
    {
        // ...
         
        // message is being sent
        $message = new Message;
        $message->setAttribute('from', 1);
        $message->setAttribute('to', 2);
        $message->setAttribute('message', 'Demo message from user 1 to user 2');
        $message->save();
         
        // want to broadcast NewMessageNotification event
        event(new NewMessageNotification($message));
         
        // ...
    }
}

در متد index ما از ویوی broadcast استفاده می‌کنیم و از این رو فایل ویوی resources/views/broadcast.blade.php را نیز ایجاد می‌کنیم:

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
 
    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">
 
    <title>Test</title>
 
    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
</head>
<body>
    <div id="app">
        <nav class="navbar navbar-default navbar-static-top">
            <div class="container">
                <div class="navbar-header">
 
                    <!-- Collapsed Hamburger -->
                    <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#app-navbar-collapse">
                        <span class="sr-only">Toggle Navigation</span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                        <span class="icon-bar"></span>
                    </button>
 
                    <!-- Branding Image -->
                    <a class="navbar-brand123" href="{{ url('/') }}">
                        Test
                    </a>
                </div>
 
                <div class="collapse navbar-collapse" id="app-navbar-collapse">
                    <!-- Left Side Of Navbar -->
                    <ul class="nav navbar-nav">
                        &nbsp;
                    </ul>
 
                    <!-- Right Side Of Navbar -->
                    <ul class="nav navbar-nav navbar-right">
                        <!-- Authentication Links -->
                        @if (Auth::guest())
                            <li><a href="{{ route('login') }}">Login</a></li>
                            <li><a href="{{ route('register') }}">Register</a></li>
                        @else
                            <li class="dropdown">
                                <a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-expanded="false">
                                    {{ Auth::user()->name }} <span class="caret"></span>
                                </a>
 
                                <ul class="dropdown-menu" role="menu">
                                    <li>
                                        <a href="{{ route('logout') }}"
                                            onclick="event.preventDefault();
                                                     document.getElementById('logout-form').submit();">
                                            Logout
                                        </a>
 
                                        <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                                            {{ csrf_field() }}
                                        </form>
                                    </li>
                                </ul>
                            </li>
                        @endif
                    </ul>
                </div>
            </div>
        </nav>
 
        <div class="content">
                <div class="m-b-md">
                    New notification will be alerted realtime!
                </div>
        </div>
    </div>
 
    <!-- receive notifications -->
    <script src="{{ asset('js/echo.js') }}"></script>
 
    <script src="https://js.pusher.com/4.1/pusher.min.js"></script>
         
        <script>
          Pusher.logToConsole = true;
         
          window.Echo = new Echo({
            broadcaster: 'pusher',
            key: 'c91c1b7e8c6ece46053b',
            cluster: 'ap2',
            encrypted: true,
            logToConsole: true
          });
         
          Echo.private('user.{{ $user_id }}')
          .listen('NewMessageNotification', (e) => {
              alert(e.message.message);
          });
        </script>
    <!-- receive notifications -->
</body>
</html>

البته باید مسیرها را در فایل routes/web.php نیز اضافه کنیم:

Route::get('message/index', 'MessageController@index');
Route::get('message/send', 'MessageController@send');

در متد سازنده کلاس کنترلر می‌توانید ببینید که از میان‌افزار auth استفاده کرده‌ایم تا مطمئن شویم که متدهای کنترلر به کاربران لاگین کرده دسترسی دارند. سپس متد index برای رندر کردن ویوی broadcast استفاده می‌شود. در ادامه مهم‌ترین بخش کد را در فایل ویو می‌نویسیم:

<!-- receive notifications -->
<script src="{{ asset('js/echo.js') }}"></script>
 
<script src="https://js.pusher.com/4.1/pusher.min.js"></script>
     
<script>
    Pusher.logToConsole = true;
 
    window.Echo = new Echo({
        broadcaster: 'pusher',
        key: 'c91c1b7e8c6ece46053b',
        cluster: 'ap2',
        encrypted: true,
        logToConsole: true
    });
 
    Echo.private('user.{{ $user_id }}')
    .listen('NewMessageNotification', (e) => {
        alert(e.message.message);
    });
</script>
<!-- receive notifications -->

در ابتدا کتابخانه‌های ضروری کلاینت که Laravel Echo و Pusher هستند را بارگذاری می‌کنیم. این کتابخانه‌ها به ما امکان می‌دهند که یک اتصال وب سوکت با سرور وب سوکت Pusher برقرار سازیم. سپس با ارائه اطلاعات ضروری دیگر مرتبط با Pusher، وهله‌ای از Echo را به عنوان آداپتر انتشار خود می‌سازیم.

در ادامه از متد خصوصی Echo برای ثبت نام در کانال خصوصی {user.{USER_ID استفاده می‌کنیم. همان طور که قبلاً گفتیم، کلاینت پیش از ثبت نام در کانال خصوصی، باید هویت خود را احراز کند. از این رو شیء Echo مراحل مورد نیاز احراز هویت را با ارسال XHR در پس‌زمینه با پارامترهای ضروری انجام می‌دهد. در نهایت لاراول تلاش می‌کند که مسیر {user.{USER_ID را بیابد و این مسیر باید با مسیری که در فایل routes/channels.php وجود دارد، مطابقت داشته باشد.

اگر همه چیز به خوبی پیش برود، باید یک اتصال وب سوکت با سرور وب سوکت Pusher باز شود و رویدادهای روی کانال {user.{USER_ID از این پس در آن فهرست شوند. بدین ترتیب قادر خواهیم بود همه رویدادهای ورودی را در این کانال دریافت کنیم.

در مورد مثال خودمان می‌خواهیم به رویداد NewMessageNotification گوش کنیم و از این رو از متد listen شیء Echo برای دستیابی به آن استفاده می‌کنیم. برای این که همه چیز ساده بماند، تنها در مورد پیام‌هایی هشدار می‌دهیم که از سوی سرور Pusher دریافت شده باشند. بنابراین اکنون همه چیز را برای دریافت رویدادها از سرور وب سوکت تنظیم کرده‌ایم. در ادامه از طریق متد send در فایل کنترلر برخی رویدادهای انتشار را ایجاد می‌کنیم. کد متد send به صورت زیر است:

public function send()
{
    // ...
     
    // message is being sent
    $message = new Message;
    $message->setAttribute('from', 1);
    $message->setAttribute('to', 2);
    $message->setAttribute('message', 'Demo message from user 1 to user 2');
    $message->save();
     
    // want to broadcast NewMessageNotification event
    event(new NewMessageNotification($message));
     
    // ...
}

ما در مثال خودمان، قصد داریم زمانی که کاربران لاگین‌کرده پیامی دریافت می‌کنند به آن‌ها هشداری بدهیم. بنابراین تلاش می‌کنیم که این رفتار را در متد send شبیه‌سازی کنیم.

در این زمان، از تابع کمکی event برای ایجاد یک رویداد NewMessageNotification استفاده می‌کنیم. از آنجا که رویداد NewMessageNotification از نوع ShouldBroadcastNow است، لاراول پیکربندی پیش‌فرض انتشار را از فایل config/broadcasting.php بارگذاری می‌کند. در نهایت لاراول رویداد NewMessageNotification را به سرور وب سوکت پیکربندی شده روی کانال انتشار می‌دهد.

در این مثال، رویداد به سرور وب سوکت Pusher روی کانال {user.{USER_ID انتشارمی یابد. اگر ID کاربر پذیرنده 1 باشد، این رویداد روی کانال user.1 انتشار پیدا می‌کند.

همان طور که قبلاً بررسی کردیم، ما از قبل یک تنظیمات داریم که به رویدادهای روی این کانال گوش می‌دهد. بنابراین می‌توانیم این رویداد را دریافت کنیم و کادر هشدار برای کاربر مربوطه نمایش پیدا می‌کند. در ادامه مراحل مورد نیاز برای تست این مثال کاربردی که ساخته‌ایم را مورد بررسی قرار می‌دهیم. در مرورگر خود به آدرس زیر بروید:

http://your-laravel-site-domain/message/index

اگر هنوز وارد نشده‌اید، به صفحه لاگین هدایت خواهید شد و زمانی که وارد حساب کاربری خود شدید، ویوی broadcast را می‌بینید که قبلاً تعریف کرده‌ایم و تاکنون کاربردی نداشته است. در واقع لاراول از قبل مقداری از کارها را در پس‌زمینه برای شما انجام داده است. از آنجا که ما تنظیمات Pusher.logToConsole ارائه شده از سوی کتابخانه کلاینت Pusher را فعال کرده‌ایم، این کتابخانه همه موارد را در کنسول مرورگر به منظور دیباگ کردن، لاگ می‌کند. با مراجعه به صفحه http://your-laravel-site-domain/message/index بررسی می‌کنیم که چه مواردی برای ما لاگ شده‌اند:

Pusher : State changed : initialized -> connecting
 
Pusher : Connecting : {"transport":"ws","url":"wss://ws-ap2.pusher.com:443/app/c91c1b7e8c6ece46053b?protocol=7&client=js&version=4.1.0&flash=false"}
 
Pusher : Connecting : {"transport":"xhr_streaming","url":"https://sockjs-ap2.pusher.com:443/pusher/app/c91c1b7e8c6ece46053b?protocol=7&client=js&version=4.1.0"}
 
Pusher : State changed : connecting -> connected with new socket ID 1386.68660
 
Pusher : Event sent : {"event":"pusher:subscribe","data":{"auth":"c91c1b7e8c6ece46053b:cd8b924580e2cbbd2977fd4ef0d41f1846eb358e9b7c327d89ff6bdc2de9082d","channel":"private-user.2"}}
 
Pusher : Event recd : {"event":"pusher_internal:subscription_succeeded","data":{},"channel":"private-user.2"}
 
Pusher : No callbacks on private-user.2 for pusher:subscription_succeeded

همان طور که می بینید یک اتصال وب سوکت با سرور وب سوکت Pusher باز شده است و برای شنیدن رویدادها روی کانال خصوصی ثبت نام صورت گرفته است. البته شما می‌توانید در مثال خودتان بر مبنای ID کاربری که گزارش را تهیه کرده است، نام کانال متفاوتی داشته باشید. اینک این صفحه را بار نگه می‌داریم و به متد send می‌رویم تا تست کنیم.

سپس URL زیر را در برگه دیگری از همان مرورگر یا در یک مرورگر دیگر باز کنید:

http://your-laravel-site-domain/message/send

اگر قصد دارید از مرورگر متفاوتی استفاده کنید، باید دوباره وارد حساب کاربری خود شود تا به این صفحه دسترسی داشته باشید. به محض این که آدرس فوق را باز کنید، می‌توانید یک پیام هشدار را در برگه دیگر با آدرس http://your-laravel-site-domain/message/index ملاحظه کنید.

اینک به کنسول می‌رویم تا ببینیم چه اتفاقی رخ داده است:

Pusher: Event recd: {"event":"App\\Events\\NewMessageNotification","data":{"message":{"id":57,"from":1,"to":2,"message":"Demo message from user 1 to user 2","created_at":"2018-01-13 07:10:10","updated_at":"2018-01-13 07:10:10"}},"channel":"private-user.2"}

همان طور که می‌بینید این پیام لاگ نشان می‌دهد که رویداد App\Events\NewMessageNotification از سوی سرور وب سوکت Pusher روی کانال private-user.2 دریافت شده است.

در واقع شما می‌توانید اتفاقاتی را که رخ داده است در حساب Pusher خود نیز ببینید. به این منظور به حساب کاربری Pusher خود بروید و به بخش application مراجعه کنید. زیر بخش Debug Console می‌توانید پیام‌هایی را که لاگ شده‌اند ببینید:

انتشار لاراول

بدین ترتیب به پایان این مقاله راهنما می‌رسیم. امیدواریم از طولانی شدن این مقاله خسته نشده باشید، چون در طی آن تلاش کردیم همه چیز را به بهترین نحو ممکن توضیح دهیم.

سخن پایانی

در این مقاله با یکی از قابلیت‌های لاراول آشنا شدیم که کمتر مورد بررسی قرار گرفته است و آن قابلیت انتشار لاراول است. این قابلیت به ما امکان می‌دهد که اعلان‌های آنی را با استفاده از وب سوکت‌ها ارسال کنیم. در طی این مقاله ما یک مثال واقعی برای نمایش مفاهیم فوق ساختیم.

اگر این مطلب برای شما مفید بوده است آموزش‌های زیر نیز به شما پیشنهاد می‌شوند:

==

بر اساس رای ۱۱ نفر
آیا این مطلب برای شما مفید بود؟
شما قبلا رای داده‌اید!
اگر بازخوردی درباره این مطلب دارید یا پرسشی دارید که بدون پاسخ مانده است، آن را از طریق بخش نظرات مطرح کنید.

«میثم لطفی» در رشته‌های ریاضیات کاربردی و مهندسی کامپیوتر به تحصیل پرداخته و شیفته فناوری است. وی در حال حاضر علاوه بر پیگیری علاقه‌مندی‌هایش در رشته‌های برنامه‌نویسی، کپی‌رایتینگ و محتوای چندرسانه‌ای، در زمینه نگارش مقالاتی با محوریت نرم‌افزار با مجله فرادرس همکاری دارد.

2 نظر در “انتشار لاراول (Laravel Broadcasting) چگونه کار می‌ کند؟ — راهنمای کاربردی

  • خیلی ممنون خیلی خوب توضیح دادید با جزئیات. ولی ای کاش ی عکس هم از خروجی ها میذاشتید چون index من متاسفانه نوت ها رو نشون نمیدم و من نمیدونم چرا. یا حدقل کل کد رو در جایی قرار میدادید.

نظر شما چیست؟

نشانی ایمیل شما منتشر نخواهد شد.

مشاهده بیشتر