从 0 开始打造聊天室,搞定 Laravel 实时通信 —— 准备工作
前言
Laravel 实时通信的三板斧分别是 Websocket 组件 Laravel-websocket(pusher)
, Laravel-echo
和 Broadcast
广播功能来实现。
之前写过一篇 博客:一起来实现单用户登录 —— 完成监听 ,本文算是一个进阶版,文章篇幅有点长,如果你能耐心看完呢,我想你会有收获的。
源码地址: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
表,创建几个话题,直接写库即可。
再打开 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 协议》,转载必须注明作者和本文链接
推荐文章: