前言

最近开发新的项目不是基于完整的 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. python之列表操作

    1.列表的增操作(四种) append(object):append object to end,directly used on list insert(index,object):insert o ...

  2. tensorflow读取数据

    线程和队列 在使用TensorFlow进行异步计算时,队列是一种强大的机制. 为了感受一下队列,让我们来看一个简单的例子.我们先创建一个“先入先出”的队列(FIFOQueue),并将其内部所有元素初始 ...

  3. 什么是 web 开发

    什么是 web 开发     这几天因为工作需要,了解了一下Web development 的技术路线,来源自     en.wikipedia.org/wiki/Web_development    ...

  4. PHP获取跳转后的URL,存到数据库,设置缓存时间

    <?php error_reporting(0); header("Content-Type: text/html; charset=utf-8"); $fid=$_GET[ ...

  5. 16 python xml模块

    1.基本概念 xml是实现不同语言或程序之间进行数据交换的协议,跟json差不多,但json使用起来更简单. 不过,古时候,在json还没诞生的黑暗年代,大家只能选择用xml呀. 至今很多传统公司如金 ...

  6. Mysql 知识(1)

    1. 请简洁地描述下MySQL中InnoDB支持的四种事务隔离级别名称,以及逐级之间的区别? 答: SQL标准定义的四个隔离级别为:read uncommited,read committed,rep ...

  7. linux numastat的理解

    numa的统计数据及理解如下, [root@localhost kernel]# numastat                     node0           node1numa_hit ...

  8. MySQL 事务 是对数据进行操作,对结构没有影响,比如创建表、删除表,事务就不起作用

  9. SpringMVC 文件上传 MultipartFile

    本的SpringMVC的搭建在我的上一篇文章里已经写过了,这篇文章主要说明一下如何使用SpringMVC进行表单上的文件上传以及多个文件同时上传的步骤 SpringMVC 基础教程 框架分析:http ...

  10. React Native 填坑一

    React Native 填坑一 关于RN的布局 分为主轴和交叉轴, 主轴可以是横向也可以是竖向,交叉轴也是对应的. 主轴默认是竖向.如果要更改用flexdirection 主轴对齐方式:justif ...