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

Laravel模型Cast访问属性触发set方法,如何阻止该行为?

问题

我在Product类中为price属性定义了Cast:

protected $casts = [
    'price' => MoneyDecimalCast::class . ':RUB,true',
];

在该Cast的set方法中加入了dd("Run");。在控制器中执行:

$test = Product::query()->get()->first();
dd($test->price);

此时不会显示“Run”消息,但修改为:

$test = Product::query()->get()->first();
dd($test->price, $test->price);

则会调用set方法并显示“Run”。另外,在资源中返回产品列表时,每次访问price都会触发set方法。请问如何阻止访问带Cast的属性时调用set方法?

补充测试用的TestCast代码:

class TestCast implements CastsAttributes
{
    public function get($model, string $key, $value, array $attributes)
    {
       return new Product();
    }

    public function set($model, string $key, $value, array $attributes)
    {
        dd('Run');
    }
}

解决方案

问题根源

Laravel模型会跟踪属性的「脏数据」状态:当Cast的get方法返回的值与数据库原始存储值的类型/内容不一致时,模型会判定该属性已被修改。后续再次访问该属性、序列化模型(比如返回资源响应)时,框架会自动触发set方法,尝试将修改后的值转换回数据库存储格式,为后续可能的保存操作做准备。

比如你提供的TestCast中,get方法返回了全新的Product实例,和原始数据库值的类型完全不同,模型直接标记该属性为脏数据,后续访问就会触发set。

解决办法

  1. 让get返回值与原始值兼容
    如果Cast的作用只是转换数据格式而非返回全新对象,调整get方法,让返回值的类型与数据库存储类型匹配(比如数据库存decimal就返回数值类型而非对象),避免模型判定属性为脏数据。

  2. 在Cast中同步原始属性值
    在get方法末尾调用$model->setRawOriginal($key, $value),强制将原始属性值同步为数据库存储的原始值,让模型认为属性没有被修改:

    public function get($model, string $key, $value, array $attributes)
    {
        // 你的转换逻辑
        $convertedValue = new Product();
        // 同步原始值,避免标记为脏数据
        $model->setRawOriginal($key, $value);
        return $convertedValue;
    }
    
  3. 访问属性后手动清除脏标记
    在控制器或资源中,第一次访问属性后,调用$model->syncOriginalAttribute('price'),手动清除脏数据标记:

    $test = Product::query()->get()->first();
    $price = $test->price;
    $test->syncOriginalAttribute('price');
    dd($price, $test->price);
    
  4. 直接获取原始值(绕过Cast)
    如果不需要Cast转换后的结果,在资源或控制器中直接调用$model->getOriginal('price')获取数据库存储的原始值,完全绕过Cast的get和set方法。

内容的提问来源于stack exchange,提问作者symfony_fan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 01:45:26