Laravel 5.3 新特性Laravel Echo 使用:实时聊天室

1、什么是 Laravel Echo
Echo是一个让我们在Laravel应用中轻松实现WebSockets(关于WebSockets工作原理和机制可参考这篇文章:WebSocket 实战)功能的工具,同时简化了构建复杂WebSockets交互中更加通用、复杂的部分。

注:Echo 还处于开发阶段,本教程代码和最终发布版本可能会有出入,望知悉。
Echo 由两部分组成:针对Laravel事件广播系统的一系列优化,以及一个新的JavaScript包。

在 Laravel 5.3 中,Echo 后端组件已经集成到Laravel核心库,不需要额外引入(不同于Cashier扩展包),你需要和前端JavaScript配合使用这些组件,而不仅仅是使用Echo JavaScript 库,还会看到 Laravel 在处理 WebSockets时在易用性上的显著优化。

Echo JavaScript 库可以通过NPM引入,这个库基于Pusher JS(JavaScript Pusher SDK)或者Socket.io(JavaScript Redis WebSockets SDK)。

2、什么时候使用Echo
当你需要发送异步实时消息给用户时WebSockets很有用 —— 不管这些消息是通知还是页面更新数据,同时保持用户在同一页面无需刷新。当然,你可以使用长轮询,或者某些定期的JavaScript ping来实现这样的功能,但是这样做在服务端没有更新的情况下对带宽造成浪费,造成一些不必要的请求。相比之下,Websockets功能强大,不会对服务器造成额外负载,可伸缩,速度极快。

如果你想要在Laravel应用中使用WebSockets,Echo提供了干净、简洁的语法来实现各种功能,简单如公共频道,复杂如认证、授权、私有和存在频道。

注:WebSockets实现提供了三种频道:public,意味着所有人可以订阅;private,认证且经过授权的用户才能订阅;presense,不允许发送消息,只通知用户在频道中是否已存在。
3、实现一个简单的广播事件
假设我们想要实现一个有多个房间的聊天室系统,这样我们就需要在每次接收到新的聊天室消息时触发一个事件。

注:你需要熟悉Laravel的事件广播机制以便能更好的理解这篇教程。
因此,首先我们创建这个事件:

php artisan make:event ChatMessageWasReceived
打开这个新生成的类( app/Events/ChatMessageWasReceived.php)并确保其实现了 ShouldBroadcast,接下来我们让其广播到一个名为“chat-room.1”的公共(public)频道。

然后我们为聊天室消息创建一个模型和对应的迁移,该模型包含user_id和message字段:

php artisan make:model ChatMessage --migration
最终,事件类ChatMessageWasReceived 的代码如下:

...
class ChatMessageWasReceived extends Event implements ShouldBroadcast
{
    use InteractsWithSockets, SerializesModels;

    public $chatMessage;
    public $user;

    public function __construct($chatMessage, $user)
    {
        $this->chatMessage = $chatMessage;
        $this->user = $user;
    }

    public function broadcastOn()
    {
        return [
            "chat-room.1"
        ];
    }

}
编辑生成的迁移类代码如下:

...
class CreateChatMessagesTable extends Migration
{
    public function up()
    {
        Schema::create('chat_messages', function (Blueprint $table) {
            $table->increments('id');
            $table->string('message');
            $table->integer('user_id')->unsigned();
            $table->timestamps();
        });
    }

    public function down()
    {
        Schema::drop('chat_messages');
    }
}
还要确保模型中的新增字段在白名单中:

...
class ChatMessage extends Model
{
    public $fillable = ['user_id', 'message'];
}
再然后,在具体场景中触发该事件。为了测试方便,我通常会创建一个Artisan命令来创建事件,让我们来试试。

php artisan make:command SendChatMessage
打开新创建的命令类 app/Console/Commands/SendChatMessage.php,编辑该文件内容如下:

...
class SendChatMessage extends Command
{
    protected $signature = 'chat:message {message}';

    protected $description = 'Send chat message.';

    public function handle()
    {
        // Fire off an event, just randomly grabbing the first user for now
        $user = \App\User::first();
        $message = \App\ChatMessage::create([
            'user_id' => $user->id,
            'message' => $this->argument('message')
        ]);

        event(new \App\Events\ChatMessageWasReceived($message, $user));
    }
}
打开 app/Console/Kernel.php,将刚创建的命令添加$commands属性以将其注册为有效的Artisan命令:

...
class Kernel extends ConsoleKernel
{
    protected $commands = [
        Commands\SendChatMessage::class,
    ];
...
至此,这个事件代码基本完成,你需要注册一个Pusher帐号(Echo也可以处理Redis和Socket.io,但是本例中我们使用Pusher),在该Pusher帐号中创建一个新的应用并获取key、secret以及App ID,然后将这些值设置到.env文件对应的 PUSHER_KEY, PUSHER_SECRET以及 PUSHER_APP_ID。

最后,引入Pusher库:

composer require pusher/pusher-php-server:~2.0
现在你可以通过运行如下命令发送事件到Pusher账户:

php artisan chat:message "Howdy everyone"
如果一切顺利,你应该可以进入到Pusher调试控制台,触发这个事件,看到如下效果:

4、通过Echo实现广播事件
刚刚我们实现了一个简单的推送事件到Pusher的系统,下面我们来看看Echo为我们提供了些什么。

安装Echo JS库

将Echo JavaScript库引入项目最简单的方式就是通过NPM和Elixir。首先,我们引入Pusher JS:

# Install the basic Elixir requirements
npm install
# Install Pusher JS and Echo, and add to package.json
npm install pusher-js --save
npm install laravel-echo --save
接下来,修改 resouces/assets/js/app.js来导入相应文件:

window.Pusher = require('pusher-js');

import Echo from "laravel-echo"

window.echo = new Echo('your pusher key here');
// @todo: Set up Echo bindings here
然后,设置Elixir的gulpfile.js文件让其生效:

var elixir = require('laravel-elixir');

elixir(function (mix) {
    mix.browserify('app.js');
});
最后运行gulp或gulp watch命令将结果文件导入HTML模板,此外还需要添加CSRF令牌输入:

<html>
    <head>
        ...
        <meta name="csrf-token" content="{{ csrf_token() }}">
        ...
    </head>
    <body>
        ...

        <script src="js/app.js"></script>
    </body>
</html>
注:如果是新安装的Laravel应用,需要在编写所有HTML之前运行php artisan make:auth,因为后续的功能需要用到Laravel的认证。
通过Echo订阅公共频道

回到 resources/assets/js/app.js,让我们来监听Echo广播到的公共频道chat-room.1,并将所有收到的信息记录到用户控制台:

window.Pusher = require('pusher-js');

import Echo from "laravel-echo"

window.echo = new Echo('your pusher key here');

echo.channel('chat-room.1')
    .listen('ChatMessageWasReceived', function (data) {
        console.log(data.user, data.chatMessage);
    });
我们告诉Echo:订阅的公共频道名字叫做chat-room.1,监听的事件是 ChatMessageWasReceived,当事件发生时,将其传递给这个匿名函数并执行其中的代码。具体显示如下:

这样通过短短几行代码,我们就可以访问到JSON格式的聊天信息以及相应用户,这些数据不仅可以用来通知用户,还可以用于更新内存数据,从而让每个WebSockets消息实现当前页面数据的更新。

5、通过Echo订阅私有频道

接下来我们让chat-room.1变成私有的。要实现这一目的首先我们需要在频道名称前加上private-前缀,然后编辑事件类ChatMessageWasReceived上的broadcastsOn()方法,设置频道名称为private-chat-room.1。

接下来,使用app.js中的 echo.private()替代之前的echo.channel()。

其他保持不变,但是现在运行脚本会报错:

这就是Echo为我们提供的又一个强大功能:认证和授权。

Echo 基本认证和授权

认证系统由两部分组成,首先,当你第一次打开应用的时候,Echo会发送POST请求到/broadcasting/socket路由,当我们在Laravel端设置好Echo工具后,这个路由会通过你的Laravel session ID关联到相应到Pusher socket ID,这样Laravel和Pusher都知道如何标识给定的Pusher socket连接是否连接到特定的Laravel session。

注:每个JavaScript发起的请求,不管是Vue还是jQuery,都会包含一个对应到socket ID的X-Socket-Id头,但是没有它应用也能正常工作——可以通过更早与session关联的socket ID获取。
其次,Echo的认证和授权功能指的是,当你想要访问一个受保护的资源时,Echo会ping /broadcasting/auth来检查你是否可以访问这个频道,由于你的socket ID会被关联到对应的Laravel session,我们可以为这个路由编写一个简单清晰的ACL规则。

首先,打开config/app.php取消这一行的注释:

// App\Providers\BroadcastServiceProvider::class,
打开这个服务提供者文件(app/Providers/BroadcastServiceProvider.php),内容如下:

...
class BroadcastServiceProvider extends ServiceProvider
{
    public function boot()
    {
        Broadcast::route(['middleware' => ['web']]);

        Broadcast::auth('channel-name.*', function ($user, $id) {
            return true;
        });
    }
其中有两个地方需要注意,首先,Broadcast::route()允许你定义要应用到/broadcasting/socket和/broadcasting/auth的中间件,你可以将其保持为web不变。其次,Broadcast::auth()让我们可以定义指定频道或频道组的权限。

编写私有频道认证权限

现在我们有一个名为private-chat-room.1的频道,以后可能还有多个频道,如private-chat-room.2等,所以我们这里为所有频道定义权限:

Broadcast::auth('chat-room.*', function ($user, $chatroomId) {
    // return whether or not this current user is authorized to visit this chat room
});
正如你所看到的,传递到闭包的第一个值是当前用户,如果有任何*被匹配到,就会作为第二个参数传进来。

注:尽管我们重命名了private-chat-room.1,你可以看到在定义访问权限的时候没必要加上private-前缀。
在这篇博客教程中,我们只是简单演示授权代码,你还需要为聊天室创建一个模型和迁移,以及与用户之间的多对多关联,然后在闭包中检查当前用户是否连接到这个聊天室,现在我们只是简单返回true:

Broadcast::auth('chat-room.*', function ($user, $chatroomId) {
    if (true) { // Replace with real ACL
        return true;
    }
});
测试一下看你会看到什么。

你应该能看到一个空的控制台日志,然后你可以触发Artisan命令,这样会看到用户和聊天室消息,和之前一样,只不过现在需要是经过授权的认证用户。

如果你看到如下消息,也是没有问题的,意思是一切工作正常,只不过你的系统判定你无权访问该聊天室:

6、通过Echo订阅存在频道

现在,我们可以在后台判断哪些用户可以访问聊天室,当用户发送消息到聊天室(类似于通过AJAX发送请求到服务器,只不过在我们的案例中通过Artisan命令取代用户请求),会触发ChatMessageWasReceived事件然后进行广播,将消息通过WebSockets发送给所有认证且授权的用户,下一步,我们要做什么?

假设,我们想要在聊天室中显示哪些用户在线,或者在用户进入或离开时做下提示,这可以通过存在频道来实现。

我们需要做两件事:一个新的Broadcast::auth()权限定义以及一个新的以presence-前缀开头的频道。有趣的是,由于认证定义不需要private-和presence-前缀,所以private-chat-room.1和presence-chat-room.1在Broadcast::auth()中可以共用同一份代码:chat-room.*,这没有什么问题,只要两者认证规则一致。但是这会给大家带来困惑,所以我准备添加一个新的命名,使用presence-chat-room-presence.1。

由于我们只是讨论是否存在,没必要将这个频道绑定到事件,取而代之,只需要在app.js中将我们直接加入到这个频道即可:

echo.join('chat-room-presence.1')
    .here(function (members) {
        // runs when you join, and when anyone else leaves or joins
        console.table(members);
    });
我们加入一个存在频道,然后提供一个回调在用户加载页面或者当有其他用户加入或离开时触发。here会在这三个事件时都触发,此外,还可以进行更加细粒度的控制,可以监听then(当前用户加入),joining(其他用户加入)以及leaving(其他用户离开):

echo.join('chat-room-presence.1')
    .then(function (members) {
        // runs when you join
        console.table(members);
    })
    .joining(function (joiningMember, members) {
        // runs when another member joins
        console.table(joiningMember);
    })
    .leaving(function (leavingMember, members) {
        // runs when another member leaves
        console.table(leavingMember);
    });
再次提醒你可以不在频道名称前加presence-前缀,据我所知,Echo中唯一必须加上presence-前缀的场景,是事件类的broadcastOn()方法中定义事件在私有频道广播。其他所有地方都可以去掉这些前缀,Echo 会自动处理(比如BroadcastServiceProvider中的认证定义),或者通过方法名(JavaScript包中的echo.channel()和echo.private()方法)。

接下来,在BroadcastServiceProvider中为这个频道设置权限:

Broadcast::auth('chat-room-presence.*', function ($user, $roomId) {
    if (true) { // Replace with real authorization
        return [
            'id' => $user->id,
            'name' => $user->name
        ];
    }
});
正如你所看到的,当用户认证后存在频道并不仅仅返回true,而是返回一个包含用户信息的数组,这些用户信息可用于在线用户之类的侧边栏。

如果一切正常,现在你可以在不同浏览器中打开应用,在控制台查看更新的会员列表:

7、排除当前用户

Echo还提供了一个功能:如果你不想让当前用户获取通知怎么做?

也许你所在的聊天室每次都会弹出各种各样的新消息,而你只想在屏幕顶部弹出少量消息,你也不想让发送消息的人收到消息,对不对?

要从接收消息列表中排除当前用户,需要在事件类的构造函数中调用$this->dontBroadcastToCurrentUser()方法:

...
class ChatMessageWasReceived extends Event implements ShouldBroadcast
{
    ...
    public function __construct($chatMessage, $user)
    {
        $this->chatMessage = $chatMessage;
        $this->user = $user;

        $this->dontBroadcastToCurrentUser();
    }

时间: 2024-08-01 17:21:55

Laravel 5.3 新特性Laravel Echo 使用:实时聊天室的相关文章

Laravel 5.2 新特性访问频率限制中间件throttle的使用

1.访问频率限制概述 频率限制经常用在API中,用于限制独立请求者对特定API的请求频率.例如,如果设置频率限制为每分钟1000次,如果一分钟内超过这个限制,那么服务器就会返回 429: Too Many Attempts.响应. 通常,一个编码良好的.实现了频率限制的应用还会回传三个响应头: X-RateLimit-Limit, X-RateLimit-Remaining和 Retry-After(如果达到限制次数只能获取到 Retry-After头). X-RateLimit-Limit告诉

Brizzly推新功能Picnic 将Twitter变成聊天室

北京时间6月7日消息,据国外http://www.aliyun.com/zixun/aggregation/31646.html">媒体报道,随着Twitter不断推出新功能,不少人认为第三方Twitter客户端的需求将受影响,但Twitter第三方客户端Brizzly日前推出的群体聊天功能Picnics有助于扭转这一看法. 据一位测试过该功能的业内人士表示,Brizzly的群体聊天功能非常有用,它可以把Twitter变成一个网络聊天室.这并非一个开创性的概念,但与Twitter的结合能让

Laravel 5.2 新特性多用户认证功能实现详解

Laravel 5.2新增多用户认证支持,即同时允许不同数据表用户(如前台用户.后台用户)登录认证.下面我们就来简单介绍多用户登录及注册功能实现. 1.生成认证脚手架 首先我们使用Laravel 5.2提供的认证脚手架完成初始化工作: php artisan make:auth 该Artisan命令会生成用户认证所需的路由.视图以及HomeController: 去查看路由文件routes.php,会发现该文件已经被更新: Route::group(['middleware' => 'web']

Laravel 5.2 新特性隐式路由模型绑定功能使用例子

Laravel 5.1 中已经提供了路由模型绑定功能,而在 Laravel 5.2中,这一功能实现变得更加简单. 1.路由模型绑定的基本使用 通常我们在URL路由中通过如下方式绑定模型: Route::group(['middleware' => ['web']], function () {     Route::get('/user/{id}',function($id){         $user = \App\User::findOrFail($id);         dd($use

Laravel 5.2 新特性中间件组的定义及使用例子

不管你创建的Laravel应用体量有多大,路由文件routes.php的体积都会随之变得越来越大.对我而言创建一个新应用首先要做的就是根据业务逻辑对路由文件进行拆分和分组,比如"admin"."auth"."public"等.通常分组的每个部分都有其对应的中间件设置,例如,admin会用到一个auth中间件,API分组可能会有不同的auth中间件,并且会有指定的限制访问频率的中间件. Laravel 5.2 引入了中间件组的概念,这是一个为路由规

Laravel 5.2 新特性表单数组输入验证功能实现方法

Laravel 5.2 新增表单数组输入验证,听起来很懵?下面我们以一个具体例子来演示这一功能. 首先在routes.php定义相应路由: Route::get('form','TestController@form'); Route::post('form/validate','TestController@validate'); 然后使用Artisan命令创建对应控制器: php artisan make:controller TestController 编辑生成的TestControll

PHP 5.4正式版重要新特性

PHP一直是在Web开发领域中十分重要而快捷方便的开发语言,深受广大开发人员的青睐.现在PHP 5.4的正式版本已经发布,其中增加了大量新的特性,官方声称性能提高20%,并且占用更少的资源.在本文中,我将带领大家学习PHP 5.4的一些新的特性. 在PHP 5.4中,首先是修复了多达100多处的bug,并且在内存及性能优化上做的更好了,而且去掉了一些之前版本的方法,比如register_globals,magic_quotes,safe_mode等,而且要注意的是,PHP 5.4中,默认的编码方

PHP 5昨天隆重推出--PHP 5/Zend Engine 2.0新特性

前言    今天突然想到PHP官方网站上一转,一眼就看到PHP5推出的通告.虽然以前看到过PHP5的预告,但还是仔细看了PHP 5/Zend Engine 2.0新特性一文,一股JAVA气息扑面而来...   特将该文试译出来,首发于CSDN网站,以飨读者. PHP 5/Zend Engine 2.0新特性徐唤春 译 sfwebsite@hotmail.comhttp://www.php.net/zend-engine-2.php 全新的对象模型PHP中的对象处理部分已完全重写,具有更佳的性能和

PHP5.0新特性(ZT)

php5 (一) Zend 2.0的诞生现在的PHP4所使用的基本文法是被称之为Zend 引擎的脚本编译引擎.这个就是PHP4的优良机能的原因之一,是作为对PHP3的改进而生成的一种语言.大家一直认为,PHP4的性能根据当初的目标,比PHP3有了很大的提升,在网络编程的世界里占据了很大的份额. 开发了Zend 引擎的Zend公司是在开发PHP4的同时,由PHP3的主要开发者Zeev Suraski和Andi Gutmans所创立的企业合并而来的.Zend的名称是由Zeev和Andi的名字合起来组