前言

最近开发新的项目不是基于完整的 laravel 框架,框架是基于 laravel ORM 的简单MVC模式。随着项目的成熟和业务需求,需要引入事件机制。简单地浏览了一下 symfony、laravel 的事件机制,都是依赖于 container 的。感觉如果引入的话开销有点大,在寻觅更好解决方案过程中发现 ORM 居然自带事件机制,完全足够目前的业务需求,又不需要改动框架,简直不能太棒!这里简单的记录一下orm observe 的使用吧。

事件触发流程

由于 ORM 事件是针对 Model 的,所以本身结合 Model 封装了一些基础的事件。感觉基本上是够使用了,如果需要添加额外事件的话,ORM 也提供了 setObservableEvents()供使用。

public function getObservableEvents()
{
return array_merge(
[
'creating', 'created', 'updating', 'updated',
'deleting', 'deleted', 'saving', 'saved',
'restoring', 'restored',
],
$this->observables
);
}

我们都知道 ORM 无论是 create 还是 update 本质是 save。因此分析一下 ORM save 时底层流程是:

public function save(array $options = [])
{
$query = $this->newQueryWithoutScopes();
//触发 saving 事件
if ($this->fireModelEvent('saving') === false) {
return false;
}
if ($this->exists) {
$saved = $this->performUpdate($query, $options);
}
else {
$saved = $this->performInsert($query, $options);
} if ($saved) {
$this->finishSave($options);
}
return $saved;
} protected function performInsert(Builder $query, array $options = [])
{
//触发 creating 事件
if ($this->fireModelEvent('creating') === false) {
return false;
}
if ($this->timestamps && Arr::get($options, 'timestamps', true)) {
$this->updateTimestamps();
}
$attributes = $this->attributes;
if ($this->getIncrementing()) {
$this->insertAndSetId($query, $attributes);
}
else {
$query->insert($attributes);
}
$this->exists = true;
$this->wasRecentlyCreated = true;
//触发 created 事件
$this->fireModelEvent('created', false);
return true;
} protected function finishSave(array $options)
{
//触发 saved 事件
$this->fireModelEvent('saved', false);
$this->syncOriginal();
if (Arr::get($options, 'touch', true)) {
$this->touchOwners();
}
}

以上是阐述 creat 时事件触发流程,显而易见依次是 saving>creating>created>saved。

update 同理就不具体贴代码了, saving>updating>updated>saved。

而 delete 不涉及 save,因此依次只触发了 deleting 和deleted。 当 restore 软删除记录时触发了 restoring 和 restored 方法。

使用

只需在 model 添加 boot 方法,可以直接在 boot 方法中定义事件,也可以面向于 model 新建 modelObserver 集中处理对应的 model 事件。

 public static function boot()
{
parent::boot();
static::setEventDispatcher(new \Illuminate\Events\Dispatcher());
//static::saving(function($model){ });
static::observe(new testObserver());
} class testObserve{
public function saving($model)
{
$model->name=name.'test';
}
}

大坑

不得不感叹这个事件真的既简单又实用,但是!当我在开发过程中有一处 update 怎么都不会触发事件,捉急的很啊。菜鸟一下看不出问题本质,只能打断点一点点排查。发现是因为该处代码前调用了belongsToMany()

  • 痛点1:dispatcher 与 container 共生死单例。
    多对多模型关联 boot 两个model,因此 boot model2 时,将 dispatcher 指向了 model2。

  • 痛点2:model 只 boot 一次
    bootIfNotBooted(),booted model 不会重新调用 boot 方法。

  • 结果:update model1 怎么都无法触发事件。
  • 解决:目前能想到的便是在调用 belongsToMany()时,调用 ORM 的 clearBootedModels()方法,清除 bootedModel,update model1 时再次 boot model1,强行将 dispatcher 指向 model1.

Eloquent Observer 的小坑的更多相关文章

  1. mciSendString 的两个小坑

    刚刚修正了自己用的小闹钟的代码. 坑1:REPEAT 选项的作用范围 原来用得好好的,之后选择 .wav 文件,居然不出声音了…… 诶,MCI 肯定支持 .wav 的啊…… 仔细想想,我以前都是选 . ...

  2. 注意Android里TextView控件的一个小坑,用android:theme来设置样式时动态载入的layout会丢失该样式

    注意Android里TextView控件的一个小坑,用android:theme来设置样式时动态载入的layout会丢失该样式 这个坑,必须要注意呀, 比如在用ListView的时候,如果在List_ ...

  3. C#中的Infinity有个小坑

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 昨天家里有事,上网也不方便,就没有推送文章.今天很累,也不长篇大论了.简单介绍一下最近遇到的 ...

  4. 使用EMMET中的小坑

    使用EMMET写HTML的时候,是一个非常爽的事情.但是今天我使用时,发现一个小坑.以前倒也没有注意,不过需要非常的小心. form[action="/process" metho ...

  5. 关于CSS3中transform变换的小坑

    2017年6月30日15:05:46 今天在写一个demo的时候,发现CSS3中transform变换的一个特性. 首先,我先描述一下我发现的情况(问题再现): <div class=" ...

  6. Vue中应用CORS实现AJAX跨域,及它在 form data 和 request payload 的小坑处理

    基本概念部分(一):理解CORS 说道Vue的跨域AJAX,我想先梳理一遍CORS跨域,"跨域资源共享"(Cross-origin resource sharing),它是一个W3 ...

  7. go的变量redeclare的问题,golang的一个小坑

    go的变量声明有几种方式: 1 通过关键字 var 进行声明 例如:var i int   然后进行赋值操作 i = 5 2 最简单的,通过符号 := 进行声明和赋值 例如: i:=5 golang会 ...

  8. MySQL中字段类型为timestamp的小坑

    之前遇到过一个MySQL的字段为timestamp类型的小坑. MySQL中一个字段存储时间类型数据的时候,该字段的类型如果为timestamp类型的话,最多只能存储到2038-01-19 11:14 ...

  9. mysql url 连接配置的一个小坑。 工作中不会遇到。 学习的时候会

    <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> & ...

随机推荐

  1. Java 知识点(转)

    1.servlet执行流程 客户端发出http请求,web服务器将请求转发到servlet容器,servlet容器解析url并根据web.xml找到相对应的servlet,并将request.resp ...

  2. Using Celery with Django

    参考1: http://docs.celeryproject.org/en/latest/django/first-steps-with-django.html#using-celery-with-d ...

  3. PCI Simple Communications Controller

    PCI Simple Communications Controller Intel Management Engine Interface (MEI)

  4. 机器学习入门-提取文章的主题词 1.jieba.analyse.extract_tags(提取主题词)

    1.jieba.analyse.extract_tags(text)  text必须是一连串的字符串才可以 第一步:进行语料库的读取 第二步:进行分词操作 第三步:载入停用词,同时对分词后的语料库进行 ...

  5. vue基础——vue介绍

    声明式渲染——文本插值: 数据和dom已经进行了关联,所有东西都是响应式的 index.html <div id="app0"> {{message}} </di ...

  6. 遍历python字典几种方法

    遍历python字典几种方法 from: http://ghostfromheaven.iteye.com/blog/1549441 aDict = {'key1':'value1', 'key2': ...

  7. tcprstat分析服务的响应速度

    Tcprstat 是免费开源的TCP分析工具,可检测网络通信状态,并且计算请求和响应之间的延迟. 它的输出格式类似 linux 的 vmstat 和 iostat 的输出格式.这个工具能够检测到某个端 ...

  8. 如何将String转换为int

    1. int i = Integer.parseInt([String]); 或 i = Integer.parseInt([String],[int radix]); Integer.parseIn ...

  9. 注释和取消注释 程序中的log日志

    有点简单,但也是原创哦..亲测有效,期待指正. 更改了log多行的问题.. 例如//Log Util: 一.注释log    import java.io.BufferedReader;import ...

  10. JDK中rt.jar、tools.jar和dt.jar作用

    dt.jar和tools.jar位于:{Java_Home}/lib/下,而rt.jar位于:{Java_Home}/jre/lib/下,其中: rt.jar是JAVA基础类库,也就是你在java d ...