Laravel broadcasting với pusher

Laravel broadcasting với pusher

Cài đặt gói pusher vào trong app laravel :

composer require pusher/pusher-php-server

Sau đó chúng ta sẽ tạo route xác thực :

Route::post('/pusher/auth', [\App\Http\Controllers\UserController::class, 'useauth_private'])->name('userauth');
Route::post('/pusher/presenceauth', [\App\Http\Controllers\UserController::class, 'useauth_presence'])->name('presenceauth');

Phương thức useauth_private trong UserController :

public function useauth_private(Request $request)
    {
        $pusher = new Pusher(env('PUSHER_APP_KEY'),env('PUSHER_APP_SECRET'),env('PUSHER_APP_ID'),[
            'cluster'=>env('PUSHER_APP_CLUSTER')
        ]);
        $user_id = Auth::id();
        Log::info($user_id);
        $channel_name = $request->channel_name;
        if ($channel_name !== 'private-chat-' . $user_id && $channel_name !== 'private-order-' . $user_id) {
            return response()->json(['message' => 'Unauthorized'], 403);
        }
        // Trả về thông tin user cho presence channel
        return response($pusher->authorizeChannel($request->channel_name, $request->socket_id));
    }

Phương thức userauth_presence

public function useauth_presence(Request $request){
        if (!Auth::check()) {
            return response()->json(['message' => 'Unauthorized'], 403);
        }
        $pusher = new Pusher(
            env('PUSHER_APP_KEY'),
            env('PUSHER_APP_SECRET'),
            env('PUSHER_APP_ID'),
            ['cluster' => env('PUSHER_APP_CLUSTER')]
        );

        $user = Auth::user();
        $presence_data = [
            'id' => $user->id,
            'name' => $user->name,
            'email' => $user->email
        ];

        return response($pusher->authorizePresenceChannel($request->channel_name, $request->socket_id, $presence_data));
    }

Trong file .env : 

PUSHER_APP_ID="2222"
PUSHER_APP_KEY="2222"
PUSHER_APP_SECRET="2222"
PUSHER_HOST=
PUSHER_PORT=443
PUSHER_SCHEME="https"
PUSHER_APP_CLUSTER="ap1"

Trong file : bootstrap/app.php thêm đoạn code :

$middleware->validateCsrfTokens(['/pusher/auth','/pusher/presenceauth']);

để loại bỏ csrf với các url

Còn ở frontend blade :

  <!-- Pusher -->
    <script src="https://js.pusher.com/8.2.0/pusher.min.js"></script>

    <script>
        // comment để bỏ log dòng sau : 
        Pusher.logToConsole = true;
        var pusher = new Pusher('{{ env('PUSHER_APP_KEY') }}', {
            cluster: 'ap1',
            userAuthentication:{
                endpoint: "{{ route('userauth') }}",
                params: {
                    _token: document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
                },
                headers: {
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '',
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
                customHandler: null,
            },
            presenceAuthentication: {
                endpoint: "{{ route('presenceauth') }}",
                transport: 'ajax',
                params: {
                    _token: document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || ''
                },
                headers: {
                    'X-CSRF-TOKEN': document.querySelector('meta[name="csrf-token"]')?.getAttribute('content') || '',
                    'Content-Type': 'application/json',
                    'Accept': 'application/json'
                },
            }
        });
        var userId = "{{ \Illuminate\Support\Facades\Auth::id() ?? '' }}"; // Nếu user chưa đăng nhập thì là ''
        if (userId) {
            var private_channel = ['private-chat-', 'private-order-'];
            private_channel.forEach((channel) => {
                const subscribedChannel = pusher.subscribe(channel + userId);
                subscribedChannel.bind("chat", function (data) {
                    console.log("New private event:", data);
                    alert(JSON.stringify(data));
                });
            });

            var presence_channel = ['presence-chat', 'presence-order'];
            presence_channel.forEach((channel) => {
                const subscribedChannel = pusher.subscribe(channel);

                // Lắng nghe sự kiện khi có người dùng mới tham gia kênh
                subscribedChannel.bind("pusher:subscription_succeeded", function (members) {
                    console.log("🔹 User list:", members);
                    document.getElementById("messages").innerHTML += "<p>✅ Connected to presence channel: " + channel + "</p>";
                });

                // Lắng nghe khi có người tham gia
                subscribedChannel.bind("pusher:member_added", function (member) {
                    console.log("➕ New member joined:", member);
                });

                // Lắng nghe khi có người rời khỏi
                subscribedChannel.bind("pusher:member_removed", function (member) {
                    console.log("❌ Member left:", member);
                });

                // Lắng nghe tin nhắn trong kênh
                subscribedChannel.bind("chat", function (data) {
                    console.log("📩 Presence Chat:", data);
                    alert(JSON.stringify(data));
                });
            });

        }

        var public_channel = ['public-chat', 'public-order'];
        public_channel.forEach((channel) => {
            const subscribedChannel = pusher.subscribe(channel);
            subscribedChannel.bind("chat", function (data) {
                console.log("New public event:", data);
                document.getElementById("messages").innerHTML += "<p>" + data.message + "</p>";
                alert(JSON.stringify(data));
            });
        });
    </script>

Trong controller ta code như sau để dùng broadcast public và private :

<?php

namespace App\Http\Controllers;

use App\Events\PrivateEvent;
use App\Events\Pusher\PublicEvent;
use App\Events\ThongbaoEvent;
use Illuminate\Http\Request;

class NotificationController extends Controller
{
    public function public()
    {
        $message = 'Xin chào';
        broadcast(new PublicEvent($message));
        return response()->json(['message' => 'Notification pusher public sent']);
    }
    public function private(){
        $message = 'Xin chào private test channel';
        broadcast(new \App\Events\Pusher\PrivateEvent($message,2));
        return response()->json(['message' => 'Notification private sent']);
    }
}

Event Public :

<?php

namespace App\Events\Pusher;

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;

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

    /**
     * Create a new event instance.
     */
    public $message;
    public function __construct($message)
    {
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn()
    {
        return [
            new Channel('public-chat'),
        ];
    }
    public function broadcastAs(){
        return 'chat';
    }
}

Event Private :

<?php

namespace App\Events\Pusher;

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 Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\Log;

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

    /**
     * Create a new event instance.
     */
    public $message;
    public  $user_id ;
    public function __construct($message,$user_id)
    {
        $this->user_id = $user_id;
        $this->message = $message;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return array<int, \Illuminate\Broadcasting\Channel>
     */
    public function broadcastOn()
    {
        $tenchanel = 'private-chat-'.$this->user_id;
        Log::info($tenchanel);
        return [
            new PrivateChannel($tenchanel),
        ];
    }
    public function broadcastAs(){
        return 'chat';
    }
    public function broadcastWith(){
        return [
            'message' => $this->message,
        ];
    }
}

Như vậy là đã xong .Ở đây mình đã test private còn presence thì chưa nhé