如何为Laravel Relation添加更多条件?

时间:2014-08-15 22:00:21

标签: laravel laravel-4 eloquent relationship

我有一个'连接'表,其中包含以下架构:

id 
requestor_id (user_id)
requested_id (user_id)

让我们说John Doe(请求者)连接到Jane Doe(请求),Jonnie Doe(请求者)连接到John Doe(请求)。列出所有John Doe的联系我必须

inner join "connections" 
  on "users"."id" = "connections"."requested_id" 
  or "users"."id" = "connections"."requestor_id"

但目前Laravel目前还不行,因为afaik没有BelongsToMany->or()方法,所以你会怎么做呢?

2 个答案:

答案 0 :(得分:1)

我设法破解了关系,并提出了一个临时解决方案:

public function connections()
{
    $relation = $this
        ->belongsToMany(static::class, 'connections', 'requestor_id', 'requested_id')
        ->withTimestamps();

    /// delete the already built inner join
    $relation
        ->getQuery() // Eloquent\Builder
        ->getQuery() // Query\Builder
        ->joins = [];

    /// create a new inner join with the needed or condition
    $relation->getQuery()->getQuery()->join('connections', function($join)
    {
        $join->on('users.id','=','connections.requestor_id');
        $join->orOn('users.id','=','connections.requested_id');
    });

    return $relation;
}

答案 1 :(得分:0)

首先,您需要修复该解决方案,因为它不会返回正确的结果(检查以下)。 其次,它不适用于急切加载,因此我不会使用它。

话虽如此,您可以创建扩展BelongsToMany的自定义关系并调整模型上的关系方法,或者使用已存在的关系方法。

所以,让我首先提出一些建议,我发现一种更好的方式(我会使用朋友而不是连接,以便更容易理解):

// friendship that I started
function friendsOfMine()
{
    return $this->belongsToMany(static::class, 'friends', 'user_id', 'friend_id');
}

// friendship that I was invited to 
function friendOf()
{
    return $this->belongsToMany(static::class, 'friends', 'friend_id', 'user_id');
}

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);
}

现在它非常易于使用,非常灵活,并且可以轻松加载:

$user = User::first();

$user->friends; // merged collection thanks to the accessor

$user->friendOf; // collection of my fans ;)

$user->friendsOfMine; // my idols

$users = User::with('friendOf', 'friendsOfMine')->get();

您的固定解决方案

问题是,仍会有where子句,这会破坏您的查询。因此,您需要将其移至joinClause并再添加一个:

public function friends()
{
    $relation = $this
        ->belongsToMany(static::class, 'friends', 'user_id', 'friend_id')
        ->withTimestamps();

    // get underlying Query\Builder
    $query = $relation->getQuery()->getQuery();

    // get rid of wrong where and its bindings
    $query->setBindings([])
    $query->wheres = [];

    // fetch and alter joinClause
    $join = reset($query->joins);
    $join->where('friends.user_id', '=', $this->getKey())
        ->orOn('users.id','=','friends.user_id')
        ->where('friends.friend_id', '=', $this->getKey());


  // Now the join looks like this:
  //
  // $key = $this->getKey();
  //
  // $query->join('friends', function($join) use ($key)
  // {
  //   $join->on('users.id', '=', 'friends.friend_id')
  //       ->where('friends.user_id', '=', $key)
  //      ->orOn('users.id', '=','friends.user_id')
  //       ->where('friends.friend_id', '=', $key);
  // });

    return $relation;   
}