Laravel模型创建Accessor属性时无法访问started_at属性的原因与优化方案
Laravel 模型访问器中 $this->started_at 未定义问题解析及优化方案
问题场景
我在Laravel模型中定义了expires访问器,代码如下:
protected function expires(): Attribute { if ($this->started_at) { $expiry = $this->started_at->addDays(20); } return Attribute::make( get: fn () => $expiry ?? null ); }
运行时抛出ErrorException: Undefined property: Models\Job::$started_at错误。
改用直接操作$this->attributes可以规避错误,但需要手动用Carbon解析,代码不够简洁:
protected function expires(): Attribute { if ($this->attributes['started_at']) { $expiry = Carbon::parse($this->attributes['started_at'])->addDays(20); } return Attribute::make( get: fn () => $expiry ?? null ); }
但奇怪的是,在if语句前执行dd($this->started_at)时,该属性已经被Laravel转换为Carbon对象,希望能直接使用这个对象简化代码。
原因分析
问题出在代码的执行时机上:
- 你在
expires()方法的闭包外部执行了$this->started_at的判断逻辑,这段代码会在第一次访问$expires属性、初始化Attribute实例时执行。 - 此时模型的属性访问机制尚未完全就绪,直接访问
$this->started_at会绕过Laravel的魔术方法(__get)处理逻辑,导致PHP认为该属性未定义。 - 而用
dd($this->started_at)时,dd函数会强制触发模型的__get方法,正确加载并转换started_at为Carbon对象,所以能正常显示,但这只是临时触发了属性加载,无法解决常规运行时的问题。
最优实现方式
把所有逻辑移到get闭包内部即可,这样代码会在实际获取expires属性值时执行,此时模型的属性系统已经完全初始化,能正常通过$this->started_at获取转换后的Carbon对象:
protected function expires(): Attribute { return Attribute::make( get: fn () => $this->started_at?->addDays(20) ); }
这里使用了PHP的空安全运算符(?->),可以替代原有的if判断和?? null,让代码更简洁:
- 如果
$this->started_at为null,空安全运算符会直接返回null,避免报错; - 如果
$this->started_at存在,则调用addDays(20)返回计算后的Carbon对象。
内容的提问来源于stack exchange,提问作者Joseph
相关产品推荐
相关产品推荐

