Увійти Реєстрація
Блог Серії
Кар'єра
Вакансії Компанії
Навчання
Документація Співбесіди Тестування Відео
Екосистема
Пакети Ресурси Проєкти
Інше
Події Про нас

Статті за тегом «База даних»

Статті, новини, туторіали та переклади від учасників спільноти

No results.
Tips 04 липня 2026

Оптимізація з Database Views

Складні join'и повторюються скрізь?

Створи database view. Запитуй його як таблицю.

Переваги:

  • Інкапсулює складну логіку
  • Повторне використання в додатку
  • База даних оптимізує view
  • Чистіший код додатку

Компроміс:

Views тільки для читання. Потрібні міграції схеми для змін.

Порада: Ідеально для запитів звітності та дашбордів.

// Створити міграцію для view
public function up()
{
    DB::statement("
        CREATE VIEW user_stats AS
        SELECT
            users.id,
            users.name,
            users.email,
            COUNT(DISTINCT orders.id) as total_orders,
            SUM(orders.total) as total_spent,
            MAX(orders.created_at) as last_order_date
        FROM users
        LEFT JOIN orders ON users.id = orders.user_id
        GROUP BY users.id, users.name, users.email
    ");
}

public function down()
{
    DB::statement("DROP VIEW IF EXISTS user_stats");
}

// Створити модель для view
namespace App\Models;

class UserStats extends Model
{
    protected $table = 'user_stats';
    public $timestamps = false;
    public $incrementing = false;
}
// Використовувати view як звичайну модель
$topCustomers = UserStats::where('total_orders', '>', 10)
    ->orderBy('total_spent', 'desc')
    ->limit(100)
    ->get();

// В контролері
public function dashboard()
{
    $stats = UserStats::where('last_order_date', '>=', now()->subDays(30))
        ->orderBy('total_spent', 'desc')
        ->paginate(50);

    return view('admin.customers', compact('stats'));
}

// Складний view звітності
public function up()
{
    DB::statement("
        CREATE VIEW monthly_revenue AS
        SELECT
            DATE_FORMAT(created_at, '%Y-%m') as month,
            COUNT(*) as order_count,
            SUM(total) as revenue,
            AVG(total) as avg_order_value,
            COUNT(DISTINCT user_id) as unique_customers
        FROM orders
        WHERE status = 'completed'
        GROUP BY DATE_FORMAT(created_at, '%Y-%m')
    ");
}
Tips 27 червня 2026

DB::raw() - коли Eloquent недостатньо

Потрібен складний SQL? Функції дат? Математичні операції?

Eloquent має межі. DB::raw() дозволяє писати сирий SQL всередині запитів.

Поширені випадки використання:

  • Функції дати/часу
  • Математичні операції
  • Специфічні для бази даних функції
  • Складна агрегація

Попередження безпеки:

Ніколи не передавай вхідні дані користувача безпосередньо в raw(). Використовувати біндінги.

Порада: Розглянь власні query scopes щоб повторно використовувати складні raw вирази.

// Функції дат
$users = User::where('created_at', '>=',
    DB::raw('DATE_SUB(NOW(), INTERVAL 30 DAY)')
)->get();

// Математичні операції
$products = Product::select('*',
    DB::raw('price * quantity as total')
)->get();

// CASE вирази
$posts = Post::select('*',
    DB::raw('CASE WHEN views > 1000 THEN "popular"
             ELSE "regular" END as status')
)->get();

// Агрегація з групуванням
$stats = Order::select(
    DB::raw('DATE(created_at) as date'),
    DB::raw('SUM(total) as revenue'),
    DB::raw('COUNT(*) as orders')
)
->groupBy('date')
->get();

// БЕЗПЕЧНО: Використання біндінгів
$status = 'published';
Post::whereRaw('LOWER(status) = ?', [strtolower($status)])->get();
Tips 27 березня 2026

whereIn() з великими масивами - пастка пам'яті

Передавання 10,000 ID в whereIn()? Рядок запиту стає величезним.

MySQL має ліміти розміру запиту. PHP використовує тонни пам'яті.

Кращий підхід:

Використовувати підзапит або тимчасову таблицю для великих наборів даних.

Продуктивність:

  • whereIn() з 100 елементами - нормально
  • whereIn() з 10,000 елементами - проблема
  • Підзапит - константна пам'ять

Порада: Якщо масив має більше 1000 елементів, розглянь використання підзапиту або chunking.

// ПОГАНО: Великий масив в whereIn
$userIds = range(1, 10000); // 10k ID
$users = User::whereIn('id', $userIds)->get();
// Величезний запит, високе використання пам'яті

// ДОБРЕ: Використовувати підзапит
$activeUserIds = DB::table('user_activity')
    ->where('last_login', '>', now()->subDays(30))
    ->select('user_id');

$users = User::whereIn('id', $activeUserIds)->get();

// Альтернатива: Join
$users = User::join('user_activity', 'users.id', '=',
    'user_activity.user_id')
    ->where('user_activity.last_login', '>', now()->subDays(30))
    ->select('users.*')
    ->get();

// Розділяй великі операції
$userIds = range(1, 10000);
collect($userIds)->chunk(500)->each(function ($chunk) {
    User::whereIn('id', $chunk)->update(['notified' => true]);
});
Tips 10 березня 2026

DB::transaction() - захист даних

Переказ $100 між рахунками. Гроші списані з Аліси. Сервер падає. Боб їх ніколи не отримує.

Без транзакцій часткові оновлення пошкодять дані.

Як це працює:

Всі запити виконуються як одна атомарна одиниця. Якщо щось не вдається - все відкочується назад. База даних ніколи не опиняється в неузгодженому стані.

Три способи використання:

  1. Closure (рекомендовано) - автоматичний rollback при винятках
  2. Ручний begin/commit - коли потрібен точний контроль
  3. З повторами - автоматична обробка deadlock

Важливо:

Транзакції працюють тільки з InnoDB таблицями. Тримати їх короткими - вони блокують рядки. Довгі операції блокують інші запити.

Не змішувати виклики зовнішніх API з транзакціями бази даних.

// ПОГАНО: Часткове збереження при помилці
$user = User::create($data);
$profile = Profile::create(['user_id' => $user->id]);
// Якщо це не вдається, користувач існує без профілю

// ДОБРЕ: Все або нічого
DB::transaction(function () use ($data) {
    $user = User::create($data);
    $profile = Profile::create(['user_id' => $user->id]);
    $user->sendWelcomeEmail();
});

// Ручний контроль
DB::beginTransaction();
try {
    $order = Order::create($orderData);
    $payment = Payment::create($paymentData);
    DB::commit();
} catch (\Exception $e) {
    DB::rollBack();
    throw $e;
}

// З повтором при deadlock
DB::transaction(function () {
    // Код
}, 3); // Повторити 3 рази якщо deadlock