Laravel使用Eloquent关联时如何输出扁平化JSON而非嵌套关联对象
Laravel Eloquent 关联数据扁平化输出方案
你当前的需求是避免Eloquent关联序列化时的嵌套结构,同时复用关联定义逻辑、不手动编写join语句,可根据使用场景选择以下三种方案:
方案1:临时处理(无需修改模型,适合一次性需求)
直接在获取数据后手动合并关联属性、移除嵌套关联,完全复用现有关联逻辑:
$activity = Activity::find(1)->load('history'); // 取关联数据:如果是hasOne关联直接取对象,hasMany关联默认取集合第一个元素 $historyData = $activity->history?->first()?->toArray() ?? $activity->history?->toArray() ?? []; // 移除关联中不需要的id字段,避免和主模型id冲突 unset($historyData['id']); // 将关联属性合并到主模型原生属性 $activity->setRawAttributes( array_merge($activity->getAttributes(), $historyData) ); // 移除嵌套的关联对象,避免序列化输出嵌套结构 unset($activity->relations['history']); // 输出扁平化JSON,完全匹配你需要的同层级结构 $json = $activity->toJSON();
优势:不需要修改模型代码,不需要核对表结构/字段名,完全基于已定义的关联逻辑取数。
方案2:模型层全局配置(适合需多次复用的场景)
如果当前模型的所有输出都需要合并指定关联的字段,可直接在模型中重写序列化方法:
// app/Models/Activity.php 中 // 隐藏默认会输出的嵌套关联对象 protected $hidden = ['history']; // 重写toArray方法,全局自动合并关联属性 public function toArray() { // 先获取主模型的默认属性数组 $baseAttributes = parent::toArray(); // 获取关联数据,无关联时返回空数组 $historyData = $this->history?->toArray() ?? []; // 移除关联中无需输出的字段(比如关联自身的id) unset($historyData['id']); // 合并后返回,所有调用模型toArray/toJSON的场景都会自动输出扁平化结构 return array_merge($baseAttributes, $historyData); }
配置完成后,你原来的调用代码不需要做任何修改:
// 原来的代码直接就能输出扁平化结果 $json = Activity::find(1)->load('history')->toJSON();
方案3:Eloquent 资源类(适合接口输出的规范场景)
如果是给前端提供接口,推荐使用Laravel官方的资源类做输出层控制,结构更规范、可控性更强:
- 先创建资源类:
php artisan make:resource ActivityFlatResource
- 在资源类中定义扁平化的输出结构:
// app/Http/Resources/ActivityFlatResource.php public function toArray($request) { return [ // 主模型字段 'id' => $this->id, 'name' => $this->name, 'history_id' => $this->history_id, 'admin_id' => $this->admin_id, 'created_at' => $this->created_at, 'updated_at' => $this->updated_at, // 直接从关联对象取字段,无关联时自动返回null 'type_id' => $this->history?->type_id, 'comment' => $this->history?->comment, 'result_id' => $this->history?->result_id, 'result_comment' => $this->history?->result_comment, 'start_time' => $this->history?->start_time, 'end_time' => $this->history?->end_time, ]; }
- 调用时直接用资源类包装模型即可:
$activity = Activity::find(1)->load('history'); $json = (new \App\Http\Resources\ActivityFlatResource($activity))->toJson();
优势:如果需要输出多个活动的集合,直接用ActivityFlatResource::collection($activityList)即可批量处理,不需要重复写合并逻辑。
补充说明
- 如果关联字段和主模型字段重名,合并时主模型字段会优先;如果需要区分可在合并时给关联字段加前缀(比如把
type_id改成history_type_id)。 - 如果是一对多关联需要取特定条件的关联项(比如最新的一条),可在加载关联时加约束:
->load(['history' => fn($query) => $query->latest()->limit(1)]),避免默认取多条关联数据。
内容的提问来源于stack exchange,提问作者Hyzyr
相关产品推荐
相关产品推荐

