目录

Laravel 模型事件

概述

在 laravel 通过 model 与数据库的连接操作中,会触发一些事件。事件允许模型在变动的时候通过构造器传送模型的实例。为此可以通过该特性以观察者的方式来做一些功能模块解耦的工作。通过文档不能直观的看到事件的触发顺序,故而整理一篇文章。

本文基于 laravel 8.x 讲解与整理相关的功能点

生命周期

事件触发条件(必须通过 ORM)
retrieved执行查询 sql 后触发
creating执行插入 sql 前触发
created执行插入 sql 后触发
updating执行修改 sql 前触发
updated执行修改 sql 后触发
saving执行插入或修改 sql 前触发
saved执行插入或修改 sql 后触发
deleting执行删除 sql 前触发(软删除也会触发)
deleted执行删除 sql 后触发(软删除也会触发)
restoring恢复软删除前触发
restored恢复软删除后触发
replicating模型执行 replicate() 函数触发

配置测试代码

表结构

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
CREATE TABLE `t_users` (
  `id` int unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(32) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL DEFAULT '',
  `age` tinyint unsigned NOT NULL DEFAULT '0',
  `sex` tinyint NOT NULL DEFAULT '0' COMMENT '1:男 2:女',
  `created_ymd` int NOT NULL DEFAULT '0',
  `created_at` int NOT NULL DEFAULT '0',
  `updated_at` int NOT NULL DEFAULT '0',
  `deleted_at` int NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci;

模型

为了更直观这里使用闭包方式

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Facades\Log;

class TUser extends BaseModel
{
    use SoftDeletes;

    protected $fillable = [
        'name',
        'age',
        'sex',
    ];

    protected $hidden = [
        'deleted_at',
    ];

    protected static function booted()
    {
        static::retrieved(function (TUser $user) {
            Log::info('retrieved');
        });
        static::creating(function (TUser $user) {
            Log::info('creating');
        });
        static::created(function (TUser $user) {
            Log::info('created');
        });
        static::updating(function (TUser $user) {
            Log::info('updating');
        });
        static::updated(function (TUser $user) {
            Log::info('updated');
        });
        static::saving(function (TUser $user) {
            Log::info('saving');
        });
        static::saved(function (TUser $user) {
            Log::info('saved');
        });
        static::deleting(function (TUser $user) {
            Log::info('deleting');
        });
        static::deleted(function (TUser $user) {
            Log::info('deleted');
        });
        static::restoring(function (TUser $user) {
            Log::info('restoring');
        });
        static::restored(function (TUser $user) {
            Log::info('restored');
        });
        static::replicating(function (TUser $user) {
            Log::info('replicating');
        });
    }
}

测试生命周期

创建记录

1
2
3
4
5
6
TUser::query()
    ->create([
        'name' => 'test',
        'age'  => '20',
        'sex'  => '1',
    ]);
1
2
3
4
5
local.INFO: saving  
local.INFO: creating  
local.INFO: insert into `t_users` (`name`, `age`, `sex`, `updated_at`, `created_ymd`, `created_at`) values ('test', '20', '30', '1651049606', '20220427', '1651049606')  
local.INFO: created  
local.INFO: saved

查询记录

1
TUser::query()->find(1);
1
2
local.INFO: select * from `t_users` where `t_users`.`id` = '1' and `t_users`.`deleted_at` is null limit 1  
local.INFO: retrieved  

修改记录

注意:通过 Eloquent 进行批量更新时,被更新模型的 saved 和 updateddeleting 和 deleted 事件不会被触发。这是因为批量更新时,并没有真的获取模型。

1
2
3
4
// 修改记录需要先获取到模型才会触发事件
// 例如这样不会触发模型事件:TUser::query()->where('id', '=', 1)->update(['age' => 21]);
$user = TUser::query()->find(1);
$user->update(['age' => 21]);
1
2
3
4
5
6
7
8
local.INFO: select * from `t_users` where `t_users`.`id` = '1' and `t_users`.`deleted_at` is null limit 1  
local.INFO: retrieved  
local.INFO: saving  
local.INFO: updating  
local.INFO: update `t_users` set `age` = '21', `t_users`.`updated_at` = '1651050127' where `id` = '1'  
local.INFO: updated  
local.INFO: saved  

删除记录

注意:destroy 方法会分别加载每个模型,并在其上调用 delete 方法,以便触发 deleting 和 deleted 事件。

1
2
// 删除同理,需要先获取到模型才能触发事件,laravel 提供 destroy 
TUser::destroy(1);
1
2
3
4
5
6
local.INFO: select * from `t_users` where `id` in ('1') and `t_users`.`deleted_at` is null  
local.INFO: retrieved  
local.INFO: deleting  
local.INFO: update `t_users` set `deleted_at` = '1651050766', `t_users`.`updated_at` = '1651050766' where `id` = '1' 
local.INFO: deleted  

恢复软删除

1
2
// 同样恢复软删除需要先把模型查询出来
TUser::onlyTrashed()->find(1)->restore();
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
local.INFO: select * from `t_users` where `t_users`.`deleted_at` is not null and `t_users`.`id` = '1' limit 1 
local.INFO: retrieved  
local.INFO: restoring  
local.INFO: saving  
local.INFO: updating  
local.INFO: update `t_users` set `deleted_at` = '', `t_users`.`updated_at` = '1651051392' where `id` = '1'  
local.INFO: updated  
local.INFO: saved  
local.INFO: restored  

复制模型

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
$user = TUser::query()
    ->create([
        'name' => 'test',
        'age'  => '20',
        'sex'  => '1',
    ]);
$newUser = $user->replicate()->fill([
    'age' => 21
]);
$newUser->save(); // 即使最后不 save 也会触发 replicating 事件
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
local.INFO: saving  
local.INFO: creating  
local.INFO: insert into `t_users` (`name`, `age`, `sex`, `updated_at`, `created_ymd`, `created_at`) values ('test', '20', '1', '1651051834', '20220427', '1651051834')
local.INFO: created  
local.INFO: saved  
local.INFO: replicating  
local.INFO: saving  
local.INFO: creating  
local.INFO: insert into `t_users` (`name`, `age`, `sex`, `created_ymd`, `updated_at`, `created_at`) values ('test', '21', '1', '20220427', '1651051834', '1651051834')
local.INFO: created  
local.INFO: saved