Laravel красноречиво применяется, где есть последняя запись о hasMany
Использование Laravel 7.0
У меня есть 3 модели (Meeting, MeetingVersion и User) и 4 таблицы (Meeting, meeting_versions, users, meeting_versions_users).
Встреча имеет много MeetingVersion моделей, в которых каждая версия имеетmany-to-many
отношения с пользователем. Каждая версия MeetingVersion имеет внешний ключmeeting_id
своему родительскому собранию. Пользователи связаны с версией собрания черезmeeting_versions_users
сводная таблица.
Теперь я хочу выбрать все собрания, где последняя версия связана с данным идентификатором пользователя.
Встреча Класс
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 |
+----+
встреча_версии
+----+------------+---------+
| 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();
Любая помощь с этим очень ценится.
2 ответа
Вы можете использовать подзапрос, чтобы получить максимальный идентификатор версии каждого собрания.
$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();
В своем вопросе вы сказали:
- Использование $query->latest('version')->limit(1) здесь не работает должным образом.
потому что laravel получает последнюю версию MeetingVersion всего запроса! не на каждую встречу
чтобы решить эту проблему, используйте красноречивый-нетерпеливый предел
установите это:
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