Laravel队列Mailable build()无法获取User追加token属性问题
Laravel队列异步发信时模型动态属性丢失问题修复
核心原因
你遇到的问题本质是Laravel队列的SerializesModels机制特性,不属于语法错误:
- 所有使用了
SerializesModelstrait的队列任务、邮件、通知类,在被推入队列时,不会完整序列化携带的Eloquent模型实例,只会存储模型的类名、主键、数据库连接标识等元数据。 - 当队列Worker消费任务时,框架会根据存储的元数据重新从数据库查询一次对应模型实例。你在请求生命周期中动态给
$user对象挂载的token属性,既不属于User表的数据库字段,也没有在模型中声明为持久化附加属性,重查模型时自然会丢失。 - 你在
UserRegistered构造函数中能打印到完整属性,是因为构造函数在当前请求的Dispatch阶段执行,此时还未进入队列序列化流程;等执行到build()方法时,已经是Worker端反序列化、重查模型后的阶段,动态挂载的token已经不存在。
可落地修复方案
任意选一种即可解决问题:
方案1:独立传递token参数 (推荐)
不依赖模型序列化逻辑,将token作为独立参数传入Mailable类,稳定性最高:- 调整
EmailVerificationController的发信逻辑:
public static function sendEmail(User $user) { // 生成$token并存储到数据库 SendEmail::dispatch(new UserRegistered($user, $token), $user->email); }- 调整
UserRegistered邮件类,单独接收存储token:
class UserRegistered extends Mailable { use Queueable, SerializesModels; protected $user; protected $token; public function __construct(User $user, string $token) { $this->user = $user; $this->token = $token; } public function build() { return $this->subject('Registration successful') ->view('emails.user_registered') ->with([ 'firstname' => $this->user->firstname, 'token' => $this->token ]); } }- 调整
方案2:将动态属性标记为模型可追加属性
如果需要将token挂载在User实例上传递,可临时将属性加入模型的自动追加列表,配合访问器保证序列化时不丢失:- 调整
sendEmail方法:
public static function sendEmail(User $user) { // 生成$token并存储到数据库 $user->token = $token; // 临时将token加入序列化附加属性列表 $user->append('token'); SendEmail::dispatch(new UserRegistered($user), $user->email); }- 在User模型中添加token属性的访问器:
public function getTokenAttribute() { return $this->attributes['token'] ?? null; }- 调整
方案3:移除
SerializesModelstrait(不推荐)
去掉SendEmail任务和UserRegistered邮件类中的SerializesModelstrait后,框架会完整序列化整个模型对象(包括所有动态挂载属性),不会在消费时重查数据库。但这种方式会增加队列存储开销,还可能出现队列中存储的模型数据和数据库最新数据不一致的问题,非特殊场景不建议使用。
内容的提问来源于stack exchange,提问作者iamsubingyawali
相关产品推荐
相关产品推荐

