Laravel: Один из многих ( hasOne + ofMany )

Laravel: Один из многих ( hasOne + ofMany )

Иногда модель может иметь множество связанных моделей, но вы хотите легко получить "самую последнюю" или "самую старую" связанную модель. Например, модель User может быть связана со многими моделями Order, но вы хотите определить удобный способ взаимодействия с последним заказом пользователя. Для этого можно использовать тип отношения hasOne в сочетании с методами ofMany:



/**
 * Получить последний (самый новый) заказ пользователя
 */
public function latestOrder()
{
    return $this->hasOne(Order::class)->latestOfMany();
}

Аналогично, вы можете определить метод для получения "самой старой" (первой) связанной модели отношения:



/**
 * Получить самый старый заказ пользователя
 */
public function oldestOrder()
{
    return $this->hasOne(Order::class)->oldestOfMany();
}

По умолчанию методы latestOfMany и oldestOfMany извлекают самую последнюю или самую старую связанную модель на основе первичного ключа модели. Этот ключ должен быть сортируемым. Чтобы использовать данные другого столбца, используйте метод ofMany. Он принимает в качестве первого аргумента сортируемый столбец и то, какую агрегатную функцию (min или max) применить при запросе связанной модели.

Например, так можно получить самый дорогой заказ пользователя:



/**
 * Получить самый дорогой заказ пользователя
 */
public function largestOrder()
{
    return $this->hasOne(Order::class)->ofMany('price', 'max');
}

Поскольку PostgreSQL не поддерживает выполнение функции MAX для столбцов UUID, в настоящее время невозможно использовать отношения "один-из-многих" в сочетании со столбцами UUID PostgreSQL.

Продвинутые возможности отношения Один-из-многих

Критерии сортировки при выборе могут быть сложными. Например, модель Product может иметь множество связанных с ней моделей Price, которые сохраняются в системе даже после публикации новых цен - например, чтобы можно было посмотреть динамику изменения цены продукта. Кроме того, новые данные о ценах на продукт могут быть опубликованы заранее, чтобы вступить в силу в определённую дату - через колонку published_at.

Таким образом, нам нужно получить последние опубликованные цены, если дата публикации не находится в будущем. Кроме того, если две цены имеют одинаковую дату публикации, мы предпочтем цену с наибольшим ID. Для этого в метод ofMany нужно передать массив, который содержит сортируемые столбцы, определяющие последнюю цену. Кроме того, в качестве второго аргумента метода ofMany передаётся функция, которая будет отвечать за добавление дополнительного фильтра по дате публикации:



/**
 * Получить актуальную цену на продукт
 */
public function currentPricing()
{
    return $this->hasOne(Price::class)->ofMany([
        'published_at' => 'max',
        'id' => 'max',
    ], function ($query) {
        $query->where('published_at', '<', now());
    });
}