Reputation: 755
What is the best way to model a friendship relationship with eloquent? My table schema is below and I would like to define one relationship where I could retrieve all the friends something like as follows.
<?php
class User extends Eloquent {
public function friends() {
return $this->belongsToMany('User', 'friendships', 'user_id', 'friend_id')->orWhere($this->id,'=', 'friend_id');
}
}
+----+---------+-----------+----------+---------------------+---------------------+
| id | user_id | friend_id | state | created_at | updated_at |
+----+---------+-----------+----------+---------------------+---------------------+
| 1 | 3 | 1 | accepted | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 |
| 2 | 2 | 3 | accepted | 0000-00-00 00:00:00 | 0000-00-00 00:00:00 |
+----+---------+-----------+----------+---------------------+---------------------+
The relationship above is close to working when I looks for friends with the user id of 3 I get users 1 and 3 but obviously I want 1 and 2.
Friendship Table
user_id: The User Id who requested the friendship
friend_id: The User Id of the targeted friend
state: Whether the friendship is pending, accepted or blocked.
created_at and updated_at
I am aware that there are solutions from Laravel Many to many self referencing table only works one way where I can retrieve the friends from both sides of the relationship but I must be two rows, for example if user 1 and 3 are friends, then in one row user_id = 3 and friend_id = 1, and in the next row vice versa. (Or if I don't have two rows I must do two queries).
Upvotes: 8
Views: 2656
Reputation: 30766
You shouldn't try and turn what should be two rows into one row, but if you are going to try and do that then you definitely don't need to hit the database twice:
select * from users where (user_id = :user_id and friend_id = :friend_id) or (friend_id = :friend_id and user_id = :user_id)
That in Laravel would be:
Users::whereRaw('(user_id = ? and friend_id = ?) or (friend_id = ? and user_id = ?)', [
$user_id,
$friend_id,
$friend_id,
$user_id
]);
You could also do sub-wheres to group them, but thats a little complicated.
Upvotes: 4
Reputation: 1
i have a suggestion that you use conditions to load the vlaues you want
in this example let's say you used query to load user_id and fiend_id with condition ,
" select * from frienddship WHERE user_ID = '$id' OR friend_ID = '$id' "
$id : is the user id that you want to show his friends.
inside the WHILE loop in PHP that u will use to load the results you can filter resulets by making condition
while ... {
if (friendship['user_id'] == $id) {
$f_id = friendship['friend_id'] ; }
// other instructionS...
else {
$f_id = friendship['user_id'] ; }
// other instructionS...
}
in this case you will load data from bothe table columns and then each time filter the column with the user id and only let his friend's id , the filter used to not say to the user that you are friend with yourself.
sorry i used mysql to explain the example
Upvotes: -3
Reputation: 60058
You could do two lookups, and use a union query, thus only hitting the database once. Put all of this in a custom function:
class User extends Eloquent {
public function friends()
{
$first = $this->belongsToMany('User', 'friendships', 'user_id', 'friend_id');
return $this->belongsToMany('User', 'friendships', 'friend_id', 'user_id')->union($first);
}
}
Upvotes: 6