从 0 开始打造聊天室,搞定 Laravel 实时通信 —— 准备工作

从 0 开始打造聊天室,搞定 Laravel 实时通信 —— 准备工作

前言

Laravel 实时通信的三板斧分别是 Websocket 组件 Laravel-websocket(pusher), Laravel-echoBroadcast 广播功能来实现。

之前写过一篇 博客:一起来实现单用户登录 —— 完成监听 ,本文算是一个进阶版,文章篇幅有点长,如果你能耐心看完呢,我想你会有收获的。

从 0 开始打造聊天室,搞定 Laravel 实时通信 —— 准备工作

源码地址:github.com/hiccup711/chatting


准备工作

新建一个 Laravel 项目,使用 Installer 或者 Composer 随意。

因为实时通信需要使用 Laravel 框架的广播功能,相关配置可以在 app/config/broadcasting.php 文件中看到,Laravel 框架默认是没有加载广播服务的,首先我们需要先加载它。

打开 app/config/app.php 找到下方的 providers

    /*
    * Application Service Providers...
    */
    App\Providers\AppServiceProvider::class,
    App\Providers\AuthServiceProvider::class,
    App\Providers\BroadcastServiceProvider::class, // 开启广播服务功能
    App\Providers\EventServiceProvider::class,
    App\Providers\RouteServiceProvider::class,

简单讲一下 App\Providers\BroadcastServiceProvider::class,打开这个文件,看到里面注册了路由 Broadcast::routes(); 并加载了 routes/channels.php 文件,这是对用户访问频道的身份验证

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

看着是不是有点眼熟?这个鉴权形式和 Policy 有些类似,可以理解为请求的用户 ID 和当前授权的用户 ID 是否相等,如果相等,就可以访问这个频道,这个我们后面使用私有频道时会再次讲到。

创建事件

php artisan make:event ChatEvent

如果需要发送广播,需要继承 ShouldBroadcast 接口。
打开 app/events/ChatEvent.php

<?php
...
class ChatEvent implements ShouldBroadcast // 实现接口

测试广播

我们已经定义好了 event 事件,下面来测试一下,打开 routes/web.php

Route::get('/', function () {
    event(new \App\Events\ChatEvent()); // 触发一下即可
    return view('welcome');
});

打开你的项目地址首页,刷新一下,然后我们去 storage/logs 目录,会看到刚刚的一条广播日志

[2022-01-22 15:55:45] local.INFO: Broadcasting [App\Events\ChatEvent] on channels [chatting] with payload:
{
    "socket": null
}

功能正常,因为我们没有写任何内容,消息是 null

想要自定义消息也很简单,回到 ChatEvent.php

class ChatEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
    public $message;
    /**
    * Create a new event instance. * * @return void
    */  
    public function __construct($message)
    {
        $this->message = $message;
    }

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

再回到 web.php

Route::get('/', function () {
 event(new \App\Events\ChatEvent('Say Hello'));
 return view('welcome');
});

再去看一眼 storage/logs

[2022-01-22 16:05:13] local.INFO: Broadcasting [App\Events\ChatEvent] on channels [chatting] with payload:
{
    "message": "Say Hello",
    "socket": null
}

至于为什么会写到日志里面,那是因为 Laravel 默认在 .env 环境文件中把 BROADCAST_DRIVER 设定为了 log。

一切都准备好了,接下来我们开始实现聊天室功能。


创建数据表

php artisan make:model Topic -a // 话题表

迁移文件

public function up()
{
 Schema::create('topics', function (Blueprint $table) {
  $table->id();
  $table->string('name');
  $table->timestamps();
  });
}

一个话题 Topic 有多个讨论 Discussions
一个用户 User 可以参加多个 Topic

php artisan make:model Discussion -a // 讨论表

迁移文件

public function up()
    {
        Schema::create('discussions', function (Blueprint $table) {
            $table->id();
            $table->integer('user_id')->index();
            $table->integer('topic_id')->index();
            $table->string('body');
            $table->timestamps();

            $table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
            $table->foreign('topic_id')->references('id')->on('topics')->onDelete('cascade');
        });
    }

一个 Topic 也可以有多个用户 User

php artisan make:migration create_topic_user_table --create=topic_user // 用户和话题的中间表,多对多关系

迁移文件

public function up()
    {
        Schema::create('topic_user', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id')->index(); // 注意字段类型必须和关联表的字段类型一致,Laravel 的 ID 字段类型是 unsignedBigInteger
            $table->unsignedBigInteger('topic_id')->index();
            $table->timestamps();
        });
    }

执行迁移

php artisan migrate

来定义一下模型关系

Models/User.php

// 一个用户拥有多条发言
public function discussions()
{
    return $this->hasMany(Discussion::class);
}
// 一个用户可以加入多个讨论话题,一个话题拥有多个用户
public function topics()
{
    return $this->belongsToMany(Topic::class);
}

Models/Disscussion

protected $guarded = [];

public function user()
{
    return $this->belongsTo(User::class);
}

public function topic()
{
    return $this->belongsTo(Topic::class);
}

Model/Topic

protected $guarded = [];

public function users()
{
    return $this->belongsToMany(User::class);
}

public function discussions()
{
    return $this->hasMany(Discussion::class)->with('user'); // 返回用户的讨论数据时,一并返回 user 信息,后面会用到
}

前端脚手架

添加前端模板,我们仅在开发环境中使用,因为正式的生产环境都是打包好的前端文件,不需要加载这个包,所以我们添加 --dev 参数。

composer require laravel/ui --dev

引入 Vue 和 Bootstrap

php artisan ui bootstrap
php artisan ui vue
php artisan ui:auth // 创建用户脚手架

安装前端依赖并编译

npm install && npm run dev

如果你遇到了以下报错:

Error: Cannot find module 'webpack/lib/rules/DescriptionDataMatcherRulePlugin'

尝试升级 vue-loader

npm update vue-loader

现在前端文件应该编译成功了,因为我们要编写一些前端代码,所以还需要配置一下 laravel mix,让开发流程更顺畅。

打开项目根目录的 webpack.mix.js 文件:

mix.js('resources/js/app.js', 'public/js')
    .vue().version()  // 添加 .version() 避免缓存
    .sass('resources/sass/app.scss', 'public/css');

mix.browserSync({
    proxy: 'chat.test' // 你的本地项目地址
});

我们使用了 broswerSync 这样就不需要每次更改前端代码后都去 F5 刷新页面。

再次执行

npm run watch

这时,你的浏览器应该会自动打开项目地址,并且每次修改前端代码都会自动刷新。

再来添加前端的 websocket 监听组件

npm install laravel-echo pusher-js --save

接下来我们来添加一些测试数据

首先打开项目地址 /register,注册两个账号,然后打开 topics 表,创建几个话题,直接写库即可。

从 0 开始打造聊天室,搞定 Laravel 实时通信 —— 前端部分

再打开 topic_user,关联用户与话题的关系,如果你懒得手动写的话,直接复制执行以下 SQL 代码。

INSERT INTO `chat`.`topic_user`(`id`, `user_id`, `topic_id`, `created_at`, `updated_at`) VALUES (1, 1, 1, '2022-01-31 13:45:50', '2022-01-31 13:45:50');
INSERT INTO `chat`.`topic_user`(`id`, `user_id`, `topic_id`, `created_at`, `updated_at`) VALUES (2, 1, 2, '2022-01-31 13:46:00', '2022-01-31 13:46:04');
INSERT INTO `chat`.`topic_user`(`id`, `user_id`, `topic_id`, `created_at`, `updated_at`) VALUES (3, 2, 1, '2022-01-31 13:46:11', '2022-01-31 13:46:13');

一切准备就绪,下面我们只需要专注开发部分就可以了。

本作品采用《CC 协议》,转载必须注明作者和本文链接
悲观者永远正确,乐观者永远前行。
本帖由系统于 2年前 自动加精
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 12

file,BroadcastServiceProvider这个文件吧

2年前 评论
MArtian (楼主) 2年前
天上白玉京

mark

2年前 评论

好文章,已收藏,感谢贡献

2年前 评论

pusher有Soketi的教程吗 :joy:

1年前 评论

跟着做有成功
不过我开两种浏览器在测试
有时候只有单边msg有同步更新
不知道是不是浏览器的问题

1年前 评论
MArtian (楼主) 1年前

laravel版本是8
npm run watch 走到这步走不通了
网上找了很多关于laravel mix ,没解决这个问题
file

1年前 评论
MArtian (楼主) 1年前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!