如何在Laravel中用Eloquent eager loading让关联查询返回字符串字段
Laravel Eloquent 关联字段扁平化输出方案
完全可以实现,以下两种是生产环境常用的优雅实现方式:
方案1:使用Eloquent访问器(最通用,无需修改查询逻辑)
适合大部分业务场景,自动处理序列化输出,不需要调整预加载逻辑:
- 给
Product模型添加访问器,同时把自定义字段追加到序列化输出列表:
// app/Models/Product.php class Product extends Model { // 定义需要追加到JSON/数组输出的自定义属性 protected $appends = ['product_tag']; // 保留原有关联定义 public function ProductTag() { return $this->hasMany(ProductTag::class); } public function Category() { return $this->belongsTo(Category::class); } // 定义product_tag字段的访问器 public function getProductTagAttribute() { // 直接从预加载的关联数据中拼接tag字符串,无数据返回空字符串 return $this->ProductTag->pluck('tag')->join(','); } }
- 查询时只需要正常预加载关联即可,不需要额外处理:
public static function list(){ // 预加载关联避免N+1问题 return Category::with(['Product.ProductTag'])->get(); }
该方案最终输出的product对象会直接携带product_tag字符串字段,完全符合预期格式。
方案2:查询时直接拼接字段(性能更高,适合大数量查询)
如果不需要保留原有的ProductTag关联数据输出,想要一次查询拿到结果,可以直接用join拼接字段:
public static function list(){ return Category::with(['Product' => function($query){ $query->selectRaw('products.*, GROUP_CONCAT(product_tags.tag) as product_tag') ->leftJoin('product_tags', 'products.id', '=', 'product_tags.product_id') ->groupBy('products.id'); }])->get(); }
注意:如果你的MySQL开启了ONLY_FULL_GROUP_BY模式,需要把products表所有需要查询的字段都加到groupBy参数中,或者调整SQL模式适配。
原有写法问题说明
你之前定义的tag是hasMany类型关联,哪怕你调用first()方法,返回结果依然是模型实例或空集合,所以一定会嵌套一层数组/对象结构。访问器方案直接把计算后的值挂载到Product模型的顶层属性,就可以去掉多余的层级。
内容的提问来源于stack exchange,提问作者Marty
相关产品推荐
相关产品推荐

