PHP 模型事件如何使用

wen PHP项目 1

本文目录导读:

PHP 模型事件如何使用

  1. 什么是模型事件
  2. 常用事件列表
  3. 事件监听方式
  4. 使用观察者(推荐)
  5. 事件的应用场景
  6. 注意事项
  7. 事件类示例
  8. 最佳实践建议

在 PHP 中,模型事件主要与 ORM(对象关系映射)框架相关,最常见的框架是 Laravel Eloquent,我将以 Laravel 为例,详细介绍模型事件的使用方法。

什么是模型事件

模型事件是数据库模型生命周期中触发的钩子,允许你在特定时刻执行自定义逻辑,Eloquent 模型会自动触发这些事件。

常用事件列表

Eloquent 提供以下模型事件:

生命周期事件(按顺序)

// 创建过程
creating → created
// 更新过程  
updating → updated
// 保存过程(创建+更新都会触发)
saving → saved
// 删除过程
deleting → deleted
// 恢复软删除
restoring → restored
// 查询过程
retrieving → retrieved

事件监听方式

模型内定义 $dispatchesEvents 属性

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    protected $dispatchesEvents = [
        'created' => UserCreated::class,
        'deleted' => UserDeleted::class,
    ];
}

模型内使用静态方法

<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
    protected static function booted()
    {
        static::creating(function ($user) {
            // 创建前触发
            $user->status = 'active';
            $user->remember_token = Str::random(20);
        });
        static::created(function ($user) {
            // 创建后触发
            Log::info('用户创建成功: ' . $user->email);
            event(new UserCreated($user));
        });
        static::updating(function ($user) {
            // 更新前触发
            $user->updated_by = auth()->id();
        });
        static::deleted(function ($user) {
            // 删除后触发
            Mail::to($user->email)->send(new AccountDeletedMail());
        });
        static::retrieved(function ($user) {
            // 查询后触发
            $user->last_retrieved_at = now();
        });
    }
}

在服务提供者中注册

<?php
namespace App\Providers;
use App\Models\User;
use App\Observers\UserObserver;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
    public function boot()
    {
        User::observe(UserObserver::class);
    }
}

使用观察者(推荐)

观察者是组织事件监听代码的最佳方式:

<?php
namespace App\Observers;
use App\Models\User;
use Illuminate\Support\Facades\Log;
class UserObserver
{
    public function creating(User $user)
    {
        // 生成唯一标识
        $user->user_code = 'USR' . time() . rand(1000, 9999);
    }
    public function created(User $user)
    {
        // 发送欢迎通知
        $user->notify(new WelcomeNotification());
        // 记录日志
        Log::info('用户创建: ' . $user->email);
        // 创建关联数据
        $user->profile()->create([
            'bio' => '新用户'
        ]);
    }
    public function updating(User $user)
    {
        // 更新前检查
        if ($user->isDirty('email')) {
            $user->email_verified_at = null;
        }
    }
    public function updated(User $user)
    {
        // 更新后的操作
        activity()->log('用户信息已更新');
    }
    public function deleting(User $user)
    {
        // 防止删除管理员
        if ($user->is_admin) {
            return false; // 返回 false 可以阻止删除
        }
    }
    public function deleted(User $user)
    {
        // 清理关联数据
        $user->logs()->delete();
        Storage::delete($user->avatar_path);
    }
    public function restored(User $user)
    {
        // 恢复软删除
        $user->restore_notes = '用户申诉恢复';
    }
}

事件的应用场景

自动填充字段

class Product extends Model
{
    protected static function booted()
    {
        static::creating(function ($product) {
            // 自动生成商品编号
            $product->sku = strtoupper(uniqid('SKU-'));
            // 设置默认库存
            if (is_null($product->stock)) {
                $product->stock = 0;
            }
            // 设置创建者
            $product->created_by = auth()->user()->id ?? null;
        });
        static::updating(function ($product) {
            // 自动更新修改者
            $product->updated_by = auth()->user()->id ?? null;
        });
    }
}

数据关联处理

class Order extends Model
{
    protected static function booted()
    {
        static::created(function ($order) {
            // 创建订单后自动生成订单日志
            $order->orderLogs()->create([
                'action' => 'order_created',
                'description' => '订单已创建',
                'operator_id' => auth()->id()
            ]);
            // 更新库存
            foreach ($order->items as $item) {
                $item->product->decrement('stock', $item->quantity);
            }
        });
        static::updated(function ($order) {
            // 订单状态变更时发送通知
            if ($order->wasChanged('status')) {
                $order->customer->notify(new OrderStatusUpdated($order));
                $order->orderLogs()->create([
                    'action' => 'status_changed',
                    'description' => "状态变更为: {$order->status}",
                    'operator_id' => auth()->id()
                ]);
            }
        });
    }
}

缓存管理

class Setting extends Model
{
    protected static function booted()
    {
        static::saved(function ($setting) {
            // 清除相关缓存
            Cache::forget('setting.' . $setting->key);
            Cache::forget('all_settings');
        });
        static::deleted(function ($setting) {
            // 清除相关缓存
            Cache::forget('setting.' . $setting->key);
            Cache::forget('all_settings');
        });
    }
}

事件与队列结合

class User extends Model
{
    protected static function booted()
    {
        static::created(function ($user) {
            // 异步发送邮件
            dispatch(function () use ($user) {
                Mail::to($user->email)->send(new WelcomeMail($user));
            })->delay(now()->addMinutes(10));
        });
        static::deleted(function ($user) {
            // 异步清理资料
            dispatch(new CleanUserDataJob($user->id))->onQueue('cleanup');
        });
    }
}

注意事项

事件中使用事务

class TransactionObserver
{
    public function created(Transaction $transaction)
    {
        // 在事务中执行
        DB::transaction(function () use ($transaction) {
            // 更新余额
            $transaction->account->balance += $transaction->amount;
            $transaction->account->save();
            // 记录变动
            $transaction->account->transactions()->create([
                'type' => 'balance_change',
                'amount' => $transaction->amount
            ]);
        });
    }
}

阻止事件执行

class UserObserver
{
    public function deleting(User $user)
    {
        // 返回 false 会阻止删除
        if ($user->hasActiveOrders()) {
            return false;
        }
    }
}
// 使用事件阻止
static::updating(function ($user) {
    // 检查邮箱是否被占用
    if (User::where('email', $user->email)->where('id', '!=', $user->id)->exists()) {
        return false; // 阻止更新
    }
});

调试事件

// 临时监听所有事件
Event::listen('eloquent.*: App\Models\User', function ($event, $data) {
    Log::info('User model event fired: ' . $event);
});

事件类示例

<?php
namespace App\Events;
use App\Models\User;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class UserCreated
{
    use Dispatchable, InteractsWithSockets, SerializesModels;
    public $user;
    public function __construct(User $user)
    {
        $this->user = $user;
    }
    public function broadcastOn()
    {
        return new Channel('users.' . $this->user->id);
    }
}

最佳实践建议

  1. 事件命名要清晰:使用明确的事件名称,如 UserCreatedOrderPaid
  2. 保持事件轻量:不要做过重的操作,大任务放到队列中
  3. 使用观察者组织:当有多个事件监听时,使用观察者类组织更清晰
  4. 避免递归触发:在事件中保存模型时,注意防止无限递归

通过合理使用模型事件,可以让代码更加解耦、易于维护,并且可以很方便地在模型生命周期中添加各种业务逻辑。

抱歉,评论功能暂时关闭!