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

Laravel 10中不修改库表结构实现字段与模型属性映射

字段名映射:Laravel模型中实现程序内字段别名,数据库查询用原字段

我正在为第三方不可修改的数据库构建Laravel 10模型,希望在程序内将数据库字段名映射为其他名称——让模型功能都用新名称,但数据库查询仍使用原字段名。

我的尝试

迁移文件

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('test_models', function (Blueprint $table) {
            $table->id();
            $table->string('test');
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('test_models');
    }
};

模型文件

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class TestModel extends Model
{
    use HasFactory;

    protected $appends = ['test_changed'];

    public $hidden = ['test'];

    public function __call($method, $parameters)
    {
        if($method === 'getTestChangedAttribute'){
            return $this->getAttribute('test');
        } else if ($method === 'serTestChangedAttribute'){
            return $this->setAttribute('test', $parameters[0]);
        }
        return parent::__call($method, $parameters);
    }

    public function getAttribute($key)
    {
        if($key === 'test_changed'){
            $key = 'test';
        }
        return parent::getAttribute($key);
    }

    public function setAttribute($key, $value)
    {
        if($key === 'test_changed'){
            $key = 'test';
        }
        return parent::setAttribute($key, $value);
    }
}

TestModel::all()可以正常工作,但执行TestModel::where('test_changed', 'test 1')->get()时出现错误:

SQLSTATE[42S22]: Column not found: 1054 Unknown column 'test_changed' in 'where clause' (Connection: mysql, SQL: select * from test_models where test_changed = test 1)

需要实现新属性test_changed在所有场景下的有效替换。


解决方案

要让查询构造器也识别别名,需要重写模型的字段解析逻辑,同时保留属性访问/设置的映射。以下是优化后的实现:

优化后的模型代码

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class TestModel extends Model
{
    use HasFactory;

    // 统一管理字段别名与原字段的映射
    protected array $columnAliases = [
        'test_changed' => 'test',
    ];

    protected $hidden = ['test'];

    // 覆盖Laravel的字段解析方法,处理查询中的别名
    protected function resolveColumn($column)
    {
        return $this->columnAliases[$column] ?? parent::resolveColumn($column);
    }

    // 处理模型实例的属性获取
    public function getAttribute($key)
    {
        if (isset($this->columnAliases[$key])) {
            $key = $this->columnAliases[$key];
        }
        return parent::getAttribute($key);
    }

    // 处理模型实例的属性赋值
    public function setAttribute($key, $value)
    {
        if (isset($this->columnAliases[$key])) {
            $key = $this->columnAliases[$key];
        }
        return parent::setAttribute($key, $value);
    }
}

关键说明

  1. $columnAliases数组:集中管理所有字段别名和原字段的映射,后续新增别名只需修改这个数组,维护更方便。
  2. resolveColumn方法:Laravel查询构造器在生成SQL时会调用该方法解析字段名,这里自动将别名替换为原字段名,确保where('test_changed', ...)能生成正确的SQL。
  3. getAttribute/setAttribute:处理模型实例的属性读写,直接通过别名访问或赋值时,会自动映射到原字段。

现在所有场景都能正常工作:

  • TestModel::all():模型实例可通过$model->test_changed访问字段值
  • TestModel::where('test_changed', 'test 1')->get():生成的SQL会使用原字段test查询
  • $model->test_changed = 'new value'; $model->save();:值会保存到原字段test中

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.22 17:06:59