Laravel get all conversations with participants current user participates in - php

I have four tables. User, Conversations, Message and Conversation_Participants.
(I hope you don't find a relationship error in this image )
I tried to add a function
public function conversations(){
return $this->belongsToMany(Conversation::class, 'conversation_participants', 'user_id', 'conversation_id');
}
to User::class but if i call User::with('conversations')->get() i only get all existing Users. What am I doing wrong? First i want to get all conversations the current user participates in and second I want to get all receivers of the conversations.
I also tried
public function participants()
{
return $this->belongsToMany(User::class, 'conversation_participants', 'user_id', 'conversation_id');
}
in the Conversation::class but Conversation::with('participants')->get() gets me all Conversation even those the user isn't participating in.
I'm really confused atm :/

Add the following in your User model:
public function conversations() {
return $this->belongsToMany(Conversation::class, 'conversation_participants', 'users_id', 'conversations_id');
}
And this to your Conversation model:
public function participants() {
return $this->belongsToMany(User::class, 'conversation_participants', 'conversation_id', 'users_id');
}
If you want to link your tables easier, read up on conventions.
To get all the conversations a user is participating in, run the following (assuming you've loaded the user): $user->conversations() to get all the conversations a user is in.
If you want all users, with all their conversations, with all the participants connected, do the following: $users = Users::with('conversations.participants')->get(). You can now loop through this as follows:
foreach($users as $user) {
foreach($user->conversations as $conversation) {
foreach($conversations->participants as $participant) {
// do fancy stuff here
}
}
}
Notice that the user from which you start is also a participant, so maybe you need to filter that one out again.
If you want to get even more fancy (but use more resources) you could even query all the messages a conversation has too!
User::with('conversations.participants', 'conversations.messages.user')->get()
But this only works when you set up a second set of relationships along the upper table in your image (conversations <-> messages <-> users)
Edit
In the comments, OP asked if it was possible to limit the amount of messages retrieved from the database, which is possible to my knowledge, but I don't now if this is the best way:
Remove the messages from the first eager loading part:
User::with('conversations.participants')
After that, when looping through the conversations, lazy load the messages:
$conversation->load(['messages' => function($query){
$query->take(5);
}, 'users']);
Access them after that with $conversation->messages.
Note
I think this could be done more easily in one go, but I don't have a setup right now to test this for you.
Edit 2
After Ronon added another comment, here's what I came up with:
Add a new relationship in the Conversation model:
public function last_messages() {
return $this->hasMany(Message::class, 'conversation_id', 'id')->latest()->limit(2);
}
Because of that, you now can do this:
User::with('conversations.participants', 'conversations.last_messages.users')->get()
If you still want all the messages, you can use the messages relationship. If you want less, use the last_messages one.

Calling User::with('conversations')->get() does not specify a User. I might be misreading but I think you are looking for something like the following:
$user = User::with('conversations')->find(1);
$userConversations = $user->conversations;
which will provide you with a user and their conversations.

Related

retrieve child object by evaluating grandchildren object in laravel

I have 2 models, User and Conversation related to each other with a many-to-many relationship, many users to many conversations.
On my models I have:
User:
public function conversation() {
return $this->belongsToMany('App\Models\Conversation');
}
Conversation:
public function users() {
return $this->belongsToMany('App\Models\User');
}
So I could get the conversations a user has by : $user->conversation; and vice versa, retrieve the users a conversation has by $conversation->users, works like a charm. The problem is that I want a way to retrieve all users a certain user has made contact before, in few words, something like:
$user = User::find(1);
$talkedUsers = $user->conversation->user.
And also a way to check all the conversation user 'x' has made with user 'y' ('if any')
$userX = User::find(1);
$userY = 2;
$talkedUser = $userX->conversation->where('user.id', '=', $userY);
Obviously, the code above won't work. I wish to know if something like this is possible to accomplish without adding a complex raw query, I hope is possible only using only QueryBuilder.
The problem is that I want a way to retrieve all users a certain user
has made contact before,
$user->conversation is actually a collection of conversations, it is better to use plural i.e. $user->conversations. Then you can use higher order messages to deal with multiple objects at once. See https://laravel.com/docs/8.x/collections#higher-order-messages for more information
$users = $user->conversations->flatMap->users->unique('id');
flatMap combines multiple User collections retrieved from multiple Conversation objects into one collection of User objects then to remove duplicate users, you can use unique('id')
And also a way to check all the conversation user 'x' has made with
user 'y' ('if any')
First you should query all conversations from x then filter only conversations that have relationships with y.
$userX = User::find($x);
$conversations = $userX->conversations()
->whereHas('users', function($query) use ($y) {
$query->where('users.id', $y);
})
->get();

Laravel: is it possible to apply a ->where() over a hasMany()?

I have this notifications table, where it has a status column. The status might be, among other things, "ack" and "noack". So, the notifications belongs to a user. If I want to use the ORM to see the user's notifications I'd use a hasMany() in the User model like:
public function notificaciones()
{
return $this->hasMany('App\Notification', 'dest_id', 'id');
}
And this works perfectly. (The dest_id means who the notification is for, there's another origin_id which tells who caused the Notification, any ways, this works)
Now, I want to see only the unacknoledged (noack) notifications, I was thinking on:
public function notificaciones()
{
return $this->hasMany('App\Notificacion', 'dest_id', 'id')
->where('status', 'noack');
}
But this produces an empty collection.
How can this be accomplished?
You can do something like that,
You create a new function in the same model and call that core method with the condition to fetch noack notifications.
public function noack_notifications() {
return $this->notificaciones()->where('status','=', 'noack');
}
When you call this method from controller with find.
$user = User::find($id);
dd($user->noack_notifications()->get());
This should solve your problem.

Relationship condition if user is logged in

I want to create a relationship that checks if a user has liked a post. In order to do this, the relationship needs to check if the user is logged in, and then use their user_id to get the like record. Something like:
public function userLike() {
if (Auth::check()) return $this->hasOne('App\Like')->where('user_id', Auth::user()->id);
}
However, this doesn't work. Additionally, if the user is not logged in and this relationship is called (which it is by default), it will return an error.
What is the proper way of doing this?
If you want to conditionally load a relation then you can do so by lazy eager loading. In your example you could define the relation as
public function userLike() {
return $this->hasOne('App\Like', 'user_id');
}
Then in your controller (or wherever else) you can do the check for if the user is question is currently logged in user or not
$loggedInUser = auth()->user();
if($loggedInUser){
$loggedInUser->load('userLike');
}
Then you can continue with whatever you want to do with the loggedInUser and the userLike.
Say you have multiple users at a point (in your code) and you want to load the likes for only the currently logged in user then you can
//$users is a collection of multiple users - assumed
foreach($users as $user){
if($user->email === auth()->user()->email){
$user->load('userLike');
}
}
Hope this helps
This is how I would do that:
1) “likes” table
I would first create a table to store all the “likes” with columns for a “post_id” and a “user_id”
2) create the relationships
User Model
public function likes()
{
    return $this->belongsToMany('App\Post', 'likes', 'user_id', 'post_id');
}
Post Model
public function likes()
{
return $this->belongsToMany('App\User', 'likes');
}
3) Post Controller - the method
I would do the followings:
check if user is logged in, otherwise throw an error
make sure post exists, otherwise throw an error
create a new entry in like table with user_id and post_id
(you can also check if the user is logged in directly on your route using a middleware)
4) In the view
I would call the controller method using an ajax call.
Not sure if I forgot something, but hopefully that can help you a little bit.
If you need to “like” more things than only post, check the polymorphic relationships.

Custom hasMany Relation Laravel

public function chats()
{
return $this->hasMany('App\Chat','sender_id')->orWhere('receiver_id',\Auth::id());
}
My requirement is that i want to fetch messages that being sent or received by User. how i can do it ?
The following code should do the trick:
public function chats()
{
return $this->hasMany('App\Chat','sender_id')->union($this->hasMany('App\Chat','receiver_id'));
}
This will return a relation that is a union of 2 queries - one that fetches chat messages where given user is the sender and another one where given user is the receiver.
You can now access user's chats with $user->chats.
You were very close. hasMany() accepts chained where() and orWhere() calls, so I would make that query like this:
public function chats()
{
return $this->hasMany('App\Chat')
->where('sender_id', \Auth::id())
->orWhere('receiver_id', \Auth::id());
}
Once this is correctly set up, you can use $users->chats to access a list of chats that were sent from OR to the current user.

Laravel model won't find messages by user id

I'm working on a website with a messages system written in Laravel. To identify a users messages the messages table has a to_user attribute which - obviously - holds the id of the user to which the message is sent. However, even though there are plenty of sample messages in this table, my Message model won't return any results when I search for messages by this attribute.
I've tried several things. First off, $messages = Message::where('to_user', USER_ID);, this results in an empty array (except for the standard Eloquent stuff, no actual results from the table). Next, I tried a prettier approach. In my User model, I added a fancy relationship like this:
/**
* Get the users messages
*/
public function messages() {
return $this->hasMany('App\Models\Message', 'to_user');
}
Unfortunately though, $messages = $currentUser->messages() returns the same empty result as it did on my first try.
Last night when I was having a shower I had an eureka moment (this is definitely essential information needed to solve the problem). I was sure I had the solution, either $currentUser->id is a string or the to_user attribute must return the id as a string for some reason. Unfortunately though, this wasn't the case. var_dump shows that both are integers.
Any ideas? I'd really appreciate it.
Thanks!
Roemer
This is a standard one-to-many relationship, the right way to handle this situation is to create the relation in both of your models:
User Model
public function messages() {
return $this->hasMany('App\Models\Message', 'to_user');
}
Message Model:
public function user() {
return $this->belongsTo('App\Models\User', 'to_user');
}
Then get the messages with:
$messages = User::find(<your_user_id>)->messages()->get()
check here for more info
You aren't fetching the results of the query.
$messages = Message::where('to_user', USER_ID); This won't return anything
$messages = Message::where('to_user', USER_ID)->get(); This will return a collection of messages
Your message method returns the HasMany class, it is not returning a resultset yet.
Try:
$messages = $currentUser->messages()->get();
or
$messages = Message::where('to_user', USER_ID)->get();
I think your problem is you do not finished the query builder.
Did you create a "belongsTo" method in the related class?

Categories