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

Tips & Tricks: статті за тегом «Оптимізація»

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

No results.
Tips 02 липня 2026

Уникати select(*) в продакшені

Завантаження всіх колонок? Витрачається пам'ять та пропускну здатність.

Обирай тільки колонки які реально використовуєш. Менший об'єм даних. Швидші запити. Менше пам'яті.

Прихована ціна:

Текстові колонки можуть бути величезними. JSON колонки ще більші. Timestamps які можливо не потрібні.

Перевага індексу:

Запити з меншою кількістю колонок можуть використовувати covering індекси.

Порада: Профілюй з EXPLAIN щоб побачити чи запит використовує index-only scan.

// ПОГАНО: Завантажує все включаючи великі текстові поля
$posts = Post::where('published', true)->get();

// ДОБРЕ: Тільки потрібні колонки
$posts = Post::select('id', 'title', 'slug', 'published_at')
    ->where('published', true)
    ->get();

// Приклад API відповіді
public function index()
{
    return Post::select([
            'id',
            'title',
            'slug',
            'excerpt',
            'published_at',
        ])
        ->with('author:id,name,avatar')
        ->where('published', true)
        ->paginate(20);
}

// Dropdown/автозаповнення
$users = User::select('id', 'name')
    ->where('active', true)
    ->get();

// Зі зв'язками - вказуй колонки для обох
$posts = Post::select('id', 'title', 'author_id')
    ->with('author:id,name')
    ->get();
Tips 26 червня 2026

remember() - кешування результатів запитів

Той самий запит виконується 100 разів за секунду. База даних потіє.

Закешування результатів. Звернись до бази один раз, обслуговуй з кешу.

Спосіб Laravel:

Використовувати remember() на запитах. Автоматично кешує результати з тегами.

Коли кешувати:

  • Дані змінюються рідко (категорії, налаштування)
  • Дорогі запити (join'и, агрегація)
  • Сторінки з високим трафіком

Інвалідація кешу:

Очищати кеш коли дані змінюються використовуючи події моделі.

Порада: Використовувати короткий час кешування (5-15 хвилин) для даних що змінюються іноді.

// ПОГАНО: Звертається до бази кожен раз
$categories = Category::all();

// ДОБРЕ: Кеш на 1 годину
$categories = Cache::remember('categories', 3600, fn() =>
    Category::all()
);

// Використання query builder
$activeUsers = User::where('active', true)->remember(600)->get(); // 10 хвилин

// Кеш з тегами
$posts = Cache::tags(['posts', 'homepage'])
    ->remember('posts.featured', 3600, function () {
        return Post::where('featured', true)->get();
    });

// Інвалідація при події моделі
class Post extends Model
{
    protected static function boot()
    {
        parent::boot();
        static::saved(function () {
            Cache::tags('posts')->flush();
        });
    }
}
// Вічний кеш (ручна інвалідація)
$settings = Cache::rememberForever('settings', fn() =>
    Setting::pluck('value', 'key')
);

// Ключ кешу з параметрами
$userId = 5;
$posts = Cache::remember("user_{$userId}_posts", 3600,
    fn() => Post::where('user_id', $userId)->get()
);

// Умовне кешування
if (config('cache.enabled')) {
    $data = Cache::remember('key', 3600, fn() => expensiveQuery());
} else {
    $data = expensiveQuery();
}

// Очистити конкретний кеш
Cache::forget('categories');

// Очистити тегований кеш
Cache::tags(['posts'])->flush();

// Перевірити чи закешовано
if (Cache::has('categories')) {
    $categories = Cache::get('categories');
}
Tips 31 березня 2026

exists() vs count() - розумна перевірка

Перевірка чи користувач має пости? Використання $user->posts->count() > 0 завантажує всі пости.

exists() зупиняється на першому збігу. Набагато швидше.

Різниця:

  • count() підраховує всі записи
  • exists() зупиняється коли знаходить перший

Вплив на продуктивність:

З 10,000 постів - count() завантажує всі, exists() знаходить один і зупиняється.

Порада: Використання exists() для булевих перевірок, count() тільки коли потрібне фактичне число.

// ПОГАНО: Завантажує всі пости, рахує їх
if ($user->posts->count() > 0) {
    echo "Користувач має пости";
}

// ДОБРЕ: Зупиняється на першому пості
if ($user->posts()->exists()) {
    echo "Користувач має пости";
}

// Перевірка чи зв'язок порожній
if ($user->posts()->doesntExist()) {
    echo "Пости не знайдено";
}

// Рівень запиту
if (Post::where('published', true)->exists()) {
    // Принаймні один опублікований пост існує
}

// Коли справді потрібна кількість
$postCount = $user->posts()->count();
echo "Користувач має {$postCount} постів";

// Перевірка порожнього на завантаженому зв'язку
if ($user->posts->isEmpty()) {
    // Вже завантажено, без додаткових запитів
}
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 14 березня 2026

Route Caching - миттєвий приріст швидкості

Файл маршрутів завантажується при кожному запиті. З 500+ маршрутами це багато.

Кешування маршрутів компілює маршрути в один закешований файл. В 10 разів швидше.

Як використовувати:

Запустити php artisan route:cache після деплою. Маршрути завантажуються з кешу, а не з файлів.

Важливі обмеження:

Closure маршрути НЕ працюють з кешуванням. Виникнуть помилки. Всі маршрути повинні використовувати синтаксис controller@method.

Найкраща практика:

  1. Ніколи не використовувати closures в маршрутах - завжди контролери
  2. Кешувати маршрути тільки в продакшені
  3. Очищати кеш коли додаються нові маршрути
  4. Додати до скрипту деплою

Порада: Поєднувати з config:cache та view:cache для максимальної продуктивності.

// ПОГАНО: Не працює з route:cache
Route::get('/users', function () {
    return User::all();
});

// ДОБРЕ: Працює з кешуванням
Route::get('/users', [UserController::class, 'index']);

// Команди деплою
php artisan config:cache  // Кешувати конфіг
php artisan route:cache   // Кешувати маршрути
php artisan view:cache    // Кешувати view

// Розробка - очистити всі кеші
php artisan optimize:clear

// Перевірити що маршрути закешовані
php artisan route:list
// Має завантажуватись миттєво якщо закешовано

// В composer.json - авто-кеш після деплою
"scripts": {
    "post-autoload-dump": [
        "@php artisan route:cache"
    ]
}