Php 获取Laravel中每个用户的最新消息(行)

Php 获取Laravel中每个用户的最新消息(行),php,laravel,Php,Laravel,TL;DR:需要每个发件人的最新消息 在我的Laravel应用程序中,我有两个表: 用户: 身份证 名字 信息: 身份证 发送者身份证 收件人id 身体 创建于 当然还有模型 用户模型: public function messages() { return $this->hasMany('App\Message', 'recipient_id'); } public function messages() { return $this->hasMany(M

TL;DR:需要每个发件人的最新消息

在我的Laravel应用程序中,我有两个表:

用户:

  • 身份证
  • 名字
信息:

  • 身份证
  • 发送者身份证
  • 收件人id
  • 身体
  • 创建于
当然还有模型

用户模型:

public function messages() {
    return $this->hasMany('App\Message', 'recipient_id');
}
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');
    });
}
消息模型:

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(收件人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();
这是可行的,但我想知道是否有可能通过拉雷维尔的方式实现这一点。可能是迫不及待地装载。我的拉威尔技巧还不够,经过几天的尝试,我没有找到解决办法


谢谢。

为什么不像这样简单地访问
消息呢-

// get the authenticated user
$user = \Auth::user(); 

// find the messages for that user
return User::with('message')->find($user->id)->messages;
你可以试试这个

Messages::where('recipient_id',**{USER_ID}**)
          ->group_by('sender_id')
          ->order_by('id','desc')
          ->get();
从中汲取灵感,最有效的方法是:

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();
DB::table('messages AS m1')
->leftjoin('消息为m2',函数($join){
$join->on('m1.sender_id','=','m2.sender_id');
$join->on('m1.id','这可能是一个解决方案(但未测试)

这是我发现的最“雄辩的方式”:

在用户模型中:

public function messages() {
    return $this->hasMany('App\Message', 'recipient_id');
}
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

也许您可以试试这个:

        $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;
$user=\Auth::user();
//获取最新日期
$last_date_created=Message::latest()->first()->created_at;//还要检查可能的空值
//仅获取日期-用于在雄辩查询的where部分中指定日期范围
$target_date=date('Y-m-d',strottime($last_date_created));
//检索消息
$latest\u posts=Message::where('recipient\u id',$user->id)
->其中('created_at','>=',$target_date.'00:00:00')

->在哪里('created_at',“我尝试了一些类似的方法,发现你只需要在找到
用户的所有消息后立即按
created_at
排序,然后你就可以使用Laravel的
集合
发件人id
对它们进行分组。因此,据我所知,以下方法应该有效,即给你用户从发件人s接收的最后一条消息:

因此,假设您有一个经过身份验证的
用户
作为
$User
,在此上下文中,该用户与
消息
表中具有
接收方id的
接收方
相同,那么:

$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
}
您应该得到如下结果:

 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",
      },
    ]
PS:需要注意的是,我不能说这可能有多高效,但有一点是肯定的,那就是数据库上的查询数量有限

希望有帮助:)

更新

我试着把它拆了~

它将属于用户的
消息
的所有记录提取到一个集合中(已由created_at排序),然后使用该文档中给出的结果

这次我没有转换为数组。相反,它是集合的集合。比如集合(集合(消息))

然后在每个索引中选择第一个模型。我已经传递到
get()
方法中的参数确保只存在这些字段(即
->get(['sender\u id','body','created\u at'])
。这与mysql groupBy()完全不同,因为它不会为每个组返回一行,而只是根据给定的标识符对所有记录进行分组

另一次更新

我后来发现,对生成的有序消息调用
unique()
方法实际上可以完成这项工作,但在这种情况下,唯一标识符将是
sender\u id
(ref:),即:

结果是,我们不需要foreach的
groupBy,我们在这里得到了结果

另一种避免重复(上述内容)的方法是,如果需要在多个地方使用,即在<代码>消息模型中,我们可以使用以下内容:

public function scopeLatestSendersMessages($query) 
{
     return $query->orderBy('created_at', 'desc')
         ->get(['sender_id', 'body', 'created_at'])
         ->unique('sender_id');
}
然后在控制器中使用:

$messages = $user->messages()->latestSendersMessages();

PS:仍然不确定哪一个比另一个更好。

我喜欢前面提到的更简单的方法

在您的
用户
模型中,除了现有的
消息()
关系之外,还要添加此关系

public function latestMessage() 
{
    return $this->hasOne(Message::class, 'recipient_id')->latest();
}
然后,当您查询时,只需像这样进行查询

$messages = User::with('latestMessage')->get();
$messages = User::with(['latestMessage' => function($message) {
    $message->orderBy('id', 'desc');
}])->get();
$messages
包含每个用户的最新消息

编辑

为了按datetime/id对结果进行排序,您可以这样做

$messages = User::with('latestMessage')->get();
$messages = User::with(['latestMessage' => function($message) {
    $message->orderBy('id', 'desc');
}])->get();

$messages
包含由
id

订购的每个用户的最新消息。我在另一个论坛中找到了此解决方案,我想这就是您要寻找的。我发布此解决方案是为了对其他用户有用

        DB::select('
            SELECT t1.*
            FROM messages AS t1
            INNER JOIN
            (
                SELECT
                    LEAST(sender_id, recipient_id) AS user_id,
                    GREATEST(sender_id, recipient_id) AS recipient_id,
                    MAX(id) AS max_id
                FROM messages
                GROUP BY
                    LEAST(sender_id, recipient_id),
                    GREATEST(sender_id, recipient_id)
            ) AS t2
                ON LEAST(t1.sender_id, t1.recipient_id) = t2.sender_id AND
                   GREATEST(t1.sender_id, t1.recipient_id) = t2.recipient_id AND
                   t1.id = t2.max_id
                WHERE t1.sender_id = ? OR t1.recipient_id = ?
            ', [auth()->guard('api')->user()->id, auth()->guard('api')->user()->id]);

原始帖子:

因为它将返回用户收到的所有邮件。但我只需要每个发件人的最新邮件。好的,你之前的问题不太清楚。无论如何,如果你想这样做,另一个解决方案可能是保留“正在阅读”在您的
消息
表中标记,默认值为
N
。稍后,一旦消息被读取,您可以将其更新为
Y
。这样,您只能筛选
is_read='N'
记录,并避免使用
max()
查询。实际上我有此标记(问题简化)。但是,这仍然没有用。如果用户从其他几个用户那里收到多条新消息,他们都会有此标志(未读取)。因此,基本上这是相同的问题,只是结果中有另一列。您的答案仍然返回所有消息。您所做的是“为第一个用户获取所有消息”,这与“获取每个用户的最新消息”我认为这个解决方案是完全好的。这个解决方案是什么