在 Laravel 8 中为自定义事件设置序列化的模型数据传输,需在模型中声明 SerializesModels trait,构造事件时传入已预加载关联的模型实例,并在监听器中通过 getOriginal() 获取原始值、避免运行时查询。

在 Laravel 8 中为自定义事件设置序列化的模型数据传输,是为了确保监听器能接收到完整、可重建的模型状态,尤其当事件被推送到队列或跨进程分发时——若直接传模型实例而不序列化,队列消费时会因反序列化失败报错“Class App\Models\User does not exist”或丢失关联关系。
确认模型支持序列化
打开模型文件(如 app/Models/User.php),检查是否已声明 SerializesModels trait:
use Illuminate\Queue\SerializesModels;
class User extends Model
{
use SerializesModels;
}
【必须添加】 缺少该 trait 时,即使模型本身可序列化,Laravel 队列系统也不会自动处理关联模型和延迟加载逻辑,导致反序列化后 $model->posts 返回空集合而非报错,极易引发静默数据缺失。
构造事件时传入模型而非原始数组
方法一:直接传递 Eloquent 模型实例
在触发事件处(如控制器或服务类),传入已加载必要关联的模型:
event(new OrderShipped($order->load('items', 'customer')));
这一步会自动调用模型的 serialize() 方法,把主模型 + 已加载关联打包成二进制字符串存入队列。
方法二:显式调用 withoutRelations() 控制范围
若只需主模型字段,不希望关联数据参与序列化(避免体积膨胀或循环引用),链式调用:
event(new OrderShipped($order->withoutRelations()->makeHidden(['api_token'])));
注意:withoutRelations() 不影响数据库查询,只剥离内存中已存在的关联副本,适合“查了但不发”的场景。
在事件类中安全访问模型属性
第一步:在事件类构造函数中接收并赋值模型
public function __construct(public Order $order) { }
第二步:在 handle() 或监听器中,优先使用 getOriginal() 获取变更前值
因为事件可能在 save() 后触发,$this->order->name 返回的是新值;而 $this->order->getOriginal('name') 才是保存前的原始值——这对审计日志、价格比对等业务至关重要。
第三步:避免在事件中调用未预加载的关联
例如 $this->order->items->count() 会触发 N+1 查询,且队列环境下可能因连接关闭而失败。正确做法是:构造事件前就 load('items'),或改用 $this->order->getRelationValue('items') 判断是否存在已加载副本。


















