В Laravel не всё взаимодействие с базой данных идёт через Eloquent. Есть три уровня абстракции — от высокого к низкому. Все три используют одно и то же подключение к БД и поддерживают транзакции.
Уровень абстракции:
Высокий [Eloquent ORM] — модели, связи, автоматизация
↓
Средний [Query Builder] — построитель запросов, без моделей
↓
Низкий [Raw SQL] — чистый SQL
1. Eloquent ORM
Основной способ работы с БД в Laravel. Каждая таблица представлена моделью — PHP-классом. Модель умеет сама заполнять поля, отслеживать timestamps, подгружать связи. Это 80–90% повседневного кода.
CRUD через Eloquent
// Создание
$order = Order::create([
'user_id' => 1,
'total_price' => 259.98,
'status' => 'pending',
]);
// Чтение
$order = Order::find(1);
$order = Order::where('status', 'pending')->first();
$orders = Order::where('status', 'paid')->get();
// Обновление
$order->update(['status' => 'paid']);
// Удаление
$order->delete();
Связи (Relations)
Eloquent автоматически подгружает связанные модели. Не нужно писать JOIN вручную:
// Модель
class Order extends Model
{
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
public function items(): HasMany
{
return $this->hasMany(OrderItem::class);
}
}
// Использование
$order = Order::with('items', 'user')->find(1);
$order->user->name; // имя покупателя
$order->items; // коллекция товаров
$order->items->count(); // количество позиций
Scopes — переиспользуемые фильтры
class Order extends Model
{
// Scope — именованный фильтр
public function scopePaid($query)
{
return $query->where('status', 'paid');
}
public function scopeRecent($query)
{
return $query->where('created_at', '>=', now()->subDays(7));
}
}
// Использование — читается как текст
$orders = Order::paid()->recent()->get();
Когда использовать Eloquent
Обычный CRUD, работа с отдельными записями, связи между моделями, валидация, события модели. Всё, где ты оперируешь объектами: «создай заказ», «обнови статус», «получи товары заказа».
2. Query Builder
Построитель запросов через фасад DB. Работает напрямую с таблицами, без моделей. Ближе к SQL, но всё ещё с удобным PHP-синтаксисом. Результат — массивы или объекты stdClass, не модели Eloquent.
Примеры
use Illuminate\Support\Facades\DB;
// Выборка
$orders = DB::table('orders')
->where('status', 'paid')
->orderBy('created_at', 'desc')
->get();
// Агрегация — сумма продаж за март
$revenue = DB::table('orders')
->where('status', 'paid')
->whereMonth('created_at', 3)
->sum('total_price');
// Средний чек по дням
$daily = DB::table('orders')
->select(DB::raw('DATE(created_at) as date'), DB::raw('AVG(total_price) as avg_price'))
->where('status', 'paid')
->groupBy('date')
->get();
// JOIN — топ товаров по продажам
$topProducts = DB::table('order_items')
->join('products', 'products.id', '=', 'order_items.product_id')
->select('products.name', DB::raw('SUM(order_items.quantity) as total_sold'))
->groupBy('products.id', 'products.name')
->orderByDesc('total_sold')
->limit(10)
->get();
// Вставка
DB::table('logs')->insert([
'action' => 'order_created',
'payload' => json_encode($data),
'created_at' => now(),
]);
// Обновление
DB::table('orders')
->where('id', 42)
->update(['status' => 'shipped']);
Когда использовать Query Builder
Отчёты, агрегации, массовые операции, сложные JOIN-ы, работа с таблицами без моделей (логи, временные таблицы). Везде, где не нужны фишки Eloquent (связи, события, мутаторы) и важна производительность или гибкость запроса.
3. Raw SQL
Чистый SQL через методы DB::select(), DB::insert(), DB::update(), DB::delete(), DB::statement(). Для случаев, когда Query Builder не может выразить нужный запрос.
Примеры
use Illuminate\Support\Facades\DB;
// SELECT — параметры через плейсхолдеры (защита от SQL-инъекций)
$results = DB::select('
SELECT products.name, SUM(order_items.quantity) as total_sold
FROM order_items
JOIN products ON products.id = order_items.product_id
WHERE order_items.created_at >= ?
GROUP BY products.id, products.name
HAVING total_sold > ?
ORDER BY total_sold DESC
', [now()->subMonth(), 100]);
// INSERT
DB::insert('INSERT INTO logs (action, created_at) VALUES (?, ?)', [
'manual_import',
now(),
]);
// UPDATE
DB::update('UPDATE orders SET status = ? WHERE created_at < ?', [
'archived',
now()->subYear(),
]);
// Произвольный SQL (CREATE, ALTER, DROP и т.д.)
DB::statement('ALTER TABLE orders ADD COLUMN notes TEXT AFTER status');
Raw-выражения внутри Eloquent и Query Builder
Необязательно полностью переходить на Raw SQL — можно вставить сырое выражение в обычный запрос:
// DB::raw() внутри Query Builder
$orders = DB::table('orders')
->select(DB::raw('YEAR(created_at) as year'), DB::raw('COUNT(*) as count'))
->groupBy('year')
->get();
// selectRaw, whereRaw, orderByRaw — в Eloquent
$orders = Order::selectRaw('MONTH(created_at) as month, COUNT(*) as count')
->whereRaw('total_price > ? AND status = ?', [100, 'paid'])
->groupByRaw('MONTH(created_at)')
->orderByRaw('count DESC')
->get();
Когда использовать Raw SQL
Сложные подзапросы, оконные функции (OVER, PARTITION BY), специфичный синтаксис конкретной СУБД, оптимизация узких мест. На практике — редко, но полезно знать, что можно.
Сравнение
Eloquent Query Builder Raw SQL
──────────────────────────────────────────────────────────────────────
Результат Модели stdClass / массивы stdClass / массивы
Связи Да (with, has) Нет (JOIN вручную) Нет
Timestamps Автоматически Вручную Вручную
События модели Да (creating, Нет Нет
updated и т.д.)
Читаемость Высокая Средняя Зависит от SQL
Производительность Чуть медленнее Быстрее Самый быстрый
Гибкость SQL Ограниченная Высокая Полная
Типичное CRUD, связи, Отчёты, агрегации, Сложные запросы,
применение бизнес-логика массовые операции оптимизация
Аналогия с WordPress
В WordPress похожая градация: WP_Query / get_posts() — это аналог Eloquent (высокий уровень, автоматизация). $wpdb->get_results() с подготовленными запросами — аналог Query Builder. $wpdb->query() с чистым SQL — аналог Raw SQL. Принцип тот же: начинаешь с высокого уровня и опускаешься ниже только когда нужно.