Laravel中的Event机制-云海天教程


本站和网页 https://www.yht7.com/news/31027 的作者无关,不对其内容负责。快照谨为网络故障时之索引,不代表被搜索网站的即时页面。

Laravel中的Event机制-云海天教程
请选择分类
HTML
HTML5
CSS
CSS3
JavaScript
HTML DOM
SQL
MySQL
C语言
C++
C#
Vue.js
Node.js
AJAX
jQuery
PHP
XML
正则表达式
Python
Python3
JAVA
Go
Hibernate
Linux
Docker
JSP
Nginx
WeFlow
ASP.NET
VB语言
Kubernetes
368
博客
B2B商城
网站首页
HTML
CSS
JS
Nodejs
Python3
Java
C++
Linux
Go
MySQL
PHP
Kubernetes
在线工具
技术新闻
当前位置:
云海天教程网 > 技术新闻 > 服务端开发/管理 > 正文
Laravel中的Event机制
作者: 佚名 来源: 网络转载
时间:2020-03-17
Laravel 的 事件(Event) 提供了简单的 观察者模式 实现,允许你 订阅 和 监听 应用中的事件。
事件类通常存放在 app/Events 目录,监听器存放在 app/Listeners。
事件为应用功能模块解耦提供了行之有效的解决办法,因为单个事件可以有多个监听器,一个监听器也可以监听多个事件,而这些事件之间,监听器之间并不相互依赖。
1,事件 放在 app/Events目录下,比如
<?php
namespace AppEventsOrderShipped;
use AppModelsOrder as OrderModel;
use IlluminateQueueSerializesModels;
use IlluminateFoundationEventsDispatchable;
use IlluminateBroadcastingInteractsWithSockets;
class Shipped
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* @var $order OrderModel
*/
public $order;
public $goodsId;
public function __construct(OrderModel $order)
$this->order = $order;
$this->goodsId = $order->goods_id;
事件类是一个处理与事件相关的简单数据容器。例如在这里,假设我们生成的 OrderShipped 事件接收一个 Eloquent ORM 对象,OrderModel, 该事件类不包含任何特定逻辑,只是一个存放被购买的 Order 对象的容器,如果事件对象被序列化的话,事件使用的 SerializesModels trait 将会使用 PHP 的 serialize 函数序列化所有 Eloquent 模型。 Eloquent 模型将会在事件被执行时优雅地序列化和反序列化。如果你的事件在构造函数中接收 Eloquent 模型,只有模型的主键会被序列化到队列,当任务真正被执行的时候,事件系统会自动从数据库中获取整个模型实例。这对应用而言是完全透明的,从而避免序列化整个 Eloquent 模型实例引起的问题。
2, 监听器 放在 app/Listeners目录下,比如
<?php
namespace AppListenersOrder;
use AppEventsOrderShipped as EventOrderShipped;
use AppJobsNotifyShippedNotify;
use IlluminateContractsQueueShouldQueue;
class Shipped implements ShouldQueue
/**
* Create the event listener.
* @return void
*/
public function __construct()
//
/**
* Handle the event.
* @param object $event
* @return void
*/
public function handle(EventOrderShipped $event)
//发送模板消息
dispatch(new ShippedNotify($event->order));
3,绑定监听 在 app/Providers/EventServiceProvider 中
<?php
namespace AppProviders;
use IlluminateAuthEventsRegistered;
use IlluminateAuthListenersSendEmailVerificationNotification;
use AppListenersQueryListener;
use IlluminateFoundationSupportProvidersEventServiceProvider as ServiceProvider;
use AppEventsOrder{
Shiped as OrderShipedEvent
};
use AppListeners{
OrderShipped as OrderShippedListener
};
class EventServiceProvider extends ServiceProvider
/**
* The event listener mappings for the application.
* @var array
*/
protected $listen = [
Registered::class => [
SendEmailVerificationNotification::class, //这一对事件和监听器是框架自带的示例
],
OrderShippedEvent::class => [
OrderShippedListener::class, //这是我们自己绑定的事件和监听器
//other listeners
],
];
/**
* Register any events for your application.
* @return void
*/
public function boot()
parent::boot();
//
注意: 监听器是按照从上到下的先后顺序执行的,前面的监听器执行完之后再执行后面的。如果其中一个监听器 return false 了,则当前事件将被中断,不再被后面的监听器监听到。
上面这种是比较“笨拙”的方式。更有效率的方式是,直接在 EventServiceProvider的 $listener 中 添加上要创建的Event和Listener,并指明目录,比如添加
protected $listen = [
OrderShippedEvent::class => [
OrderShippedListener::class, //已存在的listener
AppListenersOthersOther::class //本次新增的
],
];
然后在终端执行
php artisan event::generate
然后就会发现,在app/Listenners/Others/目录下 自动生成了 Other.php 的类文件,如下
<?php
namespace AppListenersOthers;
use AppEventsOrderShipped;
use IlluminateQueueInteractsWithQueue;
use IlluminateContractsQueueShouldQueue;
class Other
/**
* Create the event listener.
* @return void
*/
public function __construct()
//
/**
* Handle the event.
* @param Shipped $event
* @return void
*/
public function handle(Shipped $event)
//停止事件处理
return false;
//处理业务逻辑
logger()->channel("abc")->info("to process order:".$event->order->id);
var_dump($event->goodsId);
停止事件继续往下传播 有时候,你希望停止事件被传播到其它监听器,你可以通过从监听器的 handle 方法中返回 false 来实现。
注意:当使用SerializesModels trait时 可以减少 事件系统中所传递的数据大小。但是,由于它只传递了主键,然后再使用主键去表中获取数据,如果该数据在事件被监听到并被处理之前 发生了改变,则会导致 进入事件的模型数据 和 事件被处理时的模型数据 不一致。特别是 当前事件是处在一个事务中,且这个事件是一个异步的事件,比如 处在队列中时,前面的事务更改了模型的属性,在提交事务之前,加入异步事务。如果事件比异步事务更晚被处理,则事件被处理时,模型的属性仍未做变更,此时数据仍然是一致的,但会引起一个问题,比如事务是将订单从未发货状态改为已发货状态,然而事件去读的时候,发现仍然是未发货状态。但如果事务更快被处理完,则属性被更改,异步事务后续读到的属性也是被更改后的,这将引起数据的不一致性。
所以我们一定要注意事件的使用场景,事件是用来做解耦的,它既可以做同步操作,也可以把它放入队列,做异步操作。
事件监听器队列
如果监听器将要执行耗时任务比如发送邮件或者发送 HTTP 请求,那么将监听器放到队列是一个不错的选择。在队列化监听器之前,确保已经配置好队列并且在服务器或本地环境启动一个队列监听器。 要指定某个监听器需要放到队列,只需要让监听器类实现 ShouldQueue 接口即可,通过 Artisan 命令 event:generate 生成的监听器类已经将这个接口导入当前命名空间,所有你可以直接拿来使用:
<?php
namespace AppListenersOthers;
use AppEventsOrderShipped;
use IlluminateQueueInteractsWithQueue;
use IlluminateContractsQueueShouldQueue;
class Other implements ShouldQueue
/**
* Create the event listener.
* @return void
*/
public function __construct()
//
/**
* Handle the event.
* @param Shipped $event
* @return void
*/
public function handle(Shipped $event)
//
就是这么简单!当这个监听器被调用的时候,将会使用 Laravel 的队列系统通过事件分发器自动推送到队列。如果通过队列执行监听器的时候没有抛出任何异常,队列任务会在执行完成后被自动删除。
自定义队列连接 & 队列名称 如果你想要自定义事件监听器使用的队列连接和队列名称,可以在监听器类中定义 $connection 和 $queue 属性:
<?php
namespace AppListeners;
use AppEventsOrderShipped;
use IlluminateContractsQueueShouldQueue;
class SendShipmentNotification implements ShouldQueue
/**
* 任务将被推送到的连接名称.
* @var string|null
*/
public $connection = "sqs";
/**
* 任务将被推送到的连接名称.
* @var string|null
*/
public $queue = "listeners";
手动访问队列
如果你需要手动访问底层队列任务的 delete 和 release 方法,在生成的监听器中,默认导入的 IlluminateQueueInteractsWithQueue trait 为这两个方法提供了访问权限:
<?php
namespace AppListeners;
use AppEventsOrderShipped;
use IlluminateQueueInteractsWithQueue;
use IlluminateContractsQueueShouldQueue;
class SendShipmentNotification implements ShouldQueue
use InteractsWithQueue;
public function handle(OrderShipped $event)
if (true) {
$this->release(30);
其中 release 方法是将 事件 重新放入 事件队列中(参数是延迟秒数),deleted 方法 是将事件从事件队列中删除。
源代码如下
/**
* Release the job back into the queue.
* @param int $delay
* @return void
*/
public function release($delay = 0)
if ($this->job) {
return $this->job->release($delay);
/**
* Delete the job from the queue.
* @return void
*/
public function delete()
if ($this->job) {
return $this->job->delete();
处理失败任务
有时候队列中的事件监听器可能会执行失败。如果队列中的监听器任务执行时超出了队列进程定义的最大尝试次数,监听器上的 failed 方法会被调用,failed 方法接收事件实例和导致失败的异常:
<?php
namespace AppListeners;
use AppEventsOrderShipped;
use IlluminateQueueInteractsWithQueue;
use IlluminateContractsQueueShouldQueue;
class SendShipmentNotification implements ShouldQueue
use InteractsWithQueue;
public function handle(OrderShipped $event)
//
public function failed(OrderShipped $event, $exception)
//
分发事件
要分发一个事件,可以传递事件实例到辅助函数 event,这个辅助函数会分发事件到所有注册的监听器。由于辅助函数 event 全局有效,所以可以在应用的任何地方调用它:
<?php
namespace AppHttpControllers;
use AppOrder;
use AppEventsOrderShipped as EventOrderShipped;
use AppHttpControllersController;
class OrderController extends Controller
/**
* 处理给定订单.
* @param int $orderId
* @return Response
*/
public function ship($orderId)
$order = Order::findOrFail($orderId);
// 订单处理逻辑...
event(new EventOrderShipped($order));
这样,大概的流程就是,在业务层发起事务-> ORM模型被序列化(__sleep())放入事件中->事务被EventServiceProvider 监听到-> 调用监听器列表 -> 各监听器从上到下依次执行-> 事件到达第一个监听器 -> ORM模型从事件中被反序列化(__wakeup())出来 -> 监听器执行业务 -> 如果有队列,则handle中release/delete等可以使用,与handle同级有fail()可使用->如果return false,则本次事件结束,后续监听器将不能再监听到本次事件。
总结
1,Event 的使用场景,就是 解耦,将一些和当前业务没有很大关联的边支逻辑,从主业务逻辑中解耦出来,当某些主业务发生时,可以用事件来处理这些边支逻辑。比如,发信息,当订单支付的时候可以发信息,订单发货的时候也可以发信息,而订单的这些业务操作,和发信息是没有太多耦合关系的,那么就可以把 发信息 做成一个事件,每次有订单支付,订单发货,都可以调用此事件。这是Event端。在 监听器端,比如发信息,可以发短息,发微信,发邮件等等,则这个 发信息的事件,就可以对应多个 监听器了。
2,Event 当不使用 ShouldQueue时,它是一个同步操作。当监听器实现了ShouldQueue时,如果 queue.php 这个配置文件中的 default 值是 sync ,它仍然是同步操作。只有变更 default 或者 QUEUE_DRIVER 的值,比如 redis 时,它才是一个异步操作。
3,一个Event可被多个Listener监听,多个Listener会按照先后顺序依次执行,除非被 return false 中断。
4,Event 关注的是 已经发送过的事情,用在已经完成的事情,类似于英语的过去式。如 注册完成,更新完成,发布完成,发送完成。而 Job 关注 进行中的事情。
若本号内容有做得不到位的地方(比如:涉及版权或其他问题),请及时联系我们进行整改即可,会在第一时间进行处理。
下一篇linux 工具使用总结
上一篇避免git pull 输入用户名密码
技术分类
JAVA常见问题 ( 831 ) Pyhton常见问题 ( 12433 ) CMS教程 ( 39 ) 前端开发 ( 9575 ) 编程最新 ( 83967 ) 编程语言 ( 35358 ) 数据库 ( 7103 ) 开发者手册 ( 129 ) 服务端开发/管理 ( 13109 ) 随笔记录 ( 10011 ) Linux命令 ( 383 ) Docker常见问题 ( 110 ) Kubernetes教程 Web在线工具
JetBrains IDEA稳定激活码
技术相关推荐
PHP两个数组值怎么求和
php什么函数能求数组中最大值
php怎么将对象转成数组
php 输出数组乱码怎么办
痞子衡嵌入式:对比恩智浦全系列MCU(包含Kinetis/LPC/i.MXRT/MCX)的GPIO电平中断设计差异
switch多选择结构、循环结构示例详解
SpringBoot整合Zookeeper详细教程【java入门】
Python使用pandas导入csv文件内容的示例代码【python入门】
Java解析使用JSON的多种方法【java基础】【Js面试】
python 使用pandas读取csv文件的方法【python入门】
Python TypeError: ‘float‘ object is not subscriptable错误解决【python爬虫】
ECharts Canvas渲染在SVG合理运用
SpringBoot封装响应处理超详细讲解【java基础】
python pandas 解析(读取、写入)CSV 文件的操作方法【python面试】
部署vue element-ui admin报错的解决方法(vue2)【vue框架】
python+pyhyper实现识别图片中的车牌号思路详解【python基础】
python index() 与 rindex() 方法的使用示例详解【python面试】
如何使用stream从List对象中获取某列数据
uniapp开发打包多端应用完整方法指南
Python 第三方库 openpyxl 的安装过程【python面试】
热门推荐
JavaScript 教程
jQuery 教程
Vue.js 教程
AJAX 教程
Node.js 教程
HTML 教程
HTML 实例
Java 教程
CSS 教程
CSS 实例
MySQL 教程
PHP 教程
C 教程
C语言经典100例
python 教程
python3 教程
go 教程
Linux 教程
Hibernate 学习
Docker 教程
Nginx 教程
在线实例
HTML 实例
CSS 实例
JavaScript 实例
C语言100例
C++ 实例
实例推荐
jQuery 实例
Vue.js 实例
AJAX 实例
java 实例
PHP 学习计划
最新更新
Kubernetes 问题记录【kubernetes中文教程】
Kubernetes 使用技巧【kubernetes中文教程】
Kubernetes Docker最佳实践【kubernetes中文教程】
Kubernetes Kubernetes相关资讯和情报链接【kubernetes中文教程】
Kubernetes Kubernetes中的应用故障排查【kubernetes中文教程】
技术问答
Vue使用NProgress实现页面顶部的进度条显示效果【vue面试】
解读Spring框架中常用的设计模式【java面试】
ECharts图表使用及异步加载的特性示例详解
React项目使用ES6解决方案及JSX使用示例详解【Js基础】
flex布局下两端对齐,不满左对齐
关于我们
免责声明
合作联系
福利专区
Web在线工具
云海天教程网(yht7.com)版权所有 Copyright © 2019-2022
陕公网安备 61050202000585号
陕ICP备14013131号-3