Querying multiple table columns with the the same id but reversing them - php

I have a to and from columns which are integers from the users in my messages table, as I was building a chat like application on this part.
So I want to fetch all the messages of the currently logged on user. so this is the query
$all_messages = Message::where('from',$logged_user)->orWhere('to',$logged_user)->with('owner')->orderBy('created_at','desc')->get();
note that $logged_user variable is an integer / the authenticated users id
so from that,
but also I want to get also the messages between the logged_user and also the respondent_id or the id of the respondent or the user that I am trying to connect also.
So for example the currently logged on user_id is 2 and the respondent id is 3 I want the messages that have a from of 2 or 3 and also the respondent_id of 2 and 3 something like that

If I've understood the question right you're looking for the messages belonging to the conversation between two users, so what you're looking for should be following:
$loggedInUserId = 2;
$contactUserId = 3;
$messages = Message::query()
->where(function ($query) use ($loggedInUserId, $contactUserId) {
$query->where('sender_id', $loggedInUserId)
->where('recipient_id', $contactUserId);
})
->orWhere(function ($query) use ($loggedInUserId, $contactUserId) {
$query->where('recipient_id', $loggedInUserId)
->where('sender_id', $contactUserId);
})
->with('owner')
->orderBy('created_at', 'desc')
->get();
Also, if you're heavily relying on having to track "conversations" between two or more users you might think to aggregate them as threads.

Related

How to get conversation of given users (using where condition twice on same column)?

I am trying to create a chat app in Laravel. When a conversation is created between users, I don't want to record a user as host user. I would like to get a conversation filtered by given users. Example: If USER_1 and USER_2 belong to CONVERSATION_1, I want to filter out CONVERSATION_1 by USER_1 and USER_2 ids.
Here is my table structures.
conversations
id
product_id
conversation_users
id
conversation_id
user_id
Here is my conversation service.
class ConversationService
{
public static function first(User $user){
$admin = getAdmin();
$conversation = Conversation::whereHas("users", function($q) use($user, $admin){
$q->where("user_id", $user->id)->where("user_id", $admin->id);
})->first();
}
}
I know I can't use this statement. $q->where("user_id", $user->id)->where("user_id", $admin->id);
Could you please guide me how to filter a conversation by user ids?
Since the conversation must belong to both users explicitly, just double on the condition, one for each user
$conversation = Conversation::query()
->whereHas("users", function($q) use($admin){
$q->where("user_id", $admin->id);
})
->whereHas("users", function($q) use($user){
$q->where("user_id", $user->id);
})
->first();
Once you see this version of the code, it will tick that it's simple logic.

Need opinion with relations count

Very long time I searching for solution for this problem:
Lets say we have 2 tables one table is Clients table and second table is ClientAssignment table:
the ClientAssignment table is related to Clients table:
public function assignment()
{
return $this->hasOne(ClientAssignment::class, 'client_id');
}
now when I want to count how many ClientAssignment has Clients and i do it like that:
$users =[1,2,3,4 .....]
$userAssignments = array();
foreach ($users as $user) {
$user_assignments = Client::whereHas('assignment', function ($query) use ($user) {
$query->where('assigned_id', $user);
});
$ua['user_id'] = $user;
$ua['count'] = $user_assignments->count();
array_push($userAssignments, $ua);
}
The code works well but it hits the performance and query execution time ~20 + seconds on a relatively small table with 80k Clients,
My question if can be another way to do the same thing but with minimum performance and query execution time hit ?
According to your post, I think
Client --- hasOne ----> ClientAssignment <----- hasMany ---- User
[client_id, assigned_id]
So User can hasMany Client through ClientAssignment,
However, your client_id and assigned_id are all in client_assignment table. So you can not use hasManyThrough, this just like a pivot table;
Fortunately, you can directly count the client_id and get the assigned_id as user_id just use this pivot table.
The query is like this (Use distinct(client_id) for preventing the dirty records):
ClientAssignment::whereIn('assigned_id', $users)
->groupBy('assigned_id')
->select('assigned_id AS user_id',
DB::raw('COUNT(DISTINCT(client_id)) AS count'))
->get()->toArray();
And add assigned_id index to improve the performance.
$table->index('assigned_id');
Use the count()
Client::first()->assignments->count();
// Or
Client::find(ID)->assignments()->count();
// or ...

Laravel query format - Join To the Latest row [duplicate]

This question already has answers here:
Laravel eloquent selecting most recent row from joined table
(3 answers)
Closed 3 years ago.
I have two tables for example users and orders.
I have to join users with orders On users.id = orders.user_id - which is quite fine.
User table has one to many relation in orders table.
I have some conditions also, like orders.pay_type = 'xyz', 'orders.date = yesterday' , 'user.status = active' ..etc.
My problem is that I need to join my user table to the latest row of orders table correspond to that user_id.
Or need to fetch the latest details of that users orders table details along with user table data.
I already tried
->orderBy('orders.date')
->groupBy('orders.user_id')
->get();
but it has no result in o/p.
$data= users::leftJoin('orders', function($join) {
$join->on('orders.user_id', '=', 'users.id')
->on('orders.id', '=', DB::raw("(SELECT max(id) from orders WHERE orders.user_id = users.id)"));
})
->select(*)
This resolve my issue.
If you don't want to change anything in model you can go with this method as well, which is simple query builder method.
DB::table('orders')->leftJoin('users', 'orders.user_id', 'users.id')
->groupBy('orders.user_id')
->orderBy('orders.date', 'DESC')
->get();
Assuming, You might have "orders" function in User.php model
You can also go for below method. (Similar to the answer already given but for array of users instead of one user.)
$users = User::all();
foreach ($users as $user) {
$latest_order = $user->orders()->where('pay_type', 'xyz')->orderBy('date', 'DESC')->first(); // Option 1
$latest_order = $user->orders()->where('pay_type', 'xyz')->latest('date')->first(); // Option 2
}
You can use the following Relation in the User Model
public function latest_order(){
return $this->hasOne('Order')->orderBy('orders.date', 'desc')->limit(1);
}
Update
Since you can not change the Model, you can use the following code to fetch the latest Order for each User.
$user = User::first(); //get first User
$user->orders()->latest('date')->first();

Laravel Eloquent: Get record that contains two specific relations

I have a project where a user can create conversations with other users. A conversation can belongsToMany users and user can belongsToMany conversations.
I now need to get the conversation in which two specific users participate.
I tried a combination of solutions using whereIn and I tried the following:
$c = Conversation::whereHas('users', function($q)
{
$q->whereIn('user_id', array(1,3));
})
->get();
Here the problem is that whereIn('user_id', [1,3]) gets records that contains EITHER 1 or 3. I need it to return records that contains BOTH 1 and 3.
Conversation Model
class Conversation extends Model {
public function users(){
return $this->belongsToMany('App\User');
}
}
User Model
class User extends Model {
public function conversations(){
return $this->belongsToMany('App\Conversation');
}
}
Tables
conversations:
id | subject
conversation_user:
id | user_id | conversation_id
Data from table conversation_user
Your newest edit makes a lot more sense, this is actually a very easy fix. whereHas takes two additional parameters where it's going to look for the count.
$users = [1, 3];
$c = Conversation::whereHas('users', function($q) use ($users)
{
$q->whereIn('user_id', $users);
}, '>', count($users) )
->get();
This will get all conversations where user's 1 and 3 have participated in, even if there are additional users that have participated in those conversations. If you want only the conversations with only users 1 and 3, change the > to an =.
Edit: I just realized your pivot table has an id column. This method may not work if your pivot table is going to have duplicates. For example, if you have user_id of 1 in there twice with the same conversation_id both times, it will return that conversation even though it technically only has 1 user. I suggest removing the id column and creating a composite primary key of user_id and conversation_id. If there is the possibility of duplicates, it might be safer to use lukasgeiter's solution.
You are currently querying conversations which either user 1 and/or 3 takes part in. To achieve what you want you need two whereHas calls:
$c = Conversation::whereHas('users', function($q)
{
$q->where('user_id', 1);
})
->whereHas('users', function($q)
{
$q->where('user_id', 3);
}
->get();
And if you have more than two users, add them in a loop:
$users = [1, 2, 3, 4, 5];
$c = Conversation::query();
foreach($users as $userId){
$c->whereHas('users', function($q) use ($userId)
{
$q->where('user_id', $userId);
});
}
$c = $c->get();
I hope this will help you...
$userIds = array(1,3);
$c = Conversation::whereHas('users', function($q) use ($userIds)
{
$q->whereIn('user_id', $userIds);
})
->get();

Laravel get my friend list query

I am using laravel to create a friend list. Now I want to get all of my friends with the name and avatar and stuff from the USER table.
My schema:
`cid`, `uid_by`, `uid_to`, `status`
uid_by (the inviting user)
uid_to (to be friend user)
status (accepted or ignored)
I only have 1 row per friendship. meaning its a 2 way friendship.
I tried to make a query:
$contacts = DB::table('contacts')
->leftJoin('users', 'contacts.uid_by', '=', 'users.uid')
->where('uid_by', $me)
->orWhere('uid_to', $me)
->where('contacts.status', 'accepted')
->get();
But its giving me some wrong information. Like instead of giving me the friend user info. its giving me my own info. All I want is to get the friends info.
Should I redesign the db? or am I missing something?
update
technically the thing is if uid_by is my user_id then check the uid_to else if the uid_by is not my user_id then check uid_by then use those to get the data from the users table uid column.
So my uid could be on uid_by and my friend on uid_to or vice versa.
But I can't get the query right.
See Advanced wheres:
$contacts = DB::table('contacts')
->join('users', 'contacts.uid_by', '=', 'users.uid')
->where('users.uid','<>',$me)
->where('contacts.status', 'accepted')
->where(function($query)
{
$query-->where('uid_by', $me)
->orWhere('uid_to', $me)
})
->get();

Categories