Laravel eloquent apply whereHas on the latest record of hasMany 关系

Posted

技术标签:

【中文标题】Laravel eloquent apply whereHas on the latest record of hasMany 关系【英文标题】:Laravel eloquent apply whereHas on latest record of hasMany relationship 【发布时间】:2020-10-02 15:57:43 【问题描述】:

使用 Laravel 7.0

我有 3 个模型(MeetingMeetingVersionUser), 和 4 个表格(Meetingmeeting_versionsusersmeeting_versions_users)。

一个Meeting有很多MeetingVersion模型,其中每个版本都与Usermany-to-many关系。每个 MeetingVersion 都有一个指向其父 Meeting 的外键 meeting_id。用户通过 meeting_versions_users 数据透视表关联到一个 MeetingVersion。

现在,我想选择最新版本与给定用户 ID 相关联的所有会议。

会议课

public function versions()

    return $this->hasMany(MeetingVersion::class, 'meeting_id');

MeetingVersion 类

public function users()

    return $this->belongsToMany(User::class, 'meeting_versions_users', 'version_id', 'user_id');

表格

会议

+----+
| id |
+----+
| 10 |
| 11 |
+----+

meeting_versions

+----+------------+---------+
| id | meeting_id | version |
+----+------------+---------+
| 31 |         10 |       1 |
| 32 |         10 |       2 |
| 33 |         10 |       3 | <- latest version (33) for meeting id 10
| 34 |         11 |       1 |
| 35 |         11 |       2 | <- latest version (35) for meeting id 11
+----+------------+---------+

meeting_versions_users

+------------+---------+
| version_id | user_id |
+------------+---------+
|         31 |     101 |
|         32 |     101 |
|         33 |     102 | <- user associated with latest version for meeting id 10
|         34 |     101 |
|         35 |     101 | <- user associated with latest version for meeting id 11
+------------+---------+

用户

+-----+
| id  |
+-----+
| 101 |
| 102 |
+-----+

这是我正在尝试的查询。我应该只返回会议 11,但此查询返回用户 101 的两个会议。

// Some user id.
$userId = 101;


// Query to get all meetings where user is associated with latest version of meeting.

$meeting = Meeting::query()->where(function (Builder $query) use ($userId) 

        $query->whereHas('versions', function (Builder $query) use ($userId) 

            /*
             * I want to get just the "latest" version model here, so that further queries are constrained to that model only.
             * As it is now, the below whereHas query is applied to all child models.
             * Using $query->latest('version')->limit(1) doesn't work here as expected.
             *
             * So, I need to a way to limit the following whereHas query to the latest version.
             * This constraint should be applied to just the "latest" model relation, instead of all models.
            */

            $query->whereHas('users', function (Builder $query) use ($userId) 
                $query->where('id', $userId);
            );

        );
    )->get();

非常感谢任何帮助。

【问题讨论】:

【参考方案1】:

在你的问题中你说:

在此处使用 $query->latest('version')->limit(1) 无法按预期工作。

因为 laravel 得到了整个查询的最后一个 MeetingVersion!不是每次会议

解决这个问题使用eloquent-eager-limit

安装它:

composer require staudenmeir/eloquent-eager-limit:"^1.0"

然后在会议模型中:

class Meeting extends Model

use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
.....
public function lastMeetingVersion()

return $this->hasMany(MeetingVersion::class, 'meeting_id')
->orderBy('id','desc')->limit(1);

    

在 MeetingVersion 中:

class MeetingVersion extends Model

use \Staudenmeir\EloquentEagerLimit\HasEagerLimit;
......

现在限制将按您的预期工作......

$meeting = Meeting::query()->with(['lastMeetingVersion' => function ($query)
        use ($userId) 
            $query->whereHas('users', function ($q) use ($userId) 
                $q->where('id', $userId);
            );
        ]);

更多详情请见:eloquent-eager-limit

【讨论】:

谢谢,OMR。我尝试了这个解决方案,但我也没有工作。在此查询的末尾添加-&gt;get() 时,出现内存已耗尽错误。我也只是尝试使用-&gt;find(10),它返回一个结果,但对两个用户都这样做,而不是用户用户 102。【参考方案2】:

您可以使用 subselect 来获取每个会议的最大版本 ID。

$meetings = Meeting::query()
    ->whereHas('versions', function (Builder $query) use ($userId) 
        $query->addSelect([
            'max_meeting_version_id' => MeetingVersion::select('id')->whereColumn('meeting_id', 'meeting.id')
                ->orderBy('version', 'desc')->limit(1)
        ])->whereHas('users', function (Builder $query2) use ($userId) 
            $query2->where('users.id', $userId);
        )
            ->havingRaw('max_meeting_version_id = meeting_versions.id');
    )
    ->get();

【讨论】:

谢谢,邦诺基亚。我试过这个,但在havingRaw 子句中出现错误。 “未找到列:1054 Unknown column 'meeting_versions.id' in 'have Clause'” 我尝试将子句更改为 'max_meeting_version_id = id',从而防止出现错误。但是,现在它没有产生任何结果。 你有 laravel 调试栏吗,它可以显示原始的 sql 查询。我不确定为什么meeting_versions.id 不知道。

以上是关于Laravel eloquent apply whereHas on the latest record of hasMany 关系的主要内容,如果未能解决你的问题,请参考以下文章

Laravel/Lumen - 使用 Model::with()

php Laravel 5 Eloquent CheatSheet #laravel #eloquent

php Laravel 5 Eloquent CheatSheet #laravel #eloquent

php Laravel 5 Eloquent CheatSheet #laravel #eloquent

php [Eloquent CheatSheet] Eloquent #laravel #eloquent的常见查询方法

Laravel - Eloquent 连接