TL; DR:需要每个发件人的最新消息。
在我的Laravel应用程序中,我有两个表:
用户:
消息:
当然还有模特。
用户模型:
public function messages() {
return $this->hasMany('App\Message', 'recipient_id');
}
消息模型:
public function sender() {
return $this->belongsTo('App\User', 'sender_id');
}
public function recipient() {
return $this->belongsTo('App\User', 'recipient_id');
}
当用户打开收件箱时,他应该会看到来自任何其他用户的最新消息列表。
所以如果有消息:
id sender_id recipient_id body created_at
1, 2, 1, hi, 2016-06-20 12:00:00
2, 2, 1, hi, 2016-06-21 12:00:00
3, 3, 1, hi, 2016-06-20 12:00:00
4, 3, 1, hi, 2016-06-21 12:00:00
然后,id为1(recipient_id)的用户应该只能看到ID为2和4的消息。
这是用户模型中的当前解决方案:
return Message::whereIn('id', function($query) {
$query->selectRaw('max(`id`)')
->from('messages')
->where('recipient_id', '=', $this->id)
->groupBy('sender_id');
})->select('sender_id', 'body', 'created_at')
->orderBy('created_at', 'desc')
->get();
这是有效的,但如果有可能实现Laravel方式,我就会徘徊。可能有急切的装载。我的Laravel技能还不够,经过几天的尝试,我没有解决方案。
感谢。
答案 0 :(得分:5)
从this post中获取灵感,最有效的方法就是这样:
DB::table('messages AS m1')
->leftjoin('messages AS m2', function($join) {
$join->on('m1.sender_id', '=', 'm2.sender_id');
$join->on('m1.id', '<', 'm2.id')
})->whereNull('m2.id')
->select('m1.sender_id', 'm1.body', 'm1.created_at')
->orderBy('m1.created_at', 'm1.desc')->get();
虽然它不是Laravel最友好的,但它是基于性能的最佳解决方案,正如上面这个答案所链接的帖子所强调的那样
答案 1 :(得分:2)
为什么不简单地访问messages
,就像这样 -
// get the authenticated user
$user = \Auth::user();
// find the messages for that user
return User::with('message')->find($user->id)->messages;
答案 2 :(得分:2)
我尝试了一些类似的方法,发现你只需要在created_at
找到User
的所有消息后立即订购,然后你可以使用Laravel的Collection
将它们分组{ {1}}。因此,根据我的理解,以下方法应该有效,即从发件人 s :
假设您有一个经过身份验证的sender_id
为User
,在此上下文中与$user
receiver
receiver_id
相同,那么:< / p>
messages
然后遍历集合,并获取第一个:
$messages = $user->messages()
->orderBy('created_at', 'desc')
->get(['sender_id', 'body', 'created_at'])
->groupBy('sender_id'); //this is collections method
你应该得到如下结果:
$result = new \Illuminate\Database\Eloquent\Collection ();
foreach ($messages as $message){
$result->push($message->first()); //the first model in the collection is the latest message
}
PS:请注意,我不能说这可能有多高效,但有一点可以肯定的是,数据库上的查询数量有限。
希望有所帮助:)
更新:
我会在尝试时将其分解。 〜
它将属于用户的 Illuminate\Database\Eloquent\Collection {#875
all: [
App\Message {#872
sender_id: "2",
body: "hi",
created_at: "2016-06-21 12:00:00",
},
App\Message {#873
sender_id: "4",
body: "hi",
created_at: "2016-06-21 12:00:00",
},
]
的所有记录提取到一个集合中(已由created_at排序),然后使用laravel's groupBy()得到一个结果,如该文档中给出的示例。
这次我没有转换为Array。相反,它是一个收藏集合。喜欢集合(集合(消息))
然后在每个索引处选择第一个模型。我已经传递给messages
方法的参数确保只存在那些字段(即get()
。这与mysql groupBy()完全不同,因为它不为每个组返回一行,而是只需按给定的标识符对所有记录进行分组。
另一个更新
后来我发现在生成的有序消息上调用->get(['sender_id', 'body', 'created_at'])
方法实际上可以完成这项工作,但在这种情况下,唯一标识符将是unique()
。 (参考:Laravel's unique)即:
sender_id
结果是我们不需要$messages = $user->messages()
->orderBy('created_at', 'desc')
->get(['sender_id', 'body', 'created_at'])
->unique('sender_id'); //unique replaces `groupBy`
和foreach
我们在这里得到结果。
另一种避免重复的方法(如果需要在多个地方使用)是使用Laravel's query scope,即在groupBy
模型中,我们可以有这样的东西:
Message
然后在控制器中使用:
public function scopeLatestSendersMessages($query)
{
return $query->orderBy('created_at', 'desc')
->get(['sender_id', 'body', 'created_at'])
->unique('sender_id');
}
PS:仍然不确定哪一个比另一个更好。
答案 3 :(得分:1)
这可能是一个解决方案(虽未测试)
User::with([
'messages' => function ($q) {
$q->select('sender_id', 'body')->groupBy('sender_id')->orderBy('created_at', 'desc');
}
])->find(1);
答案 4 :(得分:1)
我喜欢更简单的方法,提到here。
在您的User
模型中,除现有messages()
关系外,添加此关系
public function latestMessage()
{
return $this->hasOne(Message::class, 'recipient_id')->latest();
}
然后当您查询这样的查询时。
$messages = User::with('latestMessage')->get();
$messages
包含每个用户的最新消息。
修改强>
为了按datetime / id排序结果,你可以这样做。
$messages = User::with(['latestMessage' => function($message) {
$message->orderBy('id', 'desc');
}])->get();
$messages
包含id
订购的每位用户的最新消息。 Refer this answer
答案 5 :(得分:0)
这是最有说服力的方式&#34;我发现这样做了:
在用户模型中:
public function messages() {
return $this->hasMany(Message::class, 'recipient_id');
}
public function latestMessagePerSender()
{
return $this->messages()
->whereNotExists(function ($query) {
$query->from('messages AS m2')
->whereRaw('m2.sender_id = messages.sender_id')
->whereRaw('m2.created_at > messages.created_at');
});
}
然后只是$user->latestMessagePerSender
答案 6 :(得分:0)
也许你可以尝试这个:
$user = \Auth::user();
// Get the latest date
$last_date_created = Message::latest()->first()->created_at; // also check for possible null
// Get the date only - use to specify date range in the where section in the eloquent query
$target_date = date('Y-m-d', strtotime( $last_date_created ) );
// Retrieve the messages
$latest_posts = Message::where('recipient_id', $user->id)
->where('created_at', '>=', $target_date . ' 00:00:00')
->where('created_at', '<=', $target_date . ' 23:59:59')
->groupBy('sender_id')
->groupBy('created_at')
->get();
return $latest_posts;
这可能效率不高,因为它需要两个查询来完成这项工作,但您将通过代码可读性获益。
我希望它的运作方式应该如此。虽然我还没有对它进行过测试,但我通常会这样做...干杯!
答案 7 :(得分:0)
我在另一个论坛上找到了此解决方案,我认为这就是您想要的。我发布了它,以便对其他用户有用
$conflictTraining = Training::where('fk_motorbike', $fk_motorbike)
->whereDate('date_seance', "=" , $date_start)
->where('hour_start', "<=" , $request->get('hour_start'))
->where('hour_end', ">=" , $request->get('hour_end'))
->where('fk_former', $request->get('fk_former'))
->first();
$conflictRevision = Revision::where('fk_motorbike', $fk_motorbike)
->whereDate('date_revision_start', "<=" , $date_start)
->whereDate('date_revision_end', ">=", $date_start)
->where('hour_start', "<=" , $request->get('hour_start'))
->where('hour_end', ">=" , $request->get('hour_end'))
->first();
$conflictFormer = Training::where('fk_former', $fk_former)
->whereDate('date_seance', "=" , $date_start)
->where('hour_start', "<=" , $request->get('hour_start'))
->where('hour_end', ">=" , $request->get('hour_end'))
->first();
$conflictMotorbike = Training::where('fk_motorbike', $fk_motorbike)
->whereDate('date_seance', "=" , $date_start)
->where('hour_start', "<=" , $request->get('hour_start'))
->where('hour_end', ">=" , $request->get('hour_end'))
->first();
if(isset($conflictTraining)){
return redirect()->route('trainings.index')
->with('error', 'Duplicate ! ');
}
if(isset($conflictRevision)){
return redirect()->route('trainings.index')
->with('error', 'The motorbike is in revision! ');
}
if(isset($conflictFormer)){
return redirect()->route('trainings.index')
->with('error', 'Duplicate former ! ');
}
if(isset($conflictMotorbike)){
return redirect()->route('trainings.index')
->with('error', 'Duplicate motorbik ! ');
}
答案 8 :(得分:-1)
你可以尝试这个
Messages::where('recipient_id',**{USER_ID}**)
->group_by('sender_id')
->order_by('id','desc')
->get();