Laravel Eloquent查询问题:获取带main标签或首图的媒体数据
解决Eloquent关联查询优先返回指定标签媒体的问题
方案一:数据库层面直接筛选(推荐)
通过在关联查询中排序+限制,让数据库直接返回每个模型的优先媒体,性能最优:
$models = Model::with(['media' => function ($query) { // 按是否包含'main'标签降序排序,带标签的排在最前 $query->orderByRaw("JSON_CONTAINS(custom_properties->'$.tags', '\"main\"') DESC") // 按媒体ID升序确保无标签时取关联的第一个媒体 ->orderBy('id', 'asc') // 只返回符合要求的第一条媒体 ->limit(1); }])->paginate(self::PER_PAGE); return $models;
原理说明
JSON_CONTAINS(custom_properties->'$.tags', '\"main\"')返回1(包含标签)或0(不包含),降序排序后带'main'的媒体会排在同模型媒体的最前面- 再按
id升序保证无标签时取关联的第一个媒体 limit(1)确保每个模型仅返回一条符合要求的媒体
方案二:分页后补充缺失数据(保持原分页结构)
如果需要保留原关联查询逻辑,可先分页获取带'main'标签的媒体,再批量补充无标签模型的第一个媒体,避免N+1查询:
// 第一步:获取带'main'标签媒体的分页模型 $models = Model::with(['media' => function ($query) { $query->whereJsonContains('custom_properties->tags', 'main'); }])->paginate(self::PER_PAGE); // 收集没有媒体的模型ID $missingMediaModelIds = $models->items() ->filter(fn($model) => $model->media->isEmpty()) ->pluck('id'); if ($missingMediaModelIds->isNotEmpty()) { // 批量获取这些模型的第一个媒体,按模型ID分组 $fallbackMedia = Media::whereIn('model_id', $missingMediaModelIds) ->orderBy('model_id') ->orderBy('id') ->get() ->groupBy('model_id'); // 给对应模型设置 fallback 媒体 $models->items()->each(function ($model) use ($fallbackMedia) { if ($model->media->isEmpty() && isset($fallbackMedia[$model->id])) { $model->setRelation('media', collect([$fallbackMedia[$model->id]->first()])); } }); } return $models;
注意事项
- 使用
setRelation()方法修改模型关联数据,不会破坏原LengthAwarePaginator实例结构 - 批量查询 fallback 媒体,避免循环查询带来的性能问题
内容的提问来源于stack exchange,提问作者drzej drzej
相关产品推荐
相关产品推荐

