什么是模型事件
Eloquent 模型会在生命周期的各时点自动触发事件。通过挂钩这些事件,可以在模型保存、删除等操作的前后插入自定义处理。
Eloquent 触发的事件如下。
事件 触发时机 retrieved从 DB 取回模型时 creating保存新模型前 created保存新模型后 updating更新已有模型前 updated更新已有模型后 saving新增或更新保存前 saved新增或更新保存后 deleting删除模型前 deleted删除模型后 trashed软删除后 forceDeleting物理删除前 forceDeleted物理删除后 restoring恢复软删除前 restored恢复软删除后 replicating调用 replicate() 时
以 -ing 结尾的事件在变更持久化到 DB 之前 触发,以 -ed 结尾的则在之后 触发。
批量更新与批量删除(如 User::where(...)->update(...))不会触发 saving、saved、updating、updated、deleting、deleted 事件,因为模型并未真正被实例化。
用闭包注册事件监听
若希望简单处理事件,可以在模型的 booted 方法内注册闭包。
<? php
namespace App\Models ;
use Illuminate\Database\Eloquent\ Model ;
class User extends Model
{
protected static function booted () : void
{
static :: created ( function ( User $user ) {
// 用户创建后执行的处理
});
static :: deleting ( function ( User $user ) {
// 用户删除前执行的处理
});
}
}
若要将处理放入队列异步执行,使用 queueable 辅助函数。
use function Illuminate\Events\ queueable ;
static :: created ( queueable ( function ( User $user ) {
// 会通过队列异步执行
}));
$dispatchesEvents 属性
如果想与 Laravel 的事件系统联动,可以通过 $dispatchesEvents 属性将模型事件映射到自定义事件类。
<? php
namespace App\Models ;
use App\Events\ UserDeleted ;
use App\Events\ UserSaved ;
use Illuminate\Foundation\Auth\ User as Authenticatable ;
class User extends Authenticatable
{
/**
* 模型事件与事件类的映射
*
* @var array < string , string>
*/
protected $dispatchesEvents = [
'saved' => UserSaved :: class ,
'deleted' => UserDeleted :: class ,
];
}
映射到的事件类会在构造函数中接收模型实例。
<? php
namespace App\Events ;
use App\Models\ User ;
class UserSaved
{
public function __construct (
public readonly User $user ,
) {}
}
创建 Observer 类
若要处理同一模型的多个事件,将闭包排成一列不如整合到 Observer 类中更加清爽。
用 Artisan 命令生成类
使用 make:observer 命令生成模板。通过 --model 选项指定模型时,会自动附上相应方法。 php artisan make:observer UserObserver --model=User
将生成 app/Observers/UserObserver.php。
实现各事件方法
方法名对应事件名。参数中会传入模型实例。 <? php
namespace App\Observers ;
use App\Models\ User ;
class UserObserver
{
public function created ( User $user ) : void
{
// 用户创建后的处理
}
public function updated ( User $user ) : void
{
// 用户更新后的处理
}
public function deleted ( User $user ) : void
{
// 用户删除后的处理
}
public function restored ( User $user ) : void
{
// 软删除恢复后的处理
}
public function forceDeleted ( User $user ) : void
{
// 物理删除后的处理
}
}
将 Observer 注册到 Model
注册方式有 2 种。Laravel 13 推荐使用 #[ObservedBy] 属性。 方式 1:#[ObservedBy] 属性(推荐) 只需在模型类上添加属性即可完成注册,无需修改 AppServiceProvider。 <? php
namespace App\Models ;
use App\Observers\ UserObserver ;
use Illuminate\Database\Eloquent\Attributes\ ObservedBy ;
use Illuminate\Foundation\Auth\ User as Authenticatable ;
#[ ObservedBy ( UserObserver :: class )]
class User extends Authenticatable
{
//
}
要注册多个 Observer 时,可重复使用属性或以数组形式传入。 #[ ObservedBy ( UserObserver :: class )]
#[ ObservedBy ( AuditObserver :: class )]
class User extends Authenticatable
{
//
}
方式 2:在 AppServiceProvider 中注册 在 AppServiceProvider 的 boot 方法中调用 observe。 <? php
namespace App\Providers ;
use App\Models\ User ;
use App\Observers\ UserObserver ;
use Illuminate\Support\ ServiceProvider ;
class AppServiceProvider extends ServiceProvider
{
public function boot () : void
{
User :: observe ( UserObserver :: class );
}
}
#[ObservedBy] 属性位于 Illuminate\Database\Eloquent\Attributes 命名空间。它使用 PHP 8.0 以后的原生语法,在 Laravel 13 中被积极采用。
数据库事务中的 Observer
若模型是在事务内创建或更新,有时你希望 Observer 在事务提交之后执行。实现 ShouldHandleEventsAfterCommit 接口即可获得该行为。
<? php
namespace App\Observers ;
use App\Models\ User ;
use Illuminate\Contracts\Events\ ShouldHandleEventsAfterCommit ;
class UserObserver implements ShouldHandleEventsAfterCommit
{
public function created ( User $user ) : void
{
// 事务提交后执行
}
}
在事务外部执行时,会像往常一样立即执行。
暂时禁用事件
使用 withoutEvents 在特定处理中停止事件
在传给 User::withoutEvents() 的闭包内,任何模型事件都不会触发。
use App\Models\ User ;
$user = User :: withoutEvents ( function () {
User :: findOrFail ( 1 ) -> delete ();
return User :: find ( 2 );
});
使用 saveQuietly 停止保存事件
想不触发事件地保存模型时使用 saveQuietly。
$user = User :: findOrFail ( 1 );
$user -> name = 'Victoria Faith' ;
$user -> saveQuietly ();
对应的方法在删除、恢复、复制中也提供。
$user -> deleteQuietly ();
$user -> restoreQuietly ();
实战用例
自动清理缓存
模型更新或删除时,自动清除相关缓存。
<? php
namespace App\Observers ;
use App\Models\ Post ;
use Illuminate\Support\Facades\ Cache ;
class PostObserver
{
public function saved ( Post $post ) : void
{
Cache :: forget ( "post:{ $post -> id }" );
Cache :: forget ( 'posts:latest' );
}
public function deleted ( Post $post ) : void
{
Cache :: forget ( "post:{ $post -> id }" );
Cache :: forget ( 'posts:latest' );
}
}
记录审计日志
自动记录模型的变更历史。使用 getDirty() 可以获取变更前后的值。
<? php
namespace App\Observers ;
use App\Models\ AuditLog ;
use App\Models\ User ;
class UserObserver
{
public function updating ( User $user ) : void
{
AuditLog :: create ([
'model_type' => User :: class ,
'model_id' => $user -> id ,
'changes' => $user -> getDirty (),
'user_id' => auth () -> id (),
]);
}
public function deleted ( User $user ) : void
{
AuditLog :: create ([
'model_type' => User :: class ,
'model_id' => $user -> id ,
'changes' => [ 'deleted' => true ],
'user_id' => auth () -> id (),
]);
}
}
updating 事件在写入 DB 之前 触发,因此 getDirty() 可以取到即将变更的值。如果在 updated 事件之后调用,getDirty() 会为空。
关联模型的自动更新
订单完成时自动更新库存的示例。
<? php
namespace App\Observers ;
use App\Models\ Order ;
class OrderObserver
{
public function created ( Order $order ) : void
{
foreach ( $order -> items as $item ) {
$item -> product -> decrement ( 'stock' , $item -> quantity );
}
}
public function deleted ( Order $order ) : void
{
foreach ( $order -> items as $item ) {
$item -> product -> increment ( 'stock' , $item -> quantity );
}
}
}
下一步
进阶:PHP 属性 一起学习包括 #[ObservedBy] 在内的 Laravel 13 PHP 属性。