cordova监听事件中调用其他方法_Laravel模型事件的实现原理详解

模型事件在 Laravel 的世界中,你对 Eloquent 大多数操作都会或多或少的触发一些模型事件,下面这篇文章主要给大家介绍了关于Laravel模型事件的实现原理,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴。

前言

Laravel的ORM模型在一些特定的情况下,会触发一系列的事件,目前支持的事件有这些:creating, created, updating, updated, saving, saved, deleting, deleted, restoring, restored,那么在底层是如何实现这个功能的呢?

下面话不多说了,来一起看看详细的介绍吧。

1.如何使用模型事件

先来看看如何使用模型事件,文档里面写了两种方法,实际上总共有三种方式可以定义一个模型事件,这里以saved事件来做例子,其他事件都一样。

1.events属性

直接上代码:

class User extends Authenticatable {use Notifiable;protected $events = ['saved' => UserSaved::class,];
}

这个数组只是对事件做的一个映射,它定义了在模型的saved的时候会触发UserSaved这个事件,我们还要定义该事件以及其监听器才可以:

amespace AppEvents;
use AppUser;
class UserSaved {public $user;public function __construct(User $user){$this->user = $user;}
}namespace AppListeners;
class UserSavedListener {public function handle(UserSaved $userSaved){dd($userSaved);}
}

然后还要到EventServiceProvider中去注册该事件和监听器:

class EventServiceProvider extends ServiceProvider
{/*** The event listener mappings for the application.** @var array*/protected $listen = ['AppEventsUserSaved' => ['AppListenersUserSavedListener',]];/*** Register any events for your application.** @return void*/public function boot(){parent::boot();}
}

这样在saved节点的时候,UserSaved事件会被触发,其监听器UserSavedListener的handle方法会被调用。

2.观察者

这是文档比较推崇的一个模型事件定义方法,也比较好理解,先定义一个观察者:

use AppUser;
class UserObserver
{/*** 监听用户创建事件.** @param User $user* @return void*/public function created(User $user){//}/*** 监听用户创建/更新事件.** @param User $user* @return void*/public function saved(User $user){//}
}

然后在某个服务提供者的boot方法中注册观察者:

namespace AppProviders;
use AppUser;
use AppObserversUserObserver;
use IlluminateSupportServiceProvider;
class AppServiceProvider extends ServiceProvider
{/*** Bootstrap any application services.** @return void*/public function boot(){User::observe(UserObserver::class);}/*** Register the service provider.** @return void*/public function register(){//}
}

这样在模型事件触发的时候,UserObserver的相应方法就会被调用。其实,在使用观察者的时候,除了一些系统自带的,我们还可以定义一些自己的事件:

class User extends Authenticatable {use Notifiable;protected $observables = ['customing', 'customed'];
}

然后在观察者里面定义同名方法:

class UserObserver
{/*** 监听用户创建/更新事件.** @param User $user* @return void*/public function saved(User $user){//}public function customing(User $user){}public function customed(User $user){}
}

由于是我们自己定义的事件,所以触发的时候也必须手动触发,在需要触发的地方调用模型里面的一个fireModelEvent方法即可。不过由于该方法是protected的,所以只能在自己定义的模型方法里面,当然如果通过反射来调用,或许可以直接在$user对象上触发也说不定,这个我没试,大家可以自行测试下。

class User extends Authenticatable {use Notifiable;protected $observables = ['customing', 'awesoming'];public function custom(){if ($this->fireModelEvent('customing') === false) {return false;}//TODOif ($this->fireModelEvent('customed') === false) {return false;}}
}

3.静态方法定义

我们还可以通过模型上的对应静态方法来定义一个事件,在EventServiceProvider的boot方法里面定义:

class EventServiceProvider extends ServiceProvider{/*** Register any events for your application.** @return void*/public function boot(){parent::boot();User::saved(function(User$user) {});User::saved('UserSavedListener@saved');}
}

通过静态方法定义的时候,可以直接传递进入一个闭包,也可以定义为某个类的方法,事件触发时候传递进入的参数就是该模型实例。

2.模型事件实现原理

Laravel的模型事件所有的代码都在IlluminateDatabaseEloquentConcernsHasEvents这个trait下,先来看看Laravel是如何注册这些事件的,其中的$dispatcher是一个事件的调度器IlluminateContractsEventsDispatcher实例,在IlluminateDatabaseDatabaseServiceProvider的boot方法中注入。

protected static function registerModelEvent($event, $callback){if (isset(static::$dispatcher)) {$name = static::class;static::$dispatcher->listen("eloquent.{$event}: {$name}", $callback);}}

这里是Laravel事件注册的地方,其中以eloquent.saved:AppUser为事件名,$callback作为处理器来注册。这个注册事件的方法,只会注册以观察者和静态方法定义的。假如你定义为模型的$events属性的话,Laravel是不会注册的,会在触发事件的时候同步触发,接下来会分析。

然后在HasEvents中定义了一堆的方法如下,这些就是我们上面通过静态方法来定义事件监听器的原理,不多说一看就懂。

public static function saving($callback){static::registerModelEvent('saving', $callback);
}public static function saved($callback){static::registerModelEvent('saved', $callback);
}

那么如何通过观察者的形式来定义事件监听器呢?看源码:

public static function observe($class){$instance = new static;$className = is_string($class) ? $class : get_class($class);foreach ($instance->getObservableEvents() as $event) {if (method_exists($class, $event)) {static::registerModelEvent($event, $className.'@'.$event);}}
}public function getObservableEvents()
{return array_merge(['creating', 'created', 'updating', 'updated','deleting', 'deleted', 'saving', 'saved','restoring', 'restored',],$this->observables);
}

先获取到observer的类名,然后判断是否存在事件名对应的方法,存在则调用registerModelEvent注册,这里事件名还包括我们自己定义在observables数组中的。

事件以及监听器都定义好后,就是如何触发了,前面说到有一个方法fireModelEvent,来看看源码:

protected function fireModelEvent($event, $halt = true)
{if (! isset(static::$dispatcher)) {return true;}$method = $halt ? 'until' : 'fire';$result = $this->filterModelEventResults($this->fireCustomModelEvent($event, $method));if ($result === false) {return false;}return ! empty($result) ? $result : static::$dispatcher->{$method}("eloquent.{$event}: ".static::class, $this);
}

其中比较关键的一个方法是fireCustomModelEvent,它接受一个事件名以及触发方式。顺带一提,filterModelEventResults这个方法的作用就是把监听器的返回值为null的过滤掉。

看看fireCustomModelEvent的源码:

protected function fireCustomModelEvent($event, $method)
{if (! isset($this->events[$event])) {return;}$result = static::$dispatcher->$method(new $this->events[$event]($this));if (! is_null($result)) {return $result;}
}

这个就是用来触发我们通过$events定义的事件了,假如我们这么定义:

class User extends Model{protected $events = ['saved' => UserSaved::class]
}

那这里的触发就是:

$result = static::$dispatcher->fire(new UserSaved($this));

顺带一提,Laravel中触发事件的方法有两个,一个是常用的fire,还有一个是util,这两个的差别就是fire会把监听器的返回值返回,而util永远返回null

然后接下来就是会去触发通过观察者和静态方法定义的监听器了,这一段代码:

if ($result === false) {return false;
}return ! empty($result) ? $result : static::$dispatcher->{$method}("eloquent.{$event}: ".static::class, $this
);

这里会先判断$events定义的监听器是否返回false以及返回值是否为空,如果为false则直接结束事件,如果返回不为false而且为空的话,会再去触发通过观察者和静态方法定义的监听器,并且把监听器的返回值返回。

以上内容希望帮助到大家,很多PHPer在进阶的时候总会遇到一些问题和瓶颈,业务代码写多了没有方向感,不知道该从那里入手去提升,对此我整理了一些资料,包括但不限于:分布式架构、高可扩展、高性能、高并发、服务器性能调优、TP6,laravel,YII2,Redis,Swoole、Swoft、Kafka、Mysql优化、shell脚本、Docker、微服务、Nginx等多个知识点高级进阶干货需要的可以免费分享给大家,需要

PHP进阶架构师>>>视频、面试文档免费获取​shimo.im
538ef961e4ef39ed51d8f265012c27d2.png

或 者关注咱们下面的知乎专栏

PHP大神进阶​zhuanlan.zhihu.com
d14de238b25351702964c9e61eeeb3c5.png

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/367442.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【译】10个有趣的JSCSS库(2018.10)

Tutorialzine每月都会给我们精心挑选优秀的web开发资源,这些资源可以帮助我们解锁最新和最炫酷的网络开发姿势。前端er,让我们一起先睹为快吧! WatermelonDB WatermelonDB是用于构建React和React Native应用程序的下一代数据库。快速,高度可…

自定义分页器

好久没来写东西那!今天写个自定义分页器给大家参考下吧 首先我们在自己创建的Django项目的app下新建一个utils文件夹,用来放我们的分页器组件 简单说下分页器实现原理: 1.利用ORM查询语句的限制数据条数来确定每页显示的数据 2.设置我们每页显…

五 Vue学习 首页学习 (上)

首页: http://localhost:8002/#/, 登录页面如下: index.js文件中如下的路由配置,转过去看login.vue是如何实现的。 const routes [ { path: /, component: login },(这里一个问题: logi…

linux下mqm添加用户,Linux 下MQ的安装和配置亲测

开篇之前奉上几条黄金链接:MQ参考文档http://publib.boulder.ibm.com/infocenter/wmqv7/v7r0m0/index.jsp?topic%2Fcom.ibm.mq.doc%2Fhelp_home_wmq.htmhttp://www-01.ibm.com/support/docview.wss?uidswg27006467MQ下载地址:http://www-03.ibm.com/so…

小程序tabbar这套方案全搞定!

关于微信小程序的tarbar,相信你们都不会陌生 在实现小程序微信原装的tabbar却比较呆板,不够精致,往往不符合自己的要求 这个时候怎么办呢 这套方案接着! 先简单的来说一下主要思想:自定义字体图标组件以及tabbar组件&#xff0c…

linux服务器用哪个面板好,Linux服务器管理面板哪家比较好用?

Linux服务器管理面板哪家比较好用?发布时间:2020-07-21 06:08:33来源:51CTO阅读:261作者:BirdCloud_1022现在,越来越多的站长朋友都会选择服务器用来搭建网站,但是势必需要我们自己搭建WEB环境&…

spring boot(一)入门

目录 spring boot(一)入门一、简介1、微服务的概念2、什么是spring boot3、快速入门4.springboot的快捷部署spring boot(一)入门 一、简介 1、微服务的概念 说起spring boot,我们不得不说一下“微服务”一词的兴起,微服务一词最早起源于2014年&#xff0…

Linux usb bus日志如何打开,从linux usb bus节点来认识usb linux usb认识

首先从linux dmesg来认识usb:<6>[ 19.610046] msm_hsic_host msm_hsic_host: Qualcomm EHCI Host Controller using HSIC<6>[ 19.620391] msm_hsic_host msm_hsic_host: new USB bus registered, assigned bus number 1<6>[ 19.659942] msm_hsic_host …

Spring面试问题

还可以查看我们最新的文章69Spring面试问答-最终名单 。 1&#xff09;什么是春天&#xff1f; 回答&#xff1a; Spring是控件和面向方面的容器框架的轻量级转换。 2&#xff09;解释春天&#xff1f; 回答&#xff1a; 轻巧&#xff1a;在尺寸和透明度方面&#xff0c; S…

201771010120 苏浪浪 《面向对象程序设计(java)》第二周学习总结

理论知识总结 第三章Java基本程序设计结构 1、基本知识&#xff1a;&#xff08;1&#xff09;标识符&#xff1a;是由字母、下划线、美元符号和数字组成&#xff0c;且第一个符号不能为数字。&#xff08;2&#xff09;关键字&#xff1a;剧啊语言中被赋予特定意义的一些单词。…

Apache Camel简介

Apache Camel是著名的企业集成模式的开源实现。 Camel是一个路由和中介引擎&#xff0c;可以帮助开发人员以各种领域特定语言&#xff08;DSL&#xff09;&#xff08;例如Java&#xff0c;Spring / XML&#xff0c;scala等&#xff09;创建路由和中介规则。 骆驼用途广泛 Cam…

qt结构体嵌套结构体方法_9.2 C++结构体类型变量

C定义结构体类型变量的方法 1、先声明结构体类型再定义变量名&#xff0c;在定义了结构体变量后&#xff0c;系统会为之分配内存单元。struct Student{ //自定义结构体变量int num;//学号char sex;//性别int age;//年龄};2、在声明类型的同时定义变量 一般形式为struct 结构体名…

Vue解析--如何应对面试官提问

近期不断面试中&#xff0c;面试官都会提一些关于Vue相关的源码和“全家桶”之类的问题。那么针对这些提问&#xff0c;我们应该如何更好应答呢&#xff1f;在这里我把对Vue的理解整理出来供大家来参考。 1.Vue是什么&#xff1f; Vue是一套构建用户界面的渐进式框架&#xf…

Java开发人员的Erlang

您可能没有注意到&#xff0c;但是距离我上次发布博客已经过去了几个星期。 原因是我的Soleus骨折了&#xff0c;而我的腿也石膏了。 不能动弹&#xff0c;我认为调查完全不同的东西是个好主意–要么看那天的电视&#xff0c;要么&#xff0c;尽管Kojak和Magnum PI的重播很诱人…

20165339第六周学习总结

课本知识点总结 第八章 String类 使用String类声明对象并创建对象 String s new String("We are students"); String t new String("We are students"); String (char a[])用一个字符数组a创建一个String对象 char a[] {J,a,v,a}; String s new String(a)…

keil5用jlink不到芯片_洛达芯片检测

安卓手机下载一个络达官方的刷机软件然后连接上耳机&#xff0c;打开软件&#xff0c;就可以看到耳机芯片的相关信息。软件下载地址&#xff1a;https://pan.baidu.com/s/1MOXjkHv1wfxWWIVdcsMSFg 提取码: tasc我们的耳机都是络达芯片&#xff0c;不怕你检测&#xff0c;希望用…

层次和约束:项目中使用vuex的3条优化方案

问题描述 使用vuex的store的过程中&#xff0c;发现了一些不是很优雅的地方&#xff1a; store层module太多&#xff0c;找state、getter、mutation、action对应的module比较慢。 组件里面mapGetters、mapActions、mapMutations过多&#xff0c;分不清getter、action、mutati…

怎么让员工服从管理_为什么现在的员工执行力和服从性越来越差,管理一严格就辞职?...

当有负面情绪的时候&#xff0c;不要说。管好自己的嘴&#xff0c;有时候做哑巴&#xff0c;是一种境界。现在的企业很多都存在这个问题&#xff0c;员工执行力越来越差&#xff0c;服从性也越来越差&#xff0c;管理者稍微一严格&#xff0c;员工就会辞职走人&#xff0c;留不…

Spring Data REST的实际应用

什么是春天数据休息&#xff1f; spring-data-rest是spring-data项目的新增功能&#xff0c;它是一个框架&#xff0c;可帮助您将实体直接作为RESTful Web服务端点公开。 与rails&#xff0c;grails或roo不同&#xff0c;它不会生成任何实现此目标的代码。 spring data-rest支持…

2018上半年掘金微信群日报优质文章合集:前端篇

在掘金微信交流群里的小伙伴们&#xff0c;你们每天都还在坚持读小报吗&#xff1f;如果你的回答是yes&#xff0c;那真的要给你点一万个赞了?能坚持这么久&#xff0c;真的很优秀噢&#xff01;&#xff08;嗯&#xff0c;每天坚持给大家收集文章的小饼也很优秀?&#xff09…