Skip to content

Instantly share code, notes, and snippets.

@developerdza
Last active February 5, 2024 13:18
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • Save developerdza/dfca78f31a408e52806f11cbda2dc587 to your computer and use it in GitHub Desktop.
Save developerdza/dfca78f31a408e52806f11cbda2dc587 to your computer and use it in GitHub Desktop.
Real Time Chat By Laravel And Livewire
<?php
namespace App\Http\Livewire\Forum;
use Illuminate\Support\Facades\Auth;
use Illuminate\Support\Facades\DB;
use Livewire\WithPagination;
use Livewire\Component;
use App\Message;
use App\User;
class Chat extends Component
{
use WithPagination;
// الإنصات للإيفنتس
protected $listeners = ['selected_users', 'selected_group', 'loadMore'];
//تعريف لبمتغيرات
public $message, $chat_id, $unreadedMessages, $search, $group_id , $paginate_var=10;
// get user unreaded messages -- جلب الرسائل غير المقروءة المرسلة من طرف باقي المستخدمين
public function mount()
{
$this->unreadedMessages = Auth::user()->unreadedMessages()->count();
}
public function render()
{
// get user unreaded messages -- -- تخزين الرسائل غير المقروءة في متغير
$unreadedMessages = $this->unreadedMessages;
// جلب المستخدم الذي أود الحديث معه
// select the user I want to chat with --
$id = $this->chat_id;
// get selected user Info ------معلوماته
if (isset($this->chat_id)) {
$chat_user = User::find($id);
}
else {
$chat_user = '';
}
//--------------------------------
//جلب كل لامستخدمين كي أختار واحدا من بينهم أكلمه
// get All user to select one of them
$users= User::all();
// My id
$user_id = Auth::user()->id;
// بعد اختيار المستخدم الذي سأكلمه سأقوم يجلب و إحصاء الرسائل بيننا كي أعرف إن كانت هناك بعض الرسائل التي أرسلها إلي و لم أقرأها بعد
//------- Haldeling Messages between me and the selected user
// counting حساب عدد الرسائل المرسلة من طرفه و غير مقروءة من طرفي
$messages_count = Message::where('from_user',$id)
->where( 'to_user',$user_id)
->count();
// لكي لا يكون عليك انتظار كل الرسائل بينكما لتظهر ... سيظهر عدد محدد من الرسائل في البداية فقط و عندما تسحب لأعلى ستظهر الرسائل التي سبقتها
//الكود يقوم بجلب عدد كل الرسائل بيني و بين المستخدم الاخر و يطرح منه العدد الذي أريد رؤيته و العدد الباقي من الرسائل يتخطاه
// showing a special numer of messages firstly , then I can show more by scrolling to top
$messages = Message::where('from_user',$user_id)
->where( 'to_user',$id)
->orWhere('from_user',$id)
->where('to_user',$user_id)
->skip($messages_count - $this->paginate_var)
->take($this->paginate_var)
->get();
// هنا أجلب آخر المحادثات التي خضتها
// showing Recent conversations I have
$conversations = \App\Conversation::where('first_user',Auth::user()->id)
->orWhere('second_user',Auth::user()->id)
->orderBy('last_message_time','desc')
->get();
return view('livewire.forum.chat',compact('messages','id','users','chat_user','unreadedMessages','conversations','paginate_var'));
}
// هنا أحصل على المستخدم الذي سأحادثه
// selecting the user I want to chat with by clicking his avatar
public function selected_users($id)
{
$this->chat_id = $id;
//marking our messaeges as readed
Message::where('statu','unreaded')
->where('to_user',Auth::user()->id)
->where('from_user',$id)
->update(array('statu'=>'readed'));
// يعود عدد الرسائل المرئية على أصله مع كل تحديد جديد للمستخدم
// number of messages Iwant to show
$this->paginate_var = 10;
// scrollng to bottom بمجرد الدخول لمحادثة مع مستخدم جديد سيقوم بالسحب لأسفل المحادثة مباشرة
$this->emit('scroll');
}
// sending messages -- إرسال الرسائل
public function send($id)
{
//---
$message = new Message;
$message->body = $this->message;
$message->from_user = Auth::user()->id;
$message->to_user = $this->chat_id;
$message->save();
//--
//----------------------------Conversation------------------------
//اذا كانت موجودة محادثة سابقة بيننا يقوم بإضافة اته الرسالة إليها و إلا ينشئ واحدة جديدة
//check if there is an old conversation between us
$conv_old = \App\Conversation::where('first_user',$message->from_user)
->where('second_user',$message->to_user)
->orWhere('first_user',$message->to_user)
->where('second_user',$message->from_user)
->get()->first();
// if there is an old convesation between as , just link it to this message
if($conv_old)
{
$conversation = \App\Conversation::find($conv_old->id);
$conversation->last_message_time = $message->created_at;
$conversation->save();
$message->conversation_id = $conv_old->id;
$message->save();
}
// else create a conversation and store our ids in it
else
{
$conversation = new \App\Conversation;
$conversation->first_user = $message->from_user;
$conversation->second_user =$message->to_user;
$conversation->last_message_time = $message->created_at;
$conversation->save();
$message->conversation_id = $conversation->id;
$message->save();
}
//---------------------------- End Conversation------------------------
// إرسال الايفنت
// -------------------------------Event-----------------
//get Unreaded messages
$recivedUnreadedMessages =Message::where('statu','unreaded')
->where('from_user',Auth::user()->id)
->where('to_user', $this->chat_id)
->count();
$this->message = '';
$chat_user = User::find($this->chat_id);
//sending event with message content and the user Isend it to , and the numb of the Unreaded Messages
event(new \App\Events\Chat($this->chat_id,$message,$recivedUnreadedMessages,$chat_user));
}
// مع كل صعود تام لأعلى المحادثة ستزيد 10 رسائل
// Load more then 10 messaeges by scrolling to top
public function loadMore()
{
$this->paginate_var = $this->paginate_var + 10;
$this->emit('load');
}
}
1 //---Tables--------
1-users['name' , 'avatar' , .......],
2-messages ['id' , 'body' , 'from_user' , 'to_user' , 'created_at' , 'updated_at'],
3-conversations ['id' , 'first_user' , 'second_user' , 'date_of_last_message'],
2 // ----------Relations---------
**User Model ::
public function messages()
{
return $this->hasMany(Message::Class);
}
**Convesation Model
public function messages()
{
return $this->hasMany(Message::Class);
}
public function fiirst_user()
{
return $this->belongsTo('App\User','first_user');
}
public function seecond_user()
{
return $this->belongsTo('App\User','second_user');
}
**Message Model ::
protected $fillable = array('statu');
public function user()
{
return $this->belongsTo('App\User');
}
public function froom_user()
{
return $this->belongsTo('App\User','from_user');
}
public function too_user()
{
return $this->belongsTo('App\User','to_user');
}
<?php
namespace App\Events;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
class Chat implements ShouldBroadcast
{
use Dispatchable, InteractsWithSockets, SerializesModels;
/**
* Create a new event instance.
*
* @return void
*/
public $user_id,$message,$recivedUnreadedMessages, $chat_user;
public function __construct($id,$message,$recivedUnreadedMessages,$chat_user)
{
$this->user_id = $id;
$this->message = $message;
$this->recivedUnreadedMessages = $recivedUnreadedMessages;
$this->chat_user = $message->froom_user;
}
/**
* Get the channels the event should broadcast on.
*
* @return \Illuminate\Broadcasting\Channel|array
*/
public function broadcastOn()
{
create privete chat with his id
return new PrivateChannel('Chat.'.$this->user_id);
}
public function broadcastAs()
{
return 'Chat';
}
}
1-----------------
<script type="text/javascript">
//عندما أصعد إلى أعلى أشغل الايفنت ليظهر عدد أكبر من الرسائل
// when I scroll to top I fire the event load more to show more old messages
$('#messages').scroll(function() {
var top =$('#messages').scrollTop();
if ( top == 0) {
window.livewire.emit('loadMore')
}
});
</script>
2----------------
// عند تشغيل هذا الايفنت سيتم النزول تلقائيا إلى أسفل المحادثة
<script type="text/javascript">
// after selecting the user I fire the event scroll to scroll the messages box to bottom
window.livewire.on('scroll', function() {
$('#messages').animate({
scrollTop: $('#messages')[0].scrollHeight}, "slow");
})
</script>
3----------------
// after selecting user marking or messages as read
<script type="text/javascript">
$(".user").click(function(){
$(this).find('p').html('');
});
</script>
4-------------------
<script type="text/javascript">
// storing my id
localStorage.setItem('uID',{{Auth::user()->id}});
console.log (localStorage.getItem('uID')) ;
// الانصات الى الايفنت على القناة الموسومة بالمعرف الخاص بي
//lestening to the channel 'chat'.myId
window.Echo.private('Chat.'+localStorage.getItem('uID'))
.listen('.Chat', (e) => {
// النزول الى أسفل مع قدوم أي رسالة جديدة
//--scrolling to bottom to see the new message
$('#messages').animate({
scrollTop: $('#messages')[0].scrollHeight}, "slow");
var id = @this.get('chat_id')
console.log(e);
//إذا كنت فاتح واجهة المحادثة مع الشخص الذي أرسل الرسالة ستظهر مباشرة .. و إلا فسيزيد رقمن الرسائل غير المقروءة منه فقط
// If the conversation betwwen my and the sender is open now ....
//-- appending the message tp messages box
$("."+ e.message.from_user).html(`${e.recivedUnreadedMessages}`);
if( e.message.from_user == id)
{
$('#messages-boxx').append(`
<div class="media w-50 mb-3">
<div class="media-body ml-3">
<div class="bg-light rounded py-2 px-3 mb-2">
<p class="text-small mb-0 text-muted">${e.message.body}</p>
</div>
<p class="small text-muted">${e.message.created_at}</p>
</div>
</div>
`);
}
//---- else increment the num of the enreded messages between us
else
{
console.log("#user."+e.chat_user.id);
console.log(e.recivedUnreadedMessages);
}
});
</script>
// USERS SECTION
<div class="bg-white">
//-----swching between users section and recent conversation section by alpine Js-------
<div class="messages-box" wire:ignore x-show.transition.in="tab === 'recent'">
<button :class="{ 'active': tab === 'groups' }" @click="tab = 'all'">
<img class="float-right" src="{{asset('icons/icons/group.png')}}" width="40" >
</button>
//-----End swching between users section and recent conversation section by alpine Js-------
// Conversations
<div class="list-group users rounded-0" >
@foreach($conversations as $conversation)
//---- If I started the chat betwwen us show me the avatar of the other user
@if(Auth::user()->id == $conversation->first_user)
<a class="list-group-item list-group-item-action text-white rounded-0" wire:click="$emit('selected_users',{{$conversation->second_user}})" >
<div class="media ">
<img src="{{Voyager::image($conversation->seecond_user->avatar)}}" alt="user" width="50" class="rounded-circle">
<div class="media-body ml-4 user" >
<div class="d-flex align-items-center justify-content-between mb-1">
<h6 class="mb-0" style="color: black">{{$conversation->seecond_user->name}}</h6><small class="small font-weight-bold" style="color: black"></small>
</div>
<p class="badge badge-danger {{$conversation->second_user}}" style="color: black" id="{{'user'.$conversation->first_user}}" >
{{\App\Message::where('statu','unreaded')->where('to_user',Auth::user()->id)->where('from_user',$conversation->seecond_user->id)->count()}}
</p>
</div>
</div>
</a>
//---- If I was not the one who started the chat betwwen us show me the avatar of the other userwho did it
@else
<div class="list-group-item list-group-item-action text-white rounded-0 user3" wire:click="$emit('selected_users',{{$conversation->first_user}})" >
<div class="media " >
<img src="{{Voyager::image($conversation->fiirst_user->avatar)}}" alt="user" width="50" class="rounded-circle">
<div class="media-body ml-4 user">
<div class="d-flex align-items-center justify-content-between mb-1" >
<h6 class="mb-0" style="color: black">{{$conversation->fiirst_user->name}}</h6><small class="small font-weight-bold" style="color: black"></small>
</div>
<p class="badge badge-danger {{$conversation->first_user}}" style="color: black" id="{{'user.'.$conversation->first_user}}">
{{\App\Message::where('statu','unreaded')->where('to_user',Auth::user()->id)->where('from_user',$conversation->fiirst_user->id)->count()}}</p>
</div>
</div>
<p></p>
</div>
@endif
@endforeach
</div>
</div>
// users section
<div class="messages-box list-group users rounded-0" wire:ignore x-show.transition.in="tab === 'all'">
<button class="btn" :class="{ 'active': tab === 'recent' }" @click="tab = 'recent'" style="border:none;">
<img class="float-right" src="{{asset('icons/icons/back.png')}}" width="40" >
</button>
@foreach($users as $user)
<a class="list-group-item list-group-item-action text-white rounded-0" wire:click="$emit('selected_users',{{$user->id}})" >
<div class="media ">
<img src="{{Voyager::image($user->avatar)}}" alt="user" width="50" class="rounded-circle">
<div class="media-body ml-4 user" >
<div class="d-flex align-items-center justify-content-between mb-1">
<h6 class="mb-0" style="color: black">{{$user->name}}</h6><small class="small font-weight-bold" style="color: black"></small>
</div>
</div>
</div>
</a>
@endforeach
</div>
</div>
</div>
//------------------------------------------
<!-- Chat Box-->
<div class="col-7 px-0">
<div class="py-5 chat-box bg-white messages " id="messages">
<!-- Messages-->
<div class="messages-boxx" id="messages-boxx" >
@foreach($messages as $message)
@if(Auth::user()->id == $message->from_user)
<!-- Reciever Message-->
<div class="media w-50 ml-auto mb-3">
<div class="media-body">
<div class="bg-primary rounded py-2 px-3 mb-2">
<p class="text-small mb-0 text-white">{{$message->body}}</p>
</div>
<p class="small text-muted">{{$message->created_at}}</p>
</div>
</div>
@else
<!-- Sender Message-->
<div class="media w-50 mb-3">
<img src="{{Voyager::image($message->froom_user->avatar)}}" alt="user" width="50" class="rounded-circle">
<div class="media-body ml-3">
<div class="bg-light rounded py-2 px-3 mb-2">
<p class="text-small mb-0 text-muted">{{$message->body}}</p>
</div>
<p class="small text-muted">{{$message->created_at}}</p>
</div>
</div>
@endif
@endforeach
</div>
</div>
<div class="bg-light">
<div class="input-group">
<input type="text" placeholder="Type a message" aria-describedby="button-addon2" class="form-control rounded-0 border-0 py-4 bg-light" wire:model='message'>
<div class="input-group-append">
<button id="button-addon2" type="submit" class="btn btn-link" wire:click='send'> <i class="fa fa-paper-plane" ></i></button>
</div>
</div>
</div>
</div>
@Salah856
Copy link

great

@developerdza
Copy link
Author

great

أعزك الله يا حبيبي

@Anouar-Barouchi
Copy link

بالتوفيق

@developerdza
Copy link
Author

بالتوفيق

و لك يا عزيزي بالمثل

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment