贝利信息

Laravel 双向数据转换:模型级自动字段映射与格式化

日期:2026-01-23 00:00 / 作者:心靈之曲

本文介绍如何在 laravel 中通过模型访问器(accessors)、修改器(mutators)结合自定义字段映射机制,实现数据库字段与前端字段的全自动双向转换,避免控制器中重复调用 transform(),提升大型遗留系统的可维护性。

在处理遗留数据库时,前后端字段命名不一致是常见痛点——例如数据库用 first_name,而前端期望 firstName;或需对数值做格式化(如金额存为分,返回为元)。若每次请求都在 Controller 或 Service 层手动转换,不仅冗余,还易出错、难维护。Laravel 原生提供的 访问器(Accessors)与修改器(Mutators) 是解决此问题的核心机制,再辅以轻量级字段映射逻辑,即可实现真正“模型层自治”的双向转换。

✅ 推荐方案:访问器 + 修改器 + 自定义 $maps 映射

Laravel 模型天然支持 get{Attribute}Attribute() 和 set{Attribute}Attribute() 方法,它们分别在读取和赋值属性时自动触发。结合一个简单的 $maps 配置数组,即可统一管理字段别名映射:

 'firstName',
        'last_name'   => 'lastName',
        'birth_date'  => 'birthDate',
        'salary_cents'=> 'salary', // 存储为分,对外显示为元
    ];

    // 【访问器】将数据库值转为前端所需格式
    public function getFirstNameAttribute($value)
    {
        return $value ? ucfirst(strtolower($value)) : null;
    }

    public function getSalaryAttribute($value)
    {
        return $value ? $value / 100 : 0.0;
    }

    public function getBirthDateAttribute($value)
    {
        return $value ? \Carbon\Carbon::parse($value)->format('Y-m-d') : null;
    }

    // 【修改器】将前端传入值转为数据库存储格式
    public function setFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = trim($value);
    }

    public function setSalaryAttribute($value)
    {
        $this->attributes['salary_cents'] = (int) round((float) $value * 100);
    }

    public function setBirthDateAttribute($value)
    {
        $this->attributes['birth_date'] = $value 
            ? \Carbon\Carbon::parse($value)->format('Y-m-d') 
            : null;
    }
}
? 注意:$maps 本身不被 Laravel 自动识别,它仅作为开发者约定的配置项。真正的字段别名能力由 Eloquent 的 casts、appends、hidden 及访问器/修改器协同完成。

? 使用示例:零侵入式 API 开发

启用上述模型后,Controller 完全无需手动转换:

// 创建(自动转换输入 → 数据库格式)
public function store(Request $request)
{
    $person = Person::create($request->only([
        'firstName', 'lastName', 'birthDate', 'salary'
    ]));

    return response()->json($person, 201); // 自动转换数据库值 → 前端格式
}

// 查询(自动转换数据库值 → 响应格式)
public function show($id)
{
    $person = Person::findOrFail($id);
    return response()->json($person); // 返回已格式化的 firstName、salary 等
}

此时 $person->firstName 会触发 getFirstNameAttribute(),而 $person->firstName = 'john' 会触发 setFirstNameAttribute() —— 转换完全透明、不可绕过。

⚠️ 注意事项与进阶建议

通过这种模型驱动的设计,你将告别散落在各处的 transform() 调用,让数据契约清晰固化在领域层——既符合 Laravel 的设计哲学,也显著提升了遗留系统在快速迭代中的健壮性与可读性。