#php #mysql #laravel #database-design #laravel-8
Вопрос:
У меня есть простая таблица чата один на один, где хранятся сообщения как от идентификатора пользователя, так и от идентификатора пользователя. вот схема.
Я извлекаю разговор между двумя пользователями, сопоставляя либо from_user_id с пользователем входа в систему, либо to_user_id как
-gt;where(function($q) use($user_id) { $q-gt;where('from_user_id', $user_id) -gt;orWhere('to_user_id', $user_id); })
Теперь мне нужен уникальный разговор между пользователем, вошедшим в систему, и другими пользователями. я пытался, как
-gt;groupBy('from_user_id', 'to_user_id')
но он возвращает оба идентификатора записи : 1 и 6, потому что он хранится как 65 — 66 и 66 — 65.
Так что любым способом я могу сгруппироваться с помощью уникального общения и получить только одну запись для одного и того же. или мне нужно изменить схему таблицы как conversation и conversation_message.
Пожалуйста, веди меня. Спасибо.
Комментарии:
1. Вы можете сгруппировать по необработанному выражению, что-то вроде (настроить синтаксис)
-gt;groupBy(db::RAW('LEAST(from_user_id,to_user_id), GREATEST(from_user_id,to_user_id)'))
.2. @Akina Большое тебе спасибо. это дает уникальную запись, но из этого дублированного порядка записей не работает, можно ли получить последнюю запись из тех же записей?
3. Используя показанную группировку, получите максимальную дату и время для группы. Используя это в качестве подзапроса, выберите соответствующую строку из другой копии таблицы. Это просто в чистом SQL, но я не использую Laravel и, следовательно, не знаю, как это будет выглядеть (и, возможно, у Laravel есть какой — то специальный инструмент для упрощения этой задачи comon).
Ответ №1:
Я бы рекомендовал добавить модель разговора. В основном вам нужен уникальный идентификатор для каждого отношения между пользователем и пользователем (и всех сообщений в нем), и модель разговора будет наиболее кратким способом достижения этой цели.
Тем не менее, существуют разумные способы делать то, что вы хотите, без дополнительной модели. Поскольку вам известен идентификатор пользователя, вошедшего в систему, вы можете передать обратный вызов методу groupBy коллекции, который группируется по идентификатору, не являющемуся зарегистрированным пользователем. Например:
-gt;where(function($query) { $query-gt;where('from_user_id', $user_id) -gt;orWhere('to_user_id', $user_id) }) // So the messages appear in order. -gt;orderByDesc('created_at') -gt;get() -gt;groupBy(function($item) use ($user_id) { // If the message was sent by the user, it groups on the recipient's ID. // If the message was received by the user, it groups on the sender's ID. return $item-gt;from_user_id == $user_id ? $item-gt;to_user_id : $item-gt;from_user_id; });
В этом примере не учитывается, что пользователь отправляет сообщения самому себе (что может быть возможно в вашей системе), поэтому его может потребоваться доработать.
Комментарии:
1. спасибо за ответ @Джеймс, я знаю о первом разговоре. но его уже разработали, поэтому находим решение. ваш второй ответ не работает, он группирует данные, но предоставляет все записи внутри группы. я хочу в группе запросов, а не в коллекции.
2. Не планируйте иметь миллионы строк; производительность «или» плохая.
Ответ №2:
Недавно я закодировал систему чата для своей компании. Я перепробовал много схем, но в конце концов нашел лучшую. Я могу поделиться этим с тобой.
Эта схема может состоять из нескольких таблиц и заставит вас отправлять больше запросов. Но в этой структуре вы сможете легко справляться со всеми задачами. Вот вы модель и структура контроллера:
Чат-мессенджер:
lt;?php namespace AppModels; use IlluminateDatabaseEloquentFactoriesHasFactory; use IlluminateDatabaseEloquentModel; class ChatMessage extends Model { use HasFactory; public $table = "chat_messages"; protected $fillable = ['chat_id', 'user_id','content']; public function chat() { return $this-gt;belongsTo(ChatUser::class, 'chat_id', 'id'); } public function user() { return $this-gt;belongsTo(User::class); } }
Пользователь чата:
lt;?php namespace AppModels; use IlluminateDatabaseEloquentFactoriesHasFactory; use IlluminateDatabaseEloquentModel; class ChatUser extends Model { use HasFactory; public $table = "chat_users"; protected $fillable = ['user_id_from', 'user_id_to','last_activity']; protected $dates = ['last_activity']; public function user_from() { return $this-gt;belongsTo(User::class, 'user_id_from', 'id'); } public function user_to() { return $this-gt;belongsTo(User::class, 'user_id_to', 'id'); } public function messages() { return $this-gt;hasMany(ChatMessage::class, 'chat_id', 'id')-gt;latest('id')-gt;take(15); } }
Контроллер чата:
public function index() { $chats = ChatUser::has('messages') -gt;where('user_id_from', $this-gt;user-gt;id) -gt;orWhere(function ($query) { $query-gt;where('user_id_to' , $this-gt;user-gt;id); }) -gt;with(['user_to','user_from']) -gt;orderBy('last_activity', 'DESC') -gt;get(); return $this-gt;successResponse(new ChatCollection($chats)); }
Если у вас есть какие-либо вопросы, не стесняйтесь задавать их.
Комментарии:
1. верно. у меня такая же структура для группового чата. и рабочая находка. я просто хотел, как указано выше, потому что его схема уже построена, вот почему.
2. Честно говоря, я сначала попытался использовать вашу структуру. Но я понял, что с этим очень трудно справиться. Вот почему я рекомендую вам изменить структуру. Для базы данных будет полезно использовать одну и ту же структуру как для групповой, так и для частной.