You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 04:48:29