浓重分析PHP的Laravel框架中的event事件操作_php技能_脚本之家

有时候当我们单纯的看 Laravel
手册的时候会有一些疑惑,比如说系统服务下的授权和事件,这些功能服务的应用场景是什么,其实如果没有经历过一定的开发经验有这些疑惑是很正常的事情,但是当我们在工作中多加思考会发现有时候这些服务其实我们一直都见过。下面就事件、事件监听举一个很简单的例子你就会发现。

即时交互的应用


这个例子是关于文章的浏览数的实现,当用户查看文章的时候文章的浏览数会增加1,用户查看文章就是一个事件,有了事件,就需要一个事件监听器,对监听的事件发生后执行相应的操作,其实这种监听机制在
Laravel 中是通过观察者模式实现的.

大家应该都有所体会,在现代的 Web
应用中很多场景都需要运用到即时通讯,比如说最常见的支付回调,与三方登录。这些业务场景都基本需要遵循以下流程:

注册事件以及监听器首先我们需要在
app/Providers/目录下的EventServiceProvider.php中注册事件监听器映射关系,如下:

客户端触发相关业务,并产生第三方应用的操作 客户端等待服务端响应结果
第三方应用通知服务端处理结果 服务端通知客户端处理结果
客户端依据结果做出反馈

protected $listen = [ 'AppEventsBlogView' => [ 'AppListenersBlogViewListener', ], ];

在过去,为了实现这种即时通讯,能让客户端正确响应处理结果,最为常用的技术就是轮询,因为
HTTP
协议的单向性,客户端只能一遍一遍的主动询问服务端的处理结果。这种方式有显见的缺陷,占用服务端资源不说,还不能实时获得服务端处理结果。

然后项目根目录下执行如下命令

现在,我们可以使用 WebSocket
协议来处理实时交互,它是一种双向协议,允许服务端主动推送信息到客户端。本篇我们将借助
Laravel 强大的事件系统来构建实时的交互。你将需要用到以下知识:

php artisan event:generate

Laravel Event Redis Socket.io Node.js

该命令完成后,会分别自动在 app/Events和app/Listensers目录下生成
BlogView.php和BlogViewListener.php文件。

Redis

post = $post; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return []; }}

在开始之前,我们需要开启一个 redis 服务,并在 Laravel
应用中进行配置启用,因为在整个流程中,我们需要借助 redis
的订阅和发布机制来实现即时通讯。

其实看到这些你会发现该事件类只是注入了一个
Post实例罢了,并没有包含多余的逻辑。

Redis
是一个开源高效的键值对存储系统。它通常作为一个数据结构服务器来存储键值对,它可以支持字符串,散列,列表,集合和有序结合。在
Laravel 中使用 Redis 你需用通过 Composer 来安装 predis/predis 包文件。

定义监听器事件监听器在handle方法中接收事件实例,event:generate命令将会自动在handle方法中导入合适的事件类和类型提示事件。在handle方法内,你可以执行任何需要的逻辑以响应事件,我们的代码实现如下:

配置

session = $session; } /** * Handle the event. * * @param BlogView $event * @return void */ public function handle { $post = $event->post; //先进行判断是否已经查看过 if (!$this->hasViewedBlog { //保存到数据库 $post->view_cache = $post->view_cache + 1; $post->save(); //看过之后将保存到 Session $this->storeViewedBlog; } } protected function hasViewedBlog { return array_key_exists($post->id, $this->getViewedBlogs; } protected function getViewedBlogs() { return $this->session->get; } protected function storeViewedBlog { $key = 'viewed_Blogs.'.$post->id; $this->session->put; }}

Redis 在应用中的配置文件存储在
config/database.php,在这个文件中,你可以看到一个包含了 Redis 服务信息的
redis 数组:

注释中也已经说明了一些逻辑。

'redis' => [ 'cluster' => false, 'default' => [ 'host' => '127.0.0.1', 'port' => 6379, 'database' => 0, ],]

浓重分析PHP的Laravel框架中的event事件操作_php技能_脚本之家。触发事件事件和事件监听完成后,我们要做的就是实现整个监听,即触发用户打开文章事件在此我们使用和
Event提供的 fire方法,如下:

如果你修改了 redis 服务的端口,请保持配置文件中的端口一致。

withPost; }}

Laravel Event

现在打开页面发现数据库中的`view_cache已经正常加1了,这样整个就完成了。

这里我们需要借助 Laravel 强大的事件广播能力:

事件广播简介:Laravel 5.1
之中新加入了事件广播的功能,作用是把服务器中触发的事件通过websocket服务通知客户端,也就是浏览器,客户端js根据接受到的事件,做出相应动作。本文会用简单的代码展示一个事件广播的过程。

广播事件

redis nodejs, socket.io laravel 5.1
config/broadcasting.php中,如下配置’default’ =>
env(‘BROADCAST_DRIVER’, ‘redis’),,使用redis作为php和js的通信方式。
config/database.php中配置redis的连接。

很多现代化的应用中,会使用 Web Sockets
来实现实时交互的用户接口。当一些数据在服务端变更时,一条消息会通过
WebSocket 连接来传递到客户端进行处理。

定义一个被广播的事件:根据Laravel文档的说明,想让事件被广播,必须让Event类实现一个IlluminateContractsBroadcastingShouldBroadcast接口,并且实现一个方法broadcastOn。broadcastOn返回一个数组,包含了事件发送到的channel。如下:

为了帮助你构建这种类型的应用。Laravel 使通过 WebSocket
连接进行广播事件变的非常简单。Laravel
允许你广播事件来共享事件的名称到你的服务端和客户端的 JavaScript 框架。

namespace AppEvents;use AppEventsEvent;use IlluminateQueueSerializesModels;use IlluminateContractsBroadcastingShouldBroadcast;class SomeEvent extends Event implements ShouldBroadcast{ use SerializesModels; public $user_id; /** * Create a new event instance. * * @return void */ public function __construct { $this->user_id = $user_id; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return ['test-channel']; }}

配置

被广播的数据:默认情况下,Event中的所有public属性都会被序列化后广播。上面的例子中就是$user_id这个属性。你也可以使用broadcastWith这个方法,明确的指出要广播什么数据。例如:

所有的事件广播配置选项都被存储在 config/broadcasting.php
配置文件中。Laravel 附带了几种可用的驱动如 Pusher,Redis,和
Log,我们将使用 Redis 作为广播驱动,这里需要依赖 predis/predis 类库。

public function broadcastWith(){ return ['user_id' => $this->user_id];}

由于默认的广播驱动使用的是 pusher,所以我们需要在 .env
文件中设置BROADCAST_DRIVER=redis

Redis和Websocket服务器:需要启动一个Redis,事件广播主要依赖的就是redis的sub/pub功能,具体可以看redis文档需要启动一个websocket服务器来和client通信,建议使用socket.io,代码如下:

我们创建一个 WechatLoginedEvent 事件类用来在用户扫描微信登录后进行广播:

var app = require.createServer;var io = require;var Redis = require;var redis = new Redis('6379', '192.168.1.106');app.listen { console.log;});function handler { res.writeHead;}io.on('connection', function { console.log;redis.psubscribe('*', function { console.log;redis.on('pmessage', function(subscribed, channel, message) { console.log; console.log; console.log; message = JSON.parse; io.emit(channel + ':' + message.event, message.data);});
token = $token; $this->channel = $channel; } /** * Get the channels the event should be broadcast on. * * @return array */ public function broadcastOn() { return [$this->channel]; } /** * Get the name the event should be broadcast on. * * @return string */ public function broadcastAs() { return 'wechat.login'; }}

这里需要注意的是redis.on方法的定义,接收到消息后,给client发送一个事件,事件名称为channel

其中你需要注意 broadcastOn 方法应返回一个数组,它表示所需广播的频道,而
broadcastAs 返回的是一个字符串,它表示广播所触发的事件,Laravel
默认的是返回事件类的全类名,这里是 AppEventsWechatLoginedEvent.

  • ‘:’ + message.event。

最重要的是你需要手动的让该类实现 ShouldBroadcast 契约。Laravel
在生成事件时,已经自动添加了该命名空间,该契约只约束 broadcastOn 方法。

客户端代码:客户端我们也使用socket.io,作为测试,代码尽量简化,仅仅打印一个接受到的数据即可。如下:

事件完成接下来就是触发事件了,简单的一行代码就可以:

var socket = io('http://localhost:6001');socket.on('connection', function  { console.log;socket.on('test-channel:App\Events\SomeEvent', function{ console.log;console.log;
event(new WechatLoginedEvent;

服务器触发事件:直接在router中定义个事件触发即可。如下:

这个操作会自动的触发事件的执行并将信息广播出去。该广播操作底层借助了
redis 的订阅和发布机制。

Route::get{ Event::fire(new AppEventsSomeEvent; return "hello world";});

RedisBroadcaster
会将事件中的允许公开访问的数据通过给定的频道发布出去。如果你想对公开的数据拥有更多的控制,你可以在事件中添加
broadcastWith 方法,它应该返回一个数组:

启动redis 启动websocket
打开带有客户端代码的页面,可以看到websocket已经连接成功。
触发事件,打开另一个页面 localhost/event。

/** * Get the data to broadcast. * * @return array */ public function broadcastWith() { return ['user' => $this->user->id]; }

这时就可以发现,第一个页面的console中打印出了Object{user_id:
3},说明广播成功。

Node.js 和 Socket.io

对于发布出去的信息,我们需要一个服务来对接,让其能对 redis
的发布能够进行订阅,并且能把信息以 WebSocket
协议转发出去,这里我们可以借用 Node.js 和 socket.io
来非常方便的构建这个服务:

// server.jsvar app = require.createServer;var io = require;var Redis = require;var redis = new Redis();app.listen { console.log ;});function handler { res.writeHead;}io.on('connection', function  { socket.on('message', function  { console.log socket.on('disconnect', function () { console.log});redis.psubscribe('*', function ;redis.on('pmessage', function (subscrbed, channel, message) { message = JSON.parse; io.emit(channel + ':' + message.event, message.data);});

这里我们使用 Node.js 引入 socket.io 服务端并监听 6001 端口,借用 redis
的 psubscribe 指令使用通配符来快速的批量订阅,接着在消息触发时将消息通过
WebSocket 转发出去。

Socket.io 客户端

在 web 前端,我们需要引入 Socket.io 客户端开启与服务端 6001
端口的通讯,并订阅频道事件:

// client.jslet io = requirevar socket = io socket.on($channel + ':wechat.login',  => { socket.close() // save user token and redirect to dashboard})

至此整个通讯闭环结束,开发流程看起来就是这样的:

在 Laravel 中构建一个支持广播通知的事件 设置需要进行广播的频道及事件名称
将广播设置为使用 redis 驱动 提供一个持续的服务用于订阅 redis
的发布,及将发布内容通过 WebSocket 协议推送到客户端 客户端打开服务端
WebSocket 隧道,并对事件进行订阅,根据指定事件的推送进行响应。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。

发表评论

电子邮件地址不会被公开。 必填项已用*标注