Observer 观察者模式创建步骤
- 观察者
-
监听多个事件
-
app\Observers\TopicObserver.php
class TopicObserver
{
// 监听事件
}
-
注册观察者
app\Providers\AppServiceProviders.php
namespace App\Providers;
use App\Topic;
use App\Observers\TopicObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* 注册服务提供.
*
* @return void
*/
public function register()
{
//
}
/**
* 运行所有应用.
*
* @return void
*/
public function boot()
{
// 为 Topic 模型注册观察者
Topic::observe(TopicObserver::class);
}
}
观察者中事件的发生顺序
laravel 5.8 版本下对于 Illuminate\Database\Eloquent\Model 源文件的分析,为了方便已去掉注释
public function save(array $options = [])
{
$query = $this->newModelQuery();
if ($this->fireModelEvent('saving') === false) {
return false;
}
if ($this->exists) {
$saved = $this->isDirty() ?
$this->performUpdate($query) : true;
}
else {
$saved = $this->performInsert($query);
if (! $this->getConnectionName() &&
$connection = $query->getConnection()) {
$this->setConnection($connection->getName());
}
}
if ($saved) {
$this->finishSave($options);
}
return $saved;
}
首先会触发 saving
事件
$this->fireModelEvent('saving')
- 接着判断
model
是否新创建- 若是新创建,则执行
Insert()
- 若非新创建,则调用
isDirty ()
判断此model
是否进行了修改。
- 若是新创建,则执行
如果进行了修改,则会调用 $this->performUpdate($query)
进行更新操作,如果未进行修改,则直接返回 True
进入 $this->performUpdate($query)
protected function performUpdate(Builder $query)
{
if ($this->fireModelEvent('updating') === false) {
return false;
}
if ($this->usesTimestamps()) {
$this->updateTimestamps();
}
$dirty = $this->getDirty();
if (count($dirty) > 0) {
$this->setKeysForSaveQuery($query)->update($dirty);
$this->syncChanges();
$this->fireModelEvent('updated', false);
}
return true;
}
可以看到此处触发 updating
方法:
$this->fireModelEvent('updating')
如果返回的不是 false
, 那么继续往下走,判断该模型是否使用了 Timestamp
,如果使用了,即先更新自身Timestamp
:
if ($this->usesTimestamps()) {
$this->updateTimestamps();
}
紧接着调用 getDirty
,顾名思义,获取自身脏值,进入该方法查看
public function getDirty()
{
$dirty = [];
foreach ($this->getAttributes() as $key => $value) {
if (! $this->originalIsEquivalent($key, $value)) {
$dirty[$key] = $value;
}
}
return $dirty;
}
果然该方法内初始化空数组$dirty
用于存储自身被修改后的脏值。
你试过打印
Model
模型吗?
举个栗子
Order {#359
#table: "order"
#guarded: array:1 [
0 => "id"
]
#connection: "mysql"
#primaryKey: "id"
#keyType: "int"
+incrementing: true
#with: []
#withCount: []
#perPage: 15
+exists: true
+wasRecentlyCreated: false
#attributes: array:45 [
"id" => 2026
"order_sn" => "2019080617314062621248"
"customer_id" => 62
"order_status" => 0
"shipping_status" => 99
"pay_status" => 1
......
]
#original: array:45 [
"id" => 2026
"order_sn" => "2019080617314062621248"
"customer_id" => 62
"order_status" => 0
"shipping_status" => 99
"pay_status" => 1
......
]
#changes: []
#casts: []
#dates: array:1 [
0 => "deleted_at"
]
#dateFormat: null
#appends: []
#dispatchesEvents: []
#observables: []
#relations: []
#touches: []
+timestamps: true
#hidden: []
#visible: []
#fillable: []
#forceDeleting: false
}
这是我打印的一个 order
实例,可以看到其中有 original
和 attributes
两个数组
-
original
保存的是最初始的实例属性,无法被外部直接修改 -
attributes
数组可以被自有修改,此方法即是由判断两个数组的差异而返回的脏值
ok,继续往下看 performUpdate
的代码,可以看到会进行判断是否有脏值:
if (count($dirty) > 0) {
$this->setKeysForSaveQuery($query)->update($dirty);
$this->syncChanges();
$this->fireModelEvent('updated', false);
}
存在脏值的情况下进入 if
,执行 update
操作,
在 update
结束后会执行 syncChanges
同步自身数据,继而触发 updated
事件。
performUpdate
简单解析完毕
回看
save()
方法的接下来操作
if ($saved) {
$this->finishSave($options);
}
if
判断在 update
成功下,才会触发 finishSave()
方法,此方法会接受一个参数 $options
,即是修改的属性.
进入该 finishSave()
:
protected function finishSave(array $options)
{
$this->fireModelEvent('saved', false);
if ($this->isDirty() && ($options['touch'] ?? true)) {
$this->touchOwners();
}
$this->syncOriginal();
}
这里触发了 saved
事件
- 当模型已存在,非新建时,事件触发顺序如下:
saving
->updating
->updated
->saved
- 当模型不存在,即需要新增时,事件触发顺序如下:
saving
->creating
->created
->saved
触发顺序知晓,那么又有个疑问
saving
,saved
和 updating
,updated
有什么不一样(区别)?
-
举的实例中,
Laravel
的Eloquent
会维护实例的两个数组,分别是original
,attributes
。- 两者都是在
saved
事件触发之后,Laravel
才会对两个数组执行syncOriginal
操作
- 两者都是在
saveing
和saved
会在Eloquent
实例的original
数组真值更改前后触发updating
和updated
会在数据库中的真值修改的前后触发