Avatar ·

Как создать запрос с несколькими условиями WHERE в Laravel Eloquent?

Привет всем! Я новичок в Laravel и пытаюсь разобраться с построением сложных запросов к базе данных. У меня есть таблица `products`, и мне нужно выбрать товары по нескольким условиям одновременно. Например, я хочу найти все товары, у которых `status = 'active'`, `price > 100` и `category_id = 5`. В обычном SQL я бы написал что-то вроде: ```sql SELECT * FROM products WHERE status = 'active' AND price > 100 AND category_id = 5; ``` Но как это сделать с помощью Eloquent? Я пробовал такой код: ```php $products = Product::where('status', 'active') ->where('price', '>', 100) ->where('category_id', 5) ->get(); ``` Это работает, но мне кажется, что это не совсем правильно, особенно когда условий становится много. Ещё я слышал про `orWhere` и про группировку условий, но не понимаю, как это работает. Подскажите, как правильно строить запросы с несколькими WHERE, чтобы код был читаемым и гибким? И можно ли передавать условия в виде массива? Заранее спасибо за помощь! <<>>

Основы: цепочка where()

То, что вы написали, абсолютно правильно для простых AND-условий. Laravel Eloquent позволяет вызывать where() несколько раз подряд, и все они будут объединены через AND. Ваш код:

$products = Product::where('status', 'active')
                  ->where('price', '>', 100)
                  ->where('category_id', 5)
                  ->get();

Эквивалентен SQL: WHERE status = 'active' AND price > 100 AND category_id = 5. Это самый распространённый и читаемый способ.

Передача массива условий

Если у вас много условий, можно передать их одним массивом в where():

$conditions = [
    ['status', '=', 'active'],
    ['price', '>', 100],
    ['category_id', '=', 5],
];

$products = Product::where($conditions)->get();

Каждый элемент массива — это [колонка, оператор, значение]. Если оператор не указан, по умолчанию используется '='. Это удобно, когда условия приходят из формы или конфигурации.

Группировка условий (OR и вложенные WHERE)

Для сложной логики (например, (A AND B) OR (C AND D)) используйте замыкания:

$products = Product::where(function ($query) {
                        $query->where('status', 'active')
                              ->where('price', '>', 100);
                    })
                    ->orWhere(function ($query) {
                        $query->where('status', 'pending')
                              ->where('price', '<', 50);
                    })
                    ->get();

Это даст SQL: WHERE (status = 'active' AND price > 100) OR (status = 'pending' AND price < 50).

Условные where()

Если условие применяется только при определённом значении (например, из запроса), используйте when():

$categoryId = request('category_id'); // может быть null
$minPrice = request('min_price');

$products = Product::when($categoryId, function ($query, $catId) {
                        return $query->where('category_id', $catId);
                    })
                    ->when($minPrice, function ($query, $min) {
                        return $query->where('price', '>=', $min);
                    })
                    ->get();

Метод when() выполнит замыкание только если первый аргумент истинен. Это избавляет от громоздких if-блоков.

whereIn, whereBetween и другие

Для специфических условий есть специальные методы:

  • whereIn('category_id', [1, 3, 5]) — проверка вхождения в массив.
  • whereBetween('price', [50, 200]) — диапазон значений.
  • whereNull('deleted_at') — проверка на NULL.
  • whereDate('created_at', '2023-01-01') — сравнение по дате.

Совет по читаемости

Для длинных запросов разбивайте цепочку на строки и используйте отступы. Если запрос становится слишком сложным, вынесите его в локальные скоупы модели:

class Product extends Model
{
    public function scopeActive($query)
    {
        return $query->where('status', 'active');
    }

    public function scopeCheaperThan($query, $price)
    {
        return $query->where('price', '<', $price);
    }
}

// Использование:
$products = Product::active()->cheaperThan(200)->get();

Это делает код самодокументируемым и переиспользуемым.

Итог: ваш первый пример корректен. Для простых AND-условий достаточно цепочки where(). Для сложной логики используйте замыкания, when() или скоупы. Массив условий удобен для динамических фильтров.

<<>>

Альтернатива: использование Query Builder напрямую

Иногда удобнее работать через фасад DB, особенно если запрос не привязан к конкретной модели или требует сложных JOIN:

use Illuminate\Support\Facades\DB;

$products = DB::table('products')
    ->where('status', 'active')
    ->where(function ($query) {
        $query->where('price', '>', 100)
              ->orWhere('discount', '>', 0);
    })
    ->whereIn('category_id', [1, 3, 5])
    ->get();

Это даёт SQL: WHERE status = 'active' AND (price > 100 OR discount > 0) AND category_id IN (1,3,5).

Динамические условия через when() с массивом

Если у вас много фильтров из формы, можно комбинировать when() с массивом условий для большей гибкости:

$filters = [
    'status' => request('status'),
    'min_price' => request('min_price'),
    'max_price' => request('max_price'),
];

$products = Product::query()
    ->when($filters['status'], fn($q, $v) => $q->where('status', $v))
    ->when($filters['min_price'], fn($q, $v) => $q->where('price', '>=', $v))
    ->when($filters['max_price'], fn($q, $v) => $q->where('price', '<=', $v))
    ->get();

Этот подход особенно полезен для API с фильтрацией, так как позволяет легко добавлять или убирать условия без изменения структуры запроса.