@kedrovdanil

Как сделать выборку из базы данных с отношениями и применить фильтры?

Имеется 2 таблицы в БД:
1) cars - хранит записи авто:
id | manufacturer | model | description ...

2) cars_prices_history - хранит историю цен на авто (цена может быть акционной, а может быть не акционной)
id  |  car_id  |  price    |  is_sale  |  sale_expires  |  date         |
-------------------------------------------------------------------------
1   |       1  |    23000  |        1  |    27-08-2021  |   27-04-2020  |
-------------------------------------------------------------------------
2   |       1  |    34000  |        0  |          NULL  |   27-04-2020  |
-------------------------------------------------------------------------
3   |       2  |    27500  |        0  |          NULL  |   28-04-2020  |


Есть 2 модели со связями (Laravel Relationships):
Car:
class Car extends Model
{
    protected $table = 'cars';

    public function priceHistory() {
        return $this->hasMany(Car_price_history::class);
    }
}


Car_price_history:
class Car_price_history extends Model
{
    protected $table = 'cars_prices_history';

    public function car() {
        return $this->belongsTo(Car::class);
    }
}


Контроллер, который обрабатывает запрос роута `api/v1/cars`:
public function index(Request $request, Car $cars)
{
    $cars = Car::with('priceHistory');  // получаю билдер

    if ($request->has('minPrice') && !empty($request->minPrice))
    {
     // здесь должен быть фильтр, который должен через модель Car_price_history
     // сходить к БД, найти все цены, у которых car_id будет равно id-шнику авто (непонятно, откуда
     // брать id авто, если в $cars сидит билдер, а коллекцию я получить не могу до запроса,
     // а даже если получу, то мне придётся запускать foreach для каждого авто и перелопачивать всю коллекцию??
     // или делать это при помощи методов коллекций, но всё равно выглядит странно), взять последнюю 
     // акционную цену, а если акционной нет, то взять последнюю не акционную. И только потом, получив цену, 
     // применить фильтр, который отсеит авто. И такая логика для каждого фильтра.

    $cars->where('price', '>=', $request->minPrice);  // не работающий фильтр
    }

    $cars = $cars->get();  // получаю коллекцию
    return new CarCollection($cars);
}


Мне необходимо добавить фильтры, чтобы отфильтровать все авто по нескольким фильтрам:
1) минимальная цена - minPrice
2) максимальная цена - maxPrice
3) наличие скидки - sale
и другие. Чтобы контроллер обрабатывал роут вида `api/v1/cars?minPrice=1000&maxPrice=21000&sale=1` и применял соответствующие фильтры, а потом отдавал в CarCollection (Eloquent API Resourse) только те результаты, которые прошли через фильтры.


В общем, вообще не понятно, каким образом можно сделать фильтрацию такого рода? Каким образом можно получать последнюю цену и делать фильтр на неё? Или может проще добавить в миграцию авто столбцы price и sale_price? Но у меня помимо cars_prices_history есть ещё таблицы, которые отношениями связаны с моделью Car и добавление столбцов во-первых заставит дублировать данные, а во-вторых, нарушает правила нормализации таблиц, то есть идея странная тоже.

Как сделать фильтры? Может есть вариант попроще, что бы реализовать фильтр по цене? Посоветуйте статейки, видео по этой теме
  • Вопрос задан
  • 268 просмотров
Пригласить эксперта
Ответы на вопрос 1
@NubasLol
Вместо

$cars->where('price', '>=', $request->minPrice);

Это

$cars->whereHas('priceHistory', function ($q) {
            $q->where('price', '>=', $request->minPrice);
        });
Ответ написан
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы