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

Другие вопросы по тегам