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

时间:2019-07-10 06:18:19

标签: laravel vue.js websocket ratchet

我的应用程序中有一个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 个答案:

答案 0 :(得分:0)

你好,我也遇到了同样的问题,已经解决了 但请注意,我有额外的逻辑

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()
    ]);

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

$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 or Vue or ...)

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

我只听带有我从 props 获得的票证 ID 的频道(你应该以任何方式传递它)

关键部分是1和4和5

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