Laravel Vuejs 实战:开发知乎 (37-41)私信标为已读
1.自定义MessageCollection类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Collection; 6 7 class MessageCollection extends Collection 8 { 9 public function markAsRead() 10 { 11 $this->each(function ($message) { 12 $message->markAsRead(); 13 }); 14 } 15 } 16 17
2.向Message中添加一个newCollection方法:
1 public function newCollection(array $models = []) 2 { 3 return new MessageCollection($models); 4 } 5
及markAsRead方法:
1 public function markAsRead() 2 { 3 if (is_null($this->read_at)) { 4 $this->forceFill( 5 [ 6 'read_at' => $this->freshTimestamp(), 7 ])->save(); 8 } 9 } 10
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?php 2 3 namespace App; 4 5 use Illuminate\Database\Eloquent\Model; 6 7 class Message extends Model 8 { 9 // 10 11 protected $table = 'messages'; 12 13 protected $fillable = ['from_user_id', 'to_user_id', 'content']; 14 15 public function fromUser() 16 { 17 return $this->belongsTo(User::class, 'from_user_id'); 18 } 19 20 public function toUser() 21 { 22 return $this->belongsTo(User::class, 'to_user_id'); 23 } 24 25 public function markAsRead() 26 { 27 if (is_null($this->read_at)) { 28 $this->forceFill( 29 [ 30 'read_at' => $this->freshTimestamp(), 31 ])->save(); 32 } 33 } 34 35 public function newCollection(array $models = []) 36 { 37 return new MessageCollection($models); 38 } 39 } 40 41
3.这样在InboxCollection.php中调用的时候:
1 //设置信息已读 2 $messages->markAsRead(); 3
即:
1 public function show($userId) 2 { 3 if (auth()->user()->id == $userId) { 4 return redirect()->back()->with('不能回复自己'); 5 } 6 $messages = auth()->user()->messages->where('from_user_id', $userId); 7 8 //设置信息已读 9 $messages->markAsRead(); 10 11 //获取回复信息 12 $replies = Message::query()->where('to_user_id', $userId) 13 ->where('from_user_id', auth()->user()->id) 14 ->get(); 15 if ($replies) { 16 //整合 17 foreach ($replies as $reply) { 18 $messages->push($reply); 19 } 20 } 21 //排序 22 $messages = $messages->sortBy('created_at'); 23 return view('inbox.show', compact('messages')); 24 } 25
4.显示视图:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @extends('layouts.app') 2 @section('content') 3 <div class="container"> 4 <div class="row"> 5 <div class="card"> 6 @if($messages->first()) 7 <div class="card-header"> 8 <a href="#"> 9 10 <img src="{{ $messages->first()->fromUser->avatar }}" 11 alt="{{ $messages->first()->fromUser->name }}" 12 class="img-thumbnail img-fluid card-img" style="height: 30px;width: 30px"> 13 {{ $messages->first()->fromUser->name }} 14 15 </a> 16 </div> 17 <div class="card-body"> 18 <div class="messaging"> 19 <div class="inbox_msg"> 20 21 <div class="mesgs"> 22 <div class="msg_history"> 23 @forelse($messages as $message) 24 @if($message->fromUser->id===auth()->user()->id) 25 <div class="outgoing_msg"> 26 <div class="sent_msg"> 27 @if($message->read_at) 28 <span class="text-black-50">已读</span> 29 @endif 30 <p> {{$message->content}}</p> 31 <span 32 class="time_date"> {{$message->created_at->diffForHumans()}}</span> 33 </div> 34 </div> 35 @else 36 <div class="incoming_msg"> 37 <div class="incoming_msg_img"><img 38 src="{{ $messages->first()->fromUser->avatar }}" 39 alt="{{ $messages->first()->fromUser->name }}"> 40 </div> 41 <div class="received_msg"> 42 <div class="received_withd_msg"> 43 <p> {{$message->content}}</p> 44 <span 45 class="time_date"> {{$message->created_at->diffForHumans()}}</span> 46 </div> 47 </div> 48 </div> 49 @endif 50 @empty 51 @endforelse 52 </div> 53 </div> 54 </div> 55 </div> 56 <div class="type_msg mt-2"> 57 <div class="input_msg_write"> 58 <form 59 action="{{ route('inbox.store',($message->fromUser->id===auth()->user()->id)?$message->toUser->id:$message->fromUser->id) }}" 60 method="post"> 61 @csrf 62 <input type="text" class="write_msg" name="content" placeholder="输入信息回复"/> 63 <button class="msg_send_btn" type="submit"> 64 <i class="fa fa-paper-plane-o" 65 aria-hidden="true"></i> 66 </button> 67 </form> 68 </div> 69 </div> 70 </div> 71 @else 72 <div class="card-header">不好意思!找不到你要的数据!</div> 73 @endif 74 </div> 75 </div> 76 </div> 77 @endsection 78 <style> 79 .container { 80 margin: auto; 81 } 82 83 img { 84 max-width: 100%; 85 } 86 87 .inbox_people { 88 background: #f8f8f8 none repeat scroll 0 0; 89 float: left; 90 overflow: hidden; 91 width: 40%; 92 border-right: 1px solid #c4c4c4; 93 } 94 95 .inbox_msg { 96 border: 1px solid #c4c4c4; 97 clear: both; 98 overflow: hidden; 99 } 100 101 .top_spac { 102 margin: 20px 0 0; 103 } 104 105 .recent_heading { 106 float: left; 107 width: 40%; 108 } 109 110 .srch_bar { 111 display: inline-block; 112 text-align: right; 113 width: 60%; 114 } 115 116 .recent_heading h4 { 117 color: #05728f; 118 font-size: 21px; 119 margin: auto; 120 } 121 122 .srch_bar input { 123 border: 1px solid #cdcdcd; 124 border-width: 0 0 1px 0; 125 width: 80%; 126 padding: 2px 0 4px 6px; 127 background: none; 128 } 129 130 .srch_bar .input-group-addon button { 131 background: rgba(0, 0, 0, 0) none repeat scroll 0 0; 132 border: medium none; 133 padding: 0; 134 color: #707070; 135 font-size: 18px; 136 } 137 138 .srch_bar .input-group-addon { 139 margin: 0 0 0 -27px; 140 } 141 142 .chat_ib h5 { 143 font-size: 15px; 144 color: #464646; 145 margin: 0 0 8px 0; 146 } 147 148 .chat_ib h5 span { 149 font-size: 13px; 150 float: right; 151 } 152 153 .chat_ib p { 154 font-size: 14px; 155 color: #989898; 156 margin: auto 157 } 158 159 .chat_img { 160 float: left; 161 width: 11%; 162 } 163 164 .chat_ib { 165 float: left; 166 padding: 0 0 0 15px; 167 width: 88%; 168 } 169 170 .chat_people { 171 overflow: hidden; 172 clear: both; 173 } 174 175 .chat_list { 176 border-bottom: 1px solid #c4c4c4; 177 margin: 0; 178 padding: 18px 16px 10px; 179 } 180 181 .inbox_chat { 182 height: 550px; 183 overflow-y: scroll; 184 } 185 186 .active_chat { 187 background: #ebebeb; 188 } 189 190 .incoming_msg_img { 191 display: inline-block; 192 width: 6%; 193 } 194 195 .received_msg { 196 display: inline-block; 197 padding: 0 0 0 10px; 198 vertical-align: top; 199 width: 92%; 200 } 201 202 .received_withd_msg p { 203 background: #ebebeb none repeat scroll 0 0; 204 border-radius: 3px; 205 color: #646464; 206 font-size: 14px; 207 margin: 0; 208 padding: 5px 10px 5px 12px; 209 width: 100%; 210 } 211 212 .time_date { 213 color: #747474; 214 display: block; 215 font-size: 12px; 216 margin: 8px 0 0; 217 } 218 219 .received_withd_msg { 220 width: 57%; 221 } 222 223 .mesgs { 224 float: left; 225 padding: 30px 15px 0 25px; 226 width: 60%; 227 } 228 229 .sent_msg p { 230 background: #05728f none repeat scroll 0 0; 231 border-radius: 3px; 232 font-size: 14px; 233 margin: 0; 234 color: #fff; 235 padding: 5px 10px 5px 12px; 236 width: 100%; 237 } 238 239 .outgoing_msg { 240 overflow: hidden; 241 margin: 26px 0 26px; 242 } 243 244 .sent_msg { 245 float: right; 246 width: 46%; 247 } 248 249 .input_msg_write input { 250 background: rgba(0, 0, 0, 0) none repeat scroll 0 0; 251 border: medium none; 252 color: #4c4c4c; 253 font-size: 15px; 254 min-height: 48px; 255 width: 100%; 256 } 257 258 .type_msg { 259 border-top: 1px solid #c4c4c4; 260 position: relative; 261 } 262 263 .msg_send_btn { 264 background: #05728f none repeat scroll 0 0; 265 border: medium none; 266 border-radius: 50%; 267 color: #fff; 268 cursor: pointer; 269 font-size: 17px; 270 height: 33px; 271 position: absolute; 272 right: 0; 273 top: 11px; 274 width: 33px; 275 } 276 277 .msg_history { 278 height: 516px; 279 overflow-y: auto; 280 } 281 </style> 282 283
效果:
5.添加新的未读消息加特效:
当前状态:
代码:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 @extends('layouts.app') 2 @section('content') 3 <div class="container"> 4 <div class="row"> 5 <div class="card"> 6 <div class="card-header"> 7 @if($messages->filter(function ($value, $key){ 8 return $value->where('read_at',null)->count()>0;})->count()>0) 9 <div class="alert alert-danger"> 10 你有新的消息! 11 </div> 12 @endif 13 </div> 14 <div class="card-body"> 15 16 @forelse($messages as $messageGroup) 17 <div class="card"> 18 <div class="card-header"> 19 <a href="#"> 20 <img src="{{ $messageGroup->first()->fromUser->avatar }}" 21 alt="{{ $messageGroup->first()->fromUser->name }}" 22 class="img-thumbnail img-fluid card-img" style="height: 30px;width: 30px"> 23 {{ $messageGroup->first()->fromUser->name }} 24 </a> 25 </div> 26 <div class="card-body"> 27 <p class="text-success "> 查看详细对话请点击:</p> 28 @if($messageGroup->where('read_at',null)->count()>0) 29 <p class="alert alert-warning">{{$messageGroup->where('read_at',null)->count()}} 30 条未读消息</p> 31 @endif 32 <a href=" {{ route('inbox.show', $messageGroup->first()->fromUser->id) }}" 33 class="btn btn-block bg-light"> {{ $messageGroup->first()->content }}</a> 34 </div> 35 </div> 36 @empty 37 @endforelse 38 </div> 39 </div> 40 </div> 41 </div> 42 @endsection 43 44
修改后:
修复 bug 和实现 Repository 模式:
我的代码没有那个bug。其次我这不实现Repository 模式
6.实现私信通知:
当用户收到新的私信的时候,发送一个通知告知用户。
执行:
1 php artisan make:notification NewMessageNotification
代码:
暂时只实现站内通知,
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?php 2 3 namespace App\Notifications; 4 5 use App\Message; 6 use Illuminate\Bus\Queueable; 7 use Illuminate\Contracts\Queue\ShouldQueue; 8 use Illuminate\Notifications\Messages\MailMessage; 9 use Illuminate\Notifications\Notification; 10 11 class NewMessageNotification extends Notification 12 { 13 use Queueable; 14 /** 15 * @var Message 16 */ 17 private $message; 18 19 /** 20 * Create a new notification instance. 21 * 22 * @return void 23 */ 24 public function __construct(Message $message)//使用依赖注入 25 { 26 // 27 $this->message = $message; 28 } 29 30 /** 31 * Get the notification's delivery channels. 32 * 33 * @param mixed $notifiable 34 * @return array 35 */ 36 public function via($notifiable) 37 { 38 return ['database']; 39 } 40 41 /** 42 * Get the mail representation of the notification. 43 * 44 * @param mixed $notifiable 45 * @return \Illuminate\Notifications\Messages\MailMessage 46 */ 47 public function toMail($notifiable) 48 { 49 return (new MailMessage) 50 ->line('The introduction to the notification.') 51 ->action('Notification Action', url('/')) 52 ->line('Thank you for using our application!'); 53 } 54 55 /** 56 * Get the array representation of the notification. 57 * 58 * @param mixed $notifiable 59 * @return array 60 */ 61 public function toArray($notifiable) 62 { 63 return [ 64 // 65 ]; 66 } 67 68 69 /** 70 * @param $notifiable 71 * @return array 72 */ 73 public function toDatabase($notifiable) 74 { 75 //要记录的数据 76 return [ 77 'fromUser' => $this->message->fromUser->name, 78 'id' => $this->message->fromUser->id, 79 'content' => $this->message->content, 80 ]; 81 } 82 } 83 84
添加:
1 //发送通知 2 $message 3 ->toUser //通知接受方,所以是toUser 4 ->notify(new NewMessageNotification($message));//用户类使用了Notifiable这个 trait 5
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?php 2 3 namespace App\Http\Controllers; 4 5 use App\Message; 6 7 use App\Notifications\NewMessageNotification; 8 use Illuminate\Http\Request; 9 use Illuminate\Support\Facades\Notification; 10 11 12 class InboxController extends Controller 13 { 14 public function __construct() 15 { 16 $this->middleware('auth'); 17 } 18 19 // 20 public function index() 21 { 22 $messages = auth()->user()->messages->groupBy('from_user_id'); 23 // $messages->map(function ($message) { 24 // return $message->map(function ($item) { 25 //// return $item->with('user'); 26 // return $item->fromUser; 27 // }); 28 // }); 29 return view('inbox.index', compact('messages')); 30 } 31 32 public function show($userId) 33 { 34 if (auth()->user()->id == $userId) { 35 return redirect()->back()->with('不能回复自己'); 36 } 37 $messages = auth()->user()->messages->where('from_user_id', $userId); 38 39 //设置信息已读 40 $messages->markAsRead(); 41 42 //获取回复信息 43 $replies = Message::query()->where('to_user_id', $userId) 44 ->where('from_user_id', auth()->user()->id) 45 ->get(); 46 if ($replies) { 47 //整合 48 foreach ($replies as $reply) { 49 $messages->push($reply); 50 } 51 } 52 //排序 53 $messages = $messages->sortBy('created_at'); 54 return view('inbox.show', compact('messages')); 55 } 56 57 public function store(Request $request, $userId) 58 { 59 60 if (auth()->user()->id == $userId) { 61 return redirect()->back()->with('不能回复自己'); 62 } 63 $message = Message::create( 64 [ 65 'from_user_id' => auth()->user()->id, 66 'to_user_id' => $userId, 67 'content' => $request->get('content') 68 ] 69 ); 70 71 //发送通知 72 $message 73 ->toUser //通知接受方,所以是toUser 74 ->notify(new NewMessageNotification($message));//用户类使用了Notifiable这个 trait 75 76 return redirect()->route('inbox.show', $userId)->with('success', '发送成功'); 77 } 78 } 79 80
view添加:
new_message_notification.blade.php:
1 <li class="badge badge-light text-primary"> 2 <p> "你好!" 3 <a href="{{ route('inbox.show',$notification->data['id']) }}"> 4 {{ $notification->data['fromUser']."给你发来了新私信:" }} 5 </a> 6 </p> 7 <p> {{ $notification->data['content'] }} </p> 8 </li> 9 10
7.设置通知已读:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 <?php 2 3 namespace App\Http\Controllers; 4 5 use App\User; 6 use Illuminate\Http\Request; 7 8 class NotificationController extends Controller 9 { 10 // 11 public function index() 12 { 13 $user = auth()->user(); 14 15 //设置已读字段数据 16 $user->notifications->map(function ($notification) { 17 $notification->forceFill([ 18 'read_at' => now(), 19 ])->save(); 20 }); 21 return view('notifications.index', compact('user')); 22 } 23 } 24 25
视图:略