Laravel 8+Livewire私聊实现新消息未读角标点击隐藏
Laravel + Livewire 私聊未读消息角标实现方案
1. 数据库字段调整
首先需要给消息表新增已读状态标记,区分已读/未读消息:
- 执行命令生成迁移文件:
php artisan make:migration add_is_read_to_messages_table --table=messages
- 在迁移文件的up方法中添加字段:
public function up() { Schema::table('messages', function (Blueprint $table) { $table->boolean('is_read')->default(false)->after('body'); }); }
- 执行迁移生效:
php artisan migrate
2. 调整会话列表查询逻辑
原有用户列表查询需要追加两个核心数据:和当前用户的最后一条会话消息、对方发给当前用户的未读消息总数,注意未读计数只统计「对方发送、当前用户接收、未标记已读」的消息,避免把自己发的消息算成未读。
在Livewire组件的渲染逻辑中替换原有$users查询:
use Illuminate\Support\Facades\DB; // 在render方法或者mount方法中添加 $currentUserId = auth()->id(); $users = User::where('id', '!=', $currentUserId) ->with([ // 预加载和当前用户的最后一条会话消息 'messages' => function($query) use ($currentUserId) { $query->where(function($q) use ($currentUserId) { $q->where('sender_id', $currentUserId) ->where('receiver_id', DB::raw('users.id')); })->orWhere(function($q) use ($currentUserId) { $q->where('receiver_id', $currentUserId) ->where('sender_id', DB::raw('users.id')); })->latest()->take(1); } ]) // 统计每个会话的未读消息数 ->withCount([ 'messages as unread_count' => function($query) use ($currentUserId) { $query->where('sender_id', DB::raw('users.id')) ->where('receiver_id', $currentUserId) ->where('is_read', false); } ]) ->get();
请提前确认User模型中已经定义好和Message模型的一对多关联:
public function messages() { return $this->hasMany(Message::class); }
3. 打开会话时自动标记消息已读
修改原有viewMessages方法,在选中会话用户时,将该会话下所有对方发给当前用户的未读消息批量标记为已读:
public function viewMessages($userId) { $this->selectedUser = User::findOrFail($userId); // 批量标记未读消息为已读 Message::where('sender_id', $userId) ->where('receiver_id', auth()->id()) ->where('is_read', false) ->update(['is_read' => true]); // 保留你原有加载会话消息的逻辑即可 $this->messages = Message::where(function($query) use ($userId) { $query->where('sender_id', auth()->id())->where('receiver_id', $userId); })->orWhere(function($query) use ($userId) { $query->where('sender_id', $userId)->where('receiver_id', auth()->id()); })->latest()->get(); }
4. 修改视图按条件渲染角标
替换原有视图中写死的角标代码,根据未读计数判断是否显示角标,同时修正最后一条消息的前缀显示逻辑(只有自己发的消息才显示You:前缀):
@if(count($users) > 0) @foreach ($users as $user) <a href="#" class="user" style="color:#CB6F53" wire:click.prevent="viewMessages({{ $user->id }} )"> <div class="user-card rounded {{ $user->id === $selectedUser->id ? 'bg-dark' : '' }} bg-opacity-10 mb-1 rounded mb-1"> <div class="mx-2"> <div class="d-flex pt-3"> @if($user->avator != Null) <img class="rounded-circle" width="48px" height="48px" src="{{Config('wfh.file').$user->avator}}" alt=""> @else <img class="rounded-circle" width="48px" height="48px" src="{{url('')}}/uploads/images/php55DB.tmp.png" alt=""> @endif <div class="notification-text ms-3 w-100"> <span class="username fw-bold">{{$user->full_name}}</span> @if($user->messages->last()->created_at ?? null) @php $newDate = date("d-m-Y", strtotime($user->messages->last()->created_at)); @endphp <span class="float-end small">{{ $newDate }}</span> @endif @if($user->messages->last()->body ?? null) <p class="mt-1 text-muted"> @if($user->messages->last()->sender_id === auth()->id()) You: @endif {{$user->messages->last()->body}} {{-- 未读数大于0时才显示角标 --}} @if($user->unread_count > 0) <span class="badge bg-danger float-end mt-1">{{$user->unread_count}}</span> @endif </p> @else <p class="mt-1 text-muted">暂无聊天记录</p> @endif </div> </div> </div> </div> </a> @endforeach @else <p class="text-center"><b>User not found !</b></p> @endif
5. 实时更新角标(可选)
如果需要新消息到达时无需刷新页面自动显示角标,可以用Livewire事件实现:
- 在发送消息的方法中,消息保存成功后触发全局事件:
// 消息入库逻辑执行完成后添加 $this->dispatch('newMessageNotify', receiverId: $this->selectedUser->id);
- 在会话列表对应的Livewire组件中添加事件监听,收到事件时自动刷新组件数据:
protected $listeners = ['newMessageNotify' => '$refresh'];
内容的提问来源于stack exchange,提问作者zubair syslab
相关产品推荐
相关产品推荐

