Красноречивый whereHas Отношение с ограничением на конкретную связанную сущность - PullRequest
1 голос
/ 26 марта 2020

Рассмотрим две модели Пользователь и Книга . У последней есть столбец состояния, в котором можно получить различные строковые значения active, inactive, deleted, поэтому У пользователя может быть несколько книг, и книга принадлежит пользователю.

как я могу получить только пользователей, которые имеют последний статус книги = 'неактивен'?

SQL Запрос поведения ниже:

SELECT
    *
FROM
    `users`
WHERE EXISTS
    (
    SELECT
        *
    FROM
        `books`
    WHERE
        `books`.`user_id` = `users`.`id` AND `books`.`status` = 'inactive' AND `books`.`id` =(
        SELECT
            nested.`id`
        FROM
            `books` AS nested
        WHERE
            nested.`user_id` = `users`.`id`
        ORDER BY
            nested.`created_at` DESC
        LIMIT 1
    )
)

Я использую Laravel 5.6

Ответы [ 2 ]

1 голос
/ 28 марта 2020

Создать дополнительную связь в User модели, которая возвращает желаемый результат. Для этого вам понадобятся отношения 1-1.

/**
 * @return \Illuminate\Database\Eloquent\Relations\HasOne
 */
public function inactiveBookStillLatestPerUser()
{
    return $this->hasOne(Book::class)->where(['status' => 'inactive', 'id' => function (\Illuminate\Database\Query\Builder $nested) {
        $nested->from('books as nested')
            ->selectRaw('max(id)')
            ->whereRaw('nested.user_id = books.user_id');
    }]);
}

Затем где-нибудь в коде (т. Е. В контроллере) вы вызываете его с помощью

$users = User::has('inactiveBookStillLatestPerUser')->get();
// or if books are needed too
// $users = User::has('inactiveBookStillLatestPerUser')->with(['inactiveBookStillLatestPerUser'])->get();

Я использовал id последний заказ [max(id)] в подзапросе, чтобы избежать нежелательного результата, если один пользователь сделал групповую вставку нескольких книг в один и тот же момент времени и когда все эти книги будут иметь одинаковое время вставки, так что последний для каждого созданного_этапа, возможно, не будет наиболее точным. Но вы можете сделать то же самое, вместо этого:

/**
 * @return \Illuminate\Database\Eloquent\Relations\HasOne
 */
public function inactiveBookStillLatestPerUser()
{
    return $this->hasOne(Book::class)->where(['status' => 'inactive', 'created_at' => function (\Illuminate\Database\Query\Builder $nested) {
        $nested->from('books as nested')
            ->selectRaw('max(created_at)')
            ->whereRaw('nested.user_id = books.user_id');
    }]);
}

Возможно, второй пример в порядке, но первый пример с id будет работать нормально.

0 голосов
/ 27 марта 2020
User::where('your-conditions')
    ->whereHas('books', function($query) {
        $query->where('books.status', '=', 'inactive')
            ->orderBy('id', 'desc')
            ->first();
    })->get();
...