本文介绍如何在 laravel 中通过模型访问器(accessors)、修改器(mutators)结合自定义字段映射机制,实现数据库字段与前端字段的全自动双向转换,避免控制器中重复调用 transform(),提升大型遗留系统的可维护性。
在处理遗留数据库时,前后端字段命名不一致是常见痛点——例如数据库用 first_name,而前端期望 firstName;或需对数值做格式化(如金额存为分,返回为元)。若每次请求都在 Controller 或 Service 层手动转换,不仅冗余,还易出错、难维护。Laravel 原生提供的 访问器(Accessors)与修改器(Mutators) 是解决此问题的核心机制,再辅以轻量级字段映射逻辑,即可实现真正“模型层自治”的双向转换。
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 及访问器/修改器协同完成。
启用上述模型后,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 的设计哲学,也显著提升了遗留系统在快速迭代中的健壮性与可读性。