Roy-Try-Catch
← Back to list

【Laravel】利用websocket製作聊天室

Roy • Updated 2026-10-15 16:02:37

更新提醒(2026-10):本文寫於 2022 年,使用 beyondcode/laravel-websockets、Jetstream Livewire 與 Laravel Mix 的設定。這些套件的版本與前端建置方式可能已與新版 Laravel 不相容(新版專案多半改用 Vite,官方也提供 Reverb),請自行確認各套件目前的維護狀態。較新的作法可參考 【RealTime】Laravel Reverb - 官方 WebSocket Server 完整實作。

本篇介紹如何在 Laravel 框架中,利用 WebSocket 製作一個聊天室。

先想一下整體流程:

登入→列表→加入聊天室→聊天室→傳送、接收訊息(socket)

接著規劃大致需要的資料表(Tables):

users(會員)

rooms(聊天室)

user_room(會員與聊天室的關聯)

messages(聊天訊息)

這個範例是用自己的主機當作 socket 伺服器

不想自己設定主機參數的話,可以改用 Pusher

# 新增Project
laravel new {project}

修改 .env

# .env
BROADCAST_DRIVER=pusher
PUSHER_APP_ID=chat_room
PUSHER_APP_KEY=chatchatchat
PUSHER_APP_SECRET=chatchatchatchatchatchat

修改 bootstrap.js

# /resources/js/bootstarp.js
import Echo from 'laravel-echo'
window.Pusher = require('pusher-js');
window.Echo = new Echo({
    broadcaster: 'pusher',
    encrypted: false,
    key: process.env.MIX_PUSHER_APP_KEY,
    cluster: process.env.MIX_PUSHER_APP_CLUSTER,
    wsHost: window.location.hostname,
    wsPort: 6001,
    forceTLS: false,
    disableStats: true
});

安裝套件

# 會員套件&啟用
npm install
composer require laravel/jetstream
php artisan jetstream:install livewire
npm run dev
# socket套件
composer install
composer require pusher/pusher-php-server ^4.1
npm install --save laravel-echo pusher-js 
composer require beyondcode/laravel-websockets
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="migrations"
php artisan migrate
php artisan vendor:publish --provider="BeyondCode\LaravelWebSockets\WebSocketsServiceProvider" --tag="config"

資料庫規劃

users

users

rooms

rooms

user_room

user_room

messages

messages

接著規劃 broadcast 的 channel:

至少在聊天室裡要能即時推播訊息(依 room_id 區分)。

(以下為部分程式碼)

app\Http\Controllers\MessageController

namespace App\Http\Controllers;

use App\Models\Room;
use App\Models\RoomJoin;
use App\Models\Message;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use App\Events\RoomMessageChannelEvent;
use Carbon\Carbon;

class MessageController extends Controller
{
    /**
     * @var Room
     */
    public $model;

    /**
     * @var RoomJoin
     */
    public $join;

    /**
     * @var Message
     */
    public $message;

    /**
     * RoomController constructor.
     *
     * @param Room $room
     * @param RoomJoin $join
     * @param Message $message
     */
    public function __construct(Room $room, RoomJoin $join, Message $message)
    {
        $this->model = $room;
        $this->join = $join;
        $this->message = $message;
    }

    /**
     * @param \Illuminate\Http\Request $request
     * @param $id
     *
     * @Author: Roy
     * @DateTime: 2021/10/23 下午 12:15
     */
    public function store(Request $request, $id)
    {
        if ($this->checkRoomAndUser($id) === false) {
            return response()->json([
                'status' => false,
                'message' => '房間狀態有誤',
                'redirect_uri' => route('room.chat', ['id' => $id]),
            ]);
        }
        $this->message->create(
            [
                'user_id' => Auth::user()->id,
                'room_id' => $id,
                'date' => Carbon::now()->toDateString(),
                'content' => $request->get('content'),
            ]
        );
        broadcast((new RoomMessageChannelEvent(['user' => Auth::user(), 'content' => $request->get('content')], $id)));
        return response()->json([
            'status' => true,
            'message' => null,
        ]);
    }
}

app\event\RoomMessageChannelEvent

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 Illuminate\Support\Arr;
use Carbon\Carbon;

/**
 * Class RoomMessageChannelEvent
 *
 * @package App\Events\Rooms
 * @Author: Roy
 * @DateTime: 2021/10/21 下午 02:34
 */
class RoomMessageChannelEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public $message;
    public $room_id;

    /**
     * RoomMessage constructor.
     *
     * @param $message
     * @param $room_id
     *
     * @Author: Roy
     * @DateTime: 2021/10/21 上午 11:54
     */
    public function __construct($message, $room_id)
    {
        $this->room_id = $room_id;
        $this->message = (object) [
            'user' => (object) [
                'id' => Arr::get($message, 'user.id'),
                'name' => Arr::get($message, 'user.name'),
                'image' => sprintf("https://ui-avatars.com/api/?name=%s&color=7F9CF5&background=EBF4FF",
                    Arr::get($message, 'user.name')),
            ],
            'content' => Arr::get($message, 'content'),
            'time' => Carbon::now()->format('H:i'),
        ];
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PresenceChannel(sprintf("chat.%s", $this->room_id));
    }
}

前端的 JavaScript 使用 laravel-echo、jQuery 與 Mustache。

啟動 WebSocket(預設連接埠:6001)

# 預設port:6001
php artisan websockets:serve
//php artisan websockets:serve --port=6001

範例畫面

範例

GitHub 範本

https://github.com/cc711612/chat_room

範本

https://chat.usongrat.tw

參考文獻

https://beyondco.de/docs/laravel-websockets/getting-started/introduction

https://learnku.com/docs/laravel/8.x/broadcasting/9388#presence-channels

延伸閱讀

Comments

No comments yet.

請先登入