Laravel,如何在私人聊天api结构中避免n + 1问题?

时间:2018-02-15 02:43:56

标签: php mysql sql laravel

我正在尝试创建一个如下所示的api JSON响应结构:

[
  {
  "id": 1,
  "name": "jon abraham",
  "email": "jonabe2442@gmail.com",
  "image": "/images/avatar-default.jpg",
  "messages": [
    {message: 'hello!', user_id: 1}, 
    {message: 'yo!', user_id: 3}
   ]
  },
  {
  "id": 2,
  "name": "sherry hopkins",
  "email": "shop2663@gmail.com",
  "image": "/images/avatar-default.jpg",
  "messages": [
    {message: 'hey!', user_id: 2}, 
    {message: 'hi!', user_id: 3}
   ]
  }
]

JSON响应表示属于当前登录用户所属的同一公司的所有用户的列表。它还将显示该用户与当前登录用户之间的消息数组(如果没有消息,则为空数组)。

我设法创建了大部分我想要的api结构,除了消息部分,因为在为私人聊天建立适当的数据库结构/雄辩关系时遇到了麻烦。

这是我的私人聊天数据库结构。

会话

| id | participant_1_id | participant_2_id

Conversation_messages

| id | sender_id | receiver_id |文本

这是我的控制器代码,它返回api:

public function userWithMessages
    {   

    	$user = Auth::user();
        $company_ids = $user->companies()->pluck('companies.id')->toArray();

        $users = User::join('company_user', 'users.id', 'company_user.user_id')
                ->join('companies', 'company_user.user_id', 'companies.id')
                ->whereIn('companies.id', $company_ids)
                ->select('users.name', 'users.email', 'users.image')
                ->get();

    	return response()->json($users, 200);

    }

我正在尝试编写我的查询,以便我没有遇到n + 1问题,但我似乎无法找到与用户对话的关系。原因是,在会话表中,我不知道user_id是participant_1_id还是participant_2_id,这意味着我不能使用hasMany关系。

有没有办法可以从用户那里获取有关当前登录用户的消息数据,而无需编写foreach循环并查询每个用户(n + 1)问题。

1 个答案:

答案 0 :(得分:2)

我建议使用以下数据库结构。这不仅支持用户之间的多个对话,还支持每个对话超过两个参与者,从而允许您进行群组对话。

user
-   id
-   first_name
-   last_name
...

conversations
-   id
-   created_at
-   updated_at

conversation_user
-   id
-   conversation_id
-   user_id
-   created_at

message
-   id
-   conversation_id
-   user_id
-   message
-   created_at
-   updated_at

上述模式的模型定义和关系如下所示:

<?php

class User extends Eloquent
{
    public function conversations() 
    {
        return $this->belongsToMany(Conversation::class)->latest('updated_at');
    }

    public function messages() 
    {
        return $this->hasMany(Message::class)->latest();
    }
}

class Conversation extends Eloquent
{
    public function participants() 
    {
        return $this->belongsToMany(User::class)->oldest();
    }

    public function messages()
    {
        return $this->hasMany(Message::class)->latest();
    }
}

class Message extends Eloquent
{
    public function conversation() 
    {
        return $this->belongsTo(Conversation::class);
    }

    public function sender()
    {
        return $this->belongsTo(User::class);
    }
}

要查找所有与其对话的用户,您可以使用以下查询。这将使用两个查询来查找所有用户和对话。

$users = User::with('conversations')->get();

要查找包含其邮件的所有用户,您可以使用以下查询。这将使用两个查询来查找所有用户和对话。

$users = User::with('messages')->get();

要查找具有对话和消息的所有用户,您可以合并上述查询。这将使用三个查询来查找所有用户,对话和消息。但请注意,消息不会嵌套在会话下方。

$users = User::with('conversations', 'messages')->get();

编辑:以下内容将为您提供所有用户以及他们与当前用户进行的任何对话。如果您还在conversations.messages来电中加入with(),那么您也可以在这些对话中收到消息。虽然这很快就会成为一个庞大的数据集。

$user = Auth::user();

$users = User::with([
        'conversations' => function ($query) use ($user) {
            return $query->whereHas('user', function ($query) use ($user) {
                return $query->where('user_id', $user->id);
            });
        },
        'conversations.messages',
    ])
    ->get();

我很乐意回答您对上述问题的任何后续问题。