Laravel 10中为嵌套数组实现分页功能
一对一聊天消息分组分页解决方案
针对你当前按日期分组消息后无法实现分页的问题,以下提供两种可行的实现方案,优先推荐游标分页(适配聊天场景下加载旧消息的需求):
方案一:游标分页(推荐)
通过传递最后加载的消息时间戳来获取更早的消息,避免页码分页因新消息插入导致的偏移问题。
修改后的代码
public function getMessages(Request $request) { $validatedData = Validator::make($request->all(), [ "receiver_id" => "required", "last_created_at" => "nullable|date_format:Y-m-d\TH:i:s.u\Z", "limit" => "nullable|integer|min:1|max:100", ]); if ($validatedData->fails()) { return $this->sendresponse('false', 'validation error', $validatedData->errors()); } $user_id = Auth::user()->id; $receiver_id = $request->receiver_id; $limit = $request->limit ?? 20; // 构建消息查询条件 $query = Message::where(function ($q) use ($user_id, $receiver_id) { $q->where('sender_id', $user_id)->where('receiver_id', $receiver_id); })->orWhere(function ($q) use ($user_id, $receiver_id) { $q->where('sender_id', $receiver_id)->where('receiver_id', $user_id); }); // 若传递了last_created_at,获取该时间之前的消息 if ($request->has('last_created_at')) { $query->where('created_at', '<', $request->last_created_at); } // 按时间倒序取指定条数,再反转回正序以匹配原分组逻辑 $messages = $query->orderBy('created_at', 'desc')->take($limit)->get()->reverse(); // 按日期分组并处理消息标记 $groupedMessages = $messages->groupBy(function ($message) { return $message->created_at->format('l'); })->map(function ($messagesByDay) use ($user_id) { return $messagesByDay->reverse()->values()->map(function ($message) use ($user_id) { $message->sent = $message->sender_id == $user_id ? 1 : 0; $message->received = $message->receiver_id == $user_id ? 1 : 0; $message->time = $message->created_at->format('h:i A'); return $message; }); }); // 反转分组顺序,最新日期的消息组排在最前 $groupedMessages = $groupedMessages->reverse(); // 判断是否还有更多旧消息 $hasMore = $messages->isNotEmpty() ? $query->where('created_at', '<', $messages->last()->created_at)->exists() : false; return $this->sendresponse('true', 'found', [ 'messages' => $groupedMessages, 'receiver' => AppUser::where('id', $receiver_id)->selectbasic()->withCount('followers')->first(), 'has_more' => $hasMore, 'last_created_at' => $messages->isEmpty() ? null : $messages->last()->created_at->toIso8601String() ]); }
前端调用逻辑
- 首次请求:无需传递
last_created_at,获取最新的20条消息 - 加载更多:当用户滚动到消息顶部时,传递上一次返回的
last_created_at,获取更早的消息 - 根据
has_more判断是否还有更多数据,决定是否显示“加载更多”按钮
方案二:页码分页
适用于对分页逻辑要求简单的场景,但需注意新消息插入会导致页码偏移。
修改后的代码
public function getMessages(Request $request) { $validatedData = Validator::make($request->all(), [ "receiver_id" => "required", "page" => "nullable|integer|min:1", "per_page" => "nullable|integer|min:1|max:100", ]); if ($validatedData->fails()) { return $this->sendresponse('false', 'validation error', $validatedData->errors()); } $user_id = Auth::user()->id; $receiver_id = $request->receiver_id; $perPage = $request->per_page ?? 20; // 构建消息查询条件并分页 $messages = Message::where(function ($q) use ($user_id, $receiver_id) { $q->where('sender_id', $user_id)->where('receiver_id', $receiver_id); })->orWhere(function ($q) use ($user_id, $receiver_id) { $q->where('sender_id', $receiver_id)->where('receiver_id', $user_id); })->orderBy('created_at', 'asc')->paginate($perPage); // 按日期分组并处理消息标记 $groupedMessages = $messages->getCollection()->groupBy(function ($message) { return $message->created_at->format('l'); })->map(function ($messagesByDay) use ($user_id) { return $messagesByDay->reverse()->values()->map(function ($message) use ($user_id) { $message->sent = $message->sender_id == $user_id ? 1 : 0; $message->received = $message->receiver_id == $user_id ? 1 : 0; $message->time = $message->created_at->format('h:i A'); return $message; }); }); // 反转分组顺序,最新日期的消息组排在最前 $groupedMessages = $groupedMessages->reverse(); return $this->sendresponse('true', 'found', [ 'messages' => $groupedMessages, 'receiver' => AppUser::where('id', $receiver_id)->selectbasic()->withCount('followers')->first(), 'pagination' => [ 'current_page' => $messages->currentPage(), 'last_page' => $messages->lastPage(), 'per_page' => $messages->perPage(), 'total_messages' => $messages->total() ] ]); }
关键说明
- 两种方案均保留了原有的按日期分组、消息标记(sent/received)、分组顺序反转的逻辑
- 游标分页通过
last_created_at和has_more实现滚动加载,更符合聊天应用的用户体验 - 页码分页返回标准的分页参数,适合需要跳转页码的场景
内容的提问来源于stack exchange,提问作者The Dev
相关产品推荐
相关产品推荐

