前言

最近开发新的项目不是基于完整的 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. 代码生成器 CodeSmith 的使用(四)

    在上一篇的版本中,我们生成了数据库中的字段,使生成的属性更加简洁,可读性也提高了很多,但都是钍对一个数据库的单个表,如果要将数据库中的所有 的表都生成相应的类,表中的字段也都生成属性,运行一次就可以将 ...

  2. 显式锁(二)Lock接口与显示锁介绍

    一.显式锁简介    显式锁,这个叫法是相对于隐式锁synchronized而言的,加锁和解锁都要用户显式地控制.显示锁Lock是在Java5中添加到jdk的,同synchronized一样,这也是一 ...

  3. UnicodeEncodeError: 'gbk' codec can't encode character '\xbb' in position

    python实现爬虫遇到编码问题: error:UnicodeEncodeError: 'gbk' codec can't encode character '\xXX' in position XX ...

  4. angular 中怎么获取路径上的参数 参考:https://docs.angularjs.org/api/ng/service/$location

    参考: https://docs.angularjs.org/api/ng/service/$location

  5. [ilink32 Error] Error: Unresolved external '__fastcall Data::Win::Adodb::TCustomADODataSet

    [ilink32 Error] Error: Unresolved external '__fastcall Data::Win::Adodb::TCustomADODataSet::GetParam ...

  6. myeclipse 代码提示

    from http://fuyiyuan2011.iteye.com/blog/1258264 在软件开发过程中,有了代码提示能使开发能够更加快捷与便利.但在Eclipse ,MyEclipse等ja ...

  7. delphi 泛型 c++builder 泛型

    delphi 泛型 System.Generics.Collections.pas TList<T> http://docwiki.embarcadero.com/Libraries/Be ...

  8. Spring boot @ConfigurationProperties 和@Value

      @ConfigurationProperties @Value 功能 批量注入配置文件中的属性 一个个指定 松散绑定(松散语法) 支持 不支持 SpEL 不支持 支持 JSR303数据校验 支持 ...

  9. 基于OpenGL编写一个简易的2D渲染框架-12 重构渲染器-BlockAllocator

    BlockAllocator 的内存管理情况可以用下图表示 整体思路是,先分配一大块内存 Chunk,然后将 Chunk 分割成小块 Block.由于 Block 是链表的一个结点,所以可以通过链表的 ...

  10. 事件委托,js中的一种优化方法

    在前端编程中,我们常会遇到一种叫做事件委托的方法. 那么, 什么是事件委托呢? 简单来说,事件就是指onclick,onmouseover,onmouseout等大部分事件,为什么说是大部分后面会提到 ...