可利用数据库虚拟列高效生成新字段:一、迁移中用virtualAs()/storedAs()定义;二、模型中直接读取,不可写;三、查询时用selectRaw()动态添加;四、JSON字段可提取并索引;五、PHP逻辑用$appends+访问器。

如果您在Laravel应用中需要基于已有字段动态生成新字段,又不希望在PHP层反复计算或污染模型逻辑,则可利用数据库原生支持的虚拟列(Virtual Generated Column)实现高效、一致的计算逻辑。以下是具体使用方式:
一、在迁移中定义虚拟列
MySQL 5.7+ 和 MariaDB 10.2+ 支持 virtual 和 stored 两种生成列类型,Laravel 8.x 起可通过 Blueprint 方法直接声明。virtual 列不占用磁盘空间,仅在查询时实时计算;stored 列则物化存储,支持索引。
1、在迁移文件中使用 virtualAs() 方法定义虚拟列,表达式需为字符串形式且符合数据库语法。
2、使用 storedAs() 方法定义存储型生成列,适用于需建立索引或频繁读取的场景。
3、为生成列添加索引以加速 WHERE 或 ORDER BY 查询,例如调用 ->index()。
二、在模型中映射虚拟列
Laravel 模型默认将数据库列自动映射为属性,只要迁移中已创建虚拟列,且该列存在于表结构中,模型实例即可直接访问其值,无需额外配置。注意:虚拟列不可写入,尝试赋值并保存将被忽略。
1、确保数据库表已执行迁移,使虚拟列真实存在于 schema 中。
2、在 Eloquent 模型中,将虚拟列名加入 $fillable 数组不会生效,因其非物理列;也不应加入 $casts,除非需类型转换(如转为 int)。
3、若需在 API 响应中强制包含虚拟列,且该列未被 $hidden 掩盖,则无需额外操作;否则可使用 makeVisible() 临时暴露。
三、在查询中动态添加虚拟字段
当无法修改表结构或仅需单次查询中注入标识性字段时,可用 selectRaw() 在 SELECT 子句中注入常量或表达式作为“伪虚拟列”,该字段仅在当前查询结果中存在。
1、在 Eloquent 查询中调用 selectRaw("1 as scenario_type"),为每行附加固定值字段。
2、结合 DB::raw() 构建更复杂表达式,例如 selectRaw("CONCAT(name, ' - ', price) as display_label")。
3、若需对联合查询(UNION)结果打标,可在每个子查询中分别使用 selectRaw() 注入不同标识值。
四、通过 JSON 字段提取创建可索引虚拟列
针对 JSON 类型字段(如 MySQL 的 JSON 列),无法直接索引整个 JSON 值,但可创建虚拟列提取特定键,并对其建立索引,从而提升查询性能。
1、在迁移中使用 ->virtualAs("JSON_EXTRACT(metadata, '$.status')") 提取 JSON 内指定路径的值。
2、将提取结果强制转换为目标类型,例如 ->virtualAs("CAST(JSON_EXTRACT(metadata, '$.score') AS SIGNED)")。
3、对新建虚拟列调用 ->index(),使 where('status', 'active') 等查询命中索引。
五、使用 $appends 实现模型级虚拟属性
当计算逻辑必须依赖 PHP 运行时环境(如调用外部服务、组合关联数据、格式化时间)而无法下推至数据库时,可采用模型的 $appends 机制,在序列化时动态注入字段。
1、在模型中声明 public $appends = ['full_name'];,字段名须为 snake_case。
2、定义对应访问器方法,命名格式为 getFullNameAttribute(),方法内返回计算结果。
3、确保访问器中不执行 N+1 查询;如需关联数据,应提前用 withSum() 或 selectRaw() 预加载并绑定到模型属性。











