使用Laravel-Websockets向特定用户发送消息(一对一聊天)

3

我在我的应用程序中有一个 WebSocket 群聊,它将用户的消息广播给所有其他用户。我还想创建一个一对一聊天,只向两个人广播消息。如何告诉 WebSocket 将消息广播到特定的用户?

我的控制器:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

use App\Message;

use App\Events\MessageSent;

use Auth;

class ChatsController extends Controller
{

    public function index()
    {
        if (Auth::check() === false) {

            return view('login');

        }
        else
        {

            return view('chats');

        }
    }

    public function fetchMessages()
    {
        return Message::with('user')->get();
    }

    public function sendMessage(Request $request)
    {
        $message = auth()->user()->messages()->create([
            'message' => $request->message
        ]);
        broadcast(new MessageSent($message->load('user')))->toOthers();
        return ['status' => 'success'];
    }
}

我的Vue文件:

<template>
   <div class="row">

       <div class="col-8">
           <div class="card card-default">
               <div class="card-header">Messages</div>
               <div class="card-body p-0">
                   <ul class="list-unstyled" style="height:300px; overflow-y:scroll" v-chat-scroll>
                       <li class="p-2" v-for="(message, index) in messages" :key="index" >

                          <div style="background: #009afb; padding: 8px; color: white; border-radius: 15px;">

                             <img v-if="message.user.image == 'img'" src="https://www.stickpng.com/assets/images/585e4bf3cb11b227491c339a.png" style="width: 35px; height: 35px;">
                            <img v-else v-bind:src="'../images/' + message.user.image" 
                             style="width: 35px; height: 35px;border-radius: 50%;">

                             <strong>{{ message.user.name }} {{ message.user.surname }} :</strong>

                             {{ message.message }}

                              <p style="color:lightgray;">
                                {{ message.created_at }}
                              </p>

                          </div>

                       </li>
                   </ul>
               </div>

               <input
                    @keydown="sendTypingEvent"
                    @keyup.enter="sendMessage"
                    v-model="newMessage"
                    type="text"
                    name="message"
                    placeholder="Enter your message..."
                    class="form-control">
           </div>
            <span class="text-muted" v-if="activeUser" >{{ activeUser.name }} is typing...</span>
       </div>

        <div class="col-4">
            <div class="card card-default">
                <div class="card-header">Active Users</div>
                <div class="card-body">
                    <ul>
                        <li class="py-2" v-for="(user, index) in users" :key="index">
                            {{ user.name }}
                        </li>
                    </ul>
                </div>
            </div>
        </div>

   </div>
</template>
<script>
    export default {
        props:['user'],
        data() {
            return {
                messages: [],
                newMessage: '',
                users:[],
                activeUser: false,
                typingTimer: false,
            }
        },
        created() {
            this.fetchMessages();
            Echo.join('chat')
                .here(user => {
                    this.users = user;
                })
                .joining(user => {
                    this.users.push(user);
                })
                .leaving(user => {
                    this.users = this.users.filter(u => u.id != user.id);
                })
                .listen('MessageSent',(event) => {
                    this.messages.push(event.message);
                })
                .listenForWhisper('typing', user => {
                   this.activeUser = user;
                    if(this.typingTimer) {
                        clearTimeout(this.typingTimer);
                    }
                   this.typingTimer = setTimeout(() => {
                       this.activeUser = false;
                   }, 3000);
                })
        },
        methods: {
            fetchMessages() {
                axios.get('messages').then(response => {
                    this.messages = response.data;
                })
            },
            sendMessage() {
                this.messages.push({
                    user: this.user,
                    message: this.newMessage
                });
                axios.post('messages', {message: this.newMessage});
                this.newMessage = '';
            },
            sendTypingEvent() {
                Echo.join('chat')
                    .whisper('typing', this.user);
            }
        }
    }
</script>

我的表格包含id、user_id和message字段。

1
据我所知,您需要为该用户启动一个私有频道,并在客户端上订阅该频道,在服务器上通过该频道广播您的消息。https://laravel.com/docs/5.8/broadcasting#authorizing-channels - Alex Catchpole
1个回答

2

你好,我有同样的问题,并且像这样修复了它
但是注意我为此添加了额外的逻辑

1- 在channels.php中

Broadcast::channel('chat.{ticketId}', function ($user, $ticketId) {
    $ticket = \App\Models\Ticket::find($ticketId);
    return $user->id == $ticket->asked_user_id || $user->id == $ticket->responded_user_id;
});

2- 我只允许用户彼此聊天,其中一个用户被要求支持票证或管理员回复用户

在我的事件中,我有这段代码

class MessageSentEvent implements ShouldBroadcast
{
    use Dispatchable, InteractsWithSockets, SerializesModels;


    public $message;
    public $ticket;

    /**
     * Create a new event instance.
     *
     * @return void
     */
    public function __construct(TicketMEssage $message, Ticket $ticket)
    {
        $this->message = $message;
        $this->ticket = $ticket;
    }

    /**
     * Get the channels the event should broadcast on.
     *
     * @return \Illuminate\Broadcasting\Channel|array
     */
    public function broadcastOn()
    {
        return new PresenceChannel('chat.' . $this->ticket->id);
    }

    public function toBroadcast()
    {
        return new MessageSentEvent($this->message);
    }

}

3- 我会得到这样的消息和票据

$ticket = Ticket::find($id);
    $messages = $ticket->messages;
    $messages->load('user');

    return response()->json([
        'messages' => $messages,
        'ticket' => $ticket,
        'user' => auth()->user()
    ]);

我会像下面这样存储和广播消息

$ticket = Ticket::find($request->ticket_id);
    $message = $ticket->messages()->create([
        'message' => $request->message,
        'for' => auth()->id() == $ticket->asked_user_id ? TicketMessage::FOR_USER['asked'] : TicketMessage::FOR_USER['responded'],
        'user_id' => auth()->id(),
    ]);

    $message->load('user');
    broadcast(new MessageSentEvent($message, $ticket));
    return ['status' => 'success', 'message' => $message];

5- 和js文件中的重要部分(React或Vue或...)

 window.Echo.join(`chat.${props.ticketId}`)
        .listen('MessageSentEvent', (message) => {
        setMessages((prevState) => [...prevState, message.message]);
    });

我只监听与从props获取的票据ID相匹配的频道(你应该以任何方式传递它)

关键部分是1、4和5

希望这对你有所帮助:)))


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接