Laravel中能否同时对Base表与Pivot表排序,展示最新记录
问题
能否同时对Base表(UserDocument)和Pivot表(share_documents)进行排序?我希望将最新的记录(无论是用户自有文档还是共享文档)展示在列表顶部。
当前代码实现:
public function get_user_documents() { $userId = Auth::id(); $user_documents = UserDocument::where('user_id', $userId) ->orWhereHas('share_documents', function ($query) use ($userId) { $query->where('users.id', $userId); }) ->with(['share_documents' => function ($query) use ($userId) { $query->where('users.id', $userId); }]) ->latest() ->get(); // $user_documents = $user_documents->map(function ($document) use ($userId) { // $status = $document->user_id == $userId ? 'owned' : 'shared'; // $document->status = $status; // return $document; // }); return response()->json($user_documents); }
解决方案
可以实现,核心思路是取自有文档的时间字段、共享记录的时间字段这两个值里的最大值,然后按这个最大值排序。以下是两种可行的实现方式:
方式一:数据库层面排序(推荐)
直接在SQL查询阶段计算每个文档的最新时间,性能更优,适合数据量较大的场景。假设UserDocument表有created_at字段,share_documents中间表有created_at字段:
public function get_user_documents() { $userId = Auth::id(); $user_documents = UserDocument::select('user_documents.*') // 计算每个文档的最新时间:自有文档用自身时间,共享文档取共享记录的时间 ->selectRaw('GREATEST(user_documents.created_at, COALESCE(share_documents.created_at, user_documents.created_at)) as latest_time') ->leftJoin('share_documents', function ($join) use ($userId) { $join->on('user_documents.id', '=', 'share_documents.user_document_id') ->where('share_documents.user_id', '=', $userId); }) ->where(function ($query) use ($userId) { $query->where('user_documents.user_id', $userId) ->orWhereNotNull('share_documents.id'); }) ->groupBy('user_documents.id') // 避免同一文档因共享记录重复出现 ->orderBy('latest_time', 'desc') ->with(['share_documents' => function ($query) use ($userId) { $query->where('user_id', $userId); }]) ->get(); // 可选:给文档添加自有/共享的状态标识 $user_documents->map(function ($document) use ($userId) { $document->status = $document->user_id == $userId ? 'owned' : 'shared'; return $document; }); return response()->json($user_documents); }
关键说明
GREATEST():取两个时间字段中的较大值COALESCE():处理无共享记录的情况,默认使用文档自身的时间leftJoin:关联共享表以获取共享记录的时间groupBy:确保同一文档只返回一条结果
方式二:集合层面排序
如果不想修改数据库查询逻辑,可以在获取数据集合后,通过PHP计算每个文档的最新时间再排序:
public function get_user_documents() { $userId = Auth::id(); $user_documents = UserDocument::where('user_id', $userId) ->orWhereHas('share_documents', function ($query) use ($userId) { $query->where('user_id', $userId); }) ->with(['share_documents' => function ($query) use ($userId) { $query->where('user_id', $userId); }]) ->get(); // 计算最新时间并排序 $user_documents = $user_documents->map(function ($document) use ($userId) { $document->status = $document->user_id == $userId ? 'owned' : 'shared'; // 取共享记录中的最新时间,无共享记录则用文档自身时间 $shareLatest = $document->share_documents->max('created_at'); $document->latest_time = $shareLatest ?: $document->created_at; return $document; })->sortByDesc('latest_time')->values(); return response()->json($user_documents); }
关键说明
max('created_at'):从共享记录集合中取出最新的时间sortByDesc():按最新时间倒序排列values():重置集合的键名,避免返回的JSON出现非连续索引
注意事项
- 确保
share_documents中间表存在时间字段(如created_at或updated_at),若没有需先添加 - 若想更准确反映文档的最新变动,可将
created_at替换为updated_at - 数据量大时优先选择数据库层面排序,性能更优
内容的提问来源于stack exchange,提问作者Hassan Khan
相关产品推荐
相关产品推荐

