【问题标题】:How to use JOIN in Laravel when two columns of one table refer to one column of another table?当一张表的两列引用另一张表的一列时,如何在 Laravel 中使用 JOIN?
【发布时间】:2019-04-12 20:22:45
【问题描述】:

我有两张桌子:-

A. users table

    |--------------------------------------------------------|
    |  id  |    fname    |     lname     |      deleted      |
    |--------------------------------------------------------|
    |  1   |     Jax     |     Briggs    |         n         |
    |--------------------------------------------------------|
    |  2   |    Juli     |     Briggs    |         y         |
    |--------------------------------------------------------|
    |  1   |     Nacy    |     Blew      |         n         |
    |--------------------------------------------------------|

B. friend table

    |-------------------------------------------------------------|
    |  id  |    user_id    |     receiver_id     |    status      |
    |-------------------------------------------------------------|
    |  1   |       2       |          3          |       0        |
    |-------------------------------------------------------------|
    |  2   |       1       |          3          |       1        |
    |-------------------------------------------------------------|
    |  1   |       2       |          1          |       1        |
    |-------------------------------------------------------------|

我正在尝试使用以下查询检索用户的朋友:-

"SELECT users.id, users.fname, users.lname FROM users,friend 
 WHERE 
      IF(friend.user_id != $id, friend.user_id = users.id, friend.receiver_id = users.id)
      AND (friend.user_id = $id OR friend.receiver_id = $id)
      AND (friend.status = 1)
      AND (user.deleted = 'n')"

如果friend.status = 1,这意味着接收者已经接受了好友请求。 如果user.deleted = n,则表示用户帐号没有被删除

如何将上述查询转换为 Laravel Eloquent?

我试图做这样的事情:-

$friendshipQuery    = Friend::select('users.id', 'users.fname', 'users.lname', 'users.email')
                              ->whereRaw('IF(friend.user_id != ' .$id. ', friend.user_id = users.id, friend.receiver_id = users.id)')
                              ->where(array('friend.status' =>  Globals::FR_ACCEPTED)) // status which are accepted
                              ->where(function($query) use ($id){
                                $query->where(array(
                                               'friend.user_id'       => $id        // check whether user id matches sender id 
                                        ))
                                        ->orWhere(array(
                                                'friend.receiver_id'   => $id        // check whether user id matches receiver id 
                                        ));
                                 })
                                ->where('users.deleted', 'n');

不知何故,我无法加入这两个表,因为friend.user_id 和friend.receiver_id 都引用了users.id。

我怎样才能做到这一点?

还有其他有效的方法吗?

【问题讨论】:

    标签: laravel join laravel-5.6


    【解决方案1】:

    使用 Laravel 时,这可以通过 usersusers 之间的自引用多对多关系来实现,使用 friends 作为数据透视表。

    如果您使用 Laravel 的模型,请定义 User 模型和 public function friends() 关系,范围如下:

    class User extends Model {
      ...
      public function friends(){
        return $this->belongsToMany(User::class, "friends", "user_id", "receiver_id");
      }
    }
    

    那么有了这个,你就可以简单地做:

    $user = User::with(["friends"])->first();
    $friends = $user->friends;
    dd($user, $friends);
    

    如果您需要过滤该查询以仅显示已接受的朋友,那么您只需这样做:

    $user = User::with(["friends" => function($query){
      $query->where("status", "=", Globals::FR_ACCEPTED);
    }])->first();
    $friends = $user->friends;
    dd($user, $friends);
    

    您甚至可以在关系末尾添加 status 检查,以仅返回已接受的朋友。但无论哪种方式,Laravel 都提供了一些强大的工具来有效地处理这个问题,所以请考虑使用它们。

    https://laravel.com/docs/5.7/eloquent-relationships#many-to-many

    【讨论】:

    • 这个函数($query)是从数据库还是从Web服务器端工作?
    【解决方案2】:

    好吧,我会使用... join() 并为表名加上别名以避免混淆(u 代表用户,f 代表朋友,uf 代表用户的朋友)

    $friendshipQuery = Friend::table('users as u')
                       ->join('friend as f', 'f.user_id', '=', 'u.id')
                       ->join('users as uf', 'f.receiver_id', '=', 'uf.id')
                       ->select('uf.*')
                       ->where('f.status', '1')
                       ->where('uf.deleted', 'n')
                       ->where('u.deleted', 'n')
                       ->where(function($query) use ($id)
                       {
                           $query->where(array('friend.user_id'       => $id))
                                 ->orWhere(array('friend.receiver_id' => $id));
                       });
    

    顺便说一下,笛卡尔连接很糟糕并且已被弃用

    我会这样重写 SQL 查询

    SELECT uf.id, uf.fname, uf.lname
    FROM users u
    LEFT JOIN friend f ON u.id = f.user_id
    LEFT JOIN users uf ON uf.id = f.receiver_id
    WHERE (f.user_id = $id OR f.receiver_id = $id)
      AND (f.status = 1)
      AND (uf.deleted = 'n')
      AND (u.deleted = 'n')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-09-03
      相关资源
      最近更新 更多