【问题标题】:Scan pivot table for two identical values扫描数据透视表以获取两个相同的值
【发布时间】:2015-02-18 16:21:07
【问题描述】:

我有这个数据透视表:

+----+---------+-----------------+
| id | user_id | conversation_id |
+----+---------+-----------------+
| 1  | 2       | 48              |
+----+---------+-----------------+
| 2  | 1       | 48              |
+----+---------+-----------------+

我正在尝试寻找一种方法来检测两个用户是否在同一个对话中,如果是,则返回对话。有点像:

Conversation::with([1, 2]); // should return conversation 48

我的Conversation 类如下所示:

class Conversation extend Eloquent {
    public function users() {
        return $this->belongsToMany('User');
    }

    public function messages(){
        return $this->hasMany('Message');
    }
}

到目前为止,我有以下代码,但它很笨拙且非常无效,并认为必须存在一些更清洁的 Laravel 方式:

class Conversation {

    public static function user_filter(Array $users) {

        $ids = array();

        foreach ($users as $user)
            $ids[$user->id] = $user->id;

        sort($ids);

        foreach (self::all() as $conversation){ // loop through ALL conversations (!!!)
            $u_ids = $conversation->users->lists('id');
            sort ( $u_ids );

            if ($u_ids === $ids)
                return $conversation;
        }

        return null;
    }
}

用法:

Conversation::user_filter([User::find(1), User::find(2)]); // returns conversation 48

有什么想法吗?

【问题讨论】:

    标签: laravel laravel-4 eloquent pivot-table


    【解决方案1】:

    让我们首先看看如何在不通过各种数组循环的情况下实际检索结果...

    您可以使用whereHas 函数来过滤关系。我们可以应用条件,如果模型的相关记录满足条件,则将其包含在结果中。

    $conversations = Conversation::whereHas('users', function($q){
        $q->where('user_id', 1);
    })->get();
    

    这是针对一位用户的。对于两个或更多用户,它基本上是相同的。你只需要链接whereHass

    $conversations = Conversation::whereHas('users', function($q){
        $q->where('user_id', 1);
    })
    ->whereHas('users', function($q){
        $q->where('user_id', 2);
    })->get();
    

    现在这不是很优雅。您可以创建一个query scope 来为您完成所有工作并保持控制器代码干净。在Conversation

    public function scopeWithUsers($query, array $userIds){
        foreach($userIds as $id){
            $query->whereHas('users', function($q) use ($id){
                $q->where('user_id', $id);
            });
        }
        return $query;
    }
    

    用法:

    $conversations = Conversation::withUsers([1,2])->get();
    $conversations = Conversation::withUsers([1,2,3,4])->get();
    $conversations = Conversation::withUsers([1])->get();
    

    如果它应该只是一个无论如何使用first()而不是get()

    【讨论】:

      【解决方案2】:

      这样的事情应该可以工作。

      class Conversation {
          public static function user_filter(Array $users) {
              $ids = array();
              foreach ($users as $user) {
                  $ids[] = $user->id;
              }
              return self::select("conversation_id")->whereIn("user_id", $ids)->groupBy("conversation_id")->havingRaw("count(*) = " . count($ids))->get();
          }
      }
      

      【讨论】:

      • 赞成,因为您试图翻译那个好的老式普通 SQL 技巧......但对话表本身没有 user_id(它在数据透视表中)。虽然足够接近,但可能只有一个 JOIN 距离。
      【解决方案3】:

      获取与给定用户的所有对话。

      Conversation::whereHas('users', function($query) use ($userids) {
          $query->whereIn('user_id', $userids);
      })->get();
      

      如果你需要特别的,你可以:

       Conversation::whereHas('users', function($query) use ($userids) {
              $query->whereIn('user_id', $userids);
          })->whereId($id)->first();
      

      【讨论】:

      • 这行不通,因为它还会获取仅涉及其中一个已通过用户的对话。
      • 哦,还没有数据库来测试它...:\
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-07-22
      相关资源
      最近更新 更多