使用Laravel实现好友系统:多对多关系

24

我想使用Laravel创建一个好友系统(初次尝试),但我被关系所阻碍。这是问题所在:有一个用户表和一个朋友表,其中包含以下列:

friends: id, user_id, friend_id, accepted.

看起来是多对多关系,这里是我在User类上设置的内容:

class User extends Eloquent {
    function friends()
    {
        return $this->belongsToMany('User');
    }
}

但是当我尝试一个:

$friends = User::find($id)->friends()->get()

我有这个错误:
Base table or view not found: 1146 Table 'base.user_user' doesn't exist

我想获取用户的好友列表,不管是用户发送过邀请还是收到过邀请都可以。因此,用户可以基于 user_id 或 friend_id,并且根据该列检索另一个用户的数据。
有什么思路吗?谢谢!
编辑:这是我使用的代码:
$usersWithFriends = User::with('friendsOfMine', 'friendOf')->get();
$user = User::find(Auth::id())->friends;

foreach($user as $item) {
    echo $item->first()->pivot->accepted;
} 

foreach ($user->friends as $friend) { $friend->pivot->accepted; } - Jarek Tkaczyk
好的,谢谢你花时间帮我解决问题,我已经解决了! - Madnx
我知道这是一个旧帖子,但是你如何验证好友请求呢? 你使用自己的Friend模型吗? - Kaizokupuffball
2个回答

57

简而言之,你需要两个反向关系才能使其工作,请查看下面的SETUP和USAGE


首先是错误 - 这是你的关系应该是什么样子:

function friends()
{
  return $this->belongsToMany('User', 'friends', 'user_id', 'friend_id')
    // if you want to rely on accepted field, then add this:
    ->wherePivot('accepted', '=', 1);
}

那么它将没有错误地工作:

$user->friends; // collection of User models, returns the same as:
$user->friends()->get();

设置

然而,您希望关系双向运作。Eloquent并没有提供这种类型的关系,因此您可以使用两个反向关系并合并结果来代替

// friendship that I started
function friendsOfMine()
{
  return $this->belongsToMany('User', 'friends', 'user_id', 'friend_id')
     ->wherePivot('accepted', '=', 1) // to filter only accepted
     ->withPivot('accepted'); // or to fetch accepted value
}

// friendship that I was invited to 
function friendOf()
{
  return $this->belongsToMany('User', 'friends', 'friend_id', 'user_id')
     ->wherePivot('accepted', '=', 1)
     ->withPivot('accepted');
}

// accessor allowing you call $user->friends
public function getFriendsAttribute()
{
    if ( ! array_key_exists('friends', $this->relations)) $this->loadFriends();

    return $this->getRelation('friends');
}

protected function loadFriends()
{
    if ( ! array_key_exists('friends', $this->relations))
    {
        $friends = $this->mergeFriends();

        $this->setRelation('friends', $friends);
    }
}

protected function mergeFriends()
{
    return $this->friendsOfMine->merge($this->friendOf);
}

使用方法

有了这样的设置,您可以做到以下操作:

// access all friends
$user->friends; // collection of unique User model instances

// access friends a user invited
$user->friendsOfMine; // collection

// access friends that a user was invited by
$user->friendOf; // collection

// and eager load all friends with 2 queries
$usersWithFriends = User::with('friendsOfMine', 'friendOf')->get();

// then
$users->first()->friends; // collection

// Check the accepted value:
$user->friends->first()->pivot->accepted;

谢谢您的回答,我之前并不知道我可以进行这样的关联(合并、反转等)。现在一切都很顺利,以后我也能够在我的项目中使用同样的关联方式了! :) - Madnx
我有一个最后的问题:当使用$users->friends时,如何知道接受的属性(我删除whenPivot以选择所有朋友,无论是否接受)。 - Madnx
请检查编辑 - 您需要在关系中添加 withPivot(),然后可以通过调用 friend->pivot->accepted 来访问它 - 请参见我给出的答案中的最后一个示例。 - Jarek Tkaczyk
1
谢谢。我编辑了我的第一篇帖子并附上了代码,因为它无法工作:/. 编辑:通过删除->wherePivot()它可以工作,我需要保留它吗? - Madnx
2
请告诉我们如何进行分页?$user->friends()->paginate(25) 无法正常工作。 - Ketan Yekale

5
显然,这是您的数据库和关系定义中的问题。多对多关系类型需要您使用一个中间表。以下是您需要做的:
  1. 在您的模式中创建一个user_friend (id, user_id, friend_id)表。
  2. userfriend表中删除不必要的字段。
  3. 创建适当的外键。user.id->user_friend.user_idfriend.id->user_friend.friend_id
  4. 更好地定义UserFriend模型之间的完整关系,
例如:
 class User extends Eloquent {
    function friends()
    {
        return $this->belongsToMany('User', 'user_friend', 'user_id', 'friend_id');
    }
}

你可以在Laravel文档中阅读更多内容,这里


谢谢,这真的帮助我理解了如何处理多对多关系。我刚从CodeIgniter过来,这样的关系有很多需要学习的地方(非常有趣)。不过我会接受第二个答案,因为你的答案需要一个我在这种情况下不需要的额外表 :/. - Madnx
2
OP只有一个名为“User”的表,没有“Friend”表。 - Nick Coad
1
我也遇到了这个问题。对于多对多的关系,我们需要有一个中间表。但在这种情况下,我们已经有了存储用户信息的用户表,为什么还需要创建单独的朋友表呢?我们可以创建一个名为user_friend的表,其中包含来自用户表的user_id字段、friend_id字段和status字段。这将如何运作? - vivek321

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接